From b7e930eecf1f5495e50ddb779b92b507936aa3e1 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 20 Oct 2016 12:11:02 -0700 Subject: [PATCH 001/398] Add QGC version info to bottom of Settings/General This makes it available on mobile builds --- src/QmlControls/QGroundControlQmlGlobal.h | 4 ++++ src/ui/MainWindow.cc | 23 +---------------------- src/ui/preferences/GeneralSettings.qml | 5 +++++ 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 691802184..0065962f1 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -111,6 +111,8 @@ public: Q_PROPERTY(QString appSettingsDistanceUnitsString READ appSettingsDistanceUnitsString CONSTANT) Q_PROPERTY(QString appSettingsAreaUnitsString READ appSettingsAreaUnitsString CONSTANT) + Q_PROPERTY(QString qgcVersion READ qgcVersion CONSTANT) + Q_INVOKABLE void saveGlobalSetting (const QString& key, const QString& value); Q_INVOKABLE QString loadGlobalSetting (const QString& key, const QString& defaultValue); Q_INVOKABLE void saveBoolGlobalSetting (const QString& key, bool value); @@ -209,6 +211,8 @@ public: QString missionFileExtension(void) const { return QGCApplication::missionFileExtension; } QString telemetryFileExtension(void) const { return QGCApplication::telemetryFileExtension; } + QString qgcVersion(void) const { return qgcApp()->applicationVersion(); } + // Overrides from QGCTool virtual void setToolbox(QGCToolbox* toolbox); diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 4435feddb..6cd378e1f 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -452,28 +452,7 @@ void MainWindow::storeSettings() void MainWindow::configureWindowName() { - QList hostAddresses = QNetworkInterface::allAddresses(); - QString windowname = qApp->applicationName() + " " + qApp->applicationVersion(); - - // XXX we do have UDP MAVLink heartbeat broadcast now in SITL and will have it on the - // WIFI radio, so people should not be in need any more of knowing their IP. - // this can go once we are certain its not needed any more. - #if 0 - bool prevAddr = false; - windowname.append(" (" + QHostInfo::localHostName() + ": "); - for (int i = 0; i < hostAddresses.size(); i++) - { - // Exclude loopback IPv4 and all IPv6 addresses - if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":")) - { - if(prevAddr) windowname.append("/"); - windowname.append(hostAddresses.at(i).toString()); - prevAddr = true; - } - } - windowname.append(")"); - #endif - setWindowTitle(windowname); + setWindowTitle(qApp->applicationName() + " " + qApp->applicationVersion()); } /** diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index d3d85307e..324c5af6a 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -575,6 +575,11 @@ QGCView { } } } + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("QGroundControl Version: " + QGroundControl.qgcVersion) + } } // settingsColumn } // QGCFlickable } // QGCViewPanel -- GitLab From dfbaf7f28031703de6861ff5e3d753988ce3df5a Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 16 Oct 2016 23:06:45 -0400 Subject: [PATCH 002/398] Core Mavlink log handler. --- qgroundcontrol.pro | 2 + src/QGCToolbox.cc | 5 + src/QGCToolbox.h | 4 + src/QmlControls/QGroundControlQmlGlobal.cc | 4 +- src/QmlControls/QGroundControlQmlGlobal.h | 3 + src/uas/MavlinkLogManager.cc | 349 ++++++++++++++++++++ src/uas/MavlinkLogManager.h | 130 ++++++++ src/ui/preferences/MavlinkSettings.qml | 358 +++++++++++++++++++-- 8 files changed, 825 insertions(+), 30 deletions(-) create mode 100644 src/uas/MavlinkLogManager.cc create mode 100644 src/uas/MavlinkLogManager.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 9dfad863f..c8adcb54b 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 5c63154be..fd569b7b5 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 540c917f3..1b0080de5 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 abd618bc3..c249191bd 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 691802184..b23823f44 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 000000000..2b45f8a23 --- /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 000000000..de41bc1ef --- /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 37ac33284..9f0cf1e40 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() + } + } + } + } } } } -- GitLab From 40ec8070073f761122831784ba29e6e0ab25be79 Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Fri, 21 Oct 2016 14:55:46 -0700 Subject: [PATCH 003/398] Update ArduSub mocklink parameter file. --- src/comm/APMArduSubMockLink.params | 1221 ++++++++++++++-------------- 1 file changed, 618 insertions(+), 603 deletions(-) mode change 100755 => 100644 src/comm/APMArduSubMockLink.params diff --git a/src/comm/APMArduSubMockLink.params b/src/comm/APMArduSubMockLink.params old mode 100755 new mode 100644 index 8c935cf03..6866b517e --- a/src/comm/APMArduSubMockLink.params +++ b/src/comm/APMArduSubMockLink.params @@ -1,605 +1,620 @@ -# Onboard parameters for vehicle 128 +# Onboard parameters for vehicle 1 # # MAV ID COMPONENT ID PARAM NAME VALUE (FLOAT) -128 1 ACCEL_Z_D 0.000000000000000000 9 -128 1 ACCEL_Z_FILT 20.000000000000000000 9 -128 1 ACCEL_Z_I 1.000000000000000000 9 -128 1 ACCEL_Z_IMAX 800.000000000000000000 9 -128 1 ACCEL_Z_P 0.500000000000000000 9 -128 1 ACRO_BAL_PITCH 1.000000000000000000 9 -128 1 ACRO_BAL_ROLL 1.000000000000000000 9 -128 1 ACRO_EXPO 0.300000011920928955 9 -128 1 ACRO_RP_P 4.500000000000000000 9 -128 1 ACRO_TRAINER 2 2 -128 1 ACRO_YAW_P 4.500000000000000000 9 -128 1 AHRS_COMP_BETA 0.100000001490116119 9 -128 1 AHRS_EKF_TYPE 2 2 -128 1 AHRS_GPS_GAIN 1.000000000000000000 9 -128 1 AHRS_GPS_MINSATS 6 2 -128 1 AHRS_GPS_USE 1 2 -128 1 AHRS_ORIENTATION 16 2 -128 1 AHRS_RP_P 0.200000002980232239 9 -128 1 AHRS_TRIM_X -0.017862198874354362 9 -128 1 AHRS_TRIM_Y -0.001083165174350142 9 -128 1 AHRS_TRIM_Z 0.000000000000000000 9 -128 1 AHRS_WIND_MAX 0 2 -128 1 AHRS_YAW_P 0.200000002980232239 9 -128 1 ANGLE_MAX 4500 4 -128 1 ARMING_CHECK 0 2 -128 1 ATC_ACCEL_P_MAX 110000.000000000000000000 9 -128 1 ATC_ACCEL_R_MAX 110000.000000000000000000 9 -128 1 ATC_ACCEL_Y_MAX 0.000000000000000000 9 -128 1 ATC_ANGLE_BOOST 1 2 -128 1 ATC_ANG_LIM_TC 1.000000000000000000 9 -128 1 ATC_ANG_PIT_P 6.000000000000000000 9 -128 1 ATC_ANG_RLL_P 6.000000000000000000 9 -128 1 ATC_ANG_YAW_P 6.000000000000000000 9 -128 1 ATC_RATE_FF_ENAB 1 2 -128 1 ATC_RAT_PIT_D 0.003599999938160181 9 -128 1 ATC_RAT_PIT_FILT 20.000000000000000000 9 -128 1 ATC_RAT_PIT_I 0.090000003576278687 9 -128 1 ATC_RAT_PIT_IMAX 0.444000005722045898 9 -128 1 ATC_RAT_PIT_P 0.135000005364418030 9 -128 1 ATC_RAT_RLL_D 0.000000000000000000 9 -128 1 ATC_RAT_RLL_FILT 20.000000000000000000 9 -128 1 ATC_RAT_RLL_I 0.000000000000000000 9 -128 1 ATC_RAT_RLL_IMAX 0.444000005722045898 9 -128 1 ATC_RAT_RLL_P 0.000000000000000000 9 -128 1 ATC_RAT_YAW_D 0.000000000000000000 9 -128 1 ATC_RAT_YAW_FILT 30.000000000000000000 9 -128 1 ATC_RAT_YAW_I 0.019999999552965164 9 -128 1 ATC_RAT_YAW_IMAX 0.222000002861022949 9 -128 1 ATC_RAT_YAW_P 0.180000007152557373 9 -128 1 ATC_SLEW_YAW 6000.000000000000000000 9 -128 1 ATC_THR_MIX_MAX 0.500000000000000000 9 -128 1 ATC_THR_MIX_MIN 0.100000001490116119 9 -128 1 AUTOTUNE_AGGR 0.100000001490116119 9 -128 1 AUTOTUNE_AXES 7 2 -128 1 AUTOTUNE_MIN_D 0.001000000047497451 9 -128 1 AVOID_ENABLE 1 2 -128 1 BATT2_AMP_OFFSET 0.000000000000000000 9 -128 1 BATT2_AMP_PERVOL 17.000000000000000000 9 -128 1 BATT2_CAPACITY 3300 6 -128 1 BATT2_CURR_PIN 3 2 -128 1 BATT2_MONITOR 0 2 -128 1 BATT2_VOLT_MULT 10.100000381469726562 9 -128 1 BATT2_VOLT_PIN 2 2 -128 1 BATT_AMP_OFFSET 0.000000000000000000 9 -128 1 BATT_AMP_PERVOLT 17.000000000000000000 9 -128 1 BATT_CAPACITY 18000 6 -128 1 BATT_CURR_PIN 3 2 -128 1 BATT_MONITOR 4 2 -128 1 BATT_VOLT_MULT 10.100000381469726562 9 -128 1 BATT_VOLT_PIN 2 2 -128 1 BRD_CAN_ENABLE 0 2 -128 1 BRD_IMU_TARGTEMP 0 2 -128 1 BRD_PWM_COUNT 4 2 -128 1 BRD_SAFETYENABLE 0 2 -128 1 BRD_SAFETY_MASK 0 6 -128 1 BRD_SBUS_OUT 0 2 -128 1 BRD_SER1_RTSCTS 2 2 -128 1 BRD_SER2_RTSCTS 2 2 -128 1 BRD_SERIAL_NUM 0 4 -128 1 BTN0_FUNCTION 0 2 -128 1 BTN0_SFUNCTION 0 2 -128 1 BTN10_FUNCTION 48 2 -128 1 BTN10_SFUNCTION 0 2 -128 1 BTN11_FUNCTION 42 2 -128 1 BTN11_SFUNCTION 0 2 -128 1 BTN12_FUNCTION 43 2 -128 1 BTN12_SFUNCTION 0 2 -128 1 BTN13_FUNCTION 33 2 -128 1 BTN13_SFUNCTION 45 2 -128 1 BTN14_FUNCTION 32 2 -128 1 BTN14_SFUNCTION 44 2 -128 1 BTN15_FUNCTION 0 2 -128 1 BTN15_SFUNCTION 0 2 -128 1 BTN1_FUNCTION 6 2 -128 1 BTN1_SFUNCTION 0 2 -128 1 BTN2_FUNCTION 8 2 -128 1 BTN2_SFUNCTION 0 2 -128 1 BTN3_FUNCTION 7 2 -128 1 BTN3_SFUNCTION 0 2 -128 1 BTN4_FUNCTION 22 2 -128 1 BTN4_SFUNCTION 0 2 -128 1 BTN5_FUNCTION 23 2 -128 1 BTN5_SFUNCTION 0 2 -128 1 BTN6_FUNCTION 4 2 -128 1 BTN6_SFUNCTION 0 2 -128 1 BTN7_FUNCTION 3 2 -128 1 BTN7_SFUNCTION 0 2 -128 1 BTN8_FUNCTION 1 2 -128 1 BTN8_SFUNCTION 0 2 -128 1 BTN9_FUNCTION 21 2 -128 1 BTN9_SFUNCTION 0 2 -128 1 CAM_DURATION 10 2 -128 1 CAM_FEEDBACK_PIN 0 2 -128 1 CAM_FEEDBACK_POL 1 2 -128 1 CAM_MAX_ROLL 0 4 -128 1 CAM_MIN_INTERVAL 0 4 -128 1 CAM_RELAY_ON 1 2 -128 1 CAM_SERVO_OFF 1100 4 -128 1 CAM_SERVO_ON 1300 4 -128 1 CAM_TRIGG_DIST 0.000000000000000000 9 -128 1 CAM_TRIGG_TYPE 0 2 -128 1 CH10_OPT 0 2 -128 1 CH11_OPT 0 2 -128 1 CH12_OPT 0 2 -128 1 CH7_OPT 0 2 -128 1 CH8_OPT 0 2 -128 1 CH9_OPT 0 2 -128 1 CIRCLE_RADIUS 1000.000000000000000000 9 -128 1 CIRCLE_RATE 20.000000000000000000 9 -128 1 CLI_ENABLED 0 2 -128 1 COMPASS_AUTODEC 1 2 -128 1 COMPASS_CAL_FIT 8.000000000000000000 9 -128 1 COMPASS_DEC 0.000000000000000000 9 -128 1 COMPASS_DEV_ID 131594 6 -128 1 COMPASS_DEV_ID2 0 6 -128 1 COMPASS_DEV_ID3 0 6 -128 1 COMPASS_DIA2_X 0.000000000000000000 9 -128 1 COMPASS_DIA2_Y 0.000000000000000000 9 -128 1 COMPASS_DIA2_Z 0.000000000000000000 9 -128 1 COMPASS_DIA3_X 0.000000000000000000 9 -128 1 COMPASS_DIA3_Y 0.000000000000000000 9 -128 1 COMPASS_DIA3_Z 0.000000000000000000 9 -128 1 COMPASS_DIA_X 1.000000000000000000 9 -128 1 COMPASS_DIA_Y 1.000000000000000000 9 -128 1 COMPASS_DIA_Z 1.000000000000000000 9 -128 1 COMPASS_EXTERN2 0 2 -128 1 COMPASS_EXTERN3 0 2 -128 1 COMPASS_EXTERNAL 0 2 -128 1 COMPASS_LEARN 1 2 -128 1 COMPASS_MOT2_X 0.000000000000000000 9 -128 1 COMPASS_MOT2_Y 0.000000000000000000 9 -128 1 COMPASS_MOT2_Z 0.000000000000000000 9 -128 1 COMPASS_MOT3_X 0.000000000000000000 9 -128 1 COMPASS_MOT3_Y 0.000000000000000000 9 -128 1 COMPASS_MOT3_Z 0.000000000000000000 9 -128 1 COMPASS_MOTCT 0 2 -128 1 COMPASS_MOT_X 0.000000000000000000 9 -128 1 COMPASS_MOT_Y 0.000000000000000000 9 -128 1 COMPASS_MOT_Z 0.000000000000000000 9 -128 1 COMPASS_ODI2_X 0.000000000000000000 9 -128 1 COMPASS_ODI2_Y 0.000000000000000000 9 -128 1 COMPASS_ODI2_Z 0.000000000000000000 9 -128 1 COMPASS_ODI3_X 0.000000000000000000 9 -128 1 COMPASS_ODI3_Y 0.000000000000000000 9 -128 1 COMPASS_ODI3_Z 0.000000000000000000 9 -128 1 COMPASS_ODI_X 0.000000000000000000 9 -128 1 COMPASS_ODI_Y 0.000000000000000000 9 -128 1 COMPASS_ODI_Z 0.000000000000000000 9 -128 1 COMPASS_OFS2_X 0.000000000000000000 9 -128 1 COMPASS_OFS2_Y 0.000000000000000000 9 -128 1 COMPASS_OFS2_Z 0.000000000000000000 9 -128 1 COMPASS_OFS3_X 0.000000000000000000 9 -128 1 COMPASS_OFS3_Y 0.000000000000000000 9 -128 1 COMPASS_OFS3_Z 0.000000000000000000 9 -128 1 COMPASS_OFS_X -68.000000000000000000 9 -128 1 COMPASS_OFS_Y 166.000000000000000000 9 -128 1 COMPASS_OFS_Z -435.000000000000000000 9 -128 1 COMPASS_ORIENT 0 2 -128 1 COMPASS_ORIENT2 0 2 -128 1 COMPASS_ORIENT3 0 2 -128 1 COMPASS_PRIMARY 0 2 -128 1 COMPASS_USE 1 2 -128 1 COMPASS_USE2 1 2 -128 1 COMPASS_USE3 1 2 -128 1 DISARM_DELAY 0 2 -128 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 -128 1 EK2_ACC_P_NSE 0.600000023841857910 9 -128 1 EK2_ALT_M_NSE 0.100000001490116119 9 -128 1 EK2_ALT_SOURCE 0 2 -128 1 EK2_CHECK_SCALE 100 4 -128 1 EK2_EAS_I_GATE 400 4 -128 1 EK2_EAS_M_NSE 1.399999976158142090 9 -128 1 EK2_ENABLE 1 2 -128 1 EK2_FLOW_DELAY 10 2 -128 1 EK2_FLOW_I_GATE 300 4 -128 1 EK2_FLOW_M_NSE 0.250000000000000000 9 -128 1 EK2_GBIAS_P_NSE 0.000099999997473788 9 -128 1 EK2_GLITCH_RAD 25 2 -128 1 EK2_GPS_CHECK 31 2 -128 1 EK2_GPS_DELAY 220 4 -128 1 EK2_GPS_TYPE 0 2 -128 1 EK2_GSCL_P_NSE 0.000500000023748726 9 -128 1 EK2_GYRO_P_NSE 0.029999999329447746 9 -128 1 EK2_HGT_DELAY 60 4 -128 1 EK2_HGT_I_GATE 500 4 -128 1 EK2_IMU_MASK 3 2 -128 1 EK2_LOG_MASK 1 2 -128 1 EK2_MAGB_P_NSE 0.000099999997473788 9 -128 1 EK2_MAGE_P_NSE 0.001000000047497451 9 -128 1 EK2_MAG_CAL 3 2 -128 1 EK2_MAG_I_GATE 300 4 -128 1 EK2_MAG_M_NSE 0.050000000745058060 9 -128 1 EK2_MAX_FLOW 2.500000000000000000 9 -128 1 EK2_NOAID_M_NSE 10.000000000000000000 9 -128 1 EK2_POSNE_M_NSE 1.000000000000000000 9 -128 1 EK2_POS_I_GATE 500 4 -128 1 EK2_RNG_I_GATE 500 4 -128 1 EK2_RNG_M_NSE 0.500000000000000000 9 -128 1 EK2_TAU_OUTPUT 25 2 -128 1 EK2_VELD_M_NSE 0.699999988079071045 9 -128 1 EK2_VELNE_M_NSE 0.500000000000000000 9 -128 1 EK2_VEL_I_GATE 500 4 -128 1 EK2_WIND_PSCALE 0.500000000000000000 9 -128 1 EK2_WIND_P_NSE 0.100000001490116119 9 -128 1 EK2_YAW_I_GATE 300 4 -128 1 EK2_YAW_M_NSE 0.500000000000000000 9 -128 1 EKF_ENABLE 0 2 -128 1 EPM_ENABLE 0 2 -128 1 EPM_GRAB 1900 4 -128 1 EPM_NEUTRAL 1500 4 -128 1 EPM_REGRAB 0 2 -128 1 EPM_RELEASE 1100 4 -128 1 ESC_CALIBRATION 0 2 -128 1 FENCE_ACTION 1 2 -128 1 FENCE_ALT_MAX 100.000000000000000000 9 -128 1 FENCE_DEPTH_MAX -10.000000000000000000 9 -128 1 FENCE_ENABLE 0 2 -128 1 FENCE_MARGIN 2.000000000000000000 9 -128 1 FENCE_RADIUS 300.000000000000000000 9 -128 1 FENCE_TOTAL 0 2 -128 1 FENCE_TYPE 7 2 -128 1 FLOW_ENABLE 0 2 -128 1 FLOW_FXSCALER 0 4 -128 1 FLOW_FYSCALER 0 4 -128 1 FLOW_ORIENT_YAW 0 4 -128 1 FLTMODE1 19 2 -128 1 FLTMODE2 0 2 -128 1 FLTMODE3 2 2 -128 1 FLTMODE4 0 2 -128 1 FLTMODE5 0 2 -128 1 FLTMODE6 0 2 -128 1 FRAME 1 2 -128 1 FS_BATT_ENABLE 0 2 -128 1 FS_BATT_MAH 0.000000000000000000 9 -128 1 FS_BATT_VOLTAGE 10.500000000000000000 9 -128 1 FS_CRASH_CHECK 0 2 -128 1 FS_EKF_ACTION 1 2 -128 1 FS_EKF_THRESH 0.800000011920928955 9 -128 1 FS_GCS_ENABLE 1 2 -128 1 FS_THR_ENABLE 0 2 -128 1 FS_THR_VALUE 975 4 -128 1 GCS_PID_MASK 0 4 -128 1 GND_ABS_PRESS 100721.140625000000000000 9 -128 1 GND_ALT_OFFSET 0.000000000000000000 9 -128 1 GND_BASE_PRESS 98975.476562500000000000 9 -128 1 GND_BASE_RESET 0 2 -128 1 GND_PRIMARY 1 2 -128 1 GND_SPEC_GRAV 1.000000000000000000 9 -128 1 GND_TEMP 26.966283798217773438 9 -128 1 GPS_AUTO_CONFIG 1 2 -128 1 GPS_AUTO_SWITCH 1 2 -128 1 GPS_GNSS_MODE 0 2 -128 1 GPS_GNSS_MODE2 0 2 -128 1 GPS_HDOP_GOOD 140 4 -128 1 GPS_INJECT_TO 127 2 -128 1 GPS_MIN_DGPS 100 2 -128 1 GPS_MIN_ELEV 0 2 -128 1 GPS_NAVFILTER 8 2 -128 1 GPS_RAW_DATA 0 2 -128 1 GPS_SAVE_CFG 0 2 -128 1 GPS_SBAS_MODE 2 2 -128 1 GPS_SBP_LOGMASK -256 4 -128 1 GPS_TYPE 1 2 -128 1 GPS_TYPE2 0 2 -128 1 INS_ACC2OFFS_X 0.663968503475189209 9 -128 1 INS_ACC2OFFS_Y 0.732862293720245361 9 -128 1 INS_ACC2OFFS_Z 0.392130941152572632 9 -128 1 INS_ACC2SCAL_X 1.044255375862121582 9 -128 1 INS_ACC2SCAL_Y 1.021702170372009277 9 -128 1 INS_ACC2SCAL_Z 1.012331962585449219 9 -128 1 INS_ACC3OFFS_X 0.000000000000000000 9 -128 1 INS_ACC3OFFS_Y 0.000000000000000000 9 -128 1 INS_ACC3OFFS_Z 0.000000000000000000 9 -128 1 INS_ACC3SCAL_X 0.000000000000000000 9 -128 1 INS_ACC3SCAL_Y 0.000000000000000000 9 -128 1 INS_ACC3SCAL_Z 0.000000000000000000 9 -128 1 INS_ACCEL_FILTER 20 2 -128 1 INS_ACCOFFS_X -0.093796379864215851 9 -128 1 INS_ACCOFFS_Y -0.100834839046001434 9 -128 1 INS_ACCOFFS_Z 0.410744011402130127 9 -128 1 INS_ACCSCAL_X 0.995854496955871582 9 -128 1 INS_ACCSCAL_Y 1.006878376007080078 9 -128 1 INS_ACCSCAL_Z 0.992123425006866455 9 -128 1 INS_ACC_BODYFIX 2 2 -128 1 INS_GYR2OFFS_X 0.027385253459215164 9 -128 1 INS_GYR2OFFS_Y 0.030301291495561600 9 -128 1 INS_GYR2OFFS_Z -0.016768395900726318 9 -128 1 INS_GYR3OFFS_X 0.000000000000000000 9 -128 1 INS_GYR3OFFS_Y 0.000000000000000000 9 -128 1 INS_GYR3OFFS_Z 0.000000000000000000 9 -128 1 INS_GYROFFS_X 0.015457602217793465 9 -128 1 INS_GYROFFS_Y 0.038951616734266281 9 -128 1 INS_GYROFFS_Z -0.008368885144591331 9 -128 1 INS_GYRO_FILTER 20 2 -128 1 INS_GYR_CAL 1 2 -128 1 INS_PRODUCT_ID 5 4 -128 1 INS_STILL_THRESH 0.100000001490116119 9 -128 1 INS_TRIM_OPTION 1 2 -128 1 INS_USE 1 2 -128 1 INS_USE2 1 2 -128 1 INS_USE3 0 2 -128 1 LAND_REPOSITION 1 2 -128 1 LAND_SPEED 50 4 -128 1 LAND_SPEED_HIGH 0 4 -128 1 LGR_SERVO_DEPLOY 1750 4 -128 1 LGR_SERVO_RTRACT 1250 4 -128 1 LOG_BACKEND_TYPE 1 2 -128 1 LOG_BITMASK 176126 6 -128 1 LOG_DISARMED 0 2 -128 1 LOG_FILE_BUFSIZE 16 2 -128 1 LOG_REPLAY 0 2 -128 1 MAG_ENABLE 1 2 -128 1 MIS_RESTART 0 2 -128 1 MIS_TOTAL 0 4 -128 1 MNT_ANGMAX_PAN 4500 4 -128 1 MNT_ANGMAX_ROL 4500 4 -128 1 MNT_ANGMAX_TIL 4500 4 -128 1 MNT_ANGMIN_PAN -4500 4 -128 1 MNT_ANGMIN_ROL -4500 4 -128 1 MNT_ANGMIN_TIL -4500 4 -128 1 MNT_DEFLT_MODE 3 2 -128 1 MNT_JSTICK_SPD 0 2 -128 1 MNT_LEAD_PTCH 0.000000000000000000 9 -128 1 MNT_LEAD_RLL 0.000000000000000000 9 -128 1 MNT_NEUTRAL_X 0.000000000000000000 9 -128 1 MNT_NEUTRAL_Y 0.000000000000000000 9 -128 1 MNT_NEUTRAL_Z 0.000000000000000000 9 -128 1 MNT_RC_IN_PAN 0 2 -128 1 MNT_RC_IN_ROLL 0 2 -128 1 MNT_RC_IN_TILT 8 2 -128 1 MNT_RETRACT_X 0.000000000000000000 9 -128 1 MNT_RETRACT_Y 0.000000000000000000 9 -128 1 MNT_RETRACT_Z 0.000000000000000000 9 -128 1 MNT_STAB_PAN 0 2 -128 1 MNT_STAB_ROLL 0 2 -128 1 MNT_STAB_TILT 0 2 -128 1 MNT_TYPE 1 2 -128 1 MOT_1_DIRECTION 0 2 -128 1 MOT_2_DIRECTION 0 2 -128 1 MOT_3_DIRECTION 1 2 -128 1 MOT_4_DIRECTION 1 2 -128 1 MOT_5_DIRECTION 0 2 -128 1 MOT_6_DIRECTION 1 2 -128 1 MOT_7_DIRECTION 1 2 -128 1 MOT_8_DIRECTION 1 2 -128 1 MOT_FV_CPLNG_K 1.000000000000000000 9 -128 1 NTF_BUZZ_ENABLE 1 2 -128 1 NTF_LED_BRIGHT 3 2 -128 1 NTF_LED_OVERRIDE 0 2 -128 1 PILOT_ACCEL_Z 50 4 -128 1 PILOT_THR_BHV 0 4 -128 1 PILOT_THR_FILT 0.000000000000000000 9 -128 1 PILOT_TKOFF_ALT 0.000000000000000000 9 -128 1 PILOT_TKOFF_DZ 100 4 -128 1 PILOT_VELZ_MAX 50 4 -128 1 POS_XY_P 1.000000000000000000 9 -128 1 POS_Z_P 1.000000000000000000 9 -128 1 PSC_ACC_XY_FILT 2.000000000000000000 9 -128 1 RC10_DZ 0 4 -128 1 RC10_FUNCTION 0 2 -128 1 RC10_MAX 2000 4 -128 1 RC10_MIN 1000 4 -128 1 RC10_REV 1 2 -128 1 RC10_TRIM 1500 4 -128 1 RC11_DZ 0 4 -128 1 RC11_FUNCTION 0 2 -128 1 RC11_MAX 2000 4 -128 1 RC11_MIN 1000 4 -128 1 RC11_REV 1 2 -128 1 RC11_TRIM 1500 4 -128 1 RC12_DZ 0 4 -128 1 RC12_FUNCTION 0 2 -128 1 RC12_MAX 2000 4 -128 1 RC12_MIN 1000 4 -128 1 RC12_REV 1 2 -128 1 RC12_TRIM 1500 4 -128 1 RC13_DZ 0 4 -128 1 RC13_FUNCTION 0 2 -128 1 RC13_MAX 2000 4 -128 1 RC13_MIN 1000 4 -128 1 RC13_REV 1 2 -128 1 RC13_TRIM 1500 4 -128 1 RC14_DZ 0 4 -128 1 RC14_FUNCTION 0 2 -128 1 RC14_MAX 2000 4 -128 1 RC14_MIN 1000 4 -128 1 RC14_REV 1 2 -128 1 RC14_TRIM 1500 4 -128 1 RC1_DZ 30 4 -128 1 RC1_MAX 2000 4 -128 1 RC1_MIN 1000 4 -128 1 RC1_REV 1 2 -128 1 RC1_TRIM 1500 4 -128 1 RC2_DZ 30 4 -128 1 RC2_MAX 2000 4 -128 1 RC2_MIN 1000 4 -128 1 RC2_REV 1 2 -128 1 RC2_TRIM 1500 4 -128 1 RC3_DZ 30 4 -128 1 RC3_MAX 1900 4 -128 1 RC3_MIN 1100 4 -128 1 RC3_REV 1 2 -128 1 RC3_TRIM 1100 4 -128 1 RC4_DZ 40 4 -128 1 RC4_MAX 1900 4 -128 1 RC4_MIN 1100 4 -128 1 RC4_REV 1 2 -128 1 RC4_TRIM 1500 4 -128 1 RC5_DZ 0 4 -128 1 RC5_FUNCTION 0 2 -128 1 RC5_MAX 2000 4 -128 1 RC5_MIN 1000 4 -128 1 RC5_REV 1 2 -128 1 RC5_TRIM 1500 4 -128 1 RC6_DZ 30 4 -128 1 RC6_FUNCTION 0 2 -128 1 RC6_MAX 1900 4 -128 1 RC6_MIN 1100 4 -128 1 RC6_REV 1 2 -128 1 RC6_TRIM 1500 4 -128 1 RC7_DZ 30 4 -128 1 RC7_FUNCTION 59 2 -128 1 RC7_MAX 1900 4 -128 1 RC7_MIN 1100 4 -128 1 RC7_REV 0 2 -128 1 RC7_TRIM 1500 4 -128 1 RC8_DZ 0 4 -128 1 RC8_FUNCTION 7 2 -128 1 RC8_MAX 2000 4 -128 1 RC8_MIN 1000 4 -128 1 RC8_REV 0 2 -128 1 RC8_TRIM 1500 4 -128 1 RC9_DZ 0 4 -128 1 RC9_FUNCTION 0 2 -128 1 RC9_MAX 2000 4 -128 1 RC9_MIN 1000 4 -128 1 RC9_REV 1 2 -128 1 RC9_TRIM 1500 4 -128 1 RCMAP_FORWARD 6 2 -128 1 RCMAP_LATERAL 7 2 -128 1 RCMAP_PITCH 1 2 -128 1 RCMAP_ROLL 2 2 -128 1 RCMAP_THROTTLE 3 2 -128 1 RCMAP_YAW 4 2 -128 1 RC_FEEL_RP 50 2 -128 1 RC_SPEED 490 4 -128 1 RELAY_DEFAULT 0 2 -128 1 RELAY_PIN 54 2 -128 1 RELAY_PIN2 55 2 -128 1 RELAY_PIN3 0 2 -128 1 RELAY_PIN4 0 2 -128 1 RNGFND2_ADDR 0 2 -128 1 RNGFND2_FUNCTION 0 2 -128 1 RNGFND2_GNDCLEAR 10 2 -128 1 RNGFND2_MAX_CM 700 4 -128 1 RNGFND2_MIN_CM 20 4 -128 1 RNGFND2_OFFSET 0.000000000000000000 9 -128 1 RNGFND2_PIN 0 2 -128 1 RNGFND2_RMETRIC 1 2 -128 1 RNGFND2_SCALING 3.000000000000000000 9 -128 1 RNGFND2_SETTLE 0 4 -128 1 RNGFND2_STOP_PIN 0 2 -128 1 RNGFND2_TYPE 0 2 -128 1 RNGFND_ADDR 0 2 -128 1 RNGFND_FUNCTION 0 2 -128 1 RNGFND_GAIN 0.800000011920928955 9 -128 1 RNGFND_GNDCLEAR 10 2 -128 1 RNGFND_MAX_CM 700 4 -128 1 RNGFND_MIN_CM 20 4 -128 1 RNGFND_OFFSET 0.000000000000000000 9 -128 1 RNGFND_PIN 0 2 -128 1 RNGFND_PWRRNG 0 4 -128 1 RNGFND_RMETRIC 1 2 -128 1 RNGFND_SCALING 3.000000000000000000 9 -128 1 RNGFND_SETTLE 0 4 -128 1 RNGFND_STOP_PIN 0 2 -128 1 RNGFND_TYPE 0 2 -128 1 RPM2_SCALING 1.000000000000000000 9 -128 1 RPM2_TYPE 0 2 -128 1 RPM_MAX 100000.000000000000000000 9 -128 1 RPM_MIN 10.000000000000000000 9 -128 1 RPM_MIN_QUAL 0.500000000000000000 9 -128 1 RPM_SCALING 1.000000000000000000 9 -128 1 RPM_TYPE 0 2 -128 1 RSSI_ANA_PIN 0 2 -128 1 RSSI_CHANNEL 0 2 -128 1 RSSI_CHAN_HIGH 2000 4 -128 1 RSSI_CHAN_LOW 1000 4 -128 1 RSSI_PIN_HIGH 5.000000000000000000 9 -128 1 RSSI_PIN_LOW 0.000000000000000000 9 -128 1 RSSI_TYPE 0 2 -128 1 RTL_ALT 1500 4 -128 1 RTL_ALT_FINAL 0 4 -128 1 RTL_CLIMB_MIN 0 4 -128 1 RTL_CONE_SLOPE 3.000000000000000000 9 -128 1 RTL_LOIT_TIME 5000 6 -128 1 RTL_SPEED 0 4 -128 1 SCHED_DEBUG 0 2 -128 1 SCHED_LOOP_RATE 400 4 -128 1 SERIAL0_BAUD 115 6 -128 1 SERIAL0_PROTOCOL 1 2 -128 1 SERIAL1_BAUD 57 6 -128 1 SERIAL1_PROTOCOL 1 2 -128 1 SERIAL2_BAUD 57 6 -128 1 SERIAL2_PROTOCOL 1 2 -128 1 SERIAL3_BAUD 38 6 -128 1 SERIAL3_PROTOCOL 5 2 -128 1 SERIAL4_BAUD 38 6 -128 1 SERIAL4_PROTOCOL 5 2 -128 1 SERIAL5_BAUD 57 6 -128 1 SERIAL5_PROTOCOL 0 2 -128 1 SIMPLE 0 2 -128 1 SR0_ADSB 5 4 -128 1 SR0_EXTRA1 4 4 -128 1 SR0_EXTRA2 4 4 -128 1 SR0_EXTRA3 4 4 -128 1 SR0_EXT_STAT 4 4 -128 1 SR0_PARAMS 10 4 -128 1 SR0_POSITION 4 4 -128 1 SR0_RAW_CTRL 4 4 -128 1 SR0_RAW_SENS 4 4 -128 1 SR0_RC_CHAN 4 4 -128 1 SR1_ADSB 5 4 -128 1 SR1_EXTRA1 0 4 -128 1 SR1_EXTRA2 0 4 -128 1 SR1_EXTRA3 0 4 -128 1 SR1_EXT_STAT 0 4 -128 1 SR1_PARAMS 0 4 -128 1 SR1_POSITION 0 4 -128 1 SR1_RAW_CTRL 0 4 -128 1 SR1_RAW_SENS 0 4 -128 1 SR1_RC_CHAN 0 4 -128 1 SR2_ADSB 5 4 -128 1 SR2_EXTRA1 0 4 -128 1 SR2_EXTRA2 0 4 -128 1 SR2_EXTRA3 0 4 -128 1 SR2_EXT_STAT 0 4 -128 1 SR2_PARAMS 0 4 -128 1 SR2_POSITION 0 4 -128 1 SR2_RAW_CTRL 0 4 -128 1 SR2_RAW_SENS 0 4 -128 1 SR2_RC_CHAN 0 4 -128 1 SR3_ADSB 5 4 -128 1 SR3_EXTRA1 0 4 -128 1 SR3_EXTRA2 0 4 -128 1 SR3_EXTRA3 0 4 -128 1 SR3_EXT_STAT 0 4 -128 1 SR3_PARAMS 0 4 -128 1 SR3_POSITION 0 4 -128 1 SR3_RAW_CTRL 0 4 -128 1 SR3_RAW_SENS 0 4 -128 1 SR3_RC_CHAN 0 4 -128 1 SUPER_SIMPLE 0 2 -128 1 SURFACE_DEPTH -10.000000000000000000 9 -128 1 SYSID_MYGCS 255 4 -128 1 SYSID_SW_MREV 120 4 -128 1 SYSID_SW_TYPE 10 2 -128 1 SYSID_THISMAV 1 4 -128 1 TELEM_DELAY 0 2 -128 1 TERRAIN_FOLLOW 0 2 -128 1 THROW_MOT_START 0 2 -128 1 THR_DZ 100 4 -128 1 TUNE 0 2 -128 1 TUNE_HIGH 1000 4 -128 1 TUNE_LOW 0 4 -128 1 VEL_XY_FILT_HZ 5.000000000000000000 9 -128 1 VEL_XY_I 0.500000000000000000 9 -128 1 VEL_XY_IMAX 1000.000000000000000000 9 -128 1 VEL_XY_P 1.000000000000000000 9 -128 1 VEL_Z_P 5.000000000000000000 9 -128 1 WPNAV_ACCEL 100.000000000000000000 9 -128 1 WPNAV_ACCEL_Z 100.000000000000000000 9 -128 1 WPNAV_LOIT_JERK 1000.000000000000000000 9 -128 1 WPNAV_LOIT_MAXA 250.000000000000000000 9 -128 1 WPNAV_LOIT_MINA 25.000000000000000000 9 -128 1 WPNAV_LOIT_SPEED 500.000000000000000000 9 -128 1 WPNAV_RADIUS 200.000000000000000000 9 -128 1 WPNAV_SPEED 500.000000000000000000 9 -128 1 WPNAV_SPEED_DN 150.000000000000000000 9 -128 1 WPNAV_SPEED_UP 250.000000000000000000 9 -128 1 WP_TKOFF_NAV_ALT 0.000000000000000000 9 -128 1 WP_YAW_BEHAVIOR 2 2 +1 1 ACCEL_Z_D 0.000000000000000000 9 +1 1 ACCEL_Z_FILT 20.000000000000000000 9 +1 1 ACCEL_Z_I 1.000000000000000000 9 +1 1 ACCEL_Z_IMAX 800.000000000000000000 9 +1 1 ACCEL_Z_P 0.500000000000000000 9 +1 1 ACRO_BAL_PITCH 1.000000000000000000 9 +1 1 ACRO_BAL_ROLL 1.000000000000000000 9 +1 1 ACRO_EXPO 0.300000011920928955 9 +1 1 ACRO_RP_P 4.500000000000000000 9 +1 1 ACRO_TRAINER 2 2 +1 1 ACRO_YAW_P 4.500000000000000000 9 +1 1 AHRS_COMP_BETA 0.100000001490116119 9 +1 1 AHRS_EKF_TYPE 2 2 +1 1 AHRS_GPS_GAIN 1.000000000000000000 9 +1 1 AHRS_GPS_MINSATS 6 2 +1 1 AHRS_GPS_USE 1 2 +1 1 AHRS_ORIENTATION 16 2 +1 1 AHRS_RP_P 0.200000002980232239 9 +1 1 AHRS_TRIM_X 0.000000000000000000 9 +1 1 AHRS_TRIM_Y 0.007748094387352467 9 +1 1 AHRS_TRIM_Z 0.000000000000000000 9 +1 1 AHRS_WIND_MAX 0 2 +1 1 AHRS_YAW_P 0.200000002980232239 9 +1 1 ANGLE_MAX 4500 4 +1 1 ARMING_CHECK 0 2 +1 1 ATC_ACCEL_P_MAX 110000.000000000000000000 9 +1 1 ATC_ACCEL_R_MAX 110000.000000000000000000 9 +1 1 ATC_ACCEL_Y_MAX 110000.000000000000000000 9 +1 1 ATC_ANGLE_BOOST 1 2 +1 1 ATC_ANG_LIM_TC 1.000000000000000000 9 +1 1 ATC_ANG_PIT_P 6.000000000000000000 9 +1 1 ATC_ANG_RLL_P 0.000000000000000000 9 +1 1 ATC_ANG_YAW_P 6.000000000000000000 9 +1 1 ATC_RATE_FF_ENAB 1 2 +1 1 ATC_RAT_PIT_D 0.003599999938160181 9 +1 1 ATC_RAT_PIT_FILT 20.000000000000000000 9 +1 1 ATC_RAT_PIT_I 0.090000003576278687 9 +1 1 ATC_RAT_PIT_IMAX 0.444000005722045898 9 +1 1 ATC_RAT_PIT_P 0.135000005364418030 9 +1 1 ATC_RAT_RLL_D 0.004000000189989805 9 +1 1 ATC_RAT_RLL_FILT 20.000000000000000000 9 +1 1 ATC_RAT_RLL_I 0.000000000000000000 9 +1 1 ATC_RAT_RLL_IMAX 0.444000005722045898 9 +1 1 ATC_RAT_RLL_P 0.140000000596046448 9 +1 1 ATC_RAT_YAW_D 0.000000000000000000 9 +1 1 ATC_RAT_YAW_FILT 30.000000000000000000 9 +1 1 ATC_RAT_YAW_I 0.019999999552965164 9 +1 1 ATC_RAT_YAW_IMAX 0.222000002861022949 9 +1 1 ATC_RAT_YAW_P 0.180000007152557373 9 +1 1 ATC_SLEW_YAW 6000.000000000000000000 9 +1 1 ATC_THR_MIX_MAX 0.500000000000000000 9 +1 1 ATC_THR_MIX_MIN 0.100000001490116119 9 +1 1 AUTOTUNE_AGGR 0.100000001490116119 9 +1 1 AUTOTUNE_AXES 7 2 +1 1 AUTOTUNE_MIN_D 0.001000000047497451 9 +1 1 AVOID_ENABLE 1 2 +1 1 BATT2_AMP_OFFSET 0.000000000000000000 9 +1 1 BATT2_AMP_PERVOL 17.000000000000000000 9 +1 1 BATT2_CAPACITY 3300 6 +1 1 BATT2_CURR_PIN 3 2 +1 1 BATT2_MONITOR 0 2 +1 1 BATT2_VOLT_MULT 10.100000381469726562 9 +1 1 BATT2_VOLT_PIN 2 2 +1 1 BATT_AMP_OFFSET 0.000000000000000000 9 +1 1 BATT_AMP_PERVOLT 17.000000000000000000 9 +1 1 BATT_CAPACITY 10000 6 +1 1 BATT_CURR_PIN 3 2 +1 1 BATT_MONITOR 4 2 +1 1 BATT_VOLT_MULT 10.100000381469726562 9 +1 1 BATT_VOLT_PIN 2 2 +1 1 BRD_CAN_ENABLE 0 2 +1 1 BRD_IMU_TARGTEMP 0 2 +1 1 BRD_PWM_COUNT 4 2 +1 1 BRD_SAFETYENABLE 0 2 +1 1 BRD_SAFETY_MASK 0 6 +1 1 BRD_SBUS_OUT 0 2 +1 1 BRD_SER1_RTSCTS 2 2 +1 1 BRD_SER2_RTSCTS 2 2 +1 1 BRD_SERIAL_NUM 0 4 +1 1 BTN0_FUNCTION 0 2 +1 1 BTN0_SFUNCTION 0 2 +1 1 BTN10_FUNCTION 22 2 +1 1 BTN10_SFUNCTION 0 2 +1 1 BTN11_FUNCTION 42 2 +1 1 BTN11_SFUNCTION 47 2 +1 1 BTN12_FUNCTION 43 2 +1 1 BTN12_SFUNCTION 46 2 +1 1 BTN13_FUNCTION 33 2 +1 1 BTN13_SFUNCTION 45 2 +1 1 BTN14_FUNCTION 32 2 +1 1 BTN14_SFUNCTION 44 2 +1 1 BTN15_FUNCTION 0 2 +1 1 BTN15_SFUNCTION 0 2 +1 1 BTN1_FUNCTION 6 2 +1 1 BTN1_SFUNCTION 0 2 +1 1 BTN2_FUNCTION 8 2 +1 1 BTN2_SFUNCTION 0 2 +1 1 BTN3_FUNCTION 7 2 +1 1 BTN3_SFUNCTION 0 2 +1 1 BTN4_FUNCTION 4 2 +1 1 BTN4_SFUNCTION 0 2 +1 1 BTN5_FUNCTION 1 2 +1 1 BTN5_SFUNCTION 0 2 +1 1 BTN6_FUNCTION 3 2 +1 1 BTN6_SFUNCTION 0 2 +1 1 BTN7_FUNCTION 21 2 +1 1 BTN7_SFUNCTION 0 2 +1 1 BTN8_FUNCTION 48 2 +1 1 BTN8_SFUNCTION 0 2 +1 1 BTN9_FUNCTION 23 2 +1 1 BTN9_SFUNCTION 0 2 +1 1 CAM_DURATION 10 2 +1 1 CAM_FEEDBACK_PIN 0 2 +1 1 CAM_FEEDBACK_POL 1 2 +1 1 CAM_MAX_ROLL 0 4 +1 1 CAM_MIN_INTERVAL 0 4 +1 1 CAM_RELAY_ON 1 2 +1 1 CAM_SERVO_OFF 1100 4 +1 1 CAM_SERVO_ON 1300 4 +1 1 CAM_TRIGG_DIST 0.000000000000000000 9 +1 1 CAM_TRIGG_TYPE 0 2 +1 1 CH10_OPT 0 2 +1 1 CH11_OPT 0 2 +1 1 CH12_OPT 0 2 +1 1 CH7_OPT 0 2 +1 1 CH8_OPT 0 2 +1 1 CH9_OPT 0 2 +1 1 CIRCLE_RADIUS 1000.000000000000000000 9 +1 1 CIRCLE_RATE 20.000000000000000000 9 +1 1 CLI_ENABLED 0 2 +1 1 COMPASS_AUTODEC 1 2 +1 1 COMPASS_CAL_FIT 8.000000000000000000 9 +1 1 COMPASS_DEC 0.000000000000000000 9 +1 1 COMPASS_DEV_ID 131594 6 +1 1 COMPASS_DEV_ID2 0 6 +1 1 COMPASS_DEV_ID3 0 6 +1 1 COMPASS_DIA2_X 0.000000000000000000 9 +1 1 COMPASS_DIA2_Y 0.000000000000000000 9 +1 1 COMPASS_DIA2_Z 0.000000000000000000 9 +1 1 COMPASS_DIA3_X 0.000000000000000000 9 +1 1 COMPASS_DIA3_Y 0.000000000000000000 9 +1 1 COMPASS_DIA3_Z 0.000000000000000000 9 +1 1 COMPASS_DIA_X 1.000000000000000000 9 +1 1 COMPASS_DIA_Y 1.000000000000000000 9 +1 1 COMPASS_DIA_Z 1.000000000000000000 9 +1 1 COMPASS_EXTERN2 0 2 +1 1 COMPASS_EXTERN3 0 2 +1 1 COMPASS_EXTERNAL 0 2 +1 1 COMPASS_LEARN 1 2 +1 1 COMPASS_MOT2_X 0.000000000000000000 9 +1 1 COMPASS_MOT2_Y 0.000000000000000000 9 +1 1 COMPASS_MOT2_Z 0.000000000000000000 9 +1 1 COMPASS_MOT3_X 0.000000000000000000 9 +1 1 COMPASS_MOT3_Y 0.000000000000000000 9 +1 1 COMPASS_MOT3_Z 0.000000000000000000 9 +1 1 COMPASS_MOTCT 0 2 +1 1 COMPASS_MOT_X 0.000000000000000000 9 +1 1 COMPASS_MOT_Y 0.000000000000000000 9 +1 1 COMPASS_MOT_Z 0.000000000000000000 9 +1 1 COMPASS_ODI2_X 0.000000000000000000 9 +1 1 COMPASS_ODI2_Y 0.000000000000000000 9 +1 1 COMPASS_ODI2_Z 0.000000000000000000 9 +1 1 COMPASS_ODI3_X 0.000000000000000000 9 +1 1 COMPASS_ODI3_Y 0.000000000000000000 9 +1 1 COMPASS_ODI3_Z 0.000000000000000000 9 +1 1 COMPASS_ODI_X 0.000000000000000000 9 +1 1 COMPASS_ODI_Y 0.000000000000000000 9 +1 1 COMPASS_ODI_Z 0.000000000000000000 9 +1 1 COMPASS_OFS2_X 0.000000000000000000 9 +1 1 COMPASS_OFS2_Y 0.000000000000000000 9 +1 1 COMPASS_OFS2_Z 0.000000000000000000 9 +1 1 COMPASS_OFS3_X 0.000000000000000000 9 +1 1 COMPASS_OFS3_Y 0.000000000000000000 9 +1 1 COMPASS_OFS3_Z 0.000000000000000000 9 +1 1 COMPASS_OFS_X -17.000000000000000000 9 +1 1 COMPASS_OFS_Y 389.000000000000000000 9 +1 1 COMPASS_OFS_Z 128.000000000000000000 9 +1 1 COMPASS_ORIENT 16 2 +1 1 COMPASS_ORIENT2 0 2 +1 1 COMPASS_ORIENT3 0 2 +1 1 COMPASS_PRIMARY 0 2 +1 1 COMPASS_USE 1 2 +1 1 COMPASS_USE2 1 2 +1 1 COMPASS_USE3 1 2 +1 1 DISARM_DELAY 0 2 +1 1 EK2_ABIAS_P_NSE 0.004999999888241291 9 +1 1 EK2_ACC_P_NSE 0.600000023841857910 9 +1 1 EK2_ALT_M_NSE 0.100000001490116119 9 +1 1 EK2_ALT_SOURCE 0 2 +1 1 EK2_CHECK_SCALE 100 4 +1 1 EK2_EAS_I_GATE 400 4 +1 1 EK2_EAS_M_NSE 1.399999976158142090 9 +1 1 EK2_ENABLE 1 2 +1 1 EK2_FLOW_DELAY 10 2 +1 1 EK2_FLOW_I_GATE 300 4 +1 1 EK2_FLOW_M_NSE 0.250000000000000000 9 +1 1 EK2_GBIAS_P_NSE 0.000099999997473788 9 +1 1 EK2_GLITCH_RAD 25 2 +1 1 EK2_GPS_CHECK 31 2 +1 1 EK2_GPS_DELAY 220 4 +1 1 EK2_GPS_TYPE 0 2 +1 1 EK2_GSCL_P_NSE 0.000500000023748726 9 +1 1 EK2_GYRO_P_NSE 0.029999999329447746 9 +1 1 EK2_HGT_DELAY 60 4 +1 1 EK2_HGT_I_GATE 500 4 +1 1 EK2_IMU_MASK 3 2 +1 1 EK2_LOG_MASK 1 2 +1 1 EK2_MAGB_P_NSE 0.000099999997473788 9 +1 1 EK2_MAGE_P_NSE 0.001000000047497451 9 +1 1 EK2_MAG_CAL 3 2 +1 1 EK2_MAG_I_GATE 300 4 +1 1 EK2_MAG_M_NSE 0.050000000745058060 9 +1 1 EK2_MAX_FLOW 2.500000000000000000 9 +1 1 EK2_NOAID_M_NSE 10.000000000000000000 9 +1 1 EK2_POSNE_M_NSE 1.000000000000000000 9 +1 1 EK2_POS_I_GATE 500 4 +1 1 EK2_RNG_I_GATE 500 4 +1 1 EK2_RNG_M_NSE 0.500000000000000000 9 +1 1 EK2_TAU_OUTPUT 25 2 +1 1 EK2_VELD_M_NSE 0.699999988079071045 9 +1 1 EK2_VELNE_M_NSE 0.500000000000000000 9 +1 1 EK2_VEL_I_GATE 500 4 +1 1 EK2_WIND_PSCALE 0.500000000000000000 9 +1 1 EK2_WIND_P_NSE 0.100000001490116119 9 +1 1 EK2_YAW_I_GATE 300 4 +1 1 EK2_YAW_M_NSE 0.500000000000000000 9 +1 1 EKF_ENABLE 0 2 +1 1 EPM_ENABLE 0 2 +1 1 EPM_GRAB 1900 4 +1 1 EPM_NEUTRAL 1500 4 +1 1 EPM_REGRAB 0 2 +1 1 EPM_RELEASE 1100 4 +1 1 ESC_CALIBRATION 0 2 +1 1 FENCE_ACTION 1 2 +1 1 FENCE_ALT_MAX 100.000000000000000000 9 +1 1 FENCE_DEPTH_MAX -10.000000000000000000 9 +1 1 FENCE_ENABLE 0 2 +1 1 FENCE_MARGIN 2.000000000000000000 9 +1 1 FENCE_RADIUS 300.000000000000000000 9 +1 1 FENCE_TOTAL 0 2 +1 1 FENCE_TYPE 7 2 +1 1 FLOW_ENABLE 0 2 +1 1 FLOW_FXSCALER 0 4 +1 1 FLOW_FYSCALER 0 4 +1 1 FLOW_ORIENT_YAW 0 4 +1 1 FLTMODE1 19 2 +1 1 FLTMODE2 0 2 +1 1 FLTMODE3 2 2 +1 1 FLTMODE4 0 2 +1 1 FLTMODE5 0 2 +1 1 FLTMODE6 0 2 +1 1 FRAME 1 2 +1 1 FS_BATT_ENABLE 0 2 +1 1 FS_BATT_MAH 0.000000000000000000 9 +1 1 FS_BATT_VOLTAGE 10.500000000000000000 9 +1 1 FS_CRASH_CHECK 0 2 +1 1 FS_EKF_ACTION 1 2 +1 1 FS_EKF_THRESH 0.800000011920928955 9 +1 1 FS_GCS_ENABLE 1 2 +1 1 FS_LEAK_ENABLE 1 2 +1 1 FS_PRESS_ENABLE 0 2 +1 1 FS_PRESS_MAX 105000 6 +1 1 FS_TEMP_ENABLE 0 2 +1 1 FS_TEMP_MAX 62 2 +1 1 FS_TERRAIN_ENAB 0 2 +1 1 FS_THR_ENABLE 0 2 +1 1 FS_THR_VALUE 975 4 +1 1 GCS_PID_MASK 0 4 +1 1 GND_ABS_PRESS 102237.296875000000000000 9 +1 1 GND_ALT_OFFSET 0.000000000000000000 9 +1 1 GND_BASE_PRESS 98975.476562500000000000 9 +1 1 GND_BASE_RESET 0 2 +1 1 GND_PRIMARY 1 2 +1 1 GND_SPEC_GRAV 1.000000000000000000 9 +1 1 GND_TEMP 37.426425933837890625 9 +1 1 GPS_AUTO_CONFIG 1 2 +1 1 GPS_AUTO_SWITCH 1 2 +1 1 GPS_GNSS_MODE 0 2 +1 1 GPS_GNSS_MODE2 0 2 +1 1 GPS_HDOP_GOOD 140 4 +1 1 GPS_INJECT_TO 127 2 +1 1 GPS_MIN_DGPS 100 2 +1 1 GPS_MIN_ELEV 0 2 +1 1 GPS_NAVFILTER 8 2 +1 1 GPS_RAW_DATA 0 2 +1 1 GPS_SAVE_CFG 0 2 +1 1 GPS_SBAS_MODE 2 2 +1 1 GPS_SBP_LOGMASK -256 4 +1 1 GPS_TYPE 1 2 +1 1 GPS_TYPE2 0 2 +1 1 INS_ACC2OFFS_X 0.649672329425811768 9 +1 1 INS_ACC2OFFS_Y 0.710507631301879883 9 +1 1 INS_ACC2OFFS_Z 0.387798368930816650 9 +1 1 INS_ACC2SCAL_X 1.043415188789367676 9 +1 1 INS_ACC2SCAL_Y 1.020669221878051758 9 +1 1 INS_ACC2SCAL_Z 1.008688688278198242 9 +1 1 INS_ACC3OFFS_X 0.000000000000000000 9 +1 1 INS_ACC3OFFS_Y 0.000000000000000000 9 +1 1 INS_ACC3OFFS_Z 0.000000000000000000 9 +1 1 INS_ACC3SCAL_X 0.000000000000000000 9 +1 1 INS_ACC3SCAL_Y 0.000000000000000000 9 +1 1 INS_ACC3SCAL_Z 0.000000000000000000 9 +1 1 INS_ACCEL_FILTER 20 2 +1 1 INS_ACCOFFS_X -0.088626168668270111 9 +1 1 INS_ACCOFFS_Y -0.112697608768939972 9 +1 1 INS_ACCOFFS_Z 0.440824359655380249 9 +1 1 INS_ACCSCAL_X 0.996031224727630615 9 +1 1 INS_ACCSCAL_Y 1.005982160568237305 9 +1 1 INS_ACCSCAL_Z 0.992506563663482666 9 +1 1 INS_ACC_BODYFIX 2 2 +1 1 INS_GYR2OFFS_X 0.017705552279949188 9 +1 1 INS_GYR2OFFS_Y 0.022531248629093170 9 +1 1 INS_GYR2OFFS_Z -0.017156355082988739 9 +1 1 INS_GYR3OFFS_X 0.000000000000000000 9 +1 1 INS_GYR3OFFS_Y 0.000000000000000000 9 +1 1 INS_GYR3OFFS_Z 0.000000000000000000 9 +1 1 INS_GYROFFS_X 0.015944827347993851 9 +1 1 INS_GYROFFS_Y 0.044509842991828918 9 +1 1 INS_GYROFFS_Z -0.004005374852567911 9 +1 1 INS_GYRO_FILTER 20 2 +1 1 INS_GYR_CAL 1 2 +1 1 INS_PRODUCT_ID 5 4 +1 1 INS_STILL_THRESH 0.100000001490116119 9 +1 1 INS_TRIM_OPTION 1 2 +1 1 INS_USE 1 2 +1 1 INS_USE2 1 2 +1 1 INS_USE3 0 2 +1 1 LAND_REPOSITION 1 2 +1 1 LAND_SPEED 50 4 +1 1 LAND_SPEED_HIGH 0 4 +1 1 LEAK1_LOGIC 0 2 +1 1 LEAK1_PIN -1 2 +1 1 LEAK2_LOGIC 0 2 +1 1 LEAK2_PIN -1 2 +1 1 LEAK3_LOGIC 0 2 +1 1 LEAK3_PIN -1 2 +1 1 LGR_SERVO_DEPLOY 1750 4 +1 1 LGR_SERVO_RTRACT 1250 4 +1 1 LOG_BACKEND_TYPE 1 2 +1 1 LOG_BITMASK 176126 6 +1 1 LOG_DISARMED 0 2 +1 1 LOG_FILE_BUFSIZE 16 2 +1 1 LOG_REPLAY 0 2 +1 1 MAG_ENABLE 1 2 +1 1 MIS_RESTART 0 2 +1 1 MIS_TOTAL 0 4 +1 1 MNT_ANGMAX_PAN 4500 4 +1 1 MNT_ANGMAX_ROL 4500 4 +1 1 MNT_ANGMAX_TIL 4500 4 +1 1 MNT_ANGMIN_PAN -4500 4 +1 1 MNT_ANGMIN_ROL -4500 4 +1 1 MNT_ANGMIN_TIL -4500 4 +1 1 MNT_DEFLT_MODE 3 2 +1 1 MNT_JSTICK_SPD 0 2 +1 1 MNT_LEAD_PTCH 0.000000000000000000 9 +1 1 MNT_LEAD_RLL 0.000000000000000000 9 +1 1 MNT_NEUTRAL_X 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Y 0.000000000000000000 9 +1 1 MNT_NEUTRAL_Z 0.000000000000000000 9 +1 1 MNT_RC_IN_PAN 0 2 +1 1 MNT_RC_IN_ROLL 0 2 +1 1 MNT_RC_IN_TILT 8 2 +1 1 MNT_RETRACT_X 0.000000000000000000 9 +1 1 MNT_RETRACT_Y 0.000000000000000000 9 +1 1 MNT_RETRACT_Z 0.000000000000000000 9 +1 1 MNT_STAB_PAN 0 2 +1 1 MNT_STAB_ROLL 0 2 +1 1 MNT_STAB_TILT 0 2 +1 1 MNT_TYPE 1 2 +1 1 MOT_1_DIRECTION 1 2 +1 1 MOT_2_DIRECTION -1 2 +1 1 MOT_3_DIRECTION 1 2 +1 1 MOT_4_DIRECTION 1 2 +1 1 MOT_5_DIRECTION -1 2 +1 1 MOT_6_DIRECTION 1 2 +1 1 MOT_7_DIRECTION 1 2 +1 1 MOT_8_DIRECTION 1 2 +1 1 MOT_FV_CPLNG_K 1.000000000000000000 9 +1 1 NTF_BUZZ_ENABLE 1 2 +1 1 NTF_LED_BRIGHT 3 2 +1 1 NTF_LED_OVERRIDE 0 2 +1 1 PHLD_BRAKE_ANGLE 3000 4 +1 1 PHLD_BRAKE_RATE 8 4 +1 1 PILOT_ACCEL_Z 50 4 +1 1 PILOT_THR_BHV 0 4 +1 1 PILOT_THR_FILT 0.000000000000000000 9 +1 1 PILOT_TKOFF_ALT 0.000000000000000000 9 +1 1 PILOT_TKOFF_DZ 100 4 +1 1 PILOT_VELZ_MAX 50 4 +1 1 POS_XY_P 1.000000000000000000 9 +1 1 POS_Z_P 1.000000000000000000 9 +1 1 PSC_ACC_XY_FILT 2.000000000000000000 9 +1 1 RC10_DZ 0 4 +1 1 RC10_FUNCTION 0 2 +1 1 RC10_MAX 2000 4 +1 1 RC10_MIN 1000 4 +1 1 RC10_REV 1 2 +1 1 RC10_TRIM 1500 4 +1 1 RC11_DZ 0 4 +1 1 RC11_FUNCTION 0 2 +1 1 RC11_MAX 2000 4 +1 1 RC11_MIN 1000 4 +1 1 RC11_REV 1 2 +1 1 RC11_TRIM 1500 4 +1 1 RC12_DZ 0 4 +1 1 RC12_FUNCTION 0 2 +1 1 RC12_MAX 2000 4 +1 1 RC12_MIN 1000 4 +1 1 RC12_REV 1 2 +1 1 RC12_TRIM 1500 4 +1 1 RC13_DZ 0 4 +1 1 RC13_FUNCTION 0 2 +1 1 RC13_MAX 2000 4 +1 1 RC13_MIN 1000 4 +1 1 RC13_REV 1 2 +1 1 RC13_TRIM 1500 4 +1 1 RC14_DZ 0 4 +1 1 RC14_FUNCTION 0 2 +1 1 RC14_MAX 2000 4 +1 1 RC14_MIN 1000 4 +1 1 RC14_REV 1 2 +1 1 RC14_TRIM 1500 4 +1 1 RC1_DZ 30 4 +1 1 RC1_MAX 2000 4 +1 1 RC1_MIN 1000 4 +1 1 RC1_REV 1 2 +1 1 RC1_TRIM 1500 4 +1 1 RC2_DZ 30 4 +1 1 RC2_MAX 2000 4 +1 1 RC2_MIN 1000 4 +1 1 RC2_REV 1 2 +1 1 RC2_TRIM 1500 4 +1 1 RC3_DZ 30 4 +1 1 RC3_MAX 1900 4 +1 1 RC3_MIN 1100 4 +1 1 RC3_REV 1 2 +1 1 RC3_TRIM 1100 4 +1 1 RC4_DZ 40 4 +1 1 RC4_MAX 1900 4 +1 1 RC4_MIN 1100 4 +1 1 RC4_REV 1 2 +1 1 RC4_TRIM 1500 4 +1 1 RC5_DZ 0 4 +1 1 RC5_FUNCTION 0 2 +1 1 RC5_MAX 2000 4 +1 1 RC5_MIN 1000 4 +1 1 RC5_REV 1 2 +1 1 RC5_TRIM 1500 4 +1 1 RC6_DZ 30 4 +1 1 RC6_FUNCTION 0 2 +1 1 RC6_MAX 1900 4 +1 1 RC6_MIN 1100 4 +1 1 RC6_REV 1 2 +1 1 RC6_TRIM 1500 4 +1 1 RC7_DZ 30 4 +1 1 RC7_FUNCTION 59 2 +1 1 RC7_MAX 1900 4 +1 1 RC7_MIN 1100 4 +1 1 RC7_REV 0 2 +1 1 RC7_TRIM 1500 4 +1 1 RC8_DZ 0 4 +1 1 RC8_FUNCTION 7 2 +1 1 RC8_MAX 2000 4 +1 1 RC8_MIN 1000 4 +1 1 RC8_REV 0 2 +1 1 RC8_TRIM 1500 4 +1 1 RC9_DZ 0 4 +1 1 RC9_FUNCTION 0 2 +1 1 RC9_MAX 2000 4 +1 1 RC9_MIN 1000 4 +1 1 RC9_REV 1 2 +1 1 RC9_TRIM 1500 4 +1 1 RCMAP_FORWARD 6 2 +1 1 RCMAP_LATERAL 7 2 +1 1 RCMAP_PITCH 1 2 +1 1 RCMAP_ROLL 2 2 +1 1 RCMAP_THROTTLE 3 2 +1 1 RCMAP_YAW 4 2 +1 1 RC_FEEL_RP 50 2 +1 1 RC_SPEED 100 4 +1 1 RELAY_DEFAULT 0 2 +1 1 RELAY_PIN 54 2 +1 1 RELAY_PIN2 55 2 +1 1 RELAY_PIN3 0 2 +1 1 RELAY_PIN4 0 2 +1 1 RNGFND2_ADDR 0 2 +1 1 RNGFND2_FUNCTION 0 2 +1 1 RNGFND2_GNDCLEAR 10 2 +1 1 RNGFND2_MAX_CM 700 4 +1 1 RNGFND2_MIN_CM 20 4 +1 1 RNGFND2_OFFSET 0.000000000000000000 9 +1 1 RNGFND2_PIN 0 2 +1 1 RNGFND2_RMETRIC 1 2 +1 1 RNGFND2_SCALING 3.000000000000000000 9 +1 1 RNGFND2_SETTLE 0 4 +1 1 RNGFND2_STOP_PIN 0 2 +1 1 RNGFND2_TYPE 0 2 +1 1 RNGFND_ADDR 0 2 +1 1 RNGFND_FUNCTION 0 2 +1 1 RNGFND_GAIN 0.800000011920928955 9 +1 1 RNGFND_GNDCLEAR 10 2 +1 1 RNGFND_MAX_CM 700 4 +1 1 RNGFND_MIN_CM 20 4 +1 1 RNGFND_OFFSET 0.000000000000000000 9 +1 1 RNGFND_PIN 0 2 +1 1 RNGFND_PWRRNG 0 4 +1 1 RNGFND_RMETRIC 1 2 +1 1 RNGFND_SCALING 3.000000000000000000 9 +1 1 RNGFND_SETTLE 0 4 +1 1 RNGFND_STOP_PIN 0 2 +1 1 RNGFND_TYPE 0 2 +1 1 RPM2_SCALING 1.000000000000000000 9 +1 1 RPM2_TYPE 0 2 +1 1 RPM_MAX 100000.000000000000000000 9 +1 1 RPM_MIN 10.000000000000000000 9 +1 1 RPM_MIN_QUAL 0.500000000000000000 9 +1 1 RPM_SCALING 1.000000000000000000 9 +1 1 RPM_TYPE 0 2 +1 1 RSSI_ANA_PIN 0 2 +1 1 RSSI_CHANNEL 0 2 +1 1 RSSI_CHAN_HIGH 2000 4 +1 1 RSSI_CHAN_LOW 1000 4 +1 1 RSSI_PIN_HIGH 5.000000000000000000 9 +1 1 RSSI_PIN_LOW 0.000000000000000000 9 +1 1 RSSI_TYPE 0 2 +1 1 RTL_ALT 1500 4 +1 1 RTL_ALT_FINAL 0 4 +1 1 RTL_CLIMB_MIN 0 4 +1 1 RTL_CONE_SLOPE 3.000000000000000000 9 +1 1 RTL_LOIT_TIME 5000 6 +1 1 RTL_SPEED 0 4 +1 1 SCHED_DEBUG 0 2 +1 1 SCHED_LOOP_RATE 400 4 +1 1 SERIAL0_BAUD 115 6 +1 1 SERIAL0_PROTOCOL 1 2 +1 1 SERIAL1_BAUD 57 6 +1 1 SERIAL1_PROTOCOL 1 2 +1 1 SERIAL2_BAUD 57 6 +1 1 SERIAL2_PROTOCOL 1 2 +1 1 SERIAL3_BAUD 38 6 +1 1 SERIAL3_PROTOCOL 5 2 +1 1 SERIAL4_BAUD 38 6 +1 1 SERIAL4_PROTOCOL 5 2 +1 1 SERIAL5_BAUD 57 6 +1 1 SERIAL5_PROTOCOL 0 2 +1 1 SIMPLE 0 2 +1 1 SR0_ADSB 5 4 +1 1 SR0_EXTRA1 4 4 +1 1 SR0_EXTRA2 4 4 +1 1 SR0_EXTRA3 4 4 +1 1 SR0_EXT_STAT 4 4 +1 1 SR0_PARAMS 10 4 +1 1 SR0_POSITION 4 4 +1 1 SR0_RAW_CTRL 4 4 +1 1 SR0_RAW_SENS 4 4 +1 1 SR0_RC_CHAN 4 4 +1 1 SR1_ADSB 5 4 +1 1 SR1_EXTRA1 0 4 +1 1 SR1_EXTRA2 0 4 +1 1 SR1_EXTRA3 0 4 +1 1 SR1_EXT_STAT 0 4 +1 1 SR1_PARAMS 0 4 +1 1 SR1_POSITION 0 4 +1 1 SR1_RAW_CTRL 0 4 +1 1 SR1_RAW_SENS 0 4 +1 1 SR1_RC_CHAN 0 4 +1 1 SR2_ADSB 5 4 +1 1 SR2_EXTRA1 0 4 +1 1 SR2_EXTRA2 0 4 +1 1 SR2_EXTRA3 0 4 +1 1 SR2_EXT_STAT 0 4 +1 1 SR2_PARAMS 0 4 +1 1 SR2_POSITION 0 4 +1 1 SR2_RAW_CTRL 0 4 +1 1 SR2_RAW_SENS 0 4 +1 1 SR2_RC_CHAN 0 4 +1 1 SR3_ADSB 5 4 +1 1 SR3_EXTRA1 0 4 +1 1 SR3_EXTRA2 0 4 +1 1 SR3_EXTRA3 0 4 +1 1 SR3_EXT_STAT 0 4 +1 1 SR3_PARAMS 0 4 +1 1 SR3_POSITION 0 4 +1 1 SR3_RAW_CTRL 0 4 +1 1 SR3_RAW_SENS 0 4 +1 1 SR3_RC_CHAN 0 4 +1 1 SUPER_SIMPLE 0 2 +1 1 SURFACE_DEPTH -10.000000000000000000 9 +1 1 SYSID_MYGCS 255 4 +1 1 SYSID_SW_MREV 120 4 +1 1 SYSID_SW_TYPE 10 2 +1 1 SYSID_THISMAV 1 4 +1 1 TELEM_DELAY 0 2 +1 1 TERRAIN_FOLLOW 0 2 +1 1 THROW_MOT_START 0 2 +1 1 THR_DZ 100 4 +1 1 TUNE 0 2 +1 1 TUNE_HIGH 1000 4 +1 1 TUNE_LOW 0 4 +1 1 VEL_XY_FILT_HZ 5.000000000000000000 9 +1 1 VEL_XY_I 0.500000000000000000 9 +1 1 VEL_XY_IMAX 1000.000000000000000000 9 +1 1 VEL_XY_P 1.000000000000000000 9 +1 1 VEL_Z_P 5.000000000000000000 9 +1 1 WPNAV_ACCEL 100.000000000000000000 9 +1 1 WPNAV_ACCEL_Z 100.000000000000000000 9 +1 1 WPNAV_LOIT_JERK 1000.000000000000000000 9 +1 1 WPNAV_LOIT_MAXA 250.000000000000000000 9 +1 1 WPNAV_LOIT_MINA 25.000000000000000000 9 +1 1 WPNAV_LOIT_SPEED 500.000000000000000000 9 +1 1 WPNAV_RADIUS 200.000000000000000000 9 +1 1 WPNAV_SPEED 500.000000000000000000 9 +1 1 WPNAV_SPEED_DN 150.000000000000000000 9 +1 1 WPNAV_SPEED_UP 250.000000000000000000 9 +1 1 WP_TKOFF_NAV_ALT 0.000000000000000000 9 +1 1 WP_YAW_BEHAVIOR 2 2 +1 1 XTRACK_ANG_LIM 45 2 -- GitLab From c8d67bdc8823a53bec73e123f47b3fe16ca8b8c6 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Sat, 22 Oct 2016 16:11:29 -0400 Subject: [PATCH 004/398] PX4 param and airframe metadata update (#4169) --- .../PX4/AirframeFactMetaData.xml | 183 +-- .../PX4/PX4ParameterFactMetaData.xml | 1296 ++++++++++------- 2 files changed, 852 insertions(+), 627 deletions(-) diff --git a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml index 67f947e67..ce0cb02a9 100644 --- a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml +++ b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml @@ -14,10 +14,10 @@ - + Simon Wilks <simon@px4.io> Flying Wing - https://pixhawk.org/platforms/planes/bormatec_camflyer_q + http://www.sparkletech.hk/ feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -47,10 +47,14 @@ right aileron throttle - - Lorenz Meier <lorenz@px4.io> + + Simon Wilks <simon@px4.io> Flying Wing - https://pixhawk.org/platforms/planes/z-84_wing_wing + + + Simon Wilks <simon@px4.io> + Flying Wing + https://pixhawk.org/platforms/planes/bormatec_camflyer_q feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -58,18 +62,14 @@ right aileron throttle - - Simon Wilks <simon@px4.io> - Flying Wing - Simon Wilks <simon@px4.io> Flying Wing - - Simon Wilks <simon@px4.io> + + Lorenz Meier <lorenz@px4.io> Flying Wing - http://www.sparkletech.hk/ + https://pixhawk.org/platforms/planes/z-84_wing_wing feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -82,6 +82,12 @@ Flying Wing + + + Bart Slinger <bartslinger@gmail.com> + Helicopter + + Anton Babushkin <anton@px4.io> @@ -177,16 +183,12 @@ Anton Babushkin <anton@px4.io> Quadrotor Wide - - Thomas Gubler <thomas@px4.io> - Quadrotor Wide - Simon Wilks <simon@px4.io> Quadrotor Wide - - Anton Matosov <anton.matosov@gmail.com> + + Thomas Gubler <thomas@px4.io> Quadrotor Wide @@ -195,75 +197,90 @@ Lorenz Meier <lorenz@px4.io> Quadrotor x - + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - James Goppert <james.goppert@gmail.com> + + Pavel Kirienko <pavel@px4.io> + Quadrotor x + + + Leon Mueller <thedevleon> + Quadrotor x + + + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Lorenz Meier <lorenz@px4.io> + + Blankered Quadrotor x - - Mark Whitehorn <kd0aij@gmail.com> + + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Lorenz Meier <lorenz@px4.io> + + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Lorenz Meier <lorenz@px4.io> + + James Goppert <james.goppert@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Pavel Kirienko <pavel@px4.io> + + Lorenz Meier <lorenz@px4.io> Quadrotor x Thomas Gubler <thomas@px4.io> Quadrotor x - - Andreas Antener <andreas@uaventure.com> - Quadrotor x - - - Blankered - Quadrotor x - - + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - + James Goppert <james.goppert@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel + + Quadrotor x + + + Michael Schaeuble + Quadrotor x + + + Leon Mueller <thedevleon> + Quadrotor x + + + Andreas Antener <andreas@uaventure.com> + Quadrotor x + @@ -271,6 +288,10 @@ + + Anton Babushkin <anton@px4.io> + Simulation + Lorenz Meier <lorenz@px4.io> Simulation @@ -279,28 +300,12 @@ rudder throttle - - Anton Babushkin <anton@px4.io> - Simulation - Anton Babushkin <anton@px4.io> Simulation - - Thomas Gubler <thomas@px4.io> - Simulation - - - Thomas Gubler <thomas@px4.io> - Simulation - - - Lorenz Meier <lorenz@px4.io> - Standard Plane - Lorenz Meier <lorenz@px4.io> Standard Plane @@ -313,7 +318,25 @@ throttle flaps - + + Andreas Antener <andreas@uaventure.com> + Standard Plane + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel + aileron + aileron + elevator + rudder + throttle + wheel + flaps + + + Lorenz Meier <lorenz@px4.io> + Standard Plane + + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel @@ -335,7 +358,7 @@ elevator throttle - + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel @@ -347,30 +370,8 @@ rudder flaps - - Andreas Antener <andreas@uaventure.com> - Standard Plane - feed-through of RC AUX1 channel - feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - aileron - aileron - elevator - rudder - throttle - wheel - flaps - - - Simon Wilks <simon@uaventure.com> - Standard VTOL - - - Simon Wilks <simon@uaventure.com> - Standard VTOL - Sander Smeets <sander@droneslab.com> Standard VTOL @@ -379,6 +380,14 @@ Sander Smeets <sander@droneslab.com> Standard VTOL + + Simon Wilks <simon@uaventure.com> + Standard VTOL + + + Simon Wilks <simon@uaventure.com> + Standard VTOL + Andreas Antener <andreas@uaventure.com> Standard VTOL @@ -407,24 +416,24 @@ - + Roman Bapst <roman@px4.io> VTOL Quad Tailsitter - + Roman Bapst <roman@px4.io> VTOL Quad Tailsitter - - Roman Bapst <roman@px4.io> - VTOL Tiltrotor - Samay Siga <samay_s@icloud.com> VTOL Tiltrotor + + Roman Bapst <roman@px4.io> + VTOL Tiltrotor + diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index b68ea44a4..83682333a 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -446,9 +446,9 @@ velocity Distance, mission controlled - + Camera trigger pin - Selects which pin is used, ranges from 1 to 6 (AUX1-AUX6 on px4fmu-v2 and the rail pins on px4fmu-v4). The PWM interface takes two pins per camera, while relay triggers on every pin individually. Example: Value 34 would trigger on pins 3 and 4. + Selects which pin is used, ranges from 1 to 6 (AUX1-AUX6 on px4fmu-v2 and the rail pins on px4fmu-v4). The PWM interface takes two pins per camera, while relay triggers on every pin individually. Example: Value 56 would trigger on pins 5 and 6. 1 123456 0 @@ -647,7 +647,7 @@ velocity Time-out for auto disarm after landing - A non-zero, positive value specifies the time-out period in seconds after which the vehicle will be automatically disarmed in case a landing situation has been detected during this period. A value of zero means that automatic disarming is disabled. + A non-zero, positive value specifies the time-out period in seconds after which the vehicle will be automatically disarmed in case a landing situation has been detected during this period. The vehicle will also auto-disarm right after arming if it has not even flown, however the time will be longer by a factor of 5. A value of zero means that automatic disarming is disabled. 0 20 s @@ -689,6 +689,31 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action + + Airfield home Lat + Latitude of airfield home waypoint + -900000000 + 900000000 + deg * 1e7 + modules/navigator + + + Airfield home Lon + Longitude of airfield home waypoint + -1800000000 + 1800000000 + deg * 1e7 + modules/navigator + + + Airfield home alt + Altitude of airfield home waypoint + -50 + m + 1 + 0.5 + modules/navigator + Comms hold wait time The amount of time in seconds the system should wait at the comms hold waypoint @@ -748,31 +773,6 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action modules/navigator - - Airfield home Lat - Latitude of airfield home waypoint - -900000000 - 900000000 - deg * 1e7 - modules/navigator - - - Airfield home Lon - Longitude of airfield home waypoint - -1800000000 - 1800000000 - deg * 1e7 - modules/navigator - - - Airfield home alt - Altitude of airfield home waypoint - -50 - m - 1 - 0.5 - modules/navigator - @@ -1313,7 +1313,7 @@ value will determine the minimum airspeed which will still be fused 1 modules/ekf2 - + Time constant of the velocity output prediction and smoothing filter 1.0 s @@ -1900,6 +1900,16 @@ value will determine the minimum airspeed which will still be fused + + Cruise Airspeed + The fixed wing controller tries to fly at this airspeed. + 0.0 + 40 + m/s + 1 + 0.5 + modules/navigator + Minimum Airspeed If the airspeed falls below this value, the TECS controller will try to increase airspeed more aggressively. @@ -2069,16 +2079,6 @@ value will determine the minimum airspeed which will still be fused 0.01 modules/fw_pos_control_l1 - - Cruise Airspeed - The fixed wing controller tries to fly at this airspeed. - 0.0 - 40 - m/s - 1 - 0.5 - modules/navigator - @@ -2213,45 +2213,25 @@ but also ignore less noise 1 modules/navigator - + Max horizontal distance in meters - Set to > 0 to activate a geofence action if horizontal distance to home exceeds this value. - -1 - 5000 + Maximum horizontal distance in meters the vehicle can be from home before triggering a geofence action. Disabled if 0. + 0 + 10000 m 1 modules/navigator - + Max vertical distance in meters - Set to > 0 to activate a geofence action if vertical distance to home exceeds this value. - -1 + Maximum vertical distance in meters the vehicle can be from home before triggering a geofence action. Disabled if 0. + 0 + 10000 m 1 modules/navigator - - - Consider mount operation mode - If set to 1, mount mode will be enforced. - - drivers/gimbal - - - Auxiliary switch to set mount operation mode - Set to 0 to disable manual mode control. If set to an auxiliary switch: Switch off means the gimbal is put into safe/locked position. Switch on means the gimbal can move freely, and landing gear will be retracted if applicable. - 0 - 3 - drivers/gimbal - - AUX1 - Disable - AUX3 - AUX2 - - - Multicopter max climb rate @@ -2376,6 +2356,11 @@ but also ignore less noise + + Publish AGL as Z + + modules/local_position_estimator + Optical flow z offset from center -1 @@ -2384,15 +2369,23 @@ but also ignore less noise 3 modules/local_position_estimator - - Optical flow xy standard deviation - 0.01 + + Optical flow scale + 0.1 + 10.0 + m + 3 + modules/local_position_estimator + + + Optical flow gyro compensation + -1 1 m 3 modules/local_position_estimator - + Optical flow minimum quality threshold 0 255 @@ -2431,7 +2424,7 @@ but also ignore less noise 3 modules/local_position_estimator - + Accelerometer xy noise density Data sheet noise density = 150ug/sqrt(Hz) = 0.0015 m/s^2/sqrt(Hz) Larger than data sheet to account for tilt error. 0.00001 @@ -2440,7 +2433,7 @@ but also ignore less noise 4 modules/local_position_estimator - + Accelerometer z noise density Data sheet noise density = 150ug/sqrt(Hz) = 0.0015 m/s^2/sqrt(Hz) 0.00001 @@ -2519,7 +2512,15 @@ EPV used if greater than this value 3 modules/local_position_estimator - + + Vision delay compensaton + 0 + 0.1 + sec + 2 + modules/local_position_estimator + + Vision xy standard deviation 0.01 1 @@ -2540,12 +2541,12 @@ EPV used if greater than this value modules/local_position_estimator - + Vicon position standard deviation - 0.01 + 0.0001 1 m - 3 + 4 modules/local_position_estimator @@ -2574,15 +2575,24 @@ EPV used if greater than this value 8 modules/local_position_estimator + + Terrain random walk noise density, hilly/outdoor (0.1), flat/Indoor (0.001) + 0 + 1 + (m/s)/(sqrt(hz)) + 3 + modules/local_position_estimator + - Terrain maximum percent grade, hilly/outdoor (100 = 45 deg), flat/Indoor (0 = 0 deg) + Terrain maximum percent grade, hilly/outdoor (100 = 45 deg), flat/Indoor (0 = 0 deg) +Used to calculate increased terrain random walk nosie due to movement 0 100 % 3 modules/local_position_estimator - + Flow gyro high pass filter cut off frequency 0 2 @@ -2614,12 +2624,12 @@ EPV used if greater than this value 0 modules/local_position_estimator - - Required xy standard deviation to publish position - 0.3 - 5.0 - m - 1 + + Required velocity xy standard deviation to publish position + 0.01 + 1.0 + m/s + 3 modules/local_position_estimator @@ -2630,6 +2640,14 @@ EPV used if greater than this value 1 modules/local_position_estimator + + Land detector z standard deviation + 0.001 + 10.0 + m + 3 + modules/local_position_estimator + @@ -2644,7 +2662,7 @@ EPV used if greater than this value 250 modules/mavlink - + MAVLink protocol version modules/mavlink @@ -2742,28 +2760,6 @@ EPV used if greater than this value - - Set offboard loss failsafe mode - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander - - Loiter - Land at current position - Return to Land - - - - Set offboard loss failsafe mode when RC is available - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander - - Altitude control - Position control - Return to Land - Manual - Land at current position - - Take-off altitude This is the minimum altitude the system will take off to. @@ -2915,137 +2911,132 @@ EPV used if greater than this value Return to Land - - - - Roll P gain - Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - examples/mc_att_control_multiplatform - - - Roll rate P gain - Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform + + Set offboard loss failsafe mode + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander + + Loiter + Land at current position + Return to Land + - - Roll rate I gain - Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform + + Set offboard loss failsafe mode when RC is available + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander + + Altitude control + Position control + Return to Land + Manual + Land at current position + - - Roll rate D gain - Roll rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform + + + + Mount input mode +RC uses the AUX input channels (see MNT_MAN_* parameters), +MAVLINK_ROI uses the MAV_CMD_DO_SET_ROI Mavlink message, and MAVLINK_DO_MOUNT the +MAV_CMD_DO_MOUNT_CONFIGURE and MAV_CMD_DO_MOUNT_CONTROL messages to control a mount + 0 + 3 + drivers/vmount + + RC + DISABLE + MAVLINK_DO_MOUNT + MAVLINK_ROI + - - Pitch P gain - Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - 1/s - examples/mc_att_control_multiplatform + + Mount output mode +AUX uses the mixer output Control Group #2. +MAVLINK uses the MAV_CMD_DO_MOUNT_CONFIGURE and MAV_CMD_DO_MOUNT_CONTROL MavLink messages +to control a mount (set MNT_MAV_SYSID & MNT_MAV_COMPID) + 0 + 1 + drivers/vmount + + MAVLINK + AUX + - - Pitch rate P gain - Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform + + Mavlink System ID (if MNT_MODE_OUT is MAVLINK) + drivers/vmount - - Pitch rate I gain - Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform + + Mavlink Component ID (if MNT_MODE_OUT is MAVLINK) + drivers/vmount - - Pitch rate D gain - Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform + + Mixer value for selecting normal mode +if required by the gimbal (only in AUX output mode) + -1.0 + 1.0 + 3 + drivers/vmount - - Yaw P gain - Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - 1/s - examples/mc_att_control_multiplatform + + Mixer value for selecting a locking mode +if required for the gimbal (only in AUX output mode) + -1.0 + 1.0 + 3 + drivers/vmount - - Yaw rate P gain - Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform + + This enables the mount to be manually controlled when no ROI is set + If set to 1, the mount will be controlled by the AUX channels below when no ROI is set. + + drivers/vmount - - Yaw rate I gain - Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform + + Auxiliary channel to control roll (in AUX input or manual mode) + 0 + 5 + drivers/vmount + + AUX1 + Disable + AUX3 + AUX2 + AUX5 + AUX4 + - - Yaw rate D gain - Yaw rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw feed forward - Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. - 0.0 - 1.0 - examples/mc_att_control_multiplatform - - - Max yaw rate - Limit for yaw rate, has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. - 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform - - - Max acro roll rate - 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform - - - Max acro pitch rate - 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform - - - Max acro yaw rate - 0.0 - deg/s - examples/mc_att_control_multiplatform - - - Max manual roll - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Max manual pitch - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform + + Auxiliary channel to control pitch (in AUX input or manual mode) + 0 + 5 + drivers/vmount + + AUX1 + Disable + AUX3 + AUX2 + AUX5 + AUX4 + - - Max manual yaw rate - 0.0 - deg/s - examples/mc_pos_control_multiplatform + + Auxiliary channel to control yaw (in AUX input or manual mode) + 0 + 5 + drivers/vmount + + AUX1 + Disable + AUX3 + AUX2 + AUX5 + AUX4 + + + Roll time constant Reduce if the system is too twitchy, increase if the response is too slow and sluggish. @@ -3278,114 +3269,155 @@ EPV used if greater than this value 0.01 modules/mc_att_control - - - - Minimum thrust - Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. + + Threshold for Throttle PID Attenuation (TPA) + Magnitude of throttle setpoint at which to begin attenuating roll/pitch P gain 0.0 1.0 - examples/mc_pos_control_multiplatform + 2 + 0.1 + modules/mc_att_control - - Maximum thrust - Limit max allowed thrust. + + Slope for Throttle PID Attenuation (TPA) + Rate at which to attenuate roll/pitch P gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - slope*(abs(throttle)-breakpoint)) 0.0 - 1.0 - examples/mc_pos_control_multiplatform + 2.0 + 2 + 0.1 + modules/mc_att_control - - Proportional gain for vertical position error + + Max manual roll 0.0 + 90.0 + deg examples/mc_pos_control_multiplatform - - Proportional gain for vertical velocity error + + Max manual pitch 0.0 + 90.0 + deg examples/mc_pos_control_multiplatform - - Integral gain for vertical velocity error - Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. + + Max manual yaw rate 0.0 + deg/s examples/mc_pos_control_multiplatform - - Differential gain for vertical velocity error + + Roll P gain + Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Maximum vertical velocity - Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL). + + Roll rate P gain + Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. 0.0 - m/s - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Vertical velocity feed forward - Feed forward weight for altitude control in stabilized modes (ALTCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Roll rate I gain + Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. 0.0 - 1.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Proportional gain for horizontal position error + + Roll rate D gain + Roll rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. 0.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Proportional gain for horizontal velocity error + + Pitch P gain + Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 - examples/mc_pos_control_multiplatform + 1/s + examples/mc_att_control_multiplatform - - Integral gain for horizontal velocity error - Non-zero value allows to resist wind. + + Pitch rate P gain + Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. 0.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again + + Pitch rate I gain + Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. 0.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Maximum horizontal velocity - Maximum horizontal velocity in AUTO mode and endpoint for position stabilized mode (POSCTRL). + + Pitch rate D gain + Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. 0.0 - m/s - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Horizontal velocity feed forward - Feed forward weight for position control in position control mode (POSCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Yaw P gain + Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. + 0.0 + 1/s + examples/mc_att_control_multiplatform + + + Yaw rate P gain + Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. + 0.0 + examples/mc_att_control_multiplatform + + + Yaw rate I gain + Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. + 0.0 + examples/mc_att_control_multiplatform + + + Yaw rate D gain + Yaw rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. + 0.0 + examples/mc_att_control_multiplatform + + + Yaw feed forward + Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. 0.0 1.0 - examples/mc_pos_control_multiplatform + examples/mc_att_control_multiplatform - - Maximum tilt angle in air - Limits maximum tilt in AUTO and POSCTRL modes during flight. + + Max yaw rate + Limit for yaw rate, has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform + 360.0 + deg/s + examples/mc_att_control_multiplatform - - Maximum tilt during landing - Limits maximum tilt angle on landing. + + Max acro roll rate 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform + 360.0 + deg/s + examples/mc_att_control_multiplatform - - Landing descend rate + + Max acro pitch rate 0.0 - m/s - examples/mc_pos_control_multiplatform + 360.0 + deg/s + examples/mc_att_control_multiplatform + + + Max acro yaw rate + 0.0 + deg/s + examples/mc_att_control_multiplatform + + Minimum thrust in auto thrust control It's recommended to set it > 0 to avoid free fall with zero thrust. @@ -3647,40 +3679,207 @@ EPV used if greater than this value Maximum vertical velocity for which position hold is enabled (use 0 to disable check) 0.0 - 3.0 + 3.0 + m/s + 2 + modules/mc_pos_control + + + Low pass filter cut freq. for numerical velocity derivative + 0.0 + 10 + Hz + 2 + modules/mc_pos_control + + + Maximum horizonal acceleration in velocity controlled modes + 2.0 + 15.0 + m/s/s + 2 + 1 + modules/mc_pos_control + + + Altitude control mode, note mode 1 only tested with LPE + 0 + 1 + modules/mc_pos_control + + Terrain following + Altitude following + + + + Minimum thrust + Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. + 0.0 + 1.0 + examples/mc_pos_control_multiplatform + + + Maximum thrust + Limit max allowed thrust. + 0.0 + 1.0 + examples/mc_pos_control_multiplatform + + + Proportional gain for vertical position error + 0.0 + examples/mc_pos_control_multiplatform + + + Proportional gain for vertical velocity error + 0.0 + examples/mc_pos_control_multiplatform + + + Integral gain for vertical velocity error + Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. + 0.0 + examples/mc_pos_control_multiplatform + + + Differential gain for vertical velocity error + 0.0 + examples/mc_pos_control_multiplatform + + + Maximum vertical velocity + Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL). + 0.0 + m/s + examples/mc_pos_control_multiplatform + + + Vertical velocity feed forward + Feed forward weight for altitude control in stabilized modes (ALTCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + 0.0 + 1.0 + examples/mc_pos_control_multiplatform + + + Proportional gain for horizontal position error + 0.0 + examples/mc_pos_control_multiplatform + + + Proportional gain for horizontal velocity error + 0.0 + examples/mc_pos_control_multiplatform + + + Integral gain for horizontal velocity error + Non-zero value allows to resist wind. + 0.0 + examples/mc_pos_control_multiplatform + + + Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again + 0.0 + examples/mc_pos_control_multiplatform + + + Maximum horizontal velocity + Maximum horizontal velocity in AUTO mode and endpoint for position stabilized mode (POSCTRL). + 0.0 + m/s + examples/mc_pos_control_multiplatform + + + Horizontal velocity feed forward + Feed forward weight for position control in position control mode (POSCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + 0.0 + 1.0 + examples/mc_pos_control_multiplatform + + + Maximum tilt angle in air + Limits maximum tilt in AUTO and POSCTRL modes during flight. + 0.0 + 90.0 + deg + examples/mc_pos_control_multiplatform + + + Maximum tilt during landing + Limits maximum tilt angle on landing. + 0.0 + 90.0 + deg + examples/mc_pos_control_multiplatform + + + Landing descend rate + 0.0 m/s - 2 - modules/mc_pos_control + examples/mc_pos_control_multiplatform - - Low pass filter cut freq. for numerical velocity derivative - 0.0 - 10 - Hz - 2 - modules/mc_pos_control + + + + Set the minimum PWM for the MAIN outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for industry default or 900 to increase servo travel. + 800 + 1400 + us + true + modules/sensors - - Maximum horizonal acceleration in velocity controlled modes - 2.0 - 15.0 - m/s/s - 2 - 1 - modules/mc_pos_control + + Set the maximum PWM for the MAIN outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for industry default or 2100 to increase servo travel. + 1600 + 2200 + us + true + modules/sensors - - Altitude control mode, note mode 1 only tested with LPE + + Set the disarmed PWM for MAIN outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. 0 - 1 - modules/mc_pos_control - - Terrain following - Altitude following - + 2200 + us + true + modules/sensors + + + Set the minimum PWM for the MAIN outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for default or 900 to increase servo travel + 800 + 1400 + us + true + modules/sensors + + + Set the maximum PWM for the MAIN outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for default or 2100 to increase servo travel + 1600 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for AUX outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. + 0 + 2200 + us + true + modules/sensors + + + Minimum motor rise time (slew rate limit) + Minimum time allowed for the motor input signal to pass through a range of 1000 PWM units. A value x means that the motor signal can only go from 1000 to 2000 PWM in maximum x seconds. Zero means that slew rate limiting is disabled. + 0.0 + s/(1000*PWM) + modules/sensors - - Invert direction of aux output channel 1 Set to 1 to invert the channel, 0 for default direction. @@ -3785,60 +3984,6 @@ EPV used if greater than this value drivers/px4io - - Set the minimum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for industry default or 900 to increase servo travel. - 800 - 1400 - us - true - modules/sensors - - - Set the maximum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for industry default or 2100 to increase servo travel. - 1600 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. - 0 - 2200 - us - true - modules/sensors - - - Set the minimum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for default or 900 to increase servo travel - 800 - 1400 - us - true - modules/sensors - - - Set the maximum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for default or 2100 to increase servo travel - 1600 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for AUX outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. - 0 - 2200 - us - true - modules/sensors - @@ -4256,33 +4401,6 @@ EPV used if greater than this value - - Roll trim - The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. - -0.25 - 0.25 - 2 - 0.01 - modules/commander - - - Pitch trim - The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. - -0.25 - 0.25 - 2 - 0.01 - modules/commander - - - Yaw trim - The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. - -0.25 - 0.25 - 2 - 0.01 - modules/commander - RC Channel 1 Minimum Minimum value for RC channel 1 @@ -5389,6 +5507,33 @@ EPV used if greater than this value 2000 modules/sensors + + Roll trim + The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. + -0.25 + 0.25 + 2 + 0.01 + modules/commander + + + Pitch trim + The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. + -0.25 + 0.25 + 2 + 0.01 + modules/commander + + + Yaw trim + The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. + -0.25 + 0.25 + 2 + 0.01 + modules/commander + @@ -5701,6 +5846,33 @@ EPV used if greater than this value Channel 8 + + Landing gear switch channel + 0 + 18 + modules/sensors + + Channel 11 + Channel 10 + Channel 13 + Channel 12 + Channel 15 + Channel 14 + Channel 17 + Channel 16 + Channel 18 + Channel 1 + Unassigned + Channel 3 + Channel 2 + Channel 5 + Channel 4 + Channel 7 + Channel 6 + Channel 9 + Channel 8 + + Threshold for selecting assist mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th @@ -5771,6 +5943,13 @@ EPV used if greater than this value 1 modules/sensors + + Threshold for the landing gear switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + @@ -5900,14 +6079,6 @@ FW_AIRSPD_MIN * RWTO_AIRSPD_SCL - - UTC offset (unit: min) - the difference in hours and minutes from Coordinated Universal Time (UTC) for a your place and date. for example, In case of South Korea(UTC+09:00), UTC offset is 540 min (9*60) refer to https://en.wikipedia.org/wiki/List_of_UTC_time_offsets - -1000 - 1000 - min - modules/logger - Logging rate A value of -1 indicates the commandline argument should be obeyed. A value of 0 sets the minimum rate, any other value is interpreted as rate in Hertz. This parameter is only read out before logging starts (which commonly is before arming). @@ -5948,6 +6119,14 @@ This is used for gathering replay logs for the ekf2 module Medium priority + + UTC offset (unit: min) + the difference in hours and minutes from Coordinated Universal Time (UTC) for a your place and date. for example, In case of South Korea(UTC+09:00), UTC offset is 540 min (9*60) refer to https://en.wikipedia.org/wiki/List_of_UTC_time_offsets + -1000 + 1000 + min + modules/logger + @@ -6543,6 +6722,12 @@ This is used for gathering replay logs for the ekf2 module true modules/sensors + + TeraRanger One (trone) + + true + modules/sensors + Lightware SF1xx laser rangefinder 0 @@ -6597,19 +6782,34 @@ This is used for gathering replay logs for the ekf2 module ms examples/subscriber - - Float Demonstration Parameter in the Example - examples/subscriber + + Float Demonstration Parameter in the Example + examples/subscriber + + + + + Operating channel of the NRF51 + 0 + 125 + modules/syslink + + + Operating datarate of the NRF51 + 0 + 2 + modules/syslink + + + Operating address of the NRF51 (most significant byte) + modules/syslink + + + Operating address of the NRF51 (least significant 4 bytes) + modules/syslink - - RGB Led brightness limit - Set to 0 to disable, 1 for minimum brightness up to 15 (max) - 0 - 15 - drivers/rgbled - Auto-start script index CHANGING THIS VALUE REQUIRES A RESTART. Defines the auto-start script used to bootstrap the system. @@ -6671,13 +6871,16 @@ This is used for gathering replay logs for the ekf2 module modules/systemlib FrSky Telemetry + Crazyflie (Syslink) Normal Telemetry (57600 baud, 8N1) Command Receiver (57600 baud, 8N1) OSD (57600 baud, 8N1) + Normal Telemetry (38400 baud, 8N1) Disabled ESP8266 (921600 baud, 8N1) Companion Link (57600 baud, 8N1) Companion Link (921600 baud, 8N1) + Normal Telemetry (19200 baud, 8N1) @@ -6697,6 +6900,13 @@ This is used for gathering replay logs for the ekf2 module sdlog2 (default) + + RGB Led brightness limit + Set to 0 to disable, 1 for minimum brightness up to 15 (max) + 0 + 15 + drivers/rgbled + @@ -6780,73 +6990,14 @@ This is used for gathering replay logs for the ekf2 module bit/s modules/uavcan - - - - Target throttle value for pusher/puller motor during the transition to fw mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Maximum allowed down-pitch the controller is able to demand. This prevents large, negative -lift values being created when facing strong winds. The vehicle will use the pusher motor -to accelerate forward if necessary - 0.0 - 45.0 - modules/vtol_att_control - - - Fixed wing thrust scale for hover forward flight - Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. - 0.0 - 2.0 - modules/vtol_att_control - - - Position of tilt servo in mc mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in transition mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in fw mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Duration of front transition phase 2 - Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. - 0.1 - 5.0 - s - 3 - 0.01 - modules/vtol_att_control - - - The channel number of motors that must be turned off in fixed wing mode + + UAVCAN ESC will spin at idle throttle when armed, even if the mixer outputs zero setpoints 0 - 12345678 - 0 - 1 - modules/vtol_att_control + 1 + modules/uavcan + + VTOL number of engines 0 @@ -7040,6 +7191,71 @@ to accelerate forward if necessary 200.0 modules/vtol_att_control + + Position of tilt servo in mc mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Position of tilt servo in transition mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Position of tilt servo in fw mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Duration of front transition phase 2 + Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. + 0.1 + 5.0 + s + 3 + 0.01 + modules/vtol_att_control + + + The channel number of motors that must be turned off in fixed wing mode + 0 + 12345678 + 0 + 1 + modules/vtol_att_control + + + Target throttle value for pusher/puller motor during the transition to fw mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Maximum allowed down-pitch the controller is able to demand. This prevents large, negative +lift values being created when facing strong winds. The vehicle will use the pusher motor +to accelerate forward if necessary + 0.0 + 45.0 + modules/vtol_att_control + + + Fixed wing thrust scale for hover forward flight + Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. + 0.0 + 2.0 + modules/vtol_att_control + @@ -7297,21 +7513,49 @@ Maps the change of airspeed error to the acceleration setpoint - - EXFW_HDNG_P - examples/fixedwing_control + + SEG_TH2V_P + modules/segway - - EXFW_ROLL_P - examples/fixedwing_control + + SEG_TH2V_I + modules/segway - - EXFW_PITCH_P - examples/fixedwing_control + + SEG_TH2V_I_MAX + modules/segway - - RV_YAW_P - examples/rover_steering_control + + SEG_Q2V + modules/segway + + + Failsafe channel mapping + The RC mapping index indicates which channel is used for failsafe If 0, whichever channel is mapped to throttle is used otherwise the value indicates the specific rc channel to use + 0 + 18 + modules/sensors + + Channel 11 + Channel 10 + Channel 13 + Channel 12 + Channel 15 + Channel 14 + Channel 17 + Channel 16 + Channel 18 + Channel 1 + Unassigned + Channel 3 + Channel 2 + Channel 5 + Channel 4 + Channel 7 + Channel 6 + Channel 9 + Channel 8 + First flightmode slot (1000-1160) @@ -7439,49 +7683,21 @@ Maps the change of airspeed error to the acceleration setpoint Stabilized - - SEG_TH2V_P - modules/segway - - - SEG_TH2V_I - modules/segway + + EXFW_HDNG_P + examples/fixedwing_control - - SEG_TH2V_I_MAX - modules/segway + + EXFW_ROLL_P + examples/fixedwing_control - - SEG_Q2V - modules/segway + + EXFW_PITCH_P + examples/fixedwing_control - - Failsafe channel mapping - The RC mapping index indicates which channel is used for failsafe If 0, whichever channel is mapped to throttle is used otherwise the value indicates the specific rc channel to use - 0 - 18 - modules/sensors - - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 - + + RV_YAW_P + examples/rover_steering_control -- GitLab From ca28b596f0377ec239d9507bf75d0e5d5aff5248 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Sun, 23 Oct 2016 20:16:37 +0200 Subject: [PATCH 005/398] MAVLink: Update to most recent spec --- libs/mavlink/include/mavlink/v1.0 | 2 +- libs/mavlink/include/mavlink/v2.0 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/mavlink/include/mavlink/v1.0 b/libs/mavlink/include/mavlink/v1.0 index e93ac6298..13a478092 160000 --- a/libs/mavlink/include/mavlink/v1.0 +++ b/libs/mavlink/include/mavlink/v1.0 @@ -1 +1 @@ -Subproject commit e93ac62981a338a7c823364e7c4ff1077e3f8fc1 +Subproject commit 13a478092fc9c2faa90c553c362e799ba3bad4e8 diff --git a/libs/mavlink/include/mavlink/v2.0 b/libs/mavlink/include/mavlink/v2.0 index 36f37bde1..094e765e6 160000 --- a/libs/mavlink/include/mavlink/v2.0 +++ b/libs/mavlink/include/mavlink/v2.0 @@ -1 +1 @@ -Subproject commit 36f37bde1df2dd36661fbcbd6ed5aaf0424c8269 +Subproject commit 094e765e6353390ac2973023287b7022e696a57e -- GitLab From 5c94cda474910d15b50586652156a9347337ea83 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 20 Oct 2016 12:35:45 -0700 Subject: [PATCH 006/398] Only run unit tests on pull requests --- .travis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 75f8b0e18..37f8ca580 100644 --- a/.travis.yml +++ b/.travis.yml @@ -174,12 +174,14 @@ script: #- ccache -s # unit tests linux/osx - - if [[ "${SPEC}" = "linux-g++-64" && "${CONFIG}" = "debug" ]]; then - mkdir -p ~/.config/QtProject/ && cp ${TRAVIS_BUILD_DIR}/test/qtlogging.ini ~/.config/QtProject/ && - ./debug/QGroundControl --unittest; - elif [[ "${SPEC}" = "macx-clang" && "${CONFIG}" = "debug" ]]; then - mkdir -p ~/Library/Preferences/QtProject/ && cp ${TRAVIS_BUILD_DIR}/test/qtlogging.ini ~/Library/Preferences/QtProject/ && - ./debug/qgroundcontrol.app/Contents/MacOS/QGroundControl --unittest; + - if [ "${TRAVIS_BRANCH}" != "master" ]; then + if [[ "${SPEC}" = "linux-g++-64" && "${CONFIG}" = "debug" ]]; then + mkdir -p ~/.config/QtProject/ && cp ${TRAVIS_BUILD_DIR}/test/qtlogging.ini ~/.config/QtProject/ && + ./debug/QGroundControl --unittest; + elif [[ "${SPEC}" = "macx-clang" && "${CONFIG}" = "debug" ]]; then + mkdir -p ~/Library/Preferences/QtProject/ && cp ${TRAVIS_BUILD_DIR}/test/qtlogging.ini ~/Library/Preferences/QtProject/ && + ./debug/qgroundcontrol.app/Contents/MacOS/QGroundControl --unittest; + fi fi after_success: -- GitLab From f91c5bef0813f0aabd4939278d0c3e1c536b1868 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 24 Oct 2016 21:32:22 -0400 Subject: [PATCH 007/398] Setting up Mavlink logging Start/Stop (and receiving the data) --- src/Vehicle/Vehicle.cc | 63 +++++++++++++ src/Vehicle/Vehicle.h | 10 +++ src/uas/MavlinkLogManager.cc | 120 ++++++++++++++++++++++--- src/uas/MavlinkLogManager.h | 39 +++++--- src/uas/UASInterface.h | 1 + src/ui/preferences/MavlinkSettings.qml | 57 +++++++++++- 6 files changed, 264 insertions(+), 26 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 09480d7bd..ff154aa86 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -400,6 +400,7 @@ Vehicle::resetCounters() void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) { + if (message.sysid != _id && message.sysid != 0) { return; } @@ -488,6 +489,12 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes case MAVLINK_MSG_ID_HIL_ACTUATOR_CONTROLS: _handleHilActuatorControls(message); break; + case MAVLINK_MSG_ID_LOGGING_DATA: + _handleMavlinkLoggingData(message); + break; + case MAVLINK_MSG_ID_LOGGING_DATA_ACKED: + _handleMavlinkLoggingDataAcked(message); + break; // Following are ArduPilot dialect messages @@ -1959,6 +1966,62 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject* parent) _courseOverGroundFact.setRawValue(std::numeric_limits::quiet_NaN()); } +//----------------------------------------------------------------------------- +void +Vehicle::startMavlinkLog() +{ + doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_START); +} + +//----------------------------------------------------------------------------- +void +Vehicle::stopMavlinkLog() +{ + doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_STOP); +} + +//----------------------------------------------------------------------------- +void +Vehicle::_ackMavlinkLogData(uint16_t sequence) +{ + mavlink_message_t msg; + mavlink_logging_ack_t ack; + ack.sequence = sequence; + ack.target_component = defaultComponentId(); + ack.target_system = id(); + mavlink_msg_logging_ack_encode_chan( + _mavlink->getSystemId(), + _mavlink->getComponentId(), + priorityLink()->mavlinkChannel(), + &msg, + &ack); + sendMessageOnLink(priorityLink(), msg); +} + +//----------------------------------------------------------------------------- +void +Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) +{ + qDebug() << "MAVLINK_MSG_ID_LOGGING_DATA"; + mavlink_logging_data_t log; + mavlink_msg_logging_data_decode(&message, &log); + emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.length, log.first_message_offset, log.data, false); +} + +//----------------------------------------------------------------------------- +void +Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) +{ + qDebug() << "MAVLINK_MSG_ID_LOGGING_DATA_ACKED"; + mavlink_logging_data_t log; + mavlink_msg_logging_data_decode(&message, &log); + _ackMavlinkLogData(log.sequence); + emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.length, log.first_message_offset, log.data, true); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + void VehicleGPSFactGroup::setVehicle(Vehicle* vehicle) { _vehicle = vehicle; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index c2b3bfbed..808d65092 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -485,6 +485,10 @@ public: int flowImageIndex() { return _flowImageIndex; } + //-- Mavlink Logging + void startMavlinkLog(); + void stopMavlinkLog(); + /// Requests the specified data stream from the vehicle /// @param stream Stream which is being requested /// @param rate Rate at which to send stream in Hz @@ -638,6 +642,9 @@ signals: void mavlinkScaledImu2(mavlink_message_t message); void mavlinkScaledImu3(mavlink_message_t message); + // Mavlink Log Download + void mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool acked); + private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); void _linkInactiveOrDeleted(LinkInterface* link); @@ -695,6 +702,9 @@ private: void _connectionActive(void); void _say(const QString& text); QString _vehicleIdSpeech(void); + void _handleMavlinkLoggingData(mavlink_message_t& message); + void _handleMavlinkLoggingDataAcked(mavlink_message_t& message); + void _ackMavlinkLogData(uint16_t sequence); private: int _id; ///< Mavlink system id diff --git a/src/uas/MavlinkLogManager.cc b/src/uas/MavlinkLogManager.cc index 2b45f8a23..9563dd1a4 100644 --- a/src/uas/MavlinkLogManager.cc +++ b/src/uas/MavlinkLogManager.cc @@ -21,13 +21,13 @@ 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"; - +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* kEnableAutoUploadKey = "EnableAutoUploadKey"; +static const char* kEnableAutoStartKey = "EnableAutoStartKey"; //----------------------------------------------------------------------------- MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager *manager, const QString& filePath) @@ -70,16 +70,20 @@ MavlinkLogFiles::setProgress(qreal progress) //----------------------------------------------------------------------------- MavlinkLogManager::MavlinkLogManager(QGCApplication* app) : QGCTool(app) - , _enableAutolog(true) + , _enableAutoUpload(true) + , _enableAutoStart(true) , _nam(NULL) , _currentLogfile(NULL) + , _vehicle(NULL) + , _logRunning(false) { //-- 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()); + setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool()); + setEnableAutoStart(settings.value(kEnableAutoStartKey, true).toBool()); //-- Logging location _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); _logPath += "/MavlinkLogs"; @@ -109,6 +113,7 @@ MavlinkLogManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); + connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); // _uploadURL = "http://192.168.1.21/px4"; // _uploadURL = "http://192.168.1.9:8080"; @@ -153,12 +158,22 @@ MavlinkLogManager::setUploadURL(QString url) //----------------------------------------------------------------------------- void -MavlinkLogManager::setEnableAutolog(bool enable) +MavlinkLogManager::setEnableAutoUpload(bool enable) +{ + _enableAutoUpload = enable; + QSettings settings; + settings.setValue(kEnableAutoUploadKey, enable); + emit enableAutoUploadChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setEnableAutoStart(bool enable) { - _enableAutolog = enable; + _enableAutoStart = enable; QSettings settings; - settings.setValue(kEnableAutologKey, enable); - emit enableAutologChanged(); + settings.setValue(kEnableAutoStartKey, enable); + emit enableAutoStartChanged(); } //----------------------------------------------------------------------------- @@ -218,6 +233,28 @@ MavlinkLogManager::cancelUpload() } } +//----------------------------------------------------------------------------- +void +MavlinkLogManager::startLogging() +{ + if(_vehicle) { + _vehicle->startMavlinkLog(); + _logRunning = true; + emit logRunningChanged(); + } +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::stopLogging() +{ + if(_vehicle) { + _vehicle->stopMavlinkLog(); + _logRunning = false; + emit logRunningChanged(); + } +} + //----------------------------------------------------------------------------- QHttpPart create_form_part(const QString& name, const QString& value) @@ -347,3 +384,60 @@ MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) } qCDebug(MavlinkLogManagerLog) << bytesSent << "of" << bytesTotal; } + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) +{ + //-- TODO: This is not quite right. This is being used to detect when a vehicle + // connects/disconnects. In reality, if QGC is connected to multiple vehicles, + // this is called each time the user switches from one vehicle to another. So + // far, I'm working on the assumption that multiple vehicles is a rare exception. + + // Disconnect the previous one (if any) + if (_vehicle) { + disconnect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); + disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); + _vehicle = NULL; + emit canStartLogChanged(); + } + // Connect new system + if (vehicle) + { + _vehicle = vehicle; + connect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); + connect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); + emit canStartLogChanged(); + } +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_mavlinkLogData(Vehicle * /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) +{ + Q_UNUSED(data); + qDebug() << "Mavlink Log:" << sequence << length << first_message; +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_armedChanged(bool armed) +{ + if(_vehicle) { + if(armed) { + if(_enableAutoStart) { + _vehicle->startMavlinkLog(); + _logRunning = true; + emit logRunningChanged(); + } + } else { + if(_logRunning && _enableAutoStart) { + _vehicle->stopMavlinkLog(); + emit logRunningChanged(); + if(_enableAutoUpload) { + //-- TODO: Queue log for auto upload + } + } + } + } +} diff --git a/src/uas/MavlinkLogManager.h b/src/uas/MavlinkLogManager.h index de41bc1ef..3ef3dc585 100644 --- a/src/uas/MavlinkLogManager.h +++ b/src/uas/MavlinkLogManager.h @@ -16,6 +16,7 @@ #include "QmlObjectListModel.h" #include "QGCLoggingCategory.h" #include "QGCToolbox.h" +#include "Vehicle.h" Q_DECLARE_LOGGING_CATEGORY(MavlinkLogManagerLog) @@ -67,29 +68,38 @@ 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_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 enableAutoUpload READ enableAutoUpload WRITE setEnableAutoUpload NOTIFY enableAutoUploadChanged) + Q_PROPERTY(bool enableAutoStart READ enableAutoStart WRITE setEnableAutoStart NOTIFY enableAutoStartChanged) + Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + Q_PROPERTY(bool logRunning READ logRunning NOTIFY logRunningChanged) + Q_PROPERTY(bool canStartLog READ canStartLog NOTIFY canStartLogChanged) + Q_PROPERTY(QmlObjectListModel* logFiles READ logFiles NOTIFY logFilesChanged) Q_INVOKABLE void uploadLog (); Q_INVOKABLE void deleteLog (); Q_INVOKABLE void cancelUpload (); + Q_INVOKABLE void startLogging (); + Q_INVOKABLE void stopLogging (); QString emailAddress () { return _emailAddress; } QString description () { return _description; } QString uploadURL () { return _uploadURL; } - bool enableAutolog () { return _enableAutolog; } + bool enableAutoUpload () { return _enableAutoUpload; } + bool enableAutoStart () { return _enableAutoStart; } bool busy (); + bool logRunning () { return _logRunning; } + bool canStartLog () { return _vehicle != NULL; } QmlObjectListModel* logFiles () { return &_logFiles; } void setEmailAddress (QString email); void setDescription (QString description); void setUploadURL (QString url); - void setEnableAutolog (bool enable); + void setEnableAutoUpload (bool enable); + void setEnableAutoStart (bool enable); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); @@ -98,7 +108,8 @@ signals: void emailAddressChanged (); void descriptionChanged (); void uploadURLChanged (); - void enableAutologChanged (); + void enableAutoUploadChanged (); + void enableAutoStartChanged (); void logFilesChanged (); void selectedCountChanged (); void busyChanged (); @@ -106,11 +117,16 @@ signals: void failed (); void succeed (); void abortUpload (); + void logRunningChanged (); + void canStartLogChanged (); private slots: void _uploadFinished (); void _dataAvailable (); void _uploadProgress (qint64 bytesSent, qint64 bytesTotal); + void _activeVehicleChanged (Vehicle* vehicle); + void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool acked); + void _armedChanged (bool armed); private: bool _sendLog (const QString& logFile); @@ -121,10 +137,13 @@ private: QString _emailAddress; QString _uploadURL; QString _logPath; - bool _enableAutolog; + bool _enableAutoUpload; + bool _enableAutoStart; QNetworkAccessManager* _nam; QmlObjectListModel _logFiles; MavlinkLogFiles* _currentLogfile; + Vehicle* _vehicle; + bool _logRunning; }; #endif diff --git a/src/uas/UASInterface.h b/src/uas/UASInterface.h index cf022f593..10ad42fe8 100644 --- a/src/uas/UASInterface.h +++ b/src/uas/UASInterface.h @@ -307,6 +307,7 @@ signals: // Log Download Signals void logEntry (UASInterface* uas, uint32_t time_utc, uint32_t size, uint16_t id, uint16_t num_logs, uint16_t last_log_num); void logData (UASInterface* uas, uint32_t ofs, uint16_t id, uint8_t count, const uint8_t* data); + }; Q_DECLARE_INTERFACE(UASInterface, "org.qgroundcontrol/1.0") diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 9f0cf1e40..b73fc5620 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -130,6 +130,57 @@ Rectangle { } //----------------------------------------------------------------- //-- Mavlink Logging + Item { + width: __mavlinkRoot.width * 0.8 + height: mavlogLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + id: mavlogLabel + text: qsTr("Vehicle Mavlink Logging") + font.family: ScreenTools.demiboldFontFamily + } + } + Rectangle { + height: mavlogColumn.height + (ScreenTools.defaultFontPixelHeight * 2) + width: __mavlinkRoot.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + Column { + id: mavlogColumn + width: gcsColumn.width + spacing: ScreenTools.defaultFontPixelWidth + anchors.centerIn: parent + //----------------------------------------------------------------- + //-- Enable auto log on arming + QGCCheckBox { + text: qsTr("Enable automatic logging start when vehicle is armed") + checked: QGroundControl.mavlinkLogManager.enableAutoStart + onClicked: { + QGroundControl.mavlinkLogManager.enableAutoStart = checked + } + } + //----------------------------------------------------------------- + //-- Manual Start/Stop + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCButton { + text: "Start Logging" + enabled: !QGroundControl.mavlinkLogManager.logRunning && QGroundControl.mavlinkLogManager.canStartLog + onClicked: QGroundControl.mavlinkLogManager.startLogging() + } + QGCButton { + text: "Stop Logging" + enabled: QGroundControl.mavlinkLogManager.logRunning + onClicked: QGroundControl.mavlinkLogManager.stopLogging() + } + } + } + } + //----------------------------------------------------------------- + //-- Mavlink Logging Item { width: __mavlinkRoot.width * 0.8 height: logLabel.height @@ -137,7 +188,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: logLabel - text: qsTr("Vehicle Mavlink Logging") + text: qsTr("Mavlink Log Uploads") font.family: ScreenTools.demiboldFontFamily } } @@ -214,10 +265,10 @@ Rectangle { //-- Automatic Upload QGCCheckBox { text: qsTr("Enable automatic log uploads") - checked: QGroundControl.mavlinkLogManager.enableAutolog + checked: QGroundControl.mavlinkLogManager.enableAutoUpload enabled: emailField.text !== "" && urlField !== "" onClicked: { - QGroundControl.mavlinkLogManager.enableAutolog = checked + QGroundControl.mavlinkLogManager.enableAutoUpload = checked } } } -- GitLab From 4d23912aa19b92ed78312d3dd2768ac4f33b89c9 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 25 Oct 2016 05:46:46 -0400 Subject: [PATCH 008/398] Receiving and writing logs. Forcing QGC to use Mavlink V2 if vehicle supports it. --- src/Vehicle/Vehicle.cc | 12 +- src/Vehicle/Vehicle.h | 2 +- src/comm/MAVLinkProtocol.cc | 21 +-- src/uas/MavlinkLogManager.cc | 178 ++++++++++++++++++------- src/uas/MavlinkLogManager.h | 6 + src/ui/preferences/MavlinkSettings.qml | 23 ++-- 6 files changed, 168 insertions(+), 74 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index ff154aa86..6a5044b6b 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -481,7 +481,7 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes _handleCommandAck(message); break; case MAVLINK_MSG_ID_AUTOPILOT_VERSION: - _handleAutopilotVersion(message); + _handleAutopilotVersion(link, message); break; case MAVLINK_MSG_ID_WIND_COV: _handleWindCov(message); @@ -508,11 +508,17 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes _uas->receiveMessage(message); } -void Vehicle::_handleAutopilotVersion(mavlink_message_t& message) +void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& message) { mavlink_autopilot_version_t autopilotVersion; mavlink_msg_autopilot_version_decode(&message, &autopilotVersion); + bool isMavlink2 = (autopilotVersion.capabilities & MAV_PROTOCOL_CAPABILITY_MAVLINK2) != 0; + if(isMavlink2) { + mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(link->mavlinkChannel()); + mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; + } + if (autopilotVersion.flight_sw_version != 0) { int majorVersion, minorVersion, patchVersion; FIRMWARE_VERSION_TYPE versionType; @@ -2002,7 +2008,6 @@ Vehicle::_ackMavlinkLogData(uint16_t sequence) void Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) { - qDebug() << "MAVLINK_MSG_ID_LOGGING_DATA"; mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.length, log.first_message_offset, log.data, false); @@ -2012,7 +2017,6 @@ Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) { - qDebug() << "MAVLINK_MSG_ID_LOGGING_DATA_ACKED"; mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); _ackMavlinkLogData(log.sequence); diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 808d65092..372a848e5 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -692,7 +692,7 @@ private: void _handleVibration(mavlink_message_t& message); void _handleExtendedSysState(mavlink_message_t& message); void _handleCommandAck(mavlink_message_t& message); - void _handleAutopilotVersion(mavlink_message_t& message); + void _handleAutopilotVersion(LinkInterface* link, mavlink_message_t& message); void _handleHilActuatorControls(mavlink_message_t& message); void _missionManagerError(int errorCode, const QString& errorMsg); void _geoFenceManagerError(int errorCode, const QString& errorMsg); diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 3e2381389..bf1ce9852 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -73,7 +73,7 @@ MAVLinkProtocol::MAVLinkProtocol(QGCApplication* app) MAVLinkProtocol::~MAVLinkProtocol() { storeSettings(); - + #ifndef __mobile__ _closeLogFile(); #endif @@ -162,7 +162,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) if (!_linkMgr->links()->contains(link)) { return; } - + // receiveMutex.lock(); mavlink_message_t message; mavlink_status_t status; @@ -214,6 +214,8 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) { decodedFirstPacket = true; + /* + * Handled in Vehicle.cc now. mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); if (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { qDebug() << "switch to mavlink 2.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; @@ -222,6 +224,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) qDebug() << "switch to mavlink 1.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; } + */ if(message.msgid == MAVLINK_MSG_ID_RADIO_STATUS) { @@ -255,7 +258,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) #ifndef __mobile__ // Log data - + if (!_logSuspendError && !_logSuspendReplay && _tempLogFile.isOpen()) { uint8_t buf[MAVLINK_MAX_PACKET_LEN+sizeof(quint64)]; @@ -280,7 +283,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) _stopLogging(); _logSuspendError = true; } - + // Check for the vehicle arming going by. This is used to trigger log save. if (!_logPromptForSave && message.msgid == MAVLINK_MSG_ID_HEARTBEAT) { mavlink_heartbeat_t state; @@ -412,7 +415,7 @@ bool MAVLinkProtocol::_closeLogFile(void) return true; } } - + return false; } @@ -458,11 +461,11 @@ void MAVLinkProtocol::_stopLogging(void) void MAVLinkProtocol::checkForLostLogFiles(void) { QDir tempDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)); - + QString filter(QString("*.%1").arg(_logFileExtension)); QFileInfoList fileInfoList = tempDir.entryInfoList(QStringList(filter), QDir::Files); qDebug() << "Orphaned log file count" << fileInfoList.count(); - + foreach(const QFileInfo fileInfo, fileInfoList) { qDebug() << "Orphaned log file" << fileInfo.filePath(); if (fileInfo.size() == 0) { @@ -488,10 +491,10 @@ void MAVLinkProtocol::suspendLogForReplay(bool suspend) void MAVLinkProtocol::deleteTempLogFiles(void) { QDir tempDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation)); - + QString filter(QString("*.%1").arg(_logFileExtension)); QFileInfoList fileInfoList = tempDir.entryInfoList(QStringList(filter), QDir::Files); - + foreach(const QFileInfo fileInfo, fileInfoList) { QFile::remove(fileInfo.filePath()); } diff --git a/src/uas/MavlinkLogManager.cc b/src/uas/MavlinkLogManager.cc index 9563dd1a4..17b1e8eca 100644 --- a/src/uas/MavlinkLogManager.cc +++ b/src/uas/MavlinkLogManager.cc @@ -30,7 +30,7 @@ static const char* kEnableAutoUploadKey = "EnableAutoUploadKey"; static const char* kEnableAutoStartKey = "EnableAutoStartKey"; //----------------------------------------------------------------------------- -MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager *manager, const QString& filePath) +MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath) : _manager(manager) , _size(0) , _selected(false) @@ -76,6 +76,9 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) , _currentLogfile(NULL) , _vehicle(NULL) , _logRunning(false) + , _loggingDisabled(false) + , _currentSavingFileFd(NULL) + , _sequence(0) { //-- Get saved settings QSettings settings; @@ -90,14 +93,17 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) if(!QDir(_logPath).exists()) { if(QDir().mkpath(_logPath)) { qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log download path:" << _logPath; + _loggingDisabled = true; } } - //-- Load current list of logs - QDirIterator it(_logPath, QStringList() << "*.ulg", QDir::Files); - while(it.hasNext()) { - _logFiles.append(new MavlinkLogFiles(this, it.next())); + if(!_loggingDisabled) { + //-- 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; } - qCDebug(MavlinkLogManagerLog) << "Mavlink logs directory:" << _logPath; } //----------------------------------------------------------------------------- @@ -108,19 +114,19 @@ MavlinkLogManager::~MavlinkLogManager() //----------------------------------------------------------------------------- void -MavlinkLogManager::setToolbox(QGCToolbox *toolbox) +MavlinkLogManager::setToolbox(QGCToolbox* toolbox) { - QGCTool::setToolbox(toolbox); - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); - connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); - - // _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"); - + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); + if(!_loggingDisabled) { + connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); + } + // _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"); } //----------------------------------------------------------------------------- @@ -190,7 +196,7 @@ MavlinkLogManager::uploadLog() if(_currentLogfile) { _currentLogfile->setUploading(false); } - for(int i = 0; i < _logFiles.count(); i++ ) { + for(int i = 0; i < _logFiles.count(); i++) { _currentLogfile = qobject_cast(_logFiles.get(i)); Q_ASSERT(_currentLogfile); if(_currentLogfile->selected()) { @@ -210,18 +216,49 @@ MavlinkLogManager::uploadLog() emit busyChanged(); } +//----------------------------------------------------------------------------- +int +MavlinkLogManager::_getFirstSelected() +{ + for(int i = 0; i < _logFiles.count(); i++) { + MavlinkLogFiles* f = qobject_cast(_logFiles.get(i)); + Q_ASSERT(f); + if(f->selected()) { + return i; + } + } + return -1; +} + //----------------------------------------------------------------------------- void MavlinkLogManager::deleteLog() { - //-- TODO + while (true) { + int idx = _getFirstSelected(); + if(idx < 0) { + break; + } + MavlinkLogFiles* f = qobject_cast(_logFiles.get(idx)); + QString filePath = _logPath; + filePath += "/"; + filePath += f->name(); + filePath += ".ulg"; + QFile gone(filePath); + if(!gone.remove()) { + qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath; + } + _logFiles.removeAt(idx); + delete f; + emit logFilesChanged(); + } } //----------------------------------------------------------------------------- void MavlinkLogManager::cancelUpload() { - for(int i = 0; i < _logFiles.count(); i++ ) { + for(int i = 0; i < _logFiles.count(); i++) { MavlinkLogFiles* pLogFile = qobject_cast(_logFiles.get(i)); Q_ASSERT(pLogFile); if(pLogFile->selected() && pLogFile != _currentLogfile) { @@ -238,9 +275,11 @@ void MavlinkLogManager::startLogging() { if(_vehicle) { - _vehicle->startMavlinkLog(); - _logRunning = true; - emit logRunningChanged(); + if(_createNewLog()) { + _vehicle->startMavlinkLog(); + _logRunning = true; + emit logRunningChanged(); + } } } @@ -252,6 +291,17 @@ MavlinkLogManager::stopLogging() _vehicle->stopMavlinkLog(); _logRunning = false; emit logRunningChanged(); + if(_currentSavingFileFd) { + fclose(_currentSavingFileFd); + _logFiles.append(new MavlinkLogFiles(this, _currentSavingFileStr)); + emit logFilesChanged(); + _currentSavingFileFd = NULL; + _currentSavingFileStr.clear(); + //-- TODO: If auto upload is set, schedule it + if(_enableAutoUpload) { + //-- Queue log for auto upload + } + } } } @@ -287,21 +337,23 @@ MavlinkLogManager::_sendLog(const QString& logFile) qCCritical(MavlinkLogManagerLog) << "Log file missing:" << logFile; return false; } - QFile *file = new QFile(logFile); + QFile* file = new QFile(logFile); if(!file || !file->open(QIODevice::ReadOnly)) { - if (file) delete file; + if(file) { + delete file; + } qCCritical(MavlinkLogManagerLog) << "Could not open log file:" << logFile; return false; } if(!_nam) { - _nam = new QNetworkAccessManager(this); + _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); + 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"); @@ -319,7 +371,7 @@ MavlinkLogManager::_sendLog(const QString& logFile) file->setParent(multiPart); QNetworkRequest request(_uploadURL); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QNetworkReply *reply = _nam->post(request, multiPart); + 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); @@ -332,7 +384,7 @@ MavlinkLogManager::_sendLog(const QString& logFile) //----------------------------------------------------------------------------- bool -MavlinkLogManager::_processUploadResponse(int http_code, QByteArray &data) +MavlinkLogManager::_processUploadResponse(int http_code, QByteArray& data) { qCDebug(MavlinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data); emit readyRead(data); @@ -343,7 +395,7 @@ MavlinkLogManager::_processUploadResponse(int http_code, QByteArray &data) void MavlinkLogManager::_dataAvailable() { - QNetworkReply *reply = qobject_cast(sender()); + QNetworkReply* reply = qobject_cast(sender()); if(!reply) { return; } @@ -355,7 +407,7 @@ MavlinkLogManager::_dataAvailable() void MavlinkLogManager::_uploadFinished() { - QNetworkReply *reply = qobject_cast(sender()); + QNetworkReply* reply = qobject_cast(sender()); if(!reply) { return; } @@ -379,8 +431,9 @@ MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) { if(bytesTotal) { qreal progress = (qreal)bytesSent / (qreal)bytesTotal; - if(_currentLogfile) + if(_currentLogfile) { _currentLogfile->setProgress(progress); + } } qCDebug(MavlinkLogManagerLog) << bytesSent << "of" << bytesTotal; } @@ -393,17 +446,15 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // connects/disconnects. In reality, if QGC is connected to multiple vehicles, // this is called each time the user switches from one vehicle to another. So // far, I'm working on the assumption that multiple vehicles is a rare exception. - // Disconnect the previous one (if any) - if (_vehicle) { + if(_vehicle) { disconnect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); _vehicle = NULL; emit canStartLogChanged(); } // Connect new system - if (vehicle) - { + if(vehicle) { _vehicle = vehicle; connect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); connect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); @@ -413,10 +464,47 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) //----------------------------------------------------------------------------- void -MavlinkLogManager::_mavlinkLogData(Vehicle * /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) +MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) +{ + if(_currentSavingFileFd) { + if(sequence != _sequence) { + qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data"; + if(first_message < 255) { + data += first_message; + length -= first_message; + } else { + return; + } + } + if(fwrite(data, 1, length, _currentSavingFileFd) != (size_t)length) { + fclose(_currentSavingFileFd); + _currentSavingFileFd = NULL; + qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _currentSavingFileStr; + _logRunning = false; + _vehicle->stopMavlinkLog(); + emit logRunningChanged(); + } + } else { + qCWarning(MavlinkLogManagerLog) << "Mavlink log data received when not expected."; + } + _sequence = sequence + 1; +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogManager::_createNewLog() { - Q_UNUSED(data); - qDebug() << "Mavlink Log:" << sequence << length << first_message; + if(_currentSavingFileFd) { + fclose(_currentSavingFileFd); + } + _currentSavingFileStr.sprintf("%s/%03d-%s.ulg", _logPath.toLatin1().data(), _vehicle->id(), QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data()); + _currentSavingFileFd = fopen(_currentSavingFileStr.toLatin1().data(), "wb"); + if(!_currentSavingFileFd) { + qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _currentSavingFileStr; + _currentSavingFileStr.clear(); + } + _sequence = 0; + return _currentSavingFileFd != NULL; } //----------------------------------------------------------------------------- @@ -426,17 +514,11 @@ MavlinkLogManager::_armedChanged(bool armed) if(_vehicle) { if(armed) { if(_enableAutoStart) { - _vehicle->startMavlinkLog(); - _logRunning = true; - emit logRunningChanged(); + startLogging(); } } else { if(_logRunning && _enableAutoStart) { - _vehicle->stopMavlinkLog(); - emit logRunningChanged(); - if(_enableAutoUpload) { - //-- TODO: Queue log for auto upload - } + stopLogging(); } } } diff --git a/src/uas/MavlinkLogManager.h b/src/uas/MavlinkLogManager.h index 3ef3dc585..7f5cf8594 100644 --- a/src/uas/MavlinkLogManager.h +++ b/src/uas/MavlinkLogManager.h @@ -131,6 +131,8 @@ private slots: private: bool _sendLog (const QString& logFile); bool _processUploadResponse (int http_code, QByteArray &data); + bool _createNewLog (); + int _getFirstSelected (); private: QString _description; @@ -144,6 +146,10 @@ private: MavlinkLogFiles* _currentLogfile; Vehicle* _vehicle; bool _logRunning; + bool _loggingDisabled; + FILE* _currentSavingFileFd; + QString _currentSavingFileStr; + uint16_t _sequence; }; #endif diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index b73fc5620..c5b34c976 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -37,12 +37,10 @@ Rectangle { 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) } } @@ -296,26 +294,27 @@ Rectangle { id: logFilesColumn spacing: ScreenTools.defaultFontPixelWidth anchors.centerIn: parent + width: ScreenTools.defaultFontPixelWidth * 68 Rectangle { - width: ScreenTools.defaultFontPixelWidth * 52 + width: ScreenTools.defaultFontPixelWidth * 64 height: ScreenTools.defaultFontPixelHeight * 10 anchors.horizontalCenter: parent.horizontalCenter - color: qgcPal.windowShade + color: qgcPal.window border.color: qgcPal.text border.width: 0.5 ListView { - width: ScreenTools.defaultFontPixelWidth * 50 - height: ScreenTools.defaultFontPixelHeight * 9 + width: ScreenTools.defaultFontPixelWidth * 56 + height: ScreenTools.defaultFontPixelHeight * 8.75 anchors.centerIn: parent orientation: ListView.Vertical model: QGroundControl.mavlinkLogManager.logFiles + clip: true delegate: Rectangle { - width: ScreenTools.defaultFontPixelWidth * 48 + width: ScreenTools.defaultFontPixelWidth * 52 height: ScreenTools.defaultFontPixelHeight * 1.25 - color: index % 2 == 0 ? qgcPal.window : qgcPal.windowShade - anchors.horizontalCenter: parent.horizontalCenter; + color: qgcPal.window Row { - width: ScreenTools.defaultFontPixelWidth * 46 + width: ScreenTools.defaultFontPixelWidth * 50 anchors.centerIn: parent spacing: ScreenTools.defaultFontPixelWidth QGCCheckBox { @@ -327,10 +326,10 @@ Rectangle { } QGCLabel { text: object.name - width: ScreenTools.defaultFontPixelWidth * 20 + width: ScreenTools.defaultFontPixelWidth * 28 } QGCLabel { - text: object.size + text: Number(object.size).toLocaleString(Qt.locale(), 'f', 0) visible: !object.uploading width: ScreenTools.defaultFontPixelWidth * 20; horizontalAlignment: Text.AlignRight -- GitLab From c615f477c76b67dbbfeee83da2487b06c4a91afe Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 25 Oct 2016 17:09:28 -0400 Subject: [PATCH 009/398] Finished. Needs testing and debugging. --- src/uas/MavlinkLogManager.cc | 172 +++++++++++++++++++------ src/uas/MavlinkLogManager.h | 53 +++++++- src/ui/preferences/MavlinkSettings.qml | 27 +++- 3 files changed, 197 insertions(+), 55 deletions(-) diff --git a/src/uas/MavlinkLogManager.cc b/src/uas/MavlinkLogManager.cc index 17b1e8eca..655dd37ad 100644 --- a/src/uas/MavlinkLogManager.cc +++ b/src/uas/MavlinkLogManager.cc @@ -28,6 +28,7 @@ static const char* kPx4URLKey = "MavlinkLogURL"; static const char* kDefaultPx4URL = "http://logs.px4.io/upload"; static const char* kEnableAutoUploadKey = "EnableAutoUploadKey"; static const char* kEnableAutoStartKey = "EnableAutoStartKey"; +static const char* kEnableDeletetKey = "EnableDeleteKey"; //----------------------------------------------------------------------------- MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath) @@ -42,6 +43,14 @@ MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& file _size = (quint32)fi.size(); } +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setSize(quint32 size) +{ + _size = size; + emit sizeChanged(); +} + //----------------------------------------------------------------------------- void MavlinkLogFiles::setSelected(bool selected) @@ -67,6 +76,14 @@ MavlinkLogFiles::setProgress(qreal progress) emit progressChanged(); } +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setWriting(bool writing) +{ + _writing = writing; + emit writingChanged(); +} + //----------------------------------------------------------------------------- MavlinkLogManager::MavlinkLogManager(QGCApplication* app) : QGCTool(app) @@ -77,8 +94,9 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) , _vehicle(NULL) , _logRunning(false) , _loggingDisabled(false) - , _currentSavingFileFd(NULL) + , _currentSavingFile(NULL) , _sequence(0) + , _deleteAfterUpload(false) { //-- Get saved settings QSettings settings; @@ -87,6 +105,7 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString()); setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool()); setEnableAutoStart(settings.value(kEnableAutoStartKey, true).toBool()); + setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool()); //-- Logging location _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); _logPath += "/MavlinkLogs"; @@ -100,7 +119,7 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) //-- Load current list of logs QDirIterator it(_logPath, QStringList() << "*.ulg", QDir::Files); while(it.hasNext()) { - _logFiles.append(new MavlinkLogFiles(this, it.next())); + _insertNewLog(new MavlinkLogFiles(this, it.next())); } qCDebug(MavlinkLogManagerLog) << "Mavlink logs directory:" << _logPath; } @@ -182,9 +201,19 @@ MavlinkLogManager::setEnableAutoStart(bool enable) emit enableAutoStartChanged(); } +//----------------------------------------------------------------------------- +void +MavlinkLogManager::setDeleteAfterUpload(bool enable) +{ + _deleteAfterUpload = enable; + QSettings settings; + settings.setValue(kEnableDeletetKey, enable); + emit deleteAfterUploadChanged(); +} + //----------------------------------------------------------------------------- bool -MavlinkLogManager::busy() +MavlinkLogManager::uploading() { return _currentLogfile != NULL; } @@ -208,12 +237,32 @@ MavlinkLogManager::uploadLog() filePath += _currentLogfile->name(); filePath += ".ulg"; _sendLog(filePath); - emit busyChanged(); + emit uploadingChanged(); return; } } _currentLogfile = NULL; - emit busyChanged(); + emit uploadingChanged(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_insertNewLog(MavlinkLogFiles* newLog) +{ + //-- Simpler than trying to sort this thing + int count = _logFiles.count(); + if(!count) { + _logFiles.append(newLog); + } else { + for(int i = 0; i < count; i++) { + MavlinkLogFiles* f = qobject_cast(_logFiles.get(i)); + if(newLog->name() < f->name()) { + _logFiles.insert(i, newLog); + return; + } + } + _logFiles.append(newLog); + } } //----------------------------------------------------------------------------- @@ -239,21 +288,28 @@ MavlinkLogManager::deleteLog() if(idx < 0) { break; } - MavlinkLogFiles* f = qobject_cast(_logFiles.get(idx)); - QString filePath = _logPath; - filePath += "/"; - filePath += f->name(); - filePath += ".ulg"; - QFile gone(filePath); - if(!gone.remove()) { - qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath; - } - _logFiles.removeAt(idx); - delete f; - emit logFilesChanged(); + MavlinkLogFiles* log = qobject_cast(_logFiles.get(idx)); + _deleteLog(log); } } +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_deleteLog(MavlinkLogFiles* log) +{ + QString filePath = _logPath; + filePath += "/"; + filePath += log->name(); + filePath += ".ulg"; + QFile gone(filePath); + if(!gone.remove()) { + qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath; + } + _logFiles.removeOne(log); + delete log; + emit logFilesChanged(); +} + //----------------------------------------------------------------------------- void MavlinkLogManager::cancelUpload() @@ -288,19 +344,24 @@ void MavlinkLogManager::stopLogging() { if(_vehicle) { + //-- Tell vehicle to stop sending logs _vehicle->stopMavlinkLog(); - _logRunning = false; - emit logRunningChanged(); - if(_currentSavingFileFd) { - fclose(_currentSavingFileFd); - _logFiles.append(new MavlinkLogFiles(this, _currentSavingFileStr)); - emit logFilesChanged(); - _currentSavingFileFd = NULL; - _currentSavingFileStr.clear(); - //-- TODO: If auto upload is set, schedule it - if(_enableAutoUpload) { - //-- Queue log for auto upload + if(_currentSavingFile) { + _currentSavingFile->close(); + if(_currentSavingFile->record) { + _currentSavingFile->record->setWriting(false); + if(_enableAutoUpload) { + //-- Queue log for auto upload (set selected flag) + _currentSavingFile->record->setSelected(true); + if(!uploading()) { + uploadLog(); + } + } } + delete _currentSavingFile; + _currentSavingFile = NULL; + _logRunning = false; + emit logRunningChanged(); } } } @@ -416,8 +477,14 @@ MavlinkLogManager::_uploadFinished() if(_processUploadResponse(http_code, data)) { qCDebug(MavlinkLogManagerLog) << "Log uploaded."; emit succeed(); + if(_deleteAfterUpload) { + if(_currentLogfile) { + _deleteLog(_currentLogfile); + _currentLogfile = NULL; + } + } } else { - qCDebug(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); + qCWarning(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); emit failed(); } reply->deleteLater(); @@ -466,7 +533,7 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) void MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) { - if(_currentSavingFileFd) { + if(_currentSavingFile) { if(sequence != _sequence) { qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data"; if(first_message < 255) { @@ -476,17 +543,26 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system return; } } - if(fwrite(data, 1, length, _currentSavingFileFd) != (size_t)length) { - fclose(_currentSavingFileFd); - _currentSavingFileFd = NULL; - qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _currentSavingFileStr; + if(fwrite(data, 1, length, _currentSavingFile->fd) != (size_t)length) { + qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _currentSavingFile->fileName; + delete _currentSavingFile; + _currentSavingFile = NULL; _logRunning = false; _vehicle->stopMavlinkLog(); emit logRunningChanged(); } } else { + length = 0; qCWarning(MavlinkLogManagerLog) << "Mavlink log data received when not expected."; } + //-- Update file size + if(_currentSavingFile) { + _currentSavingFile->close(); + if(_currentSavingFile->record) { + quint32 size = _currentSavingFile->record->size() + length; + _currentSavingFile->record->setSize(size); + } + } _sequence = sequence + 1; } @@ -494,17 +570,29 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system bool MavlinkLogManager::_createNewLog() { - if(_currentSavingFileFd) { - fclose(_currentSavingFileFd); + if(_currentSavingFile) { + delete _currentSavingFile; + _currentSavingFile = NULL; } - _currentSavingFileStr.sprintf("%s/%03d-%s.ulg", _logPath.toLatin1().data(), _vehicle->id(), QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data()); - _currentSavingFileFd = fopen(_currentSavingFileStr.toLatin1().data(), "wb"); - if(!_currentSavingFileFd) { - qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _currentSavingFileStr; - _currentSavingFileStr.clear(); + _currentSavingFile = new CurrentRunningLog; + _currentSavingFile->fileName.sprintf("%s/%03d-%s.ulg", + _logPath.toLatin1().data(), + _vehicle->id(), + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data()); + _currentSavingFile->fd = fopen(_currentSavingFile->fileName.toLatin1().data(), "wb"); + if(_currentSavingFile->fd) { + MavlinkLogFiles* newLog = new MavlinkLogFiles(this, _currentSavingFile->fileName); + newLog->setWriting(true); + _insertNewLog(newLog); + _currentSavingFile->record = newLog; + emit logFilesChanged(); + } else { + qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _currentSavingFile->fileName; + delete _currentSavingFile; + _currentSavingFile = NULL; } _sequence = 0; - return _currentSavingFileFd != NULL; + return _currentSavingFile != NULL; } //----------------------------------------------------------------------------- diff --git a/src/uas/MavlinkLogManager.h b/src/uas/MavlinkLogManager.h index 7f5cf8594..aabe10413 100644 --- a/src/uas/MavlinkLogManager.h +++ b/src/uas/MavlinkLogManager.h @@ -31,25 +31,31 @@ public: MavlinkLogFiles (MavlinkLogManager* manager, const QString& filePath); Q_PROPERTY(QString name READ name CONSTANT) - Q_PROPERTY(quint32 size READ size CONSTANT) + Q_PROPERTY(quint32 size READ size NOTIFY sizeChanged) 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) + Q_PROPERTY(bool writing READ writing NOTIFY writingChanged) QString name () { return _name; } quint32 size () { return _size; } bool selected () { return _selected; } bool uploading () { return _uploading; } qreal progress () { return _progress; } + bool writing () { return _writing; } void setSelected (bool selected); void setUploading (bool uploading); void setProgress (qreal progress); + void setWriting (bool writing); + void setSize (quint32 size); signals: + void sizeChanged (); void selectedChanged (); void uploadingChanged (); void progressChanged (); + void writingChanged (); private: MavlinkLogManager* _manager; @@ -58,8 +64,37 @@ private: bool _selected; bool _uploading; qreal _progress; + bool _writing; }; +//----------------------------------------------------------------------------- +class CurrentRunningLog +{ +public: + CurrentRunningLog() + : fd(NULL) + , record(NULL) + , written(0) + { + } + ~CurrentRunningLog() + { + close(); + } + void close() + { + if(fd) { + fclose(fd); + fd = NULL; + } + } + FILE* fd; + QString fileName; + MavlinkLogFiles* record; + quint32 written; +}; + +//----------------------------------------------------------------------------- class MavlinkLogManager : public QGCTool { Q_OBJECT @@ -73,7 +108,8 @@ public: Q_PROPERTY(QString uploadURL READ uploadURL WRITE setUploadURL NOTIFY uploadURLChanged) Q_PROPERTY(bool enableAutoUpload READ enableAutoUpload WRITE setEnableAutoUpload NOTIFY enableAutoUploadChanged) Q_PROPERTY(bool enableAutoStart READ enableAutoStart WRITE setEnableAutoStart NOTIFY enableAutoStartChanged) - Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) + Q_PROPERTY(bool deleteAfterUpload READ deleteAfterUpload WRITE setDeleteAfterUpload NOTIFY deleteAfterUploadChanged) + Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged) Q_PROPERTY(bool logRunning READ logRunning NOTIFY logRunningChanged) Q_PROPERTY(bool canStartLog READ canStartLog NOTIFY canStartLogChanged) Q_PROPERTY(QmlObjectListModel* logFiles READ logFiles NOTIFY logFilesChanged) @@ -89,9 +125,10 @@ public: QString uploadURL () { return _uploadURL; } bool enableAutoUpload () { return _enableAutoUpload; } bool enableAutoStart () { return _enableAutoStart; } - bool busy (); + bool uploading (); bool logRunning () { return _logRunning; } bool canStartLog () { return _vehicle != NULL; } + bool deleteAfterUpload () { return _deleteAfterUpload; } QmlObjectListModel* logFiles () { return &_logFiles; } @@ -100,6 +137,7 @@ public: void setUploadURL (QString url); void setEnableAutoUpload (bool enable); void setEnableAutoStart (bool enable); + void setDeleteAfterUpload(bool enable); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); @@ -112,13 +150,14 @@ signals: void enableAutoStartChanged (); void logFilesChanged (); void selectedCountChanged (); - void busyChanged (); + void uploadingChanged (); void readyRead (QByteArray data); void failed (); void succeed (); void abortUpload (); void logRunningChanged (); void canStartLogChanged (); + void deleteAfterUploadChanged (); private slots: void _uploadFinished (); @@ -133,6 +172,8 @@ private: bool _processUploadResponse (int http_code, QByteArray &data); bool _createNewLog (); int _getFirstSelected (); + void _insertNewLog (MavlinkLogFiles* newLog); + void _deleteLog (MavlinkLogFiles* log); private: QString _description; @@ -147,9 +188,9 @@ private: Vehicle* _vehicle; bool _logRunning; bool _loggingDisabled; - FILE* _currentSavingFileFd; - QString _currentSavingFileStr; + CurrentRunningLog* _currentSavingFile; uint16_t _sequence; + bool _deleteAfterUpload; }; #endif diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index c5b34c976..f7ea0101a 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -262,6 +262,7 @@ Rectangle { //----------------------------------------------------------------- //-- Automatic Upload QGCCheckBox { + id: autoUploadCheck text: qsTr("Enable automatic log uploads") checked: QGroundControl.mavlinkLogManager.enableAutoUpload enabled: emailField.text !== "" && urlField !== "" @@ -269,6 +270,16 @@ Rectangle { QGroundControl.mavlinkLogManager.enableAutoUpload = checked } } + //----------------------------------------------------------------- + //-- Delete log after upload + QGCCheckBox { + text: qsTr("Delete log file after uploading") + checked: QGroundControl.mavlinkLogManager.deleteAfterUpload + enabled: emailField.text !== "" && urlField !== "" && autoUploadCheck.checked + onClicked: { + QGroundControl.mavlinkLogManager.deleteAfterUpload = checked + } + } } } //----------------------------------------------------------------- @@ -327,11 +338,13 @@ Rectangle { QGCLabel { text: object.name width: ScreenTools.defaultFontPixelWidth * 28 + color: object.writing ? qgcPal.warningText : qgcPal.text } QGCLabel { text: Number(object.size).toLocaleString(Qt.locale(), 'f', 0) visible: !object.uploading width: ScreenTools.defaultFontPixelWidth * 20; + color: object.writing ? qgcPal.warningText : qgcPal.text horizontalAlignment: Text.AlignRight } ProgressBar { @@ -352,7 +365,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCButton { text: "Check All" - enabled: !QGroundControl.mavlinkLogManager.busy + enabled: !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: { for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) @@ -362,7 +375,7 @@ Rectangle { } QGCButton { text: "Check None" - enabled: !QGroundControl.mavlinkLogManager.busy + enabled: !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: { for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) @@ -372,7 +385,7 @@ Rectangle { } QGCButton { text: "Delete Selected" - enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.busy + enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: deleteDialog.open() MessageDialog { id: deleteDialog @@ -388,8 +401,8 @@ Rectangle { } QGCButton { text: "Upload Selected" - enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.busy - visible: !QGroundControl.mavlinkLogManager.busy + enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning + visible: !QGroundControl.mavlinkLogManager.uploading onClicked: { QGroundControl.mavlinkLogManager.emailAddress = emailField.text if(QGroundControl.mavlinkLogManager.emailAddress === "") @@ -411,8 +424,8 @@ Rectangle { } QGCButton { text: "Cancel" - enabled: QGroundControl.mavlinkLogManager.busy - visible: QGroundControl.mavlinkLogManager.busy + enabled: QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning + visible: QGroundControl.mavlinkLogManager.uploading onClicked: cancelDialog.open() MessageDialog { id: cancelDialog -- GitLab From dc2be3bff251b9199e38ed1bfb8244eb3c206e79 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 25 Oct 2016 17:30:01 -0400 Subject: [PATCH 010/398] Bug fixes. Moving source files into Vehicles as it is a more coherent place. --- qgroundcontrol.pro | 4 ++-- src/{uas => Vehicle}/MavlinkLogManager.cc | 15 +++++++++++++-- src/{uas => Vehicle}/MavlinkLogManager.h | 8 +------- src/ui/preferences/MavlinkSettings.qml | 1 + 4 files changed, 17 insertions(+), 11 deletions(-) rename src/{uas => Vehicle}/MavlinkLogManager.cc (98%) rename src/{uas => Vehicle}/MavlinkLogManager.h (98%) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index c8adcb54b..a070e4a2d 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -334,7 +334,7 @@ HEADERS += \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ - src/uas/MavlinkLogManager.h \ + src/Vehicle/MavlinkLogManager.h \ src/ui/toolbar/MainToolBarController.h \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ @@ -499,7 +499,7 @@ SOURCES += \ src/QmlControls/QmlObjectListModel.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ - src/uas/MavlinkLogManager.cc \ + src/Vehicle/MavlinkLogManager.cc \ src/ui/toolbar/MainToolBarController.cc \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ diff --git a/src/uas/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc similarity index 98% rename from src/uas/MavlinkLogManager.cc rename to src/Vehicle/MavlinkLogManager.cc index 655dd37ad..56270fb3e 100644 --- a/src/uas/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -37,6 +37,7 @@ MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& file , _selected(false) , _uploading(false) , _progress(0) + , _writing(false) { QFileInfo fi(filePath); _name = fi.baseName(); @@ -84,6 +85,17 @@ MavlinkLogFiles::setWriting(bool writing) emit writingChanged(); } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CurrentRunningLog::close() +{ + if(fd) { + fclose(fd); + fd = NULL; + } +} + +//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- MavlinkLogManager::MavlinkLogManager(QGCApplication* app) : QGCTool(app) @@ -533,7 +545,7 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) void MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) { - if(_currentSavingFile) { + if(_currentSavingFile && _currentSavingFile->fd) { if(sequence != _sequence) { qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data"; if(first_message < 255) { @@ -557,7 +569,6 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system } //-- Update file size if(_currentSavingFile) { - _currentSavingFile->close(); if(_currentSavingFile->record) { quint32 size = _currentSavingFile->record->size() + length; _currentSavingFile->record->setSize(size); diff --git a/src/uas/MavlinkLogManager.h b/src/Vehicle/MavlinkLogManager.h similarity index 98% rename from src/uas/MavlinkLogManager.h rename to src/Vehicle/MavlinkLogManager.h index aabe10413..701c69e2d 100644 --- a/src/uas/MavlinkLogManager.h +++ b/src/Vehicle/MavlinkLogManager.h @@ -81,13 +81,7 @@ public: { close(); } - void close() - { - if(fd) { - fclose(fd); - fd = NULL; - } - } + void close(); FILE* fd; QString fileName; MavlinkLogFiles* record; diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index f7ea0101a..d0b2426c7 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -331,6 +331,7 @@ Rectangle { QGCCheckBox { width: ScreenTools.defaultFontPixelWidth * 4 checked: object.selected + enabled: !object.writing && !object.uploading onClicked: { object.selected = checked } -- GitLab From a85cdd5946f6e8e4330e19ffb6d159c757688614 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 25 Oct 2016 22:13:21 -0400 Subject: [PATCH 011/398] Final UI tweaks and fixes. --- src/Vehicle/MavlinkLogManager.cc | 91 +++++++++++++++++++------- src/Vehicle/MavlinkLogManager.h | 10 ++- src/ui/preferences/MavlinkSettings.qml | 71 +++++++++++++------- 3 files changed, 121 insertions(+), 51 deletions(-) diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc index 56270fb3e..bc9e14daa 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -29,19 +29,28 @@ static const char* kDefaultPx4URL = "http://logs.px4.io/upload"; static const char* kEnableAutoUploadKey = "EnableAutoUploadKey"; static const char* kEnableAutoStartKey = "EnableAutoStartKey"; static const char* kEnableDeletetKey = "EnableDeleteKey"; +static const char* kUlogExtension = ".ulg"; +static const char* kSidecarExtension = ".uploaded"; //----------------------------------------------------------------------------- -MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath) +MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath, bool newFile) : _manager(manager) , _size(0) , _selected(false) , _uploading(false) , _progress(0) , _writing(false) + , _uploaded(false) { QFileInfo fi(filePath); _name = fi.baseName(); - _size = (quint32)fi.size(); + if(!newFile) { + _size = (quint32)fi.size(); + QString sideCar = filePath; + sideCar.replace(kUlogExtension, kSidecarExtension); + QFileInfo sc(sideCar); + _uploaded = sc.exists(); + } } //----------------------------------------------------------------------------- @@ -85,6 +94,14 @@ MavlinkLogFiles::setWriting(bool writing) emit writingChanged(); } +//----------------------------------------------------------------------------- +void +MavlinkLogFiles::setUploaded(bool uploaded) +{ + _uploaded = uploaded; + emit uploadedChanged(); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CurrentRunningLog::close() @@ -122,14 +139,16 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); _logPath += "/MavlinkLogs"; if(!QDir(_logPath).exists()) { - if(QDir().mkpath(_logPath)) { + if(!QDir().mkpath(_logPath)) { qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log download path:" << _logPath; _loggingDisabled = true; } } if(!_loggingDisabled) { //-- Load current list of logs - QDirIterator it(_logPath, QStringList() << "*.ulg", QDir::Files); + QString filter = "*"; + filter += kUlogExtension; + QDirIterator it(_logPath, QStringList() << filter, QDir::Files); while(it.hasNext()) { _insertNewLog(new MavlinkLogFiles(this, it.next())); } @@ -153,11 +172,6 @@ MavlinkLogManager::setToolbox(QGCToolbox* toolbox) if(!_loggingDisabled) { connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); } - // _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"); } //----------------------------------------------------------------------------- @@ -242,15 +256,14 @@ MavlinkLogManager::uploadLog() 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 uploadingChanged(); - return; + if(!_currentLogfile->uploaded() && !_emailAddress.isEmpty() && !_uploadURL.isEmpty()) { + _currentLogfile->setUploading(true); + _currentLogfile->setProgress(0.0); + QString filePath = _makeFilename(_currentLogfile->name()); + _sendLog(filePath); + emit uploadingChanged(); + return; + } } } _currentLogfile = NULL; @@ -309,14 +322,18 @@ MavlinkLogManager::deleteLog() void MavlinkLogManager::_deleteLog(MavlinkLogFiles* log) { - QString filePath = _logPath; - filePath += "/"; - filePath += log->name(); - filePath += ".ulg"; + QString filePath = _makeFilename(log->name()); QFile gone(filePath); if(!gone.remove()) { qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath; } + //-- Remove sidecar file (if any) + filePath.replace(kUlogExtension, kSidecarExtension); + QFile sgone(filePath); + if(sgone.exists()) { + sgone.remove(); + } + //-- Remove file from list and delete record _logFiles.removeOne(log); delete log; emit logFilesChanged(); @@ -494,6 +511,17 @@ MavlinkLogManager::_uploadFinished() _deleteLog(_currentLogfile); _currentLogfile = NULL; } + } else { + if(_currentLogfile) { + _currentLogfile->setUploaded(true); + //-- Write side-car file to flag it as uploaded + QString sideCar = _makeFilename(_currentLogfile->name()); + sideCar.replace(kUlogExtension, kSidecarExtension); + FILE* f = fopen(sideCar.toLatin1().data(), "wb"); + if(f) { + fclose(f); + } + } } } else { qCWarning(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); @@ -525,6 +553,7 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // connects/disconnects. In reality, if QGC is connected to multiple vehicles, // this is called each time the user switches from one vehicle to another. So // far, I'm working on the assumption that multiple vehicles is a rare exception. + // For now, we only handle one log download at a time. // Disconnect the previous one (if any) if(_vehicle) { disconnect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); @@ -586,13 +615,14 @@ MavlinkLogManager::_createNewLog() _currentSavingFile = NULL; } _currentSavingFile = new CurrentRunningLog; - _currentSavingFile->fileName.sprintf("%s/%03d-%s.ulg", + _currentSavingFile->fileName.sprintf("%s/%03d-%s%s", _logPath.toLatin1().data(), _vehicle->id(), - QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data()); + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data(), + kUlogExtension); _currentSavingFile->fd = fopen(_currentSavingFile->fileName.toLatin1().data(), "wb"); if(_currentSavingFile->fd) { - MavlinkLogFiles* newLog = new MavlinkLogFiles(this, _currentSavingFile->fileName); + MavlinkLogFiles* newLog = new MavlinkLogFiles(this, _currentSavingFile->fileName, true); newLog->setWriting(true); _insertNewLog(newLog); _currentSavingFile->record = newLog; @@ -622,3 +652,14 @@ MavlinkLogManager::_armedChanged(bool armed) } } } + +//----------------------------------------------------------------------------- +QString +MavlinkLogManager::_makeFilename(const QString& baseName) +{ + QString filePath = _logPath; + filePath += "/"; + filePath += baseName; + filePath += kUlogExtension; + return filePath; +} diff --git a/src/Vehicle/MavlinkLogManager.h b/src/Vehicle/MavlinkLogManager.h index 701c69e2d..0ea7c8b99 100644 --- a/src/Vehicle/MavlinkLogManager.h +++ b/src/Vehicle/MavlinkLogManager.h @@ -28,7 +28,7 @@ class MavlinkLogFiles : public QObject { Q_OBJECT public: - MavlinkLogFiles (MavlinkLogManager* manager, const QString& filePath); + MavlinkLogFiles (MavlinkLogManager* manager, const QString& filePath, bool newFile = false); Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(quint32 size READ size NOTIFY sizeChanged) @@ -36,6 +36,7 @@ public: Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged) Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) Q_PROPERTY(bool writing READ writing NOTIFY writingChanged) + Q_PROPERTY(bool uploaded READ uploaded NOTIFY uploadedChanged) QString name () { return _name; } quint32 size () { return _size; } @@ -43,12 +44,14 @@ public: bool uploading () { return _uploading; } qreal progress () { return _progress; } bool writing () { return _writing; } + bool uploaded () { return _uploaded; } void setSelected (bool selected); void setUploading (bool uploading); void setProgress (qreal progress); void setWriting (bool writing); void setSize (quint32 size); + void setUploaded (bool uploaded); signals: void sizeChanged (); @@ -56,6 +59,7 @@ signals: void uploadingChanged (); void progressChanged (); void writingChanged (); + void uploadedChanged (); private: MavlinkLogManager* _manager; @@ -65,6 +69,7 @@ private: bool _uploading; qreal _progress; bool _writing; + bool _uploaded; }; //----------------------------------------------------------------------------- @@ -144,7 +149,7 @@ signals: void enableAutoStartChanged (); void logFilesChanged (); void selectedCountChanged (); - void uploadingChanged (); + void uploadingChanged (); void readyRead (QByteArray data); void failed (); void succeed (); @@ -168,6 +173,7 @@ private: int _getFirstSelected (); void _insertNewLog (MavlinkLogFiles* newLog); void _deleteLog (MavlinkLogFiles* log); + QString _makeFilename (const QString& baseName); private: QString _description; diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index d0b2426c7..836e902fd 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -28,6 +28,8 @@ Rectangle { property real _labelWidth: ScreenTools.defaultFontPixelWidth * 28 property real _valueWidth: ScreenTools.defaultFontPixelWidth * 24 property int _selectedCount: 0 + property real _columnSpacing: ScreenTools.defaultFontPixelHeight * 0.25 + QGCPalette { id: qgcPal } @@ -59,6 +61,7 @@ Rectangle { anchors.margins: ScreenTools.defaultFontPixelWidth contentHeight: settingsColumn.height contentWidth: settingsColumn.width + flickableDirection: Flickable.VerticalFlick Column { id: settingsColumn @@ -86,7 +89,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter Column { id: gcsColumn - spacing: ScreenTools.defaultFontPixelWidth + spacing: _columnSpacing anchors.centerIn: parent Row { spacing: ScreenTools.defaultFontPixelWidth @@ -148,31 +151,40 @@ Rectangle { Column { id: mavlogColumn width: gcsColumn.width - spacing: ScreenTools.defaultFontPixelWidth + spacing: _columnSpacing anchors.centerIn: parent //----------------------------------------------------------------- - //-- Enable auto log on arming - QGCCheckBox { - text: qsTr("Enable automatic logging start when vehicle is armed") - checked: QGroundControl.mavlinkLogManager.enableAutoStart - onClicked: { - QGroundControl.mavlinkLogManager.enableAutoStart = checked - } - } - //----------------------------------------------------------------- //-- Manual Start/Stop Row { spacing: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { + width: _labelWidth + text: qsTr("Manual Start/Stop:") + anchors.verticalCenter: parent.verticalCenter + } QGCButton { - text: "Start Logging" - enabled: !QGroundControl.mavlinkLogManager.logRunning && QGroundControl.mavlinkLogManager.canStartLog - onClicked: QGroundControl.mavlinkLogManager.startLogging() + text: qsTr("Start Logging") + width: (_valueWidth * 0.5) - (ScreenTools.defaultFontPixelWidth * 0.5) + enabled: !QGroundControl.mavlinkLogManager.logRunning && QGroundControl.mavlinkLogManager.canStartLog + onClicked: QGroundControl.mavlinkLogManager.startLogging() + anchors.verticalCenter: parent.verticalCenter } QGCButton { - text: "Stop Logging" - enabled: QGroundControl.mavlinkLogManager.logRunning - onClicked: QGroundControl.mavlinkLogManager.stopLogging() + text: qsTr("Stop Logging") + width: (_valueWidth * 0.5) - (ScreenTools.defaultFontPixelWidth * 0.5) + enabled: QGroundControl.mavlinkLogManager.logRunning + onClicked: QGroundControl.mavlinkLogManager.stopLogging() + anchors.verticalCenter: parent.verticalCenter + } + } + //----------------------------------------------------------------- + //-- Enable auto log on arming + QGCCheckBox { + text: qsTr("Enable automatic logging") + checked: QGroundControl.mavlinkLogManager.enableAutoStart + onClicked: { + QGroundControl.mavlinkLogManager.enableAutoStart = checked } } } @@ -198,7 +210,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter Column { id: logColumn - spacing: ScreenTools.defaultFontPixelWidth + spacing: _columnSpacing anchors.centerIn: parent //----------------------------------------------------------------- //-- Email address Field @@ -303,35 +315,37 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter Column { id: logFilesColumn - spacing: ScreenTools.defaultFontPixelWidth + spacing: _columnSpacing * 4 anchors.centerIn: parent width: ScreenTools.defaultFontPixelWidth * 68 Rectangle { width: ScreenTools.defaultFontPixelWidth * 64 - height: ScreenTools.defaultFontPixelHeight * 10 + height: ScreenTools.defaultFontPixelHeight * 14 anchors.horizontalCenter: parent.horizontalCenter color: qgcPal.window border.color: qgcPal.text border.width: 0.5 ListView { width: ScreenTools.defaultFontPixelWidth * 56 - height: ScreenTools.defaultFontPixelHeight * 8.75 + height: ScreenTools.defaultFontPixelHeight * 12 anchors.centerIn: parent orientation: ListView.Vertical model: QGroundControl.mavlinkLogManager.logFiles clip: true delegate: Rectangle { width: ScreenTools.defaultFontPixelWidth * 52 - height: ScreenTools.defaultFontPixelHeight * 1.25 + height: selectCheck.height color: qgcPal.window Row { width: ScreenTools.defaultFontPixelWidth * 50 anchors.centerIn: parent spacing: ScreenTools.defaultFontPixelWidth QGCCheckBox { + id: selectCheck width: ScreenTools.defaultFontPixelWidth * 4 checked: object.selected enabled: !object.writing && !object.uploading + anchors.verticalCenter: parent.verticalCenter onClicked: { object.selected = checked } @@ -340,16 +354,25 @@ Rectangle { text: object.name width: ScreenTools.defaultFontPixelWidth * 28 color: object.writing ? qgcPal.warningText : qgcPal.text + anchors.verticalCenter: parent.verticalCenter } QGCLabel { text: Number(object.size).toLocaleString(Qt.locale(), 'f', 0) - visible: !object.uploading + visible: !object.uploading && !object.uploaded width: ScreenTools.defaultFontPixelWidth * 20; color: object.writing ? qgcPal.warningText : qgcPal.text horizontalAlignment: Text.AlignRight + anchors.verticalCenter: parent.verticalCenter + } + QGCLabel { + text: "Uploaded" + visible: object.uploaded + width: ScreenTools.defaultFontPixelWidth * 20; + horizontalAlignment: Text.AlignRight + anchors.verticalCenter: parent.verticalCenter } ProgressBar { - visible: object.uploading + visible: object.uploading && !object.uploaded width: ScreenTools.defaultFontPixelWidth * 20; height: ScreenTools.defaultFontPixelHeight anchors.verticalCenter: parent.verticalCenter -- GitLab From 62bc5a63c4407407bad349db5fd16386b42aaba2 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 25 Oct 2016 22:55:55 -0400 Subject: [PATCH 012/398] Qt 5.5.x doesn't know how to handle redirects. --- src/Vehicle/MavlinkLogManager.cc | 2 ++ src/ui/preferences/MavlinkSettings.qml | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc index bc9e14daa..7da59ae9a 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -460,7 +460,9 @@ MavlinkLogManager::_sendLog(const QString& logFile) multiPart->append(logPart); file->setParent(multiPart); QNetworkRequest request(_uploadURL); +#if QT_VERSION > 0x050600 request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); +#endif QNetworkReply* reply = _nam->post(request, multiPart); connect(reply, &QNetworkReply::finished, this, &MavlinkLogManager::_uploadFinished); connect(this, &MavlinkLogManager::abortUpload, reply, &QNetworkReply::abort); diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 836e902fd..3beaa6067 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -25,22 +25,28 @@ Rectangle { color: qgcPal.window anchors.fill: parent - property real _labelWidth: ScreenTools.defaultFontPixelWidth * 28 - property real _valueWidth: ScreenTools.defaultFontPixelWidth * 24 - property int _selectedCount: 0 - property real _columnSpacing: ScreenTools.defaultFontPixelHeight * 0.25 - + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 28 + property real _valueWidth: ScreenTools.defaultFontPixelWidth * 24 + property int _selectedCount: 0 + property real _columnSpacing: ScreenTools.defaultFontPixelHeight * 0.25 + property bool _uploadedSelected: false QGCPalette { id: qgcPal } Connections { target: QGroundControl.mavlinkLogManager onSelectedCountChanged: { + _uploadedSelected = false var selected = 0 for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { var logFile = QGroundControl.mavlinkLogManager.logFiles.get(i) - if(logFile.selected) + if(logFile.selected) { selected++ + //-- If an uploaded file is selected, disable "Upload" button + if(logFile.uploaded) { + _uploadedSelected = true + } + } } _selectedCount = selected } @@ -425,7 +431,7 @@ Rectangle { } QGCButton { text: "Upload Selected" - enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning + enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning && !_uploadedSelected visible: !QGroundControl.mavlinkLogManager.uploading onClicked: { QGroundControl.mavlinkLogManager.emailAddress = emailField.text -- GitLab From 95f0259e52f6951418d8ca2c9d3a7bb8035214da Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 12:45:19 -0400 Subject: [PATCH 013/398] Handle COMMAND_ACK for Start/Stop logging. --- src/Vehicle/MavlinkLogManager.cc | 114 +++++++++++++++++++++++++++---- src/Vehicle/MavlinkLogManager.h | 5 ++ src/Vehicle/Vehicle.cc | 11 ++- 3 files changed, 113 insertions(+), 17 deletions(-) diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc index 7da59ae9a..5d6a95863 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -19,6 +19,8 @@ #include #include +#define kTimeOutMilliseconds 1000 + QGC_LOGGING_CATEGORY(MavlinkLogManagerLog, "MavlinkLogManagerLog") static const char* kEmailAddressKey = "MavlinkLogEmail"; @@ -126,6 +128,7 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) , _currentSavingFile(NULL) , _sequence(0) , _deleteAfterUpload(false) + , _loggingCmdTryCount(0) { //-- Get saved settings QSettings settings; @@ -171,6 +174,7 @@ MavlinkLogManager::setToolbox(QGCToolbox* toolbox) qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); if(!_loggingDisabled) { connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); + connect(&_ackTimer, &QTimer::timeout, this, &MavlinkLogManager::_processCmdAck); } } @@ -363,6 +367,8 @@ MavlinkLogManager::startLogging() if(_createNewLog()) { _vehicle->startMavlinkLog(); _logRunning = true; + _loggingCmdTryCount = 0; + _ackTimer.start(kTimeOutMilliseconds); emit logRunningChanged(); } } @@ -375,23 +381,28 @@ MavlinkLogManager::stopLogging() if(_vehicle) { //-- Tell vehicle to stop sending logs _vehicle->stopMavlinkLog(); - if(_currentSavingFile) { - _currentSavingFile->close(); - if(_currentSavingFile->record) { - _currentSavingFile->record->setWriting(false); - if(_enableAutoUpload) { - //-- Queue log for auto upload (set selected flag) - _currentSavingFile->record->setSelected(true); - if(!uploading()) { - uploadLog(); - } + } + if(_currentSavingFile) { + _currentSavingFile->close(); + if(_currentSavingFile->record) { + _currentSavingFile->record->setWriting(false); + if(_enableAutoUpload) { + //-- Queue log for auto upload (set selected flag) + _currentSavingFile->record->setSelected(true); + if(!uploading()) { + uploadLog(); } } - delete _currentSavingFile; - _currentSavingFile = NULL; - _logRunning = false; - emit logRunningChanged(); } + delete _currentSavingFile; + _currentSavingFile = NULL; + _logRunning = false; + if(_vehicle) { + //-- Setup a timer to make sure vehicle received the command + _loggingCmdTryCount = 0; + _ackTimer.start(kTimeOutMilliseconds); + } + emit logRunningChanged(); } } @@ -560,7 +571,10 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) if(_vehicle) { disconnect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); + disconnect(_vehicle, &Vehicle::commandLongAck, this, &MavlinkLogManager::_commandLongAck); _vehicle = NULL; + //-- Stop logging (if that's the case) + stopLogging(); emit canStartLogChanged(); } // Connect new system @@ -568,14 +582,49 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) _vehicle = vehicle; connect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); connect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); + connect(_vehicle, &Vehicle::commandLongAck, this, &MavlinkLogManager::_commandLongAck); emit canStartLogChanged(); } } +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_processCmdAck() +{ + if(_loggingCmdTryCount++ > 3) { + _ackTimer.stop(); + //-- Give up + if(_logRunning) { + qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command had no response."; + _discardLog(); + } else { + qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command had no response."; + } + } else { + if(_vehicle) { + if(_logRunning) { + _vehicle->startMavlinkLog(); + qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command sent again."; + } else { + _vehicle->stopMavlinkLog(); + qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command sent again."; + } + _ackTimer.start(kTimeOutMilliseconds); + } else { + //-- Vehicle went away on us + _ackTimer.stop(); + } + } +} + //----------------------------------------------------------------------------- void MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) { + //-- Disable timer if we got a message before an ACK for the start command + if(_logRunning) { + _ackTimer.stop(); + } if(_currentSavingFile && _currentSavingFile->fd) { if(sequence != _sequence) { qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data"; @@ -608,6 +657,43 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system _sequence = sequence + 1; } +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t result) +{ + if(command == MAV_CMD_LOGGING_START || command == MAV_CMD_LOGGING_STOP) { + _ackTimer.stop(); + //-- Did it fail? + if(result) { + if(command == MAV_CMD_LOGGING_STOP) { + //-- Not that it could happen but... + qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command failed."; + } else { + //-- Could not start logging for some reason. + qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command failed."; + _discardLog(); + } + } + } +} + +//----------------------------------------------------------------------------- +void +MavlinkLogManager::_discardLog() +{ + //-- Delete (empty) log file (and record) + if(_currentSavingFile) { + _currentSavingFile->close(); + if(_currentSavingFile->record) { + _deleteLog(_currentSavingFile->record); + } + delete _currentSavingFile; + _currentSavingFile = NULL; + } + _logRunning = false; + emit logRunningChanged(); +} + //----------------------------------------------------------------------------- bool MavlinkLogManager::_createNewLog() diff --git a/src/Vehicle/MavlinkLogManager.h b/src/Vehicle/MavlinkLogManager.h index 0ea7c8b99..b0c69fbb5 100644 --- a/src/Vehicle/MavlinkLogManager.h +++ b/src/Vehicle/MavlinkLogManager.h @@ -165,6 +165,8 @@ private slots: void _activeVehicleChanged (Vehicle* vehicle); void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool acked); void _armedChanged (bool armed); + void _commandLongAck (uint8_t compID, uint16_t command, uint8_t result); + void _processCmdAck (); private: bool _sendLog (const QString& logFile); @@ -173,6 +175,7 @@ private: int _getFirstSelected (); void _insertNewLog (MavlinkLogFiles* newLog); void _deleteLog (MavlinkLogFiles* log); + void _discardLog (); QString _makeFilename (const QString& baseName); private: @@ -191,6 +194,8 @@ private: CurrentRunningLog* _currentSavingFile; uint16_t _sequence; bool _deleteAfterUpload; + int _loggingCmdTryCount; + QTimer _ackTimer; }; #endif diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 6a5044b6b..f4ac52a02 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -562,9 +562,14 @@ void Vehicle::_handleCommandAck(mavlink_message_t& message) emit commandLongAck(message.compid, ack.command, ack.result); - if (ack.command == MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES) { - // Disregard failures - return; + // Disregard failures for these (handled above) + switch (ack.command) { + case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES: + case MAV_CMD_LOGGING_START: + case MAV_CMD_LOGGING_STOP: + return; + default: + break; } QString commandName = qgcApp()->toolbox()->missionCommandTree()->friendlyName((MAV_CMD)ack.command); -- GitLab From 8244624177fd010d33e3c6f40b174e318118cf88 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 17:29:00 -0400 Subject: [PATCH 014/398] Objectify log processor. --- src/Vehicle/MavlinkLogManager.cc | 272 ++++++++++++++++++++++++------- src/Vehicle/MavlinkLogManager.h | 44 ++--- src/Vehicle/Vehicle.cc | 6 +- src/Vehicle/Vehicle.h | 2 +- 4 files changed, 239 insertions(+), 85 deletions(-) diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc index 5d6a95863..ccb067f69 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -106,14 +106,189 @@ MavlinkLogFiles::setUploaded(bool uploaded) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void CurrentRunningLog::close() +MavlinkLogProcessor::MavlinkLogProcessor() + : _fd(NULL) + , _written(0) + , _sequence(-1) + , _numDrops(0) + , _gotHeader(false) + , _error(false) + , _record(NULL) { - if(fd) { - fclose(fd); - fd = NULL; +} + +//----------------------------------------------------------------------------- +MavlinkLogProcessor::~MavlinkLogProcessor() +{ + close(); +} + +//----------------------------------------------------------------------------- +void +MavlinkLogProcessor::close() +{ + if(_fd) { + fclose(_fd); + _fd = NULL; } } +//----------------------------------------------------------------------------- +bool +MavlinkLogProcessor::valid() +{ + return (_fd != NULL) && (_record != NULL); +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogProcessor::create(MavlinkLogManager* manager, const QString path, uint8_t id) +{ + _fileName.sprintf("%s/%03d-%s%s", + path.toLatin1().data(), + id, + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data(), + kUlogExtension); + _fd = fopen(_fileName.toLatin1().data(), "wb"); + if(_fd) { + _record = new MavlinkLogFiles(manager, _fileName, true); + _record->setWriting(true); + _sequence = -1; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogProcessor::_checkSequence(uint16_t seq, int& num_drops) +{ + num_drops = 0; + //-- Check if a sequence is newer than the one previously received and if + // there were dropped messages between the last one and this. + if(_sequence == -1) { + _sequence = seq; + return true; + } + if((uint16_t)_sequence == seq) { + return false; + } + if(seq > (uint16_t)_sequence) { + // Account for wrap-arounds, sequence is 2 bytes + if((seq - _sequence) > (1 << 15)) { // Assume reordered + return false; + } + num_drops = seq - _sequence - 1; + _numDrops += num_drops; + _sequence = seq; + return true; + } else { + if((_sequence - seq) > (1 << 15)) { + num_drops = (1 << 16) - _sequence - 1 + seq; + _numDrops += num_drops; + _sequence = seq; + return true; + } + return false; + } +} + +//----------------------------------------------------------------------------- +void +MavlinkLogProcessor::_writeData(void* data, int len) +{ + if(!_error) { + _error = fwrite(data, 1, len, _fd) != (size_t)len; + if(!_error) { + _written += len; + if(_record) { + _record->setSize(_written); + } + } else { + qCDebug(MavlinkLogManagerLog) << "File IO error:" << len << "bytes into" << _fileName; + } + } +} + +//----------------------------------------------------------------------------- +QByteArray +MavlinkLogProcessor::_writeUlogMessage(QByteArray& data) +{ + //-- Write ulog data w/o integrity checking, assuming data starts with a + // valid ulog message. returns the remaining data at the end. + while(data.length() > 2) { + uint8_t* ptr = (uint8_t*)data.data(); + int message_length = ptr[0] + (ptr[1] * 256) + 3; // 3 = ULog msg header + if(message_length > data.length()) + break; + _writeData(data.data(), message_length); + data.remove(0, message_length); + return data; + } + return data; +} + +//----------------------------------------------------------------------------- +bool +MavlinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, QByteArray data) +{ + int num_drops = 0; + _error = false; + while(_checkSequence(sequence, num_drops)) { + //-- The first 16 bytes need special treatment (this sounds awfully brittle) + if(!_gotHeader) { + if(data.size() < 16) { + //-- Shouldn't happen but if it does, we might as well close shop. + qCCritical(MavlinkLogManagerLog) << "Corrupt log header. Canceling log download."; + return false; + } + //-- Write header + _writeData(data.data(), 16); + data.remove(0, 16); + _gotHeader = true; + // What about data start offset now that we removed 16 bytes off the start? + } + if(_gotHeader && num_drops > 0) { + if(num_drops > 25) num_drops = 25; + //-- Hocus Pocus + // Write a dropout message. We don't really know the actual duration, + // so just use the number of drops * 10 ms + uint8_t bogus[] = {2, 0, 79, 0, 0}; + bogus[3] = num_drops * 10; + _writeData(bogus, sizeof(bogus)); + } + if(num_drops > 0) { + _writeUlogMessage(_ulogMessage); + _ulogMessage.clear(); + //-- If no usefull information in this message. Drop it. + if(first_message == 255) { + break; + } + if(first_message > 0) { + data.remove(0, first_message); + first_message = 0; + } + } + if(first_message == 255 && _ulogMessage.length() > 0) { + _ulogMessage.append(data); + break; + } + if(_ulogMessage.length()) { + _writeData(_ulogMessage.data(), _ulogMessage.length()); + if(first_message) { + _writeData(data.left(first_message).data(), first_message); + } + _ulogMessage.clear(); + } + if(first_message) { + data.remove(0, first_message); + } + _ulogMessage = _writeUlogMessage(data); + break; + } + return !_error; +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- MavlinkLogManager::MavlinkLogManager(QGCApplication* app) @@ -125,8 +300,7 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) , _vehicle(NULL) , _logRunning(false) , _loggingDisabled(false) - , _currentSavingFile(NULL) - , _sequence(0) + , _logProcessor(NULL) , _deleteAfterUpload(false) , _loggingCmdTryCount(0) { @@ -382,20 +556,20 @@ MavlinkLogManager::stopLogging() //-- Tell vehicle to stop sending logs _vehicle->stopMavlinkLog(); } - if(_currentSavingFile) { - _currentSavingFile->close(); - if(_currentSavingFile->record) { - _currentSavingFile->record->setWriting(false); + if(_logProcessor) { + _logProcessor->close(); + if(_logProcessor->record()) { + _logProcessor->record()->setWriting(false); if(_enableAutoUpload) { //-- Queue log for auto upload (set selected flag) - _currentSavingFile->record->setSelected(true); + _logProcessor->record()->setSelected(true); if(!uploading()) { uploadLog(); } } } - delete _currentSavingFile; - _currentSavingFile = NULL; + delete _logProcessor; + _logProcessor = NULL; _logRunning = false; if(_vehicle) { //-- Setup a timer to make sure vehicle received the command @@ -619,42 +793,24 @@ MavlinkLogManager::_processCmdAck() //----------------------------------------------------------------------------- void -MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool /*acked*/) +MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t first_message, QByteArray data, bool /*acked*/) { //-- Disable timer if we got a message before an ACK for the start command if(_logRunning) { _ackTimer.stop(); } - if(_currentSavingFile && _currentSavingFile->fd) { - if(sequence != _sequence) { - qCWarning(MavlinkLogManagerLog) << "Dropped Mavlink log data"; - if(first_message < 255) { - data += first_message; - length -= first_message; - } else { - return; - } - } - if(fwrite(data, 1, length, _currentSavingFile->fd) != (size_t)length) { - qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _currentSavingFile->fileName; - delete _currentSavingFile; - _currentSavingFile = NULL; + if(_logProcessor && _logProcessor->valid()) { + if(!_logProcessor->processStreamData(sequence, first_message, data)) { + qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _logProcessor->fileName(); + delete _logProcessor; + _logProcessor = NULL; _logRunning = false; _vehicle->stopMavlinkLog(); emit logRunningChanged(); } } else { - length = 0; qCWarning(MavlinkLogManagerLog) << "Mavlink log data received when not expected."; } - //-- Update file size - if(_currentSavingFile) { - if(_currentSavingFile->record) { - quint32 size = _currentSavingFile->record->size() + length; - _currentSavingFile->record->setSize(size); - } - } - _sequence = sequence + 1; } //----------------------------------------------------------------------------- @@ -682,13 +838,13 @@ void MavlinkLogManager::_discardLog() { //-- Delete (empty) log file (and record) - if(_currentSavingFile) { - _currentSavingFile->close(); - if(_currentSavingFile->record) { - _deleteLog(_currentSavingFile->record); + if(_logProcessor) { + _logProcessor->close(); + if(_logProcessor->record()) { + _deleteLog(_logProcessor->record()); } - delete _currentSavingFile; - _currentSavingFile = NULL; + delete _logProcessor; + _logProcessor = NULL; } _logRunning = false; emit logRunningChanged(); @@ -698,30 +854,20 @@ MavlinkLogManager::_discardLog() bool MavlinkLogManager::_createNewLog() { - if(_currentSavingFile) { - delete _currentSavingFile; - _currentSavingFile = NULL; + if(_logProcessor) { + delete _logProcessor; + _logProcessor = NULL; } - _currentSavingFile = new CurrentRunningLog; - _currentSavingFile->fileName.sprintf("%s/%03d-%s%s", - _logPath.toLatin1().data(), - _vehicle->id(), - QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz").toLatin1().data(), - kUlogExtension); - _currentSavingFile->fd = fopen(_currentSavingFile->fileName.toLatin1().data(), "wb"); - if(_currentSavingFile->fd) { - MavlinkLogFiles* newLog = new MavlinkLogFiles(this, _currentSavingFile->fileName, true); - newLog->setWriting(true); - _insertNewLog(newLog); - _currentSavingFile->record = newLog; + _logProcessor = new MavlinkLogProcessor; + if(_logProcessor->create(this, _logPath, _vehicle->id())) { + _insertNewLog(_logProcessor->record()); emit logFilesChanged(); } else { - qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _currentSavingFile->fileName; - delete _currentSavingFile; - _currentSavingFile = NULL; + qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _logProcessor->fileName(); + delete _logProcessor; + _logProcessor = NULL; } - _sequence = 0; - return _currentSavingFile != NULL; + return _logProcessor != NULL; } //----------------------------------------------------------------------------- diff --git a/src/Vehicle/MavlinkLogManager.h b/src/Vehicle/MavlinkLogManager.h index b0c69fbb5..b39d2528c 100644 --- a/src/Vehicle/MavlinkLogManager.h +++ b/src/Vehicle/MavlinkLogManager.h @@ -73,24 +73,31 @@ private: }; //----------------------------------------------------------------------------- -class CurrentRunningLog +class MavlinkLogProcessor { public: - CurrentRunningLog() - : fd(NULL) - , record(NULL) - , written(0) - { - } - ~CurrentRunningLog() - { - close(); - } - void close(); - FILE* fd; - QString fileName; - MavlinkLogFiles* record; - quint32 written; + MavlinkLogProcessor(); + ~MavlinkLogProcessor(); + void close (); + bool valid (); + bool create (MavlinkLogManager *manager, const QString path, uint8_t id); + MavlinkLogFiles* record () { return _record; } + QString fileName () { return _fileName; } + bool processStreamData(uint16_t _sequence, uint8_t first_message, QByteArray data); +private: + bool _checkSequence(uint16_t seq, int &num_drops); + QByteArray _writeUlogMessage(QByteArray &data); + void _writeData(void* data, int len); +private: + FILE* _fd; + quint32 _written; + int _sequence; + int _numDrops; + bool _gotHeader; + bool _error; + QByteArray _ulogMessage; + QString _fileName; + MavlinkLogFiles* _record; }; //----------------------------------------------------------------------------- @@ -163,7 +170,7 @@ private slots: void _dataAvailable (); void _uploadProgress (qint64 bytesSent, qint64 bytesTotal); void _activeVehicleChanged (Vehicle* vehicle); - void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool acked); + void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked); void _armedChanged (bool armed); void _commandLongAck (uint8_t compID, uint16_t command, uint8_t result); void _processCmdAck (); @@ -191,8 +198,7 @@ private: Vehicle* _vehicle; bool _logRunning; bool _loggingDisabled; - CurrentRunningLog* _currentSavingFile; - uint16_t _sequence; + MavlinkLogProcessor* _logProcessor; bool _deleteAfterUpload; int _loggingCmdTryCount; QTimer _ackTimer; diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index f4ac52a02..c9ed3a839 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2015,7 +2015,8 @@ Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) { mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); - emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.length, log.first_message_offset, log.data, false); + emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, + log.first_message_offset, QByteArray((const char*)log.data, log.length), false); } //----------------------------------------------------------------------------- @@ -2025,7 +2026,8 @@ Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); _ackMavlinkLogData(log.sequence); - emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.length, log.first_message_offset, log.data, true); + emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, + log.first_message_offset, QByteArray((const char*)log.data, log.length), true); } //----------------------------------------------------------------------------- diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 372a848e5..0f6243519 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -643,7 +643,7 @@ signals: void mavlinkScaledImu3(mavlink_message_t message); // Mavlink Log Download - void mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t length, uint8_t first_message, const uint8_t* data, bool acked); + void mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked); private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); -- GitLab From c77d8df1ba52cea8d374f77e8adabc2ff9ec2d7f Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 17:47:29 -0400 Subject: [PATCH 015/398] Make sure user knows an email is needed for log uploading. --- src/ui/preferences/MavlinkSettings.qml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 3beaa6067..b98e2b5c3 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -57,8 +57,8 @@ Rectangle { visible: false icon: StandardIcon.Warning standardButtons: StandardButton.Close - title: qsTr("Uploading Log Files") - text: qsTr("Please enter an email address before uploading log files.") + title: qsTr("MAVLink Logging") + text: qsTr("Please enter an email address before uploading MAVLink log files.") } QGCFlickable { @@ -235,6 +235,11 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter onEditingFinished: { QGroundControl.mavlinkLogManager.emailAddress = emailField.text + if(emailField.text === "") { + autoUploadCheck.checked = false + QGroundControl.mavlinkLogManager.enableAutoUpload = false + console.log("forcing enableAutoUpload to false") + } } } } @@ -283,9 +288,14 @@ Rectangle { id: autoUploadCheck text: qsTr("Enable automatic log uploads") checked: QGroundControl.mavlinkLogManager.enableAutoUpload - enabled: emailField.text !== "" && urlField !== "" onClicked: { - QGroundControl.mavlinkLogManager.enableAutoUpload = checked + QGroundControl.mavlinkLogManager.emailAddress = emailField.text + if(checked && QGroundControl.mavlinkLogManager.emailAddress === "") { + checked = false + emptyEmailDialog.open() + } else { + QGroundControl.mavlinkLogManager.enableAutoUpload = checked + } } } //----------------------------------------------------------------- @@ -293,7 +303,7 @@ Rectangle { QGCCheckBox { text: qsTr("Delete log file after uploading") checked: QGroundControl.mavlinkLogManager.deleteAfterUpload - enabled: emailField.text !== "" && urlField !== "" && autoUploadCheck.checked + enabled: autoUploadCheck.checked onClicked: { QGroundControl.mavlinkLogManager.deleteAfterUpload = checked } -- GitLab From f6ef6a490f59a458a2941eaaa5d8e82446aab5bf Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 21:13:58 -0400 Subject: [PATCH 016/398] I18N --- src/ui/preferences/MavlinkSettings.qml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index b98e2b5c3..35afc392d 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -238,7 +238,6 @@ Rectangle { if(emailField.text === "") { autoUploadCheck.checked = false QGroundControl.mavlinkLogManager.enableAutoUpload = false - console.log("forcing enableAutoUpload to false") } } } @@ -381,7 +380,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter } QGCLabel { - text: "Uploaded" + text: qsTr("Uploaded") visible: object.uploaded width: ScreenTools.defaultFontPixelWidth * 20; horizontalAlignment: Text.AlignRight @@ -404,7 +403,7 @@ Rectangle { spacing: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter QGCButton { - text: "Check All" + text: qsTr("Check All") enabled: !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: { for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { @@ -414,7 +413,7 @@ Rectangle { } } QGCButton { - text: "Check None" + text: qsTr("Check None") enabled: !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: { for(var i = 0; i < QGroundControl.mavlinkLogManager.logFiles.count; i++) { @@ -424,7 +423,7 @@ Rectangle { } } QGCButton { - text: "Delete Selected" + text: qsTr("Delete Selected") enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning onClicked: deleteDialog.open() MessageDialog { @@ -440,7 +439,7 @@ Rectangle { } } QGCButton { - text: "Upload Selected" + text: qsTr("Upload Selected") enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning && !_uploadedSelected visible: !QGroundControl.mavlinkLogManager.uploading onClicked: { @@ -463,7 +462,7 @@ Rectangle { } } QGCButton { - text: "Cancel" + text: qsTr("Cancel") enabled: QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning visible: QGroundControl.mavlinkLogManager.uploading onClicked: cancelDialog.open() -- GitLab From 44a441e7e974ee32502c1bfb3c0480306cddb290 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 22:26:41 -0400 Subject: [PATCH 017/398] Replacing a few placeholder icons with real ones. --- qgcresources.qrc | 4 +-- src/AnalyzeView/AnalyzeView.qml | 22 ++++++------ src/AnalyzeView/GeoTagIcon.png | Bin 502 -> 0 bytes src/AnalyzeView/GeoTagIcon.svg | 17 ++++++++++ src/AnalyzeView/LogDownloadIcon.png | Bin 502 -> 0 bytes src/AnalyzeView/LogDownloadIcon.svg | 23 +++++++++++++ src/QmlControls/SubMenuButton.qml | 5 ++- src/ui/toolbar/Images/Analyze.svg | 51 ++++++++++------------------ 8 files changed, 74 insertions(+), 48 deletions(-) delete mode 100644 src/AnalyzeView/GeoTagIcon.png create mode 100644 src/AnalyzeView/GeoTagIcon.svg delete mode 100644 src/AnalyzeView/LogDownloadIcon.png create mode 100644 src/AnalyzeView/LogDownloadIcon.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 703a8361f..0ac994c11 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -54,11 +54,11 @@ src/AutoPilotPlugins/PX4/Images/DatalinkLossLight.svg src/AutoPilotPlugins/PX4/Images/GeoFence.svg src/AutoPilotPlugins/PX4/Images/GeoFenceLight.svg - src/AnalyzeView/GeoTagIcon.png + src/AnalyzeView/GeoTagIcon.svg src/AutoPilotPlugins/PX4/Images/LandMode.svg src/AutoPilotPlugins/PX4/Images/LandModeCopter.svg src/AutoPilotPlugins/APM/Images/LightsComponentIcon.png - src/AnalyzeView/LogDownloadIcon.png + src/AnalyzeView/LogDownloadIcon.svg src/AutoPilotPlugins/PX4/Images/LowBattery.svg src/AutoPilotPlugins/PX4/Images/LowBatteryLight.svg src/AutoPilotPlugins/Common/Images/MotorComponentIcon.svg diff --git a/src/AnalyzeView/AnalyzeView.qml b/src/AnalyzeView/AnalyzeView.qml index 33545d0f9..cb827796f 100644 --- a/src/AnalyzeView/AnalyzeView.qml +++ b/src/AnalyzeView/AnalyzeView.qml @@ -86,25 +86,25 @@ Rectangle { model: ListModel { ListElement { - buttonImage: "/qmlimages/LogDownloadIcon" - buttonText: qsTr("Log Download") - pageSource: "LogDownloadPage.qml" + buttonImage: "/qmlimages/LogDownloadIcon" + buttonText: qsTr("Log Download") + pageSource: "LogDownloadPage.qml" } ListElement { - buttonImage: "/qmlimages/GeoTagIcon" - buttonText: qsTr("GeoTag Images") - pageSource: "GeoTagPage.qml" + buttonImage: "/qmlimages/GeoTagIcon" + buttonText: qsTr("GeoTag Images") + pageSource: "GeoTagPage.qml" } } Component.onCompleted: itemAt(0).checked = true SubMenuButton { - imageResource: buttonImage - setupIndicator: false - exclusiveGroup: setupButtonGroup - text: buttonText - onClicked: panelLoader.source = pageSource + imageResource: buttonImage + setupIndicator: false + exclusiveGroup: setupButtonGroup + text: buttonText + onClicked: panelLoader.source = pageSource } } } diff --git a/src/AnalyzeView/GeoTagIcon.png b/src/AnalyzeView/GeoTagIcon.png deleted file mode 100644 index 6aa8343a341df18048e0c9feeeb9491e2c63aaa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 zcmV4hVSb4Mz~&Q&|7Z z&UwJ@ya9HBqBx1P)|D|~jGYBW85^yg3FfMA>TTaE1&sNT#y*VM?;of8n{sAmb;d5v ze@+Qw)dJLAlrS2yOnPkt6^w0b0LoXJ8)gQo!9o%_VWy?AMG=6O#<5&5Q|*DVRb=cn z7tECMj?WMocgX=`r5xXWWvs-)FsLb8VIDTEUyBx0V_-Us)96-ZnAugOg1M)>6Gv?H z>r=qYDPK9UIKo#Q4Wr>eb%AQ+{%b-|9Wuzjl=m_e8>@;fUxNgc0iXf9?3gmJ1}FkP z{oa{=7H|yJlpT9g9C6;UK%3f;B*8n=0H~ZdoiCme>fy2mfXo|L`!|i0kA&D6$0H?1Qb3707*qoM6N<$f(`)6&Hw-a diff --git a/src/AnalyzeView/GeoTagIcon.svg b/src/AnalyzeView/GeoTagIcon.svg new file mode 100644 index 000000000..ba15cb0ad --- /dev/null +++ b/src/AnalyzeView/GeoTagIcon.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/AnalyzeView/LogDownloadIcon.png b/src/AnalyzeView/LogDownloadIcon.png deleted file mode 100644 index 6aa8343a341df18048e0c9feeeb9491e2c63aaa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 zcmV4hVSb4Mz~&Q&|7Z z&UwJ@ya9HBqBx1P)|D|~jGYBW85^yg3FfMA>TTaE1&sNT#y*VM?;of8n{sAmb;d5v ze@+Qw)dJLAlrS2yOnPkt6^w0b0LoXJ8)gQo!9o%_VWy?AMG=6O#<5&5Q|*DVRb=cn z7tECMj?WMocgX=`r5xXWWvs-)FsLb8VIDTEUyBx0V_-Us)96-ZnAugOg1M)>6Gv?H z>r=qYDPK9UIKo#Q4Wr>eb%AQ+{%b-|9Wuzjl=m_e8>@;fUxNgc0iXf9?3gmJ1}FkP z{oa{=7H|yJlpT9g9C6;UK%3f;B*8n=0H~ZdoiCme>fy2mfXo|L`!|i0kA&D6$0H?1Qb3707*qoM6N<$f(`)6&Hw-a diff --git a/src/AnalyzeView/LogDownloadIcon.svg b/src/AnalyzeView/LogDownloadIcon.svg new file mode 100644 index 000000000..a8d9b0b7a --- /dev/null +++ b/src/AnalyzeView/LogDownloadIcon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/src/QmlControls/SubMenuButton.qml b/src/QmlControls/SubMenuButton.qml index 640458ceb..6698d27f1 100644 --- a/src/QmlControls/SubMenuButton.qml +++ b/src/QmlControls/SubMenuButton.qml @@ -7,9 +7,11 @@ import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 Button { + id: _rootButton property bool setupComplete: true ///< true: setup complete indicator shows as completed property bool setupIndicator: true ///< true: show setup complete indicator property string imageResource: "/qmlimages/subMenuButtonImage.png" ///< Button image + property size sourceSize: Qt.size(ScreenTools.defaultFontPixelHeight * 2, ScreenTools.defaultFontPixelHeight * 2) text: "Button" ///< Pass in your own button text @@ -40,9 +42,10 @@ Button { width: ScreenTools.defaultFontPixelHeight * 2 height: ScreenTools.defaultFontPixelHeight * 2 fillMode: Image.PreserveAspectFit - smooth: true + mipmap: true color: control.setupComplete ? qgcPal.button : "red" source: control.imageResource + sourceSize: _rootButton.sourceSize } QGCLabel { diff --git a/src/ui/toolbar/Images/Analyze.svg b/src/ui/toolbar/Images/Analyze.svg index 308898715..d96303a5e 100644 --- a/src/ui/toolbar/Images/Analyze.svg +++ b/src/ui/toolbar/Images/Analyze.svg @@ -1,34 +1,17 @@ - - - - - - - - - - + + + + + + + -- GitLab From c3f0a51f0ac287ea25d68f27652ca80efdbf0475 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 23:08:35 -0400 Subject: [PATCH 018/398] Making the "brand" MAVLink consistent. --- src/Vehicle/MavlinkLogManager.cc | 10 +++++----- src/ui/AppSettings.qml | 2 +- src/ui/preferences/MavlinkSettings.qml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MavlinkLogManager.cc index ccb067f69..85074c9b6 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MavlinkLogManager.cc @@ -23,10 +23,10 @@ QGC_LOGGING_CATEGORY(MavlinkLogManagerLog, "MavlinkLogManagerLog") -static const char* kEmailAddressKey = "MavlinkLogEmail"; -static const char* kDescriptionsKey = "MavlinkLogDescription"; +static const char* kEmailAddressKey = "MAVLinkLogEmail"; +static const char* kDescriptionsKey = "MAVLinkLogDescription"; static const char* kDefaultDescr = "QGroundControl Session"; -static const char* kPx4URLKey = "MavlinkLogURL"; +static const char* kPx4URLKey = "MAVLinkLogURL"; static const char* kDefaultPx4URL = "http://logs.px4.io/upload"; static const char* kEnableAutoUploadKey = "EnableAutoUploadKey"; static const char* kEnableAutoStartKey = "EnableAutoStartKey"; @@ -329,7 +329,7 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) while(it.hasNext()) { _insertNewLog(new MavlinkLogFiles(this, it.next())); } - qCDebug(MavlinkLogManagerLog) << "Mavlink logs directory:" << _logPath; + qCDebug(MavlinkLogManagerLog) << "MAVLink logs directory:" << _logPath; } } @@ -809,7 +809,7 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system emit logRunningChanged(); } } else { - qCWarning(MavlinkLogManagerLog) << "Mavlink log data received when not expected."; + qCWarning(MavlinkLogManagerLog) << "MAVLink log data received when not expected."; } } diff --git a/src/ui/AppSettings.qml b/src/ui/AppSettings.qml index f6ce5a450..e807c815d 100644 --- a/src/ui/AppSettings.qml +++ b/src/ui/AppSettings.qml @@ -120,7 +120,7 @@ Rectangle { QGCButton { height: _buttonHeight - text: qsTr("MavLink") + text: qsTr("MAVLink") exclusiveGroup: panelActionGroup onClicked: { if(__rightPanel.source != "MavlinkSettings.qml") { diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 35afc392d..466704b22 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -102,7 +102,7 @@ Rectangle { QGCLabel { width: _labelWidth anchors.baseline: sysidField.baseline - text: qsTr("MavLink System ID:") + text: qsTr("MAVLink System ID:") } QGCTextField { id: sysidField @@ -204,7 +204,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: logLabel - text: qsTr("Mavlink Log Uploads") + text: qsTr("MAVLink Log Uploads") font.family: ScreenTools.demiboldFontFamily } } -- GitLab From f1aee303bb37c8b57c2efa1aa6717a5c3adaf762 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 26 Oct 2016 23:24:22 -0400 Subject: [PATCH 019/398] Just being really OCD. --- qgroundcontrol.pro | 4 +- src/QGCToolbox.cc | 4 +- src/QGCToolbox.h | 6 +- src/QmlControls/QGroundControlQmlGlobal.h | 6 +- ...linkLogManager.cc => MAVLinkLogManager.cc} | 192 +++++++++--------- ...avlinkLogManager.h => MAVLinkLogManager.h} | 40 ++-- src/ui/preferences/MavlinkSettings.qml | 2 +- 7 files changed, 127 insertions(+), 127 deletions(-) rename src/Vehicle/{MavlinkLogManager.cc => MAVLinkLogManager.cc} (81%) rename src/Vehicle/{MavlinkLogManager.h => MAVLinkLogManager.h} (90%) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index a070e4a2d..bf438801a 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -334,7 +334,7 @@ HEADERS += \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ - src/Vehicle/MavlinkLogManager.h \ + src/Vehicle/MAVLinkLogManager.h \ src/ui/toolbar/MainToolBarController.h \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ @@ -499,7 +499,7 @@ SOURCES += \ src/QmlControls/QmlObjectListModel.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ - src/Vehicle/MavlinkLogManager.cc \ + src/Vehicle/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 fd569b7b5..1d80469cd 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -28,7 +28,7 @@ #include "FollowMe.h" #include "PositionManager.h" #include "VideoManager.h" -#include "MavlinkLogManager.h" +#include "MAVLinkLogManager.h" QGCToolbox::QGCToolbox(QGCApplication* app) : _audioOutput(NULL) @@ -73,7 +73,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) _qgcPositionManager = new QGCPositionManager(app); _followMe = new FollowMe(app); _videoManager = new VideoManager(app); - _mavlinkLogManager = new MavlinkLogManager(app); + _mavlinkLogManager = new MAVLinkLogManager(app); } void QGCToolbox::setChildToolboxes(void) diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 1b0080de5..cb36a06e6 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -32,7 +32,7 @@ class QGCImageProvider; class UASMessageHandler; class QGCPositionManager; class VideoManager; -class MavlinkLogManager; +class MAVLinkLogManager; /// This is used to manage all of our top level services/tools class QGCToolbox { @@ -57,7 +57,7 @@ public: FollowMe* followMe(void) { return _followMe; } QGCPositionManager* qgcPositionManager(void) { return _qgcPositionManager; } VideoManager* videoManager(void) { return _videoManager; } - MavlinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; } + MAVLinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; } #ifndef __mobile__ GPSManager* gpsManager(void) { return _gpsManager; } @@ -86,7 +86,7 @@ private: FollowMe* _followMe; QGCPositionManager* _qgcPositionManager; VideoManager* _videoManager; - MavlinkLogManager* _mavlinkLogManager; + MAVLinkLogManager* _mavlinkLogManager; friend class QGCApplication; }; diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index b23823f44..473f7a72f 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -72,7 +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(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 @@ -167,7 +167,7 @@ public: QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; } MissionCommandTree* missionCommandTree () { return _missionCommandTree; } VideoManager* videoManager () { return _videoManager; } - MavlinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } + MAVLinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } @@ -239,7 +239,7 @@ private: QGCPositionManager* _qgcPositionManager; MissionCommandTree* _missionCommandTree; VideoManager* _videoManager; - MavlinkLogManager* _mavlinkLogManager; + MAVLinkLogManager* _mavlinkLogManager; bool _virtualTabletJoystick; qreal _baseFontPointSize; diff --git a/src/Vehicle/MavlinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc similarity index 81% rename from src/Vehicle/MavlinkLogManager.cc rename to src/Vehicle/MAVLinkLogManager.cc index 85074c9b6..ea153d396 100644 --- a/src/Vehicle/MavlinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -7,7 +7,7 @@ * ****************************************************************************/ -#include "MavlinkLogManager.h" +#include "MAVLinkLogManager.h" #include "QGCApplication.h" #include #include @@ -21,7 +21,7 @@ #define kTimeOutMilliseconds 1000 -QGC_LOGGING_CATEGORY(MavlinkLogManagerLog, "MavlinkLogManagerLog") +QGC_LOGGING_CATEGORY(MAVLinkLogManagerLog, "MAVLinkLogManagerLog") static const char* kEmailAddressKey = "MAVLinkLogEmail"; static const char* kDescriptionsKey = "MAVLinkLogDescription"; @@ -35,7 +35,7 @@ static const char* kUlogExtension = ".ulg"; static const char* kSidecarExtension = ".uploaded"; //----------------------------------------------------------------------------- -MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& filePath, bool newFile) +MAVLinkLogFiles::MAVLinkLogFiles(MAVLinkLogManager* manager, const QString& filePath, bool newFile) : _manager(manager) , _size(0) , _selected(false) @@ -57,7 +57,7 @@ MavlinkLogFiles::MavlinkLogFiles(MavlinkLogManager* manager, const QString& file //----------------------------------------------------------------------------- void -MavlinkLogFiles::setSize(quint32 size) +MAVLinkLogFiles::setSize(quint32 size) { _size = size; emit sizeChanged(); @@ -65,7 +65,7 @@ MavlinkLogFiles::setSize(quint32 size) //----------------------------------------------------------------------------- void -MavlinkLogFiles::setSelected(bool selected) +MAVLinkLogFiles::setSelected(bool selected) { _selected = selected; emit selectedChanged(); @@ -74,7 +74,7 @@ MavlinkLogFiles::setSelected(bool selected) //----------------------------------------------------------------------------- void -MavlinkLogFiles::setUploading(bool uploading) +MAVLinkLogFiles::setUploading(bool uploading) { _uploading = uploading; emit uploadingChanged(); @@ -82,7 +82,7 @@ MavlinkLogFiles::setUploading(bool uploading) //----------------------------------------------------------------------------- void -MavlinkLogFiles::setProgress(qreal progress) +MAVLinkLogFiles::setProgress(qreal progress) { _progress = progress; emit progressChanged(); @@ -90,7 +90,7 @@ MavlinkLogFiles::setProgress(qreal progress) //----------------------------------------------------------------------------- void -MavlinkLogFiles::setWriting(bool writing) +MAVLinkLogFiles::setWriting(bool writing) { _writing = writing; emit writingChanged(); @@ -98,7 +98,7 @@ MavlinkLogFiles::setWriting(bool writing) //----------------------------------------------------------------------------- void -MavlinkLogFiles::setUploaded(bool uploaded) +MAVLinkLogFiles::setUploaded(bool uploaded) { _uploaded = uploaded; emit uploadedChanged(); @@ -106,7 +106,7 @@ MavlinkLogFiles::setUploaded(bool uploaded) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -MavlinkLogProcessor::MavlinkLogProcessor() +MAVLinkLogProcessor::MAVLinkLogProcessor() : _fd(NULL) , _written(0) , _sequence(-1) @@ -118,14 +118,14 @@ MavlinkLogProcessor::MavlinkLogProcessor() } //----------------------------------------------------------------------------- -MavlinkLogProcessor::~MavlinkLogProcessor() +MAVLinkLogProcessor::~MAVLinkLogProcessor() { close(); } //----------------------------------------------------------------------------- void -MavlinkLogProcessor::close() +MAVLinkLogProcessor::close() { if(_fd) { fclose(_fd); @@ -135,14 +135,14 @@ MavlinkLogProcessor::close() //----------------------------------------------------------------------------- bool -MavlinkLogProcessor::valid() +MAVLinkLogProcessor::valid() { return (_fd != NULL) && (_record != NULL); } //----------------------------------------------------------------------------- bool -MavlinkLogProcessor::create(MavlinkLogManager* manager, const QString path, uint8_t id) +MAVLinkLogProcessor::create(MAVLinkLogManager* manager, const QString path, uint8_t id) { _fileName.sprintf("%s/%03d-%s%s", path.toLatin1().data(), @@ -151,7 +151,7 @@ MavlinkLogProcessor::create(MavlinkLogManager* manager, const QString path, uint kUlogExtension); _fd = fopen(_fileName.toLatin1().data(), "wb"); if(_fd) { - _record = new MavlinkLogFiles(manager, _fileName, true); + _record = new MAVLinkLogFiles(manager, _fileName, true); _record->setWriting(true); _sequence = -1; return true; @@ -161,7 +161,7 @@ MavlinkLogProcessor::create(MavlinkLogManager* manager, const QString path, uint //----------------------------------------------------------------------------- bool -MavlinkLogProcessor::_checkSequence(uint16_t seq, int& num_drops) +MAVLinkLogProcessor::_checkSequence(uint16_t seq, int& num_drops) { num_drops = 0; //-- Check if a sequence is newer than the one previously received and if @@ -195,7 +195,7 @@ MavlinkLogProcessor::_checkSequence(uint16_t seq, int& num_drops) //----------------------------------------------------------------------------- void -MavlinkLogProcessor::_writeData(void* data, int len) +MAVLinkLogProcessor::_writeData(void* data, int len) { if(!_error) { _error = fwrite(data, 1, len, _fd) != (size_t)len; @@ -205,14 +205,14 @@ MavlinkLogProcessor::_writeData(void* data, int len) _record->setSize(_written); } } else { - qCDebug(MavlinkLogManagerLog) << "File IO error:" << len << "bytes into" << _fileName; + qCDebug(MAVLinkLogManagerLog) << "File IO error:" << len << "bytes into" << _fileName; } } } //----------------------------------------------------------------------------- QByteArray -MavlinkLogProcessor::_writeUlogMessage(QByteArray& data) +MAVLinkLogProcessor::_writeUlogMessage(QByteArray& data) { //-- Write ulog data w/o integrity checking, assuming data starts with a // valid ulog message. returns the remaining data at the end. @@ -230,7 +230,7 @@ MavlinkLogProcessor::_writeUlogMessage(QByteArray& data) //----------------------------------------------------------------------------- bool -MavlinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, QByteArray data) +MAVLinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, QByteArray data) { int num_drops = 0; _error = false; @@ -239,7 +239,7 @@ MavlinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, if(!_gotHeader) { if(data.size() < 16) { //-- Shouldn't happen but if it does, we might as well close shop. - qCCritical(MavlinkLogManagerLog) << "Corrupt log header. Canceling log download."; + qCCritical(MAVLinkLogManagerLog) << "Corrupt log header. Canceling log download."; return false; } //-- Write header @@ -291,7 +291,7 @@ MavlinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -MavlinkLogManager::MavlinkLogManager(QGCApplication* app) +MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) : QGCTool(app) , _enableAutoUpload(true) , _enableAutoStart(true) @@ -314,10 +314,10 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool()); //-- Logging location _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - _logPath += "/MavlinkLogs"; + _logPath += "/MAVLinkLogs"; if(!QDir(_logPath).exists()) { if(!QDir().mkpath(_logPath)) { - qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log download path:" << _logPath; + qCCritical(MAVLinkLogManagerLog) << "Could not create MAVLink log download path:" << _logPath; _loggingDisabled = true; } } @@ -327,34 +327,34 @@ MavlinkLogManager::MavlinkLogManager(QGCApplication* app) filter += kUlogExtension; QDirIterator it(_logPath, QStringList() << filter, QDir::Files); while(it.hasNext()) { - _insertNewLog(new MavlinkLogFiles(this, it.next())); + _insertNewLog(new MAVLinkLogFiles(this, it.next())); } - qCDebug(MavlinkLogManagerLog) << "MAVLink logs directory:" << _logPath; + qCDebug(MAVLinkLogManagerLog) << "MAVLink logs directory:" << _logPath; } } //----------------------------------------------------------------------------- -MavlinkLogManager::~MavlinkLogManager() +MAVLinkLogManager::~MAVLinkLogManager() { _logFiles.clear(); } //----------------------------------------------------------------------------- void -MavlinkLogManager::setToolbox(QGCToolbox* toolbox) +MAVLinkLogManager::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.MavlinkLogManager", 1, 0, "MavlinkLogManager", "Reference only"); + qmlRegisterUncreatableType("QGroundControl.MAVLinkLogManager", 1, 0, "MAVLinkLogManager", "Reference only"); if(!_loggingDisabled) { - connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MavlinkLogManager::_activeVehicleChanged); - connect(&_ackTimer, &QTimer::timeout, this, &MavlinkLogManager::_processCmdAck); + connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MAVLinkLogManager::_activeVehicleChanged); + connect(&_ackTimer, &QTimer::timeout, this, &MAVLinkLogManager::_processCmdAck); } } //----------------------------------------------------------------------------- void -MavlinkLogManager::setEmailAddress(QString email) +MAVLinkLogManager::setEmailAddress(QString email) { _emailAddress = email; QSettings settings; @@ -364,7 +364,7 @@ MavlinkLogManager::setEmailAddress(QString email) //----------------------------------------------------------------------------- void -MavlinkLogManager::setDescription(QString description) +MAVLinkLogManager::setDescription(QString description) { _description = description; QSettings settings; @@ -374,7 +374,7 @@ MavlinkLogManager::setDescription(QString description) //----------------------------------------------------------------------------- void -MavlinkLogManager::setUploadURL(QString url) +MAVLinkLogManager::setUploadURL(QString url) { _uploadURL = url; if(_uploadURL.isEmpty()) { @@ -387,7 +387,7 @@ MavlinkLogManager::setUploadURL(QString url) //----------------------------------------------------------------------------- void -MavlinkLogManager::setEnableAutoUpload(bool enable) +MAVLinkLogManager::setEnableAutoUpload(bool enable) { _enableAutoUpload = enable; QSettings settings; @@ -397,7 +397,7 @@ MavlinkLogManager::setEnableAutoUpload(bool enable) //----------------------------------------------------------------------------- void -MavlinkLogManager::setEnableAutoStart(bool enable) +MAVLinkLogManager::setEnableAutoStart(bool enable) { _enableAutoStart = enable; QSettings settings; @@ -407,7 +407,7 @@ MavlinkLogManager::setEnableAutoStart(bool enable) //----------------------------------------------------------------------------- void -MavlinkLogManager::setDeleteAfterUpload(bool enable) +MAVLinkLogManager::setDeleteAfterUpload(bool enable) { _deleteAfterUpload = enable; QSettings settings; @@ -417,20 +417,20 @@ MavlinkLogManager::setDeleteAfterUpload(bool enable) //----------------------------------------------------------------------------- bool -MavlinkLogManager::uploading() +MAVLinkLogManager::uploading() { return _currentLogfile != NULL; } //----------------------------------------------------------------------------- void -MavlinkLogManager::uploadLog() +MAVLinkLogManager::uploadLog() { if(_currentLogfile) { _currentLogfile->setUploading(false); } for(int i = 0; i < _logFiles.count(); i++) { - _currentLogfile = qobject_cast(_logFiles.get(i)); + _currentLogfile = qobject_cast(_logFiles.get(i)); Q_ASSERT(_currentLogfile); if(_currentLogfile->selected()) { _currentLogfile->setSelected(false); @@ -450,7 +450,7 @@ MavlinkLogManager::uploadLog() //----------------------------------------------------------------------------- void -MavlinkLogManager::_insertNewLog(MavlinkLogFiles* newLog) +MAVLinkLogManager::_insertNewLog(MAVLinkLogFiles* newLog) { //-- Simpler than trying to sort this thing int count = _logFiles.count(); @@ -458,7 +458,7 @@ MavlinkLogManager::_insertNewLog(MavlinkLogFiles* newLog) _logFiles.append(newLog); } else { for(int i = 0; i < count; i++) { - MavlinkLogFiles* f = qobject_cast(_logFiles.get(i)); + MAVLinkLogFiles* f = qobject_cast(_logFiles.get(i)); if(newLog->name() < f->name()) { _logFiles.insert(i, newLog); return; @@ -470,10 +470,10 @@ MavlinkLogManager::_insertNewLog(MavlinkLogFiles* newLog) //----------------------------------------------------------------------------- int -MavlinkLogManager::_getFirstSelected() +MAVLinkLogManager::_getFirstSelected() { for(int i = 0; i < _logFiles.count(); i++) { - MavlinkLogFiles* f = qobject_cast(_logFiles.get(i)); + MAVLinkLogFiles* f = qobject_cast(_logFiles.get(i)); Q_ASSERT(f); if(f->selected()) { return i; @@ -484,26 +484,26 @@ MavlinkLogManager::_getFirstSelected() //----------------------------------------------------------------------------- void -MavlinkLogManager::deleteLog() +MAVLinkLogManager::deleteLog() { while (true) { int idx = _getFirstSelected(); if(idx < 0) { break; } - MavlinkLogFiles* log = qobject_cast(_logFiles.get(idx)); + MAVLinkLogFiles* log = qobject_cast(_logFiles.get(idx)); _deleteLog(log); } } //----------------------------------------------------------------------------- void -MavlinkLogManager::_deleteLog(MavlinkLogFiles* log) +MAVLinkLogManager::_deleteLog(MAVLinkLogFiles* log) { QString filePath = _makeFilename(log->name()); QFile gone(filePath); if(!gone.remove()) { - qCWarning(MavlinkLogManagerLog) << "Could not delete Mavlink log file:" << _logPath; + qCWarning(MAVLinkLogManagerLog) << "Could not delete MAVLink log file:" << _logPath; } //-- Remove sidecar file (if any) filePath.replace(kUlogExtension, kSidecarExtension); @@ -519,10 +519,10 @@ MavlinkLogManager::_deleteLog(MavlinkLogFiles* log) //----------------------------------------------------------------------------- void -MavlinkLogManager::cancelUpload() +MAVLinkLogManager::cancelUpload() { for(int i = 0; i < _logFiles.count(); i++) { - MavlinkLogFiles* pLogFile = qobject_cast(_logFiles.get(i)); + MAVLinkLogFiles* pLogFile = qobject_cast(_logFiles.get(i)); Q_ASSERT(pLogFile); if(pLogFile->selected() && pLogFile != _currentLogfile) { pLogFile->setSelected(false); @@ -535,7 +535,7 @@ MavlinkLogManager::cancelUpload() //----------------------------------------------------------------------------- void -MavlinkLogManager::startLogging() +MAVLinkLogManager::startLogging() { if(_vehicle) { if(_createNewLog()) { @@ -550,7 +550,7 @@ MavlinkLogManager::startLogging() //----------------------------------------------------------------------------- void -MavlinkLogManager::stopLogging() +MAVLinkLogManager::stopLogging() { if(_vehicle) { //-- Tell vehicle to stop sending logs @@ -592,24 +592,24 @@ create_form_part(const QString& name, const QString& value) //----------------------------------------------------------------------------- bool -MavlinkLogManager::_sendLog(const QString& logFile) +MAVLinkLogManager::_sendLog(const QString& logFile) { QString defaultDescription = _description; if(_description.isEmpty()) { - qCWarning(MavlinkLogManagerLog) << "Log description missing. Using defaults."; + qCWarning(MAVLinkLogManagerLog) << "Log description missing. Using defaults."; defaultDescription = kDefaultDescr; } if(_emailAddress.isEmpty()) { - qCCritical(MavlinkLogManagerLog) << "User email missing."; + qCCritical(MAVLinkLogManagerLog) << "User email missing."; return false; } if(_uploadURL.isEmpty()) { - qCCritical(MavlinkLogManagerLog) << "Upload URL missing."; + qCCritical(MAVLinkLogManagerLog) << "Upload URL missing."; return false; } QFileInfo fi(logFile); if(!fi.exists()) { - qCCritical(MavlinkLogManagerLog) << "Log file missing:" << logFile; + qCCritical(MAVLinkLogManagerLog) << "Log file missing:" << logFile; return false; } QFile* file = new QFile(logFile); @@ -617,7 +617,7 @@ MavlinkLogManager::_sendLog(const QString& logFile) if(file) { delete file; } - qCCritical(MavlinkLogManagerLog) << "Could not open log file:" << logFile; + qCCritical(MAVLinkLogManagerLog) << "Could not open log file:" << logFile; return false; } if(!_nam) { @@ -649,40 +649,40 @@ MavlinkLogManager::_sendLog(const QString& logFile) request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); #endif 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); + 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."; + qCDebug(MAVLinkLogManagerLog) << "Log" << fi.baseName() << "Uploading." << fi.size() << "bytes."; _nam->setProxy(savedProxy); return true; } //----------------------------------------------------------------------------- bool -MavlinkLogManager::_processUploadResponse(int http_code, QByteArray& data) +MAVLinkLogManager::_processUploadResponse(int http_code, QByteArray& data) { - qCDebug(MavlinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data); + qCDebug(MAVLinkLogManagerLog) << "Uploaded response:" << QString::fromUtf8(data); emit readyRead(data); return http_code == 200; } //----------------------------------------------------------------------------- void -MavlinkLogManager::_dataAvailable() +MAVLinkLogManager::_dataAvailable() { QNetworkReply* reply = qobject_cast(sender()); if(!reply) { return; } QByteArray data = reply->readAll(); - qCDebug(MavlinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data); + qCDebug(MAVLinkLogManagerLog) << "Uploaded response data:" << QString::fromUtf8(data); } //----------------------------------------------------------------------------- void -MavlinkLogManager::_uploadFinished() +MAVLinkLogManager::_uploadFinished() { QNetworkReply* reply = qobject_cast(sender()); if(!reply) { @@ -691,7 +691,7 @@ MavlinkLogManager::_uploadFinished() const int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); QByteArray data = reply->readAll(); if(_processUploadResponse(http_code, data)) { - qCDebug(MavlinkLogManagerLog) << "Log uploaded."; + qCDebug(MAVLinkLogManagerLog) << "Log uploaded."; emit succeed(); if(_deleteAfterUpload) { if(_currentLogfile) { @@ -711,7 +711,7 @@ MavlinkLogManager::_uploadFinished() } } } else { - qCWarning(MavlinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); + qCWarning(MAVLinkLogManagerLog) << QString("Log Upload Error: %1 status: %2").arg(reply->errorString(), reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toString()); emit failed(); } reply->deleteLater(); @@ -721,7 +721,7 @@ MavlinkLogManager::_uploadFinished() //----------------------------------------------------------------------------- void -MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) +MAVLinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) { if(bytesTotal) { qreal progress = (qreal)bytesSent / (qreal)bytesTotal; @@ -729,12 +729,12 @@ MavlinkLogManager::_uploadProgress(qint64 bytesSent, qint64 bytesTotal) _currentLogfile->setProgress(progress); } } - qCDebug(MavlinkLogManagerLog) << bytesSent << "of" << bytesTotal; + qCDebug(MAVLinkLogManagerLog) << bytesSent << "of" << bytesTotal; } //----------------------------------------------------------------------------- void -MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) +MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) { //-- TODO: This is not quite right. This is being used to detect when a vehicle // connects/disconnects. In reality, if QGC is connected to multiple vehicles, @@ -743,9 +743,9 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // For now, we only handle one log download at a time. // Disconnect the previous one (if any) if(_vehicle) { - disconnect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); - disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); - disconnect(_vehicle, &Vehicle::commandLongAck, this, &MavlinkLogManager::_commandLongAck); + disconnect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); + disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); + disconnect(_vehicle, &Vehicle::commandLongAck, this, &MAVLinkLogManager::_commandLongAck); _vehicle = NULL; //-- Stop logging (if that's the case) stopLogging(); @@ -754,34 +754,34 @@ MavlinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // Connect new system if(vehicle) { _vehicle = vehicle; - connect(_vehicle, &Vehicle::armedChanged, this, &MavlinkLogManager::_armedChanged); - connect(_vehicle, &Vehicle::mavlinkLogData, this, &MavlinkLogManager::_mavlinkLogData); - connect(_vehicle, &Vehicle::commandLongAck, this, &MavlinkLogManager::_commandLongAck); + connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); + connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); + connect(_vehicle, &Vehicle::commandLongAck, this, &MAVLinkLogManager::_commandLongAck); emit canStartLogChanged(); } } //----------------------------------------------------------------------------- void -MavlinkLogManager::_processCmdAck() +MAVLinkLogManager::_processCmdAck() { if(_loggingCmdTryCount++ > 3) { _ackTimer.stop(); //-- Give up if(_logRunning) { - qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command had no response."; + qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command had no response."; _discardLog(); } else { - qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command had no response."; + qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command had no response."; } } else { if(_vehicle) { if(_logRunning) { _vehicle->startMavlinkLog(); - qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command sent again."; + qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command sent again."; } else { _vehicle->stopMavlinkLog(); - qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command sent again."; + qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command sent again."; } _ackTimer.start(kTimeOutMilliseconds); } else { @@ -793,7 +793,7 @@ MavlinkLogManager::_processCmdAck() //----------------------------------------------------------------------------- void -MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t first_message, QByteArray data, bool /*acked*/) +MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t first_message, QByteArray data, bool /*acked*/) { //-- Disable timer if we got a message before an ACK for the start command if(_logRunning) { @@ -801,7 +801,7 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system } if(_logProcessor && _logProcessor->valid()) { if(!_logProcessor->processStreamData(sequence, first_message, data)) { - qCCritical(MavlinkLogManagerLog) << "Error writing Mavlink log file:" << _logProcessor->fileName(); + qCCritical(MAVLinkLogManagerLog) << "Error writing MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = NULL; _logRunning = false; @@ -809,13 +809,13 @@ MavlinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system emit logRunningChanged(); } } else { - qCWarning(MavlinkLogManagerLog) << "MAVLink log data received when not expected."; + qCWarning(MAVLinkLogManagerLog) << "MAVLink log data received when not expected."; } } //----------------------------------------------------------------------------- void -MavlinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t result) +MAVLinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t result) { if(command == MAV_CMD_LOGGING_START || command == MAV_CMD_LOGGING_STOP) { _ackTimer.stop(); @@ -823,10 +823,10 @@ MavlinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t if(result) { if(command == MAV_CMD_LOGGING_STOP) { //-- Not that it could happen but... - qCWarning(MavlinkLogManagerLog) << "Stop MAVLink log command failed."; + qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command failed."; } else { //-- Could not start logging for some reason. - qCWarning(MavlinkLogManagerLog) << "Start MAVLink log command failed."; + qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command failed."; _discardLog(); } } @@ -835,7 +835,7 @@ MavlinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t //----------------------------------------------------------------------------- void -MavlinkLogManager::_discardLog() +MAVLinkLogManager::_discardLog() { //-- Delete (empty) log file (and record) if(_logProcessor) { @@ -852,18 +852,18 @@ MavlinkLogManager::_discardLog() //----------------------------------------------------------------------------- bool -MavlinkLogManager::_createNewLog() +MAVLinkLogManager::_createNewLog() { if(_logProcessor) { delete _logProcessor; _logProcessor = NULL; } - _logProcessor = new MavlinkLogProcessor; + _logProcessor = new MAVLinkLogProcessor; if(_logProcessor->create(this, _logPath, _vehicle->id())) { _insertNewLog(_logProcessor->record()); emit logFilesChanged(); } else { - qCCritical(MavlinkLogManagerLog) << "Could not create Mavlink log file:" << _logProcessor->fileName(); + qCCritical(MAVLinkLogManagerLog) << "Could not create MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = NULL; } @@ -872,7 +872,7 @@ MavlinkLogManager::_createNewLog() //----------------------------------------------------------------------------- void -MavlinkLogManager::_armedChanged(bool armed) +MAVLinkLogManager::_armedChanged(bool armed) { if(_vehicle) { if(armed) { @@ -889,7 +889,7 @@ MavlinkLogManager::_armedChanged(bool armed) //----------------------------------------------------------------------------- QString -MavlinkLogManager::_makeFilename(const QString& baseName) +MAVLinkLogManager::_makeFilename(const QString& baseName) { QString filePath = _logPath; filePath += "/"; diff --git a/src/Vehicle/MavlinkLogManager.h b/src/Vehicle/MAVLinkLogManager.h similarity index 90% rename from src/Vehicle/MavlinkLogManager.h rename to src/Vehicle/MAVLinkLogManager.h index b39d2528c..f0319502b 100644 --- a/src/Vehicle/MavlinkLogManager.h +++ b/src/Vehicle/MAVLinkLogManager.h @@ -8,8 +8,8 @@ ****************************************************************************/ -#ifndef MavlinkLogManager_H -#define MavlinkLogManager_H +#ifndef MAVLinkLogManager_H +#define MAVLinkLogManager_H #include @@ -18,17 +18,17 @@ #include "QGCToolbox.h" #include "Vehicle.h" -Q_DECLARE_LOGGING_CATEGORY(MavlinkLogManagerLog) +Q_DECLARE_LOGGING_CATEGORY(MAVLinkLogManagerLog) class QNetworkAccessManager; -class MavlinkLogManager; +class MAVLinkLogManager; //----------------------------------------------------------------------------- -class MavlinkLogFiles : public QObject +class MAVLinkLogFiles : public QObject { Q_OBJECT public: - MavlinkLogFiles (MavlinkLogManager* manager, const QString& filePath, bool newFile = false); + MAVLinkLogFiles (MAVLinkLogManager* manager, const QString& filePath, bool newFile = false); Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(quint32 size READ size NOTIFY sizeChanged) @@ -62,7 +62,7 @@ signals: void uploadedChanged (); private: - MavlinkLogManager* _manager; + MAVLinkLogManager* _manager; QString _name; quint32 _size; bool _selected; @@ -73,15 +73,15 @@ private: }; //----------------------------------------------------------------------------- -class MavlinkLogProcessor +class MAVLinkLogProcessor { public: - MavlinkLogProcessor(); - ~MavlinkLogProcessor(); + MAVLinkLogProcessor(); + ~MAVLinkLogProcessor(); void close (); bool valid (); - bool create (MavlinkLogManager *manager, const QString path, uint8_t id); - MavlinkLogFiles* record () { return _record; } + bool create (MAVLinkLogManager *manager, const QString path, uint8_t id); + MAVLinkLogFiles* record () { return _record; } QString fileName () { return _fileName; } bool processStreamData(uint16_t _sequence, uint8_t first_message, QByteArray data); private: @@ -97,17 +97,17 @@ private: bool _error; QByteArray _ulogMessage; QString _fileName; - MavlinkLogFiles* _record; + MAVLinkLogFiles* _record; }; //----------------------------------------------------------------------------- -class MavlinkLogManager : public QGCTool +class MAVLinkLogManager : public QGCTool { Q_OBJECT public: - MavlinkLogManager (QGCApplication* app); - ~MavlinkLogManager (); + MAVLinkLogManager (QGCApplication* app); + ~MAVLinkLogManager (); Q_PROPERTY(QString emailAddress READ emailAddress WRITE setEmailAddress NOTIFY emailAddressChanged) Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) @@ -180,8 +180,8 @@ private: bool _processUploadResponse (int http_code, QByteArray &data); bool _createNewLog (); int _getFirstSelected (); - void _insertNewLog (MavlinkLogFiles* newLog); - void _deleteLog (MavlinkLogFiles* log); + void _insertNewLog (MAVLinkLogFiles* newLog); + void _deleteLog (MAVLinkLogFiles* log); void _discardLog (); QString _makeFilename (const QString& baseName); @@ -194,11 +194,11 @@ private: bool _enableAutoStart; QNetworkAccessManager* _nam; QmlObjectListModel _logFiles; - MavlinkLogFiles* _currentLogfile; + MAVLinkLogFiles* _currentLogfile; Vehicle* _vehicle; bool _logRunning; bool _loggingDisabled; - MavlinkLogProcessor* _logProcessor; + MAVLinkLogProcessor* _logProcessor; bool _deleteAfterUpload; int _loggingCmdTryCount; QTimer _ackTimer; diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 466704b22..8cec2eb59 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -144,7 +144,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: mavlogLabel - text: qsTr("Vehicle Mavlink Logging") + text: qsTr("Vehicle MAVLink Logging") font.family: ScreenTools.demiboldFontFamily } } -- GitLab From 47e9e985f4b51a2527bf440ec17a5e0ad9afc2a9 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 4 Nov 2016 12:37:46 -0400 Subject: [PATCH 020/398] Checking for MAVLink 2.0 when first message is received. --- src/comm/MAVLinkProtocol.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index bf1ce9852..4d9be09c4 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -212,19 +212,17 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) } if (decodeState == 1) { - decodedFirstPacket = true; - - /* - * Handled in Vehicle.cc now. - mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); - if (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { - qDebug() << "switch to mavlink 2.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; - mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; - } else if ((mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && !(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { - qDebug() << "switch to mavlink 1.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; - mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; + if(!decodedFirstPacket) { + mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); + if (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { + qDebug() << "switch to mavlink 2.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; + mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; + } else if ((mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && !(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { + qDebug() << "switch to mavlink 1.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; + mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; + } + decodedFirstPacket = true; } - */ if(message.msgid == MAVLINK_MSG_ID_RADIO_STATUS) { -- GitLab From a82877ee0e15dc38c63614605fe3c9eb78f050b4 Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Fri, 14 Oct 2016 16:54:38 +0200 Subject: [PATCH 021/398] geotagging backend --- qgroundcontrol.pro | 6 +- src/AnalyzeView/ExifParser.cpp | 219 ++++++++++++++++++++++++++++ src/AnalyzeView/ExifParser.h | 16 ++ src/AnalyzeView/GeoTagController.cc | 181 ++++++++++++++++++++++- src/AnalyzeView/GeoTagController.h | 17 ++- 5 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 src/AnalyzeView/ExifParser.cpp create mode 100644 src/AnalyzeView/ExifParser.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index bf438801a..c919ffda6 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -340,7 +340,8 @@ HEADERS += \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ src/QmlControls/QGCImageProvider.h \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ - src/PositionManager/PositionManager.h + src/PositionManager/PositionManager.h \ + src/AnalyzeView/ExifParser.h AndroidBuild { HEADERS += \ @@ -506,7 +507,8 @@ SOURCES += \ src/QmlControls/QGCImageProvider.cc \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ src/PositionManager/SimulatedPosition.cc \ - src/PositionManager/PositionManager.cpp + src/PositionManager/PositionManager.cpp \ + src/AnalyzeView/ExifParser.cpp DebugBuild { SOURCES += \ diff --git a/src/AnalyzeView/ExifParser.cpp b/src/AnalyzeView/ExifParser.cpp new file mode 100644 index 000000000..083aff242 --- /dev/null +++ b/src/AnalyzeView/ExifParser.cpp @@ -0,0 +1,219 @@ +#include "ExifParser.h" +#include +#include + +ExifParser::ExifParser() +{ + +} + +ExifParser::~ExifParser() +{ + +} + +double ExifParser::readTime(QByteArray& buf) +{ + char tiffHeader[] = {0x49,0x49,0x2A,0x00}; + char createDateHeader[] = {0x04,0x90,0x02,0x00}; + + // find header position + uint32_t tiffHeaderIndex = buf.indexOf(tiffHeader); + + // find creation date header index + uint32_t createDateHeaderIndex = buf.indexOf(createDateHeader); + + // extract size of date-time string, -1 accounting for null-termination + uint32_t* sizeString = reinterpret_cast(buf.mid(createDateHeaderIndex + 4, 4).data()); + uint32_t createDateStringSize = qFromLittleEndian(*sizeString) - 1; + + // extract location of date-time string + uint32_t* dataIndex = reinterpret_cast(buf.mid(createDateHeaderIndex + 8, 4).data()); + uint32_t createDateStringDataIndex = qFromLittleEndian(*dataIndex) + tiffHeaderIndex; + + // read out data of create date-time field + QString createDate = buf.mid(createDateStringDataIndex, createDateStringSize); + + QStringList createDateList = createDate.split(' '); + if (createDateList.count() < 2) { + qWarning() << "Could not decode creation time and date: " << createDateList; + return -1.0; + } + QStringList dateList = createDateList[0].split(':'); + if (dateList.count() < 3) { + qWarning() << "Could not decode creation date: " << dateList; + return -1.0; + } + QStringList timeList = createDateList[1].split(':'); + if (timeList.count() < 3) { + qWarning() << "Could not decode creation time: " << timeList; + return -1.0; + } + QDate date(dateList[0].toInt(), dateList[1].toInt(), dateList[2].toInt()); + QTime time(timeList[0].toInt(), timeList[1].toInt(), timeList[2].toInt()); + QDateTime tagTime(date, time); + return tagTime.toMSecsSinceEpoch()/1000.0; +} + +bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) +{ + char app1Header[2] = {0xff, 0xe1}; + uint32_t app1HeaderInd = buf.indexOf(app1Header); + uint16_t *conversionPointer = reinterpret_cast(buf.mid(app1HeaderInd + 2, 2).data()); + uint16_t app1Size = *conversionPointer; + uint16_t app1SizeEndian = qFromBigEndian(app1Size) + 0xa5; // change wrong endian + char tiffHeader[4] = {0x49, 0x49, 0x2A, 0x00}; + uint32_t tiffHeaderInd = buf.indexOf(tiffHeader); + conversionPointer = reinterpret_cast(buf.mid(tiffHeaderInd + 8, 2).data()); + uint16_t numberOfTiffFields = *conversionPointer; + uint32_t nextIfdOffsetInd = tiffHeaderInd + 10 + 12 * (numberOfTiffFields); + conversionPointer = reinterpret_cast(buf.mid(nextIfdOffsetInd, 2).data()); + uint16_t nextIfdOffset = *conversionPointer; + + // Definition of usefull unions and structs + union char2uint32_u { + char c[4]; + uint32_t i; + }; + union char2uint16_u { + char c[2]; + uint16_t i; + }; + // This struct describes a standart field used in exif files + struct field_s { + uint16_t tagID; // Describes which information is added here, e.g. GPS Lat + uint16_t type; // Describes the data type, e.g. string, uint8_t,... + uint32_t size; // Describes the size + uint32_t content; // Either contains the information, or the offset to the exif header where the information is stored (if 32 bits is not enough) + }; + // This struct contains all the fields that we want to add to the image + struct fields_s { + field_s gpsVersion; + field_s gpsLatRef; + field_s gpsLat; + field_s gpsLonRef; + field_s gpsLon; + field_s gpsAltRef; + field_s gpsAlt; + field_s gpsMapDatum; + uint32_t finishedDataField; + }; + // These are the additional information that can not be put into a single uin32_t + struct extended_s { + uint32_t gpsLat[6]; + uint32_t gpsLon[6]; + uint32_t gpsAlt[2]; + char mapDatum[7];// = {0x57,0x47,0x53,0x2D,0x38,0x34,0x00}; + }; + // This struct contains all the information we want to add to the image + struct readable_s { + fields_s fields; + extended_s extendedData; + }; + + // This union is used because for writing the information we have to use a char array, but we still want the information to be available in a more descriptive way + union { + char c[0xa3]; + readable_s readable; + } gpsData; + + + char2uint32_u gpsIFDInd; + gpsIFDInd.i = nextIfdOffset; + + // this will stay constant + char gpsInfo[12] = {0x25, 0x88, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, gpsIFDInd.c[0], gpsIFDInd.c[1], gpsIFDInd.c[2], gpsIFDInd.c[3]}; + + // filling values to gpsData + uint32_t gpsDataExtInd = gpsIFDInd.i + 2 + sizeof(fields_s); + + // Filling up the fields with the corresponding values + gpsData.readable.fields.gpsVersion.tagID = 0; + gpsData.readable.fields.gpsVersion.type = 1; + gpsData.readable.fields.gpsVersion.size = 4; + gpsData.readable.fields.gpsVersion.content = 2; + + gpsData.readable.fields.gpsLatRef.tagID = 1; + gpsData.readable.fields.gpsLatRef.type = 2; + gpsData.readable.fields.gpsLatRef.size = 2; + gpsData.readable.fields.gpsLatRef.content = coordinate.latitude() > 0 ? 'N' : 'S'; + + gpsData.readable.fields.gpsLat.tagID = 2; + gpsData.readable.fields.gpsLat.type = 5; + gpsData.readable.fields.gpsLat.size = 3; + gpsData.readable.fields.gpsLat.content = gpsDataExtInd; + + gpsData.readable.fields.gpsLonRef.tagID = 3; + gpsData.readable.fields.gpsLonRef.type = 2; + gpsData.readable.fields.gpsLonRef.size = 2; + gpsData.readable.fields.gpsLonRef.content = coordinate.longitude() > 0 ? 'E' : 'W'; + + gpsData.readable.fields.gpsLon.tagID = 4; + gpsData.readable.fields.gpsLon.type = 5; + gpsData.readable.fields.gpsLon.size = 3; + gpsData.readable.fields.gpsLon.content = gpsDataExtInd + 6 * 4; + + gpsData.readable.fields.gpsAltRef.tagID = 5; + gpsData.readable.fields.gpsAltRef.type = 2; + gpsData.readable.fields.gpsAltRef.size = 2; + gpsData.readable.fields.gpsAltRef.content = 0x00; + + gpsData.readable.fields.gpsAlt.tagID = 6; + gpsData.readable.fields.gpsAlt.type = 5; + gpsData.readable.fields.gpsAlt.size = 1; + gpsData.readable.fields.gpsAlt.content = gpsDataExtInd + 6 * 4 * 2; + + gpsData.readable.fields.gpsMapDatum.tagID = 18; + gpsData.readable.fields.gpsMapDatum.type = 2; + gpsData.readable.fields.gpsMapDatum.size = 7; + gpsData.readable.fields.gpsMapDatum.content = gpsDataExtInd + 6 * 4 * 2 + 2 * 4; + + gpsData.readable.fields.finishedDataField = 0; + + // Filling up the additional information that does not fit into the fields + gpsData.readable.extendedData.gpsLat[0] = abs(static_cast(coordinate.latitude())); + gpsData.readable.extendedData.gpsLat[1] = 1; + gpsData.readable.extendedData.gpsLat[2] = static_cast((fabs(coordinate.latitude()) - std::floor(fabs(coordinate.latitude()))) * 60000.0); + gpsData.readable.extendedData.gpsLat[3] = 1000; + gpsData.readable.extendedData.gpsLat[4] = 0; + gpsData.readable.extendedData.gpsLat[5] = 1; + + gpsData.readable.extendedData.gpsLon[0] = abs(static_cast(coordinate.longitude())); + gpsData.readable.extendedData.gpsLon[1] = 1; + gpsData.readable.extendedData.gpsLon[2] = static_cast((fabs(coordinate.longitude()) - std::floor(fabs(coordinate.longitude()))) * 60000.0); + gpsData.readable.extendedData.gpsLon[3] = 1000; + gpsData.readable.extendedData.gpsLon[4] = 0; + gpsData.readable.extendedData.gpsLon[5] = 1; + + gpsData.readable.extendedData.gpsAlt[0] = coordinate.altitude() * 100; + gpsData.readable.extendedData.gpsAlt[1] = 100; + gpsData.readable.extendedData.mapDatum[0] = 'W'; + gpsData.readable.extendedData.mapDatum[1] = 'G'; + gpsData.readable.extendedData.mapDatum[2] = 'S'; + gpsData.readable.extendedData.mapDatum[3] = '-'; + gpsData.readable.extendedData.mapDatum[4] = '8'; + gpsData.readable.extendedData.mapDatum[5] = '4'; + gpsData.readable.extendedData.mapDatum[6] = 0x00; + + // remove 12 spaces from image description, as otherwise we need to loop through every field and correct the new address values + buf.remove(nextIfdOffsetInd + 4, 12); + // TODO correct size in image description + // insert Gps Info to image file + buf.insert(nextIfdOffsetInd, gpsInfo, 12); + char numberOfFields[2] = {0x08, 0x00}; + // insert number of gps specific fields that we want to add + buf.insert(gpsIFDInd.i + tiffHeaderInd, numberOfFields, 2); + // insert the gps data + buf.insert(gpsIFDInd.i + 2 + tiffHeaderInd, gpsData.c, 0xa3); + + // update the new file size and exif offsets + char2uint16_u converter; + converter.i = qToBigEndian(app1SizeEndian); + buf.replace(app1HeaderInd + 2, 2, converter.c, 2); + converter.i = nextIfdOffset + 12 + 0xa5; + buf.replace(nextIfdOffsetInd + 12, 2, converter.c, 2); + + converter.i = (numberOfTiffFields) + 1; + buf.replace(tiffHeaderInd + 8, 2, converter.c, 2); + return true; +} diff --git a/src/AnalyzeView/ExifParser.h b/src/AnalyzeView/ExifParser.h new file mode 100644 index 000000000..9a29da380 --- /dev/null +++ b/src/AnalyzeView/ExifParser.h @@ -0,0 +1,16 @@ +#ifndef EXIFPARSER_H +#define EXIFPARSER_H + +#include +#include + +class ExifParser +{ +public: + ExifParser(); + ~ExifParser(); + double readTime(QByteArray& buf); + bool write(QByteArray& data, QGeoCoordinate coordinate); +}; + +#endif // EXIFPARSER_H diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index f9314c31e..820be6b3d 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -9,6 +9,9 @@ #include "GeoTagController.h" #include "QGCFileDialog.h" +#include "ExifParser.h" +#include +#include GeoTagController::GeoTagController(void) : _progress(0) @@ -73,15 +76,191 @@ void GeoTagWorker::run(void) _cancel = false; emit progressChanged(0); + //used to time operations to get a feel for how much to progress the progressBar + QElapsedTimer timerTotal; + QElapsedTimer timerLoadImages; + QElapsedTimer timerParseExif; + QElapsedTimer timerFilter; + QElapsedTimer timerLoadLogFile; + QElapsedTimer timerGeotag; + + timerTotal.start(); + + + //////////// Load Images + timerLoadImages.start(); + + QDir imageDirectory = QDir(_imageDirectory); + if(!imageDirectory.exists()) { + emit error(tr("Cannot find the image directory")); + return; + } + if(!imageDirectory.mkdir(_imageDirectory + "/TAGGED")) { + emit error(tr("Images have already been tagged")); + return; + } + + imageDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); + imageDirectory.setSorting(QDir::Name); + QStringList nameFilters; + nameFilters << "*.jpg" << "*.JPG"; + imageDirectory.setNameFilters(nameFilters); + + QFileInfoList imageList = imageDirectory.entryInfoList(); + if(imageList.isEmpty()) { + emit error(tr("The image directory doesn't contain images, make sure your images are of the JPG format")); + return; + } + + _imageBuffers.clear(); + for (int i = 0; i < imageList.size(); ++i) { + QFile file(imageList.at(i).absoluteFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + continue; + } + _imageBuffers.append(QByteArray(file.readAll())); + file.close(); + } + + qWarning() << "Image loading time elapsed: " << timerLoadImages.elapsed() << " milliseconds"; + + ////////// Parse exif data + timerParseExif.start(); + + // Parse EXIF + ExifParser exifParser; + _tagTime.clear(); + for (int i = 0; i < _imageBuffers.count(); i++) { + _tagTime.append(exifParser.readTime(_imageBuffers[i])); + } + + qWarning() << "Parse exif data time elapsed: " << timerParseExif.elapsed() << " milliseconds"; + + ////////// Load PX4 log + timerLoadLogFile.start(); + + _geoRef.clear(); + _triggerTime.clear(); + if (!parsePX4Log()) { + qWarning() << "Geotagging failed"; + return; + } + qWarning() << "Found " << _geoRef.count() << " trigger logs."; + + qWarning() << "Log loading time elapsed: " << timerLoadLogFile.elapsed() << " milliseconds"; + + ////////// Filter Trigger + timerFilter.start(); + + if (!triggerFiltering()) { + qWarning() << "Geotagging failed"; + return; + } + + qWarning() << "Filter time elapsed: " << timerFilter.elapsed() << " milliseconds"; + + //////////// Tag images + timerGeotag.start(); + + for(int i = 0; i < _imageIndices.count() && i < _triggerIndices.count() && i < imageList.count(); i++) { + if (!exifParser.write(_imageBuffers[_imageIndices[i]], _geoRef[_triggerIndices[i]])) { + _cancel = true; + break; + } else { + QFile file(_imageDirectory + "/TAGGED/" + imageList[_imageIndices[i]].fileName()); + if (file.open( QFile::WriteOnly)) { + file.write(_imageBuffers[_imageIndices[i]]); + file.close(); + } + } + } + + qWarning() << "Tagging images time elapsed: " << timerGeotag.elapsed() << " milliseconds"; + for (int i=0; i<10;i++) { if (_cancel) { emit error(tr("Tagging cancelled")); return; } emit progressChanged(i*10); - sleep(1); + //sleep(1); } + qWarning() << "Total time elapsed: " << timerTotal.elapsed() << " milliseconds"; emit progressChanged(100); emit taggingComplete(); } + +bool GeoTagWorker::parsePX4Log() +{ + // general message header + // char header[] = {0xA3, 0x95, 0x00}; + // header for GPOS message + char gposHeader[] = {0xA3, 0x95, 0x10, 0x00}; + int gposOffsets[3] = {3, 7, 11}; + int gposLengths[3] = {4, 4, 4}; + // header for trigger message + char triggerHeader[] = {0xA3, 0x95, 0x37, 0x00}; + int triggerOffsets[2] = {3, 11}; + int triggerLengths[2] = {8, 4}; + // load log + QFile file(_logFile); + if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open log file"; + return false; + } + QByteArray log = file.readAll(); + file.close(); + + // extract trigger data + int index = 1; + int sequence = -1; + QGeoCoordinate lastCoordinate; + while(index < log.count() - 1) { + int gposIndex = log.indexOf(gposHeader, index + 1); + int triggerIndex = log.indexOf(triggerHeader, index + 1); + // check for whether last entry has been passed + if ((gposIndex < 0 && triggerIndex < 0) || (gposIndex >= log.count() - 1 && triggerIndex >= log.count() - 1)) { + break; + } else if (gposIndex < 0) { + gposIndex = triggerIndex + 1; + } else if (triggerIndex < 0) { + triggerIndex = gposIndex + 1; + } + // extract next entry, gpos or trigger + if (gposIndex < triggerIndex) { + // TODO: somehow verify that the gposIndex is really the header of a gpos message + int32_t* lat = reinterpret_cast(log.mid(gposIndex + gposOffsets[0], gposLengths[0]).data()); + double latitude = static_cast(qFromLittleEndian(*lat))/1.0e7; + lastCoordinate.setLatitude(latitude); + int32_t* lon = reinterpret_cast(log.mid(gposIndex + gposOffsets[1], gposLengths[1]).data()); + double longitude = static_cast(qFromLittleEndian(*lon))/1.0e7; + longitude = fmod(180.0 + longitude, 360.0) - 180.0; + lastCoordinate.setLongitude(longitude); + float* alt = reinterpret_cast(log.mid(gposIndex + gposOffsets[2], gposLengths[2]).data()); + lastCoordinate.setAltitude(qFromLittleEndian(*alt)); + index = gposIndex; + } else { + uint64_t* time = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[0], triggerLengths[0]).data()); + double timeDouble = static_cast(qFromLittleEndian(*time)); + uint32_t* seq = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[1], triggerLengths[1]).data()); + int seqInt = static_cast(qFromLittleEndian(*seq)); + if (sequence < seqInt && sequence + 20 > seqInt) { // assume that logging has not skipped more than 20 triggers. this prevents wrong header detection + _geoRef.append(lastCoordinate); + _triggerTime.append(timeDouble/1000000.0); + sequence = seqInt; + } + index = triggerIndex; + } + } + return true; +} + +bool GeoTagWorker::triggerFiltering() +{ + for (int i = 0; i < _triggerTime.count() && i < _tagTime.count(); i++) { + _triggerIndices.append(i); + _imagesIndices.append(i); + } + return true; +} diff --git a/src/AnalyzeView/GeoTagController.h b/src/AnalyzeView/GeoTagController.h index a2820fa35..ef870b174 100644 --- a/src/AnalyzeView/GeoTagController.h +++ b/src/AnalyzeView/GeoTagController.h @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include class GeoTagWorker : public QThread { @@ -27,7 +30,7 @@ public: void setLogFile(const QString& logFile) { _logFile = logFile; } void setImageDirectory(const QString& imageDirectory) { _imageDirectory = imageDirectory; } - void cancellTagging(void) { _cancel = true; } + void cancelTagging(void) { _cancel = true; } protected: void run(void) final; @@ -38,9 +41,19 @@ signals: void progressChanged(double progress); private: + bool parsePX4Log(); + bool triggerFiltering(); + + bool _cancel; QString _logFile; QString _imageDirectory; + QList _imageBuffers; + QList _tagTime; + QList _geoRef; + QList _triggerTime; + QList _imageIndices; + QList _triggerIndices; }; /// Controller for GeoTagPage.qml. Supports geotagging images based on logfile camera tags. @@ -67,7 +80,7 @@ public: Q_INVOKABLE void pickLogFile(void); Q_INVOKABLE void pickImageDirectory(void); Q_INVOKABLE void startTagging(void); - Q_INVOKABLE void cancelTagging(void) { _worker.cancellTagging(); } + Q_INVOKABLE void cancelTagging(void) { _worker.cancelTagging(); } QString logFile (void) const { return _worker.logFile(); } QString imageDirectory (void) const { return _worker.imageDirectory(); } -- GitLab From b36d59c03009373377377b2003668bd57ae46e8a Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Mon, 31 Oct 2016 11:03:21 +0100 Subject: [PATCH 022/398] updating the geotagging implementation --- qgroundcontrol.pro | 2 +- .../{ExifParser.cpp => ExifParser.cc} | 15 +- src/AnalyzeView/GeoTagController.cc | 319 ++++++++++++------ src/AnalyzeView/GeoTagController.h | 74 ++-- src/AnalyzeView/GeoTagPage.qml | 100 ++++-- src/QGCLoggingCategory.cc | 3 +- src/QGCLoggingCategory.h | 1 + 7 files changed, 337 insertions(+), 177 deletions(-) rename src/AnalyzeView/{ExifParser.cpp => ExifParser.cc} (90%) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index c919ffda6..8d2ea11d0 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -508,7 +508,7 @@ SOURCES += \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ src/PositionManager/SimulatedPosition.cc \ src/PositionManager/PositionManager.cpp \ - src/AnalyzeView/ExifParser.cpp + src/AnalyzeView/ExifParser.cc DebugBuild { SOURCES += \ diff --git a/src/AnalyzeView/ExifParser.cpp b/src/AnalyzeView/ExifParser.cc similarity index 90% rename from src/AnalyzeView/ExifParser.cpp rename to src/AnalyzeView/ExifParser.cc index 083aff242..4ef943e3b 100644 --- a/src/AnalyzeView/ExifParser.cpp +++ b/src/AnalyzeView/ExifParser.cc @@ -1,4 +1,5 @@ #include "ExifParser.h" +#include #include #include @@ -14,8 +15,8 @@ ExifParser::~ExifParser() double ExifParser::readTime(QByteArray& buf) { - char tiffHeader[] = {0x49,0x49,0x2A,0x00}; - char createDateHeader[] = {0x04,0x90,0x02,0x00}; + char tiffHeader[] = {static_cast(0x49),static_cast(0x49),static_cast(0x2A),static_cast(0x00)}; + char createDateHeader[] = {static_cast(0x04),static_cast(0x90),static_cast(0x02),static_cast(0x00)}; // find header position uint32_t tiffHeaderIndex = buf.indexOf(tiffHeader); @@ -57,7 +58,9 @@ double ExifParser::readTime(QByteArray& buf) bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) { - char app1Header[2] = {0xff, 0xe1}; + QByteArray app1Header; + app1Header.append(0xff); + app1Header.append(0xe1); uint32_t app1HeaderInd = buf.indexOf(app1Header); uint16_t *conversionPointer = reinterpret_cast(buf.mid(app1HeaderInd + 2, 2).data()); uint16_t app1Size = *conversionPointer; @@ -122,7 +125,7 @@ bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) gpsIFDInd.i = nextIfdOffset; // this will stay constant - char gpsInfo[12] = {0x25, 0x88, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, gpsIFDInd.c[0], gpsIFDInd.c[1], gpsIFDInd.c[2], gpsIFDInd.c[3]}; + char gpsInfo[12] = {static_cast(0x25), static_cast(0x88), static_cast(0x04), static_cast(0x00), static_cast(0x01), static_cast(0x00), static_cast(0x00), static_cast(0x00), static_cast(gpsIFDInd.c[0]), static_cast(gpsIFDInd.c[1]), static_cast(gpsIFDInd.c[2]), static_cast(gpsIFDInd.c[3])}; // filling values to gpsData uint32_t gpsDataExtInd = gpsIFDInd.i + 2 + sizeof(fields_s); @@ -173,14 +176,14 @@ bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) // Filling up the additional information that does not fit into the fields gpsData.readable.extendedData.gpsLat[0] = abs(static_cast(coordinate.latitude())); gpsData.readable.extendedData.gpsLat[1] = 1; - gpsData.readable.extendedData.gpsLat[2] = static_cast((fabs(coordinate.latitude()) - std::floor(fabs(coordinate.latitude()))) * 60000.0); + gpsData.readable.extendedData.gpsLat[2] = static_cast((fabs(coordinate.latitude()) - floor(fabs(coordinate.latitude()))) * 60000.0); gpsData.readable.extendedData.gpsLat[3] = 1000; gpsData.readable.extendedData.gpsLat[4] = 0; gpsData.readable.extendedData.gpsLat[5] = 1; gpsData.readable.extendedData.gpsLon[0] = abs(static_cast(coordinate.longitude())); gpsData.readable.extendedData.gpsLon[1] = 1; - gpsData.readable.extendedData.gpsLon[2] = static_cast((fabs(coordinate.longitude()) - std::floor(fabs(coordinate.longitude()))) * 60000.0); + gpsData.readable.extendedData.gpsLon[2] = static_cast((fabs(coordinate.longitude()) - floor(fabs(coordinate.longitude()))) * 60000.0); gpsData.readable.extendedData.gpsLon[3] = 1000; gpsData.readable.extendedData.gpsLon[4] = 0; gpsData.readable.extendedData.gpsLon[5] = 1; diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index 820be6b3d..a367e654c 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -8,9 +8,13 @@ ****************************************************************************/ #include "GeoTagController.h" -#include "QGCFileDialog.h" #include "ExifParser.h" +#include "QGCFileDialog.h" +#include "QGCLoggingCategory.h" +#include #include +#include +#include #include GeoTagController::GeoTagController(void) @@ -46,10 +50,81 @@ void GeoTagController::pickImageDirectory(void) } } +void GeoTagController::pickSaveDirectory(void) +{ + QString dir = QGCFileDialog::getExistingDirectory(NULL, "Select save directory"); + if (!dir.isEmpty()) { + _worker.setSaveDirectory(dir); + emit saveDirectoryChanged(dir); + } +} + void GeoTagController::startTagging(void) { _errorMessage.clear(); emit errorMessageChanged(_errorMessage); + + QDir imageDirectory = QDir(_worker.imageDirectory()); + if(!imageDirectory.exists()) { + _errorMessage = tr("Cannot find the image directory"); + emit errorMessageChanged(_errorMessage); + return; + } + if(_worker.saveDirectory() == "") { + if(!imageDirectory.mkdir(_worker.imageDirectory() + "/TAGGED")) { + QMessageBox msgBox(QMessageBox::Question, + tr("Images have alreay been tagged."), + tr("The images have already been tagged. Do you want to replace the previously tagged images?"), + QMessageBox::Cancel); + msgBox.setWindowModality(Qt::ApplicationModal); + msgBox.addButton(tr("Replace"), QMessageBox::ActionRole); + if (msgBox.exec() == QMessageBox::Cancel) { + _errorMessage = tr("Images have already been tagged"); + emit errorMessageChanged(_errorMessage); + return; + } + QDir oldTaggedFolder = QDir(_worker.imageDirectory() + "/TAGGED"); + oldTaggedFolder.removeRecursively(); + if(!imageDirectory.mkdir(_worker.imageDirectory() + "/TAGGED")) { + _errorMessage = tr("Couldn't replace the previously tagged images"); + emit errorMessageChanged(_errorMessage); + return; + } + } + } else { + QDir saveDirectory = QDir(_worker.saveDirectory()); + if(!saveDirectory.exists()) { + _errorMessage = tr("Cannot find the save directory"); + emit errorMessageChanged(_errorMessage); + return; + } + saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); + QStringList nameFilters; + nameFilters << "*.jpg" << "*.JPG"; + saveDirectory.setNameFilters(nameFilters); + QStringList imageList = saveDirectory.entryList(); + if(!imageList.isEmpty()) { + QMessageBox msgBox(QMessageBox::Question, + tr("Save folder not empty."), + tr("The save folder already contains images. Do you want to replace them?"), + QMessageBox::Cancel); + msgBox.setWindowModality(Qt::ApplicationModal); + msgBox.addButton(tr("Replace"), QMessageBox::ActionRole); + if (msgBox.exec() == QMessageBox::Cancel) { + _errorMessage = tr("Save folder not empty"); + emit errorMessageChanged(_errorMessage); + return; + } + foreach(QString dirFile, imageList) + { + if(!saveDirectory.remove(dirFile)) { + _errorMessage = tr("Couldn't replace the existing images"); + emit errorMessageChanged(_errorMessage); + return; + } + } + } + } _worker.start(); } @@ -67,6 +142,9 @@ void GeoTagController::_workerError(QString errorMessage) GeoTagWorker::GeoTagWorker(void) : _cancel(false) + , _logFile("") + , _imageDirectory("") + , _saveDirectory("") { } @@ -74,139 +152,148 @@ GeoTagWorker::GeoTagWorker(void) void GeoTagWorker::run(void) { _cancel = false; - emit progressChanged(0); - - //used to time operations to get a feel for how much to progress the progressBar - QElapsedTimer timerTotal; - QElapsedTimer timerLoadImages; - QElapsedTimer timerParseExif; - QElapsedTimer timerFilter; - QElapsedTimer timerLoadLogFile; - QElapsedTimer timerGeotag; - - timerTotal.start(); - - - //////////// Load Images - timerLoadImages.start(); + emit progressChanged(1); + double nSteps = 5; + // Load Images + _imageList.clear(); QDir imageDirectory = QDir(_imageDirectory); - if(!imageDirectory.exists()) { - emit error(tr("Cannot find the image directory")); - return; - } - if(!imageDirectory.mkdir(_imageDirectory + "/TAGGED")) { - emit error(tr("Images have already been tagged")); - return; - } - imageDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); imageDirectory.setSorting(QDir::Name); QStringList nameFilters; nameFilters << "*.jpg" << "*.JPG"; imageDirectory.setNameFilters(nameFilters); - - QFileInfoList imageList = imageDirectory.entryInfoList(); - if(imageList.isEmpty()) { + _imageList = imageDirectory.entryInfoList(); + if(_imageList.isEmpty()) { emit error(tr("The image directory doesn't contain images, make sure your images are of the JPG format")); return; } + emit progressChanged((100/nSteps)); - _imageBuffers.clear(); - for (int i = 0; i < imageList.size(); ++i) { - QFile file(imageList.at(i).absoluteFilePath()); + // Parse EXIF + ExifParser exifParser; + _tagTime.clear(); + for (int i = 0; i < _imageList.size(); ++i) { + QFile file(_imageList.at(i).absoluteFilePath()); if (!file.open(QIODevice::ReadOnly)) { - continue; + emit error(tr("Geotagging failed. Couldn't open an image.")); + return; } - _imageBuffers.append(QByteArray(file.readAll())); + QByteArray imageBuffer = file.readAll(); file.close(); - } - qWarning() << "Image loading time elapsed: " << timerLoadImages.elapsed() << " milliseconds"; + _tagTime.append(exifParser.readTime(imageBuffer)); - ////////// Parse exif data - timerParseExif.start(); + emit progressChanged((100/nSteps) + ((100/nSteps) / _imageList.size())*i); - // Parse EXIF - ExifParser exifParser; - _tagTime.clear(); - for (int i = 0; i < _imageBuffers.count(); i++) { - _tagTime.append(exifParser.readTime(_imageBuffers[i])); + if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; + emit error(tr("Tagging cancelled")); + return; + } } - qWarning() << "Parse exif data time elapsed: " << timerParseExif.elapsed() << " milliseconds"; - - ////////// Load PX4 log - timerLoadLogFile.start(); - + // Load PX4 log _geoRef.clear(); _triggerTime.clear(); if (!parsePX4Log()) { - qWarning() << "Geotagging failed"; - return; + if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; + emit error(tr("Tagging cancelled")); + return; + } else { + qCDebug(GeotaggingLog) << "Log parsing failed"; + emit error(tr("Log parsing failed - tagging cancelled")); + return; + } } - qWarning() << "Found " << _geoRef.count() << " trigger logs."; + emit progressChanged(3*(100/nSteps)); - qWarning() << "Log loading time elapsed: " << timerLoadLogFile.elapsed() << " milliseconds"; + qCDebug(GeotaggingLog) << "Found " << _geoRef.count() << " trigger logs."; - ////////// Filter Trigger - timerFilter.start(); + if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; + emit error(tr("Tagging cancelled")); + return; + } + // Filter Trigger if (!triggerFiltering()) { - qWarning() << "Geotagging failed"; + qCDebug(GeotaggingLog) << "Geotagging failed in trigger filtering"; + emit error(tr("Geotagging failed in trigger filtering")); return; } + emit progressChanged(4*(100/nSteps)); - qWarning() << "Filter time elapsed: " << timerFilter.elapsed() << " milliseconds"; + if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; + emit error(tr("Tagging cancelled")); + return; + } - //////////// Tag images - timerGeotag.start(); + // Tag images + int maxIndex = std::min(_imageIndices.count(), _triggerIndices.count()); + maxIndex = std::min(maxIndex, _imageList.count()); + for(int i = 0; i < maxIndex; i++) { + QFile fileRead(_imageList.at(_imageIndices[i]).absoluteFilePath()); + if (!fileRead.open(QIODevice::ReadOnly)) { + emit error(tr("Geotagging failed. Couldn't open an image.")); + return; + } + QByteArray imageBuffer = fileRead.readAll(); + fileRead.close(); - for(int i = 0; i < _imageIndices.count() && i < _triggerIndices.count() && i < imageList.count(); i++) { - if (!exifParser.write(_imageBuffers[_imageIndices[i]], _geoRef[_triggerIndices[i]])) { - _cancel = true; - break; + if (!exifParser.write(imageBuffer, _geoRef[_triggerIndices[i]])) { + emit error(tr("Geotagging failed. Couldn't write to image.")); + return; } else { - QFile file(_imageDirectory + "/TAGGED/" + imageList[_imageIndices[i]].fileName()); - if (file.open( QFile::WriteOnly)) { - file.write(_imageBuffers[_imageIndices[i]]); - file.close(); + QFile fileWrite; + if(_saveDirectory == "") { + fileWrite.setFileName(_imageDirectory + "/TAGGED/" + _imageList.at(_imageIndices[i]).fileName()); + } else { + fileWrite.setFileName(_saveDirectory + "/" + _imageList.at(_imageIndices[i]).fileName()); + } + if (!fileWrite.open(QFile::WriteOnly)) { + emit error(tr("Geotagging failed. Couldn't write to an image.")); + return; } + fileWrite.write(imageBuffer); + fileWrite.close(); } - } + emit progressChanged(4*(100/nSteps) + ((100/nSteps) / maxIndex)*i); - qWarning() << "Tagging images time elapsed: " << timerGeotag.elapsed() << " milliseconds"; - - for (int i=0; i<10;i++) { if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; emit error(tr("Tagging cancelled")); return; } - emit progressChanged(i*10); - //sleep(1); } - qWarning() << "Total time elapsed: " << timerTotal.elapsed() << " milliseconds"; + if (_cancel) { + qCDebug(GeotaggingLog) << "Tagging cancelled"; + emit error(tr("Tagging cancelled")); + return; + } + emit progressChanged(100); - emit taggingComplete(); } bool GeoTagWorker::parsePX4Log() { // general message header - // char header[] = {0xA3, 0x95, 0x00}; + char header[] = {(char)0xA3, (char)0x95, (char)0x00}; // header for GPOS message - char gposHeader[] = {0xA3, 0x95, 0x10, 0x00}; + char gposHeader[] = {(char)0xA3, (char)0x95, (char)0x10, (char)0x00}; int gposOffsets[3] = {3, 7, 11}; int gposLengths[3] = {4, 4, 4}; // header for trigger message - char triggerHeader[] = {0xA3, 0x95, 0x37, 0x00}; + char triggerHeader[] = {(char)0xA3, (char)0x95, (char)0x37, (char)0x00}; int triggerOffsets[2] = {3, 11}; int triggerLengths[2] = {8, 4}; // load log QFile file(_logFile); if (!file.open(QIODevice::ReadOnly)) { - qWarning() << "Could not open log file"; + qCDebug(GeotaggingLog) << "Could not open log file " << _logFile; return false; } QByteArray log = file.readAll(); @@ -217,40 +304,55 @@ bool GeoTagWorker::parsePX4Log() int sequence = -1; QGeoCoordinate lastCoordinate; while(index < log.count() - 1) { - int gposIndex = log.indexOf(gposHeader, index + 1); + + if (_cancel) { + return false; + } + + // first extract trigger int triggerIndex = log.indexOf(triggerHeader, index + 1); // check for whether last entry has been passed - if ((gposIndex < 0 && triggerIndex < 0) || (gposIndex >= log.count() - 1 && triggerIndex >= log.count() - 1)) { + if (triggerIndex < 0) { break; - } else if (gposIndex < 0) { - gposIndex = triggerIndex + 1; - } else if (triggerIndex < 0) { - triggerIndex = gposIndex + 1; } - // extract next entry, gpos or trigger - if (gposIndex < triggerIndex) { - // TODO: somehow verify that the gposIndex is really the header of a gpos message - int32_t* lat = reinterpret_cast(log.mid(gposIndex + gposOffsets[0], gposLengths[0]).data()); - double latitude = static_cast(qFromLittleEndian(*lat))/1.0e7; - lastCoordinate.setLatitude(latitude); - int32_t* lon = reinterpret_cast(log.mid(gposIndex + gposOffsets[1], gposLengths[1]).data()); - double longitude = static_cast(qFromLittleEndian(*lon))/1.0e7; - longitude = fmod(180.0 + longitude, 360.0) - 180.0; - lastCoordinate.setLongitude(longitude); - float* alt = reinterpret_cast(log.mid(gposIndex + gposOffsets[2], gposLengths[2]).data()); - lastCoordinate.setAltitude(qFromLittleEndian(*alt)); + uint64_t* time = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[0], triggerLengths[0]).data()); + double timeDouble = static_cast(qFromLittleEndian(*time)); + uint32_t* seq = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[1], triggerLengths[1]).data()); + int seqInt = static_cast(qFromLittleEndian(*seq)); + if (sequence < seqInt && sequence + 20 > seqInt) { // assume that logging has not skipped more than 20 triggers. this prevents wrong header detection + _triggerTime.append(timeDouble/1000000.0); + sequence = seqInt; + } + index = triggerIndex; // extract next entry gpos + + // second extract position + bool lookForGpos = true; + while (lookForGpos) { + + if (_cancel) { + return false; + } + + int gposIndex = log.indexOf(gposHeader, index + 1); + if (gposIndex < 0) { + _geoRef.append(lastCoordinate); + break; + } index = gposIndex; - } else { - uint64_t* time = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[0], triggerLengths[0]).data()); - double timeDouble = static_cast(qFromLittleEndian(*time)); - uint32_t* seq = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[1], triggerLengths[1]).data()); - int seqInt = static_cast(qFromLittleEndian(*seq)); - if (sequence < seqInt && sequence + 20 > seqInt) { // assume that logging has not skipped more than 20 triggers. this prevents wrong header detection - _geoRef.append(lastCoordinate); - _triggerTime.append(timeDouble/1000000.0); - sequence = seqInt; + // verify that at an offset of 27 the next log message starts + if (gposIndex + 27 == log.indexOf(header, gposIndex + 1)) { + int32_t* lat = reinterpret_cast(log.mid(gposIndex + gposOffsets[0], gposLengths[0]).data()); + double latitude = static_cast(qFromLittleEndian(*lat))/1.0e7; + lastCoordinate.setLatitude(latitude); + int32_t* lon = reinterpret_cast(log.mid(gposIndex + gposOffsets[1], gposLengths[1]).data()); + double longitude = static_cast(qFromLittleEndian(*lon))/1.0e7; + longitude = fmod(180.0 + longitude, 360.0) - 180.0; + lastCoordinate.setLongitude(longitude); + float* alt = reinterpret_cast(log.mid(gposIndex + gposOffsets[2], gposLengths[2]).data()); + lastCoordinate.setAltitude(qFromLittleEndian(*alt)); + _geoRef.append(lastCoordinate); + break; } - index = triggerIndex; } } return true; @@ -258,9 +360,12 @@ bool GeoTagWorker::parsePX4Log() bool GeoTagWorker::triggerFiltering() { - for (int i = 0; i < _triggerTime.count() && i < _tagTime.count(); i++) { + _imageIndices.clear(); + _triggerIndices.clear(); + for(int i = 0; i < _tagTime.count() && i < _triggerTime.count(); i++) { + _imageIndices.append(i); _triggerIndices.append(i); - _imagesIndices.append(i); } + return true; } diff --git a/src/AnalyzeView/GeoTagController.h b/src/AnalyzeView/GeoTagController.h index ef870b174..a9c67f68c 100644 --- a/src/AnalyzeView/GeoTagController.h +++ b/src/AnalyzeView/GeoTagController.h @@ -10,9 +10,13 @@ #ifndef GeoTagController_H #define GeoTagController_H +#include "QmlObjectListModel.h" +#include "Fact.h" +#include "FactMetaData.h" #include #include #include +#include #include #include #include @@ -24,36 +28,38 @@ class GeoTagWorker : public QThread public: GeoTagWorker(void); - QString logFile(void) const { return _logFile; } - QString imageDirectory(void) const { return _imageDirectory; } + void setLogFile (const QString& logFile) { _logFile = logFile; } + void setImageDirectory (const QString& imageDirectory) { _imageDirectory = imageDirectory; } + void setSaveDirectory (const QString& saveDirectory) { _saveDirectory = saveDirectory; } - void setLogFile(const QString& logFile) { _logFile = logFile; } - void setImageDirectory(const QString& imageDirectory) { _imageDirectory = imageDirectory; } + QString logFile (void) const { return _logFile; } + QString imageDirectory (void) const { return _imageDirectory; } + QString saveDirectory (void) const { return _saveDirectory; } - void cancelTagging(void) { _cancel = true; } + void cancelTagging (void) { _cancel = true; } protected: void run(void) final; signals: - void error(QString errorMsg); - void taggingComplete(void); - void progressChanged(double progress); + void error (QString errorMsg); + void taggingComplete (void); + void progressChanged (double progress); private: bool parsePX4Log(); bool triggerFiltering(); - - bool _cancel; - QString _logFile; - QString _imageDirectory; - QList _imageBuffers; - QList _tagTime; - QList _geoRef; - QList _triggerTime; - QList _imageIndices; - QList _triggerIndices; + bool _cancel; + QString _logFile; + QString _imageDirectory; + QString _saveDirectory; + QFileInfoList _imageList; + QList _tagTime; + QList _geoRef; + QList _triggerTime; + QList _imageIndices; + QList _triggerIndices; }; /// Controller for GeoTagPage.qml. Supports geotagging images based on logfile camera tags. @@ -67,6 +73,7 @@ public: Q_PROPERTY(QString logFile READ logFile NOTIFY logFileChanged) Q_PROPERTY(QString imageDirectory READ imageDirectory NOTIFY imageDirectoryChanged) + Q_PROPERTY(QString saveDirectory READ saveDirectory NOTIFY saveDirectoryChanged) /// Set to an error message is geotagging fails Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) @@ -79,32 +86,35 @@ public: Q_INVOKABLE void pickLogFile(void); Q_INVOKABLE void pickImageDirectory(void); + Q_INVOKABLE void pickSaveDirectory(void); Q_INVOKABLE void startTagging(void); Q_INVOKABLE void cancelTagging(void) { _worker.cancelTagging(); } - QString logFile (void) const { return _worker.logFile(); } - QString imageDirectory (void) const { return _worker.imageDirectory(); } - double progress (void) const { return _progress; } - bool inProgress (void) const { return _worker.isRunning(); } - QString errorMessage (void) const { return _errorMessage; } + QString logFile (void) const { return _worker.logFile(); } + QString imageDirectory (void) const { return _worker.imageDirectory(); } + QString saveDirectory (void) const { return _worker.saveDirectory(); } + double progress (void) const { return _progress; } + bool inProgress (void) const { return _worker.isRunning(); } + QString errorMessage (void) const { return _errorMessage; } signals: - void logFileChanged(QString logFile); - void imageDirectoryChanged(QString imageDirectory); - void progressChanged(double progress); - void inProgressChanged(void); - void errorMessageChanged(QString errorMessage); + void logFileChanged (QString logFile); + void imageDirectoryChanged (QString imageDirectory); + void saveDirectoryChanged (QString saveDirectory); + void progressChanged (double progress); + void inProgressChanged (void); + void errorMessageChanged (QString errorMessage); private slots: void _workerProgressChanged(double progress); void _workerError(QString errorMsg); private: - QString _errorMessage; - double _progress; - bool _inProgress; + QString _errorMessage; + double _progress; + bool _inProgress; - GeoTagWorker _worker; + GeoTagWorker _worker; }; #endif diff --git a/src/AnalyzeView/GeoTagPage.qml b/src/AnalyzeView/GeoTagPage.qml index a7d840710..a4881a7f8 100644 --- a/src/AnalyzeView/GeoTagPage.qml +++ b/src/AnalyzeView/GeoTagPage.qml @@ -11,7 +11,10 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 +import QGroundControl 1.0 import QGroundControl.Palette 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Controllers 1.0 @@ -19,17 +22,17 @@ import QGroundControl.Controllers 1.0 AnalyzePage { id: geoTagPage pageComponent: pageComponent - pageName: qsTr("GeoTag Images (WIP)") - pageDescription: qsTr("GetTag Images is used to tag a set of images from a survey mission with gps coordinates. You must provide the binary log from the flight as well as the directory which contains the images to tag.") + pageName: qsTr("GeoTag Images") + pageDescription: qsTr("GeoTag Images is used to tag a set of images from a survey mission with gps coordinates. You must provide the binary log from the flight as well as the directory which contains the images to tag.") - property real _margin: ScreenTools.defaultFontPixelWidth + property real _margin: ScreenTools.defaultFontPixelWidth * 2 GeoTagController { id: controller } Component { - id: pageComponent + id: pageComponent Column { id: mainColumn @@ -37,67 +40,104 @@ AnalyzePage { spacing: _margin Row { - spacing: _margin + spacing: ScreenTools.defaultFontPixelWidth * 2 - QGCLabel { - text: "Log file:" + ProgressBar { + id: progressBar + width: qgcView.width -_margin * 5 + maximumValue: 100 + value: controller.progress } - QGCLabel { - text: controller.logFile + BusyIndicator { + running: controller.progress > 0 && controller.progress < 100 && controller.errorMessage === "" + width: progressBar.height + height: progressBar.height } + } + + QGCLabel { + text: controller.errorMessage + font.bold: true + font.pointSize: ScreenTools.largeFontPointSize + color: "red" + } + + // Horizontal spacer line + Rectangle { + height: 1 + width: qgcView.width * 1.0 + color: qgcPal.windowShadeDark + anchors.horizontalCenter: parent.horizontalCenter + } + + Row { + spacing: _margin QGCButton { text: qsTr("Select log file") + width: ScreenTools.defaultFontPixelWidth * 30 onClicked: controller.pickLogFile() + anchors.verticalCenter: parent.verticalCenter + } + + QGCLabel { + text: controller.logFile + anchors.verticalCenter: parent.verticalCenter } } Row { spacing: _margin - QGCLabel { - text: "Image directory:" + QGCButton { + text: qsTr("Select image directory") + width: ScreenTools.defaultFontPixelWidth * 30 + onClicked: controller.pickImageDirectory() + anchors.verticalCenter: parent.verticalCenter } QGCLabel { text: controller.imageDirectory + anchors.verticalCenter: parent.verticalCenter } + } + + Row { + spacing: _margin QGCButton { - text: qsTr("Select image directory") - onClicked: controller.pickImageDirectory() + text: qsTr("(Optionally) Select save directory") + width: ScreenTools.defaultFontPixelWidth * 30 + onClicked: controller.pickSaveDirectory() + anchors.verticalCenter: parent.verticalCenter + } + + QGCLabel { + text: controller.saveDirectory != "" ? controller.saveDirectory : "/TAGGED folder in your image folder" + anchors.verticalCenter: parent.verticalCenter } } - QGCLabel { text: "NYI - Simulated only" } + // Horizontal spacer line + Rectangle { + height: 1 + width: qgcView.width * 1.0 + color: qgcPal.windowShadeDark + anchors.horizontalCenter: parent.horizontalCenter + } QGCButton { text: controller.inProgress ? qsTr("Cancel Tagging") : qsTr("Start Tagging") - + width: ScreenTools.defaultFontPixelWidth * 30 onClicked: { if (controller.inProgress) { controller.cancelTagging() } else { - if (controller.logFile == "" || controller.imageDirectory == "") { - geoTagPage.showMessage(qsTr("Error"), qsTr("You must select a log file and image directory before you can start tagging."), StandardButton.Ok) - return - } controller.startTagging() } } } - - QGCLabel { - text: controller.errorMessage - } - - ProgressBar { - anchors.left: parent.left - anchors.right: parent.right - maximumValue: 100 - value: controller.progress - } } // Column } // Component } // AnalyzePage diff --git a/src/QGCLoggingCategory.cc b/src/QGCLoggingCategory.cc index f83818fb8..b2220c18a 100644 --- a/src/QGCLoggingCategory.cc +++ b/src/QGCLoggingCategory.cc @@ -20,7 +20,8 @@ QGC_LOGGING_CATEGORY(FirmwareUpgradeLog, "FirmwareUpgradeLog") QGC_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog, "FirmwareUpgradeVerboseLog") QGC_LOGGING_CATEGORY(MissionCommandsLog, "MissionCommandsLog") QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog") -QGC_LOGGING_CATEGORY(ParameterManagerLog, "ParameterManagerLog") +QGC_LOGGING_CATEGORY(ParameterManagerLog, "ParameterManagerLog") +QGC_LOGGING_CATEGORY(GeotaggingLog, "GeotaggingLog") QGCLoggingCategoryRegister* _instance = NULL; const char* QGCLoggingCategoryRegister::_filterRulesSettingsGroup = "LoggingFilters"; diff --git a/src/QGCLoggingCategory.h b/src/QGCLoggingCategory.h index 9dad9bbcc..13dfcb199 100644 --- a/src/QGCLoggingCategory.h +++ b/src/QGCLoggingCategory.h @@ -23,6 +23,7 @@ Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog) Q_DECLARE_LOGGING_CATEGORY(MissionCommandsLog) Q_DECLARE_LOGGING_CATEGORY(MissionItemLog) Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog) +Q_DECLARE_LOGGING_CATEGORY(GeotaggingLog) /// @def QGC_LOGGING_CATEGORY /// This is a QGC specific replacement for Q_LOGGING_CATEGORY. It will register the category name into a -- GitLab From f973e35e6fa81f3a00d7520655881c72f78f4eb5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 10 Nov 2016 10:25:45 -0800 Subject: [PATCH 023/398] Change plan element selection to radio buttons --- src/MissionEditor/MissionEditor.qml | 51 ++++------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 34dea1a2f..95c1a5600 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -562,66 +562,27 @@ QGCView { } } - RoundButton { + QGCRadioButton { id: planElementMission - radius: parent._buttonRadius - buttonImage: "/qmlimages/Plan.svg" - lightBorders: _lightWidgetBorders exclusiveGroup: planElementSelectorGroup + text: qsTr("Mission") checked: true } - QGCLabel { - text: qsTr("Mission") - color: mapPal.text - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - anchors.fill: parent - onClicked: planElementMission.checked = true - } - } - Item { height: 1; width: 1 } - RoundButton { + QGCRadioButton { id: planElementGeoFence - radius: parent._buttonRadius - buttonImage: "/qmlimages/Plan.svg" - lightBorders: _lightWidgetBorders exclusiveGroup: planElementSelectorGroup - } - - QGCLabel { - text: qsTr("Fence") - color: mapPal.text - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - anchors.fill: parent - onClicked: planElementGeoFence.checked = true - } + text: qsTr("Fence") } Item { height: 1; width: 1 } - RoundButton { + QGCRadioButton { id: planElementRallyPoints - radius: parent._buttonRadius - buttonImage: "/qmlimages/Plan.svg" - lightBorders: _lightWidgetBorders exclusiveGroup: planElementSelectorGroup - } - - QGCLabel { - text: qsTr("Rally") - color: mapPal.text - anchors.verticalCenter: parent.verticalCenter - - MouseArea { - anchors.fill: parent - onClicked: planElementRallyPoints.checked = true - } + text: qsTr("Rally") } } // Row - Plan Element Selector -- GitLab From 9f98dfe9ab22a9d79c67d7d938ada045e273dd49 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 11 Nov 2016 11:10:38 -0800 Subject: [PATCH 024/398] Change default takeoff alt to 3 meters This gets it above your head --- src/FlightDisplay/FlightDisplayViewWidgets.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index dd054d326..c37074955 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -426,7 +426,7 @@ Item { break; case confirmTakeoff: altitudeSlider.visible = true - altitudeSlider.setInitialValueMeters(2) + altitudeSlider.setInitialValueMeters(3) guidedModeConfirm.confirmText = qsTr("takeoff") break; case confirmLand: -- GitLab From 141976c4310c6916fcc24b7728416889bee5de56 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 12 Nov 2016 08:22:37 -0800 Subject: [PATCH 025/398] Bug fixes - Incorrect signaling causing dirty but to be incorrect - Remove all not working - Min fence point count incorrect --- src/FirmwarePlugin/APM/APMGeoFenceManager.cc | 49 +++++++++----------- src/FirmwarePlugin/APM/APMGeoFenceManager.h | 3 +- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc index 7b019f73b..0590c11ef 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc @@ -22,11 +22,12 @@ APMGeoFenceManager::APMGeoFenceManager(Vehicle* vehicle) : GeoFenceManager(vehicle) , _fenceSupported(false) , _breachReturnSupported(vehicle->fixedWing()) + , _circleSupported(false) + , _polygonSupported(false) , _firstParamLoadComplete(false) , _circleRadiusFact(NULL) , _readTransactionInProgress(false) , _writeTransactionInProgress(false) - , _fenceEnableFact(NULL) , _fenceTypeFact(NULL) { connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &APMGeoFenceManager::_mavlinkMessageReceived); @@ -80,7 +81,7 @@ void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const fenceEnableFact->setRawValue(0); // Total point count, +1 polygon close in last index, +1 for breach in index 0 - _cWriteFencePoints = (validatedPolygonCount ? (validatedPolygonCount + 1) : 0) + 1 ; + _cWriteFencePoints = validatedPolygonCount ? validatedPolygonCount + 1 + 1 : 0; _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceTotalParam)->setRawValue(_cWriteFencePoints); // FIXME: No validation of correct fence received @@ -106,11 +107,11 @@ void APMGeoFenceManager::loadFromVehicle(void) return; } - // Point 0: Breach return point (ArduPlane only) + // Point 0: Breach return point (always sent, but supported by ArduPlane only) // Point [1,N]: Polygon points // Point N+1: Close polygon point (same as point 1) int cFencePoints = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceTotalParam)->rawValue().toInt(); - int minFencePoints = 6; + int minFencePoints = 5; qCDebug(GeoFenceManagerLog) << "APMGeoFenceManager::loadFromVehicle" << cFencePoints; if (cFencePoints == 0) { // No fence @@ -242,8 +243,19 @@ bool APMGeoFenceManager::_geoFenceSupported(void) void APMGeoFenceManager::_updateSupportedFlags(void) { - emit circleSupportedChanged(circleSupported()); - emit polygonSupportedChanged(polygonSupported()); + bool newCircleSupported = _fenceSupported && _vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 2); + if (newCircleSupported != _circleSupported) { + _circleSupported = newCircleSupported; + emit circleSupportedChanged(newCircleSupported); + } + + bool newPolygonSupported = _fenceSupported && + ((_vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 4)) || + _vehicle->fixedWing()); + if (newPolygonSupported != _polygonSupported) { + _polygonSupported = newPolygonSupported; + emit polygonSupportedChanged(newPolygonSupported); + } } void APMGeoFenceManager::_parametersReady(void) @@ -258,11 +270,9 @@ void APMGeoFenceManager::_parametersReady(void) QStringList paramLabels; if (_vehicle->multiRotor()) { - _fenceEnableFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_ENABLE")); _fenceTypeFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_TYPE")); - connect(_fenceEnableFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateSupportedFlags); - connect(_fenceTypeFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateSupportedFlags); + connect(_fenceTypeFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateSupportedFlags); _circleRadiusFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_RADIUS")); connect(_circleRadiusFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_circleRadiusRawValueChanged); @@ -293,8 +303,7 @@ void APMGeoFenceManager::_parametersReady(void) emit paramLabelsChanged(_paramLabels); emit fenceSupportedChanged(_fenceSupported); - emit circleSupportedChanged(circleSupported()); - emit polygonSupportedChanged(polygonSupported()); + _updateSupportedFlags(); } qCDebug(GeoFenceManagerLog) << "fenceSupported:circleSupported:polygonSupported:breachReturnSupported" << @@ -318,26 +327,12 @@ void APMGeoFenceManager::_circleRadiusRawValueChanged(QVariant value) bool APMGeoFenceManager::circleSupported(void) const { - if (_fenceSupported && _vehicle->multiRotor() && _fenceEnableFact && _fenceTypeFact) { - return _fenceEnableFact->rawValue().toBool() && (_fenceTypeFact->rawValue().toInt() & 2); - } - - return false; + return _circleSupported; } bool APMGeoFenceManager::polygonSupported(void) const { - if (_fenceSupported) { - if (_vehicle->multiRotor()) { - if (_fenceEnableFact && _fenceTypeFact) { - return _fenceEnableFact->rawValue().toBool() && (_fenceTypeFact->rawValue().toInt() & 4); - } - } else if (_vehicle->fixedWing()) { - return true; - } - } - - return false; + return _polygonSupported; } QString APMGeoFenceManager::editorQml(void) const diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.h b/src/FirmwarePlugin/APM/APMGeoFenceManager.h index fe98aaeb9..33fe15912 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.h +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.h @@ -49,6 +49,8 @@ private: private: bool _fenceSupported; bool _breachReturnSupported; + bool _circleSupported; + bool _polygonSupported; bool _firstParamLoadComplete; QVariantList _params; @@ -63,7 +65,6 @@ private: uint8_t _cWriteFencePoints; uint8_t _currentFencePoint; - Fact* _fenceEnableFact; Fact* _fenceTypeFact; static const char* _fenceTotalParam; -- GitLab From 1b3824906658c76a43e972564e7d71e3a1b69d5a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 12 Nov 2016 08:23:00 -0800 Subject: [PATCH 026/398] Rally point count incorrect --- src/FirmwarePlugin/APM/APMRallyPointManager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FirmwarePlugin/APM/APMRallyPointManager.cc b/src/FirmwarePlugin/APM/APMRallyPointManager.cc index 8061f8e57..76eb2a876 100644 --- a/src/FirmwarePlugin/APM/APMRallyPointManager.cc +++ b/src/FirmwarePlugin/APM/APMRallyPointManager.cc @@ -90,7 +90,7 @@ void APMRallyPointManager::_mavlinkMessageReceived(const mavlink_message_t& mess QGeoCoordinate point((float)rallyPoint.lat / 1e7, (float)rallyPoint.lng / 1e7, rallyPoint.alt); _rgPoints.append(point); - if (rallyPoint.idx < _cReadRallyPoints - 2) { + if (rallyPoint.idx < _cReadRallyPoints - 1) { // Still more points to request _requestRallyPoint(++_currentRallyPoint); } else { -- GitLab From f52b62818de56fe96be707fbf238b031f40e3f93 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 12 Nov 2016 08:23:21 -0800 Subject: [PATCH 027/398] Signalling fixes --- src/MissionManager/GeoFenceController.cc | 5 +++-- src/MissionManager/GeoFenceController.h | 1 + src/MissionManager/GeoFenceManager.cc | 2 +- src/MissionManager/RallyPointController.cc | 1 + src/MissionManager/RallyPointController.h | 1 + src/MissionManager/RallyPointManager.cc | 2 +- 6 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index dbf9a5444..64d78bc13 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -333,9 +333,9 @@ void GeoFenceController::loadFromVehicle(void) void GeoFenceController::sendToVehicle(void) { if (_activeVehicle->parameterManager()->parametersReady() && !syncInProgress()) { - setDirty(false); - _polygon.setDirty(false); _activeVehicle->geoFenceManager()->sendToVehicle(_breachReturnPoint, _polygon.coordinateList()); + _polygon.setDirty(false); + setDirty(false); } else { qCWarning(GeoFenceControllerLog) << "GeoFenceController::loadFromVehicle call at wrong time" << _activeVehicle->parameterManager()->parametersReady() << syncInProgress(); } @@ -433,6 +433,7 @@ void GeoFenceController::_loadComplete(const QGeoCoordinate& breachReturn, const _setReturnPointFromManager(breachReturn); _setPolygonFromManager(polygon); setDirty(false); + emit loadComplete(); } QString GeoFenceController::fileExtension(void) const diff --git a/src/MissionManager/GeoFenceController.h b/src/MissionManager/GeoFenceController.h index d5f14cf3f..654b933c6 100644 --- a/src/MissionManager/GeoFenceController.h +++ b/src/MissionManager/GeoFenceController.h @@ -78,6 +78,7 @@ signals: void paramsChanged (QVariantList params); void paramLabelsChanged (QStringList paramLabels); void editorQmlChanged (QString editorQml); + void loadComplete (void); private slots: void _polygonDirtyChanged(bool dirty); diff --git a/src/MissionManager/GeoFenceManager.cc b/src/MissionManager/GeoFenceManager.cc index 24d8d5534..331cfe5eb 100644 --- a/src/MissionManager/GeoFenceManager.cc +++ b/src/MissionManager/GeoFenceManager.cc @@ -34,7 +34,7 @@ void GeoFenceManager::_sendError(ErrorCode_t errorCode, const QString& errorMsg) void GeoFenceManager::loadFromVehicle(void) { // No geofence support in unknown vehicle - loadComplete(QGeoCoordinate(), QList()); + emit loadComplete(QGeoCoordinate(), QList()); } void GeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const QList& polygon) diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index e924c2531..0b3bfeb01 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -254,6 +254,7 @@ void RallyPointController::_loadComplete(const QList rgPoints) _points.swapObjectList(pointList); setDirty(false); _setFirstPointCurrent(); + emit loadComplete(); } QString RallyPointController::fileExtension(void) const diff --git a/src/MissionManager/RallyPointController.h b/src/MissionManager/RallyPointController.h index 7ac1af8be..4585d93e9 100644 --- a/src/MissionManager/RallyPointController.h +++ b/src/MissionManager/RallyPointController.h @@ -61,6 +61,7 @@ public: signals: void rallyPointsSupportedChanged(bool rallyPointsSupported); void currentRallyPointChanged(QObject* rallyPoint); + void loadComplete(void); private slots: void _loadComplete(const QList rgPoints); diff --git a/src/MissionManager/RallyPointManager.cc b/src/MissionManager/RallyPointManager.cc index 2698fc5df..ba92da6a1 100644 --- a/src/MissionManager/RallyPointManager.cc +++ b/src/MissionManager/RallyPointManager.cc @@ -34,7 +34,7 @@ void RallyPointManager::_sendError(ErrorCode_t errorCode, const QString& errorMs void RallyPointManager::loadFromVehicle(void) { // No support in generic vehicle - loadComplete(QList()); + emit loadComplete(QList()); } void RallyPointManager::sendToVehicle(const QList& rgPoints) -- GitLab From 49177bc2cc6c5b32c20bb0c4dd5e726df6a108da Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 12 Nov 2016 08:23:32 -0800 Subject: [PATCH 028/398] Better map viewport management --- src/MissionEditor/MissionEditor.qml | 189 ++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 27 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 95c1a5600..550cf2461 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -86,31 +86,114 @@ QGCView { return lon + 180.0 } - /// Fix the map viewport to the current mission items. - function fitViewportToMissionItems() { - if (_visualItems.count == 1) { + /// Fits the visible region of the map to inclues all of the specified coordinates. If no coordinates + /// are specified the map will fit to the home position + function fitMapViewportToAllCoordinates(coordList) { + if (coordList.length == 0) { editorMap.center = _visualItems.get(0).coordinate - } else { - var missionItem = _visualItems.get(0) - var north = normalizeLat(missionItem.coordinate.latitude) - var south = north - var east = normalizeLon(missionItem.coordinate.longitude) - var west = east - - for (var i=1; i<_visualItems.count; i++) { - missionItem = _visualItems.get(i) - - if (missionItem.specifiesCoordinate && !missionItem.isStandaloneCoordinate) { - var lat = normalizeLat(missionItem.coordinate.latitude) - var lon = normalizeLon(missionItem.coordinate.longitude) - - north = Math.max(north, lat) - south = Math.min(south, lat) - east = Math.max(east, lon) - west = Math.min(west, lon) - } + return + } + + // Determine the size of the inner portion of the map available for display + var toolbarHeight = qgcView.height - ScreenTools.availableHeight + var rightPanelWidth = _rightPanelWidth + var leftToolWidth = centerMapButton.x + centerMapButton.width + var availableWidth = qgcView.width - rightPanelWidth - leftToolWidth + var availableHeight = qgcView.height - toolbarHeight + + // Create the normalized lat/lon corners for the coordinate bounding rect from the list of coordinates + var north = normalizeLat(coordList[0].latitude) + var south = north + var east = normalizeLon(coordList[0].longitude) + var west = east + for (var i=1; i 2) { + for (var i=0; i Date: Sat, 12 Nov 2016 13:40:38 -0800 Subject: [PATCH 029/398] New vehicle health display using SYS_STATUS info --- qgroundcontrol.qrc | 1 + src/FlightMap/Widgets/InstrumentSwipeView.qml | 79 +++++++++---------- src/FlightMap/Widgets/QGCInstrumentWidget.qml | 4 +- src/FlightMap/Widgets/VehicleHealthWidget.qml | 75 ++++++++++++++++++ src/FlightMap/qmldir | 1 + src/Vehicle/Vehicle.cc | 67 ++++++++++++++++ src/Vehicle/Vehicle.h | 7 ++ 7 files changed, 193 insertions(+), 41 deletions(-) create mode 100644 src/FlightMap/Widgets/VehicleHealthWidget.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 69dd1d0bf..96cdbe10d 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -128,6 +128,7 @@ src/FlightMap/Widgets/QGCPitchIndicator.qml src/FlightMap/QGCVideoBackground.qml src/FlightMap/Widgets/ValuesWidget.qml + src/FlightMap/Widgets/VehicleHealthWidget.qml src/FlightMap/Widgets/VibrationWidget.qml src/FlightMap/MapItems/VehicleMapItem.qml src/FlightMap/Widgets/InstrumentSwipeView.qml diff --git a/src/FlightMap/Widgets/InstrumentSwipeView.qml b/src/FlightMap/Widgets/InstrumentSwipeView.qml index 7e5128d4e..0a31ffb25 100644 --- a/src/FlightMap/Widgets/InstrumentSwipeView.qml +++ b/src/FlightMap/Widgets/InstrumentSwipeView.qml @@ -15,24 +15,41 @@ Item { property color backgroundColor property var maxHeight ///< Maximum height that should be taken, smaller than this is ok - property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real _pageWidth: _root.width + property int _currentPage: 0 + property int _maxPage: 2 function showPicker() { valuesPage.showPicker() } + function showPage(pageIndex) { + _root.height = Qt.binding(function() { return _root.children[pageIndex].height + pageIndicatorRow.anchors.topMargin + pageIndicatorRow.height } ) + _root.children[0].x = -(pageIndex * _pageWidth) + } + ValuesWidget { id: valuesPage - width: _root.width + width: _pageWidth qgcView: _root.qgcView textColor: _root.textColor maxHeight: _root.maxHeight } + VehicleHealthWidget { + id: healthPage + anchors.left: valuesPage.right + width: _pageWidth + qgcView: _root.qgcView + textColor: _root.textColor + maxHeight: _root.maxHeight + } + VibrationWidget { id: vibrationPage - anchors.left: valuesPage.right - width: _root.width + anchors.left: healthPage.right + width: _pageWidth textColor: _root.textColor backgroundColor: _root.backgroundColor maxHeight: _root.maxHeight @@ -44,24 +61,17 @@ Item { anchors.horizontalCenter: parent.horizontalCenter spacing: _margins - Rectangle { - id: valuesPageIndicator - height: radius * 2 - width: radius * 2 - radius: 2.5 - border.color: textColor - border.width: 1 - color: textColor - } + Repeater { + model: _maxPage + 1 - Rectangle { - id: vibrationPageIndicator - height: radius * 2 - width: radius * 2 - radius: 2.5 - border.color: textColor - border.width: 1 - color: "transparent" + Rectangle { + height: radius * 2 + width: radius * 2 + radius: 2.5 + border.color: textColor + border.width: 1 + color: _currentPage == index ? textColor : "transparent" + } } } @@ -69,40 +79,29 @@ Item { anchors.fill: parent property real xDragStart - property real xValuesPageSave + property real xFirstPageSave onPressed: { if (mouse.button == Qt.LeftButton) { mouse.accepted = true xDragStart = mouse.x - xValuesPageSave = valuesPage.x + xFirstPageSave = _root.children[0].x } } onPositionChanged: { - valuesPage.x = xValuesPageSave + mouse.x - xDragStart + _root.children[0].x = xFirstPageSave + mouse.x - xDragStart } onReleased: { if (mouse.x < xDragStart) { - if (xValuesPageSave == 0) { - valuesPage.x = -valuesPage.width - _root.height = Qt.binding(function() { return vibrationPage.height + pageIndicatorRow.anchors.topMargin + pageIndicatorRow.height } ) - valuesPageIndicator.color = "transparent" - vibrationPageIndicator.color = textColor - } else { - valuesPage.x = xValuesPageSave - } + // Swipe left + _currentPage = Math.min(_currentPage + 1, _maxPage) } else { - if (xValuesPageSave != 0) { - valuesPage.x = 0 - _root.height = Qt.binding(function() { return valuesPage.height + pageIndicatorRow.anchors.topMargin + pageIndicatorRow.height } ) - valuesPageIndicator.color = textColor - vibrationPageIndicator.color = "transparent" - } else { - valuesPage.x = xValuesPageSave - } + // Swipe right + _currentPage = Math.max(_currentPage - 1, 0) } + showPage(_currentPage) } } } diff --git a/src/FlightMap/Widgets/QGCInstrumentWidget.qml b/src/FlightMap/Widgets/QGCInstrumentWidget.qml index 98548effd..a5352549c 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidget.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidget.qml @@ -128,7 +128,9 @@ Item { InstrumentSwipeView { id: _valuesWidget - width: parent.width + anchors.margins: 1 + anchors.left: parent.left + anchors.right: parent.right qgcView: instrumentPanel.qgcView textColor: qgcPal.text backgroundColor: _backgroundColor diff --git a/src/FlightMap/Widgets/VehicleHealthWidget.qml b/src/FlightMap/Widgets/VehicleHealthWidget.qml new file mode 100644 index 000000000..4b9b11663 --- /dev/null +++ b/src/FlightMap/Widgets/VehicleHealthWidget.qml @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +QGCFlickable { + id: _root + height: Math.min(maxHeight, healthColumn.y + healthColumn.height) + contentHeight: healthColumn.y + healthColumn.height + flickableDirection: Flickable.VerticalFlick + clip: true + + property var qgcView + property color textColor + property var maxHeight + + property var unhealthySensors: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.unhealthySensors : [ ] + + // Any time the unhealthy sensors list changes, switch to the health page + onUnhealthySensorsChanged: { + if (unhealthySensors.length != 0) { + showPage(1) + } + } + + Column { + id: healthColumn + width: parent.width + + QGCLabel { + width: parent.width + horizontalAlignment: Text.AlignHCenter + color: textColor + text: qsTr("Vehicle Health") + } + + QGCLabel { + width: parent.width + horizontalAlignment: Text.AlignHCenter + color: textColor + text: qsTr("All systems healthy") + visible: healthRepeater.count == 0 + } + + Repeater { + id: healthRepeater + model: unhealthySensors + + Row { + Image { + source: "/qmlimages/Yield.svg" + height: ScreenTools.defaultFontPixelHeight + sourceSize.height: height + fillMode: Image.PreserveAspectFit + } + + QGCLabel { + color: textColor + text: modelData + } + } + } + } +} diff --git a/src/FlightMap/qmldir b/src/FlightMap/qmldir index 8bc71ce14..761753032 100644 --- a/src/FlightMap/qmldir +++ b/src/FlightMap/qmldir @@ -16,6 +16,7 @@ QGCInstrumentWidgetAlternate 1.0 QGCInstrumentWidgetAlternate.qml QGCPitchIndicator 1.0 QGCPitchIndicator.qml QGCSlider 1.0 QGCSlider.qml ValuesWidget 1.0 ValuesWidget.qml +VehicleHealthWidget 1.0 VehicleHealthWidget.qml VibrationWidget 1.0 VibrationWidget.qml # Map items diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index c9ed3a839..a3eaa1de6 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -96,6 +96,10 @@ Vehicle::Vehicle(LinkInterface* link, , _rcRSSIstore(255) , _autoDisconnect(false) , _flying(false) + , _onboardControlSensorsPresent(0) + , _onboardControlSensorsEnabled(0) + , _onboardControlSensorsHealth(0) + , _onboardControlSensorsUnhealthy(0) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -296,6 +300,11 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _rcRSSI(255) , _rcRSSIstore(255) , _autoDisconnect(false) + , _flying(false) + , _onboardControlSensorsPresent(0) + , _onboardControlSensorsEnabled(0) + , _onboardControlSensorsHealth(0) + , _onboardControlSensorsUnhealthy(0) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -669,6 +678,16 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); } } + + _onboardControlSensorsPresent = sysStatus.onboard_control_sensors_present; + _onboardControlSensorsEnabled = sysStatus.onboard_control_sensors_enabled; + _onboardControlSensorsHealth = sysStatus.onboard_control_sensors_health; + + uint32_t newSensorsUnhealthy = _onboardControlSensorsEnabled & ~_onboardControlSensorsHealth; + if (newSensorsUnhealthy != _onboardControlSensorsUnhealthy) { + _onboardControlSensorsUnhealthy = newSensorsUnhealthy; + emit unhealthySensorsChanged(); + } } void Vehicle::_handleBatteryStatus(mavlink_message_t& message) @@ -1951,6 +1970,54 @@ QString Vehicle::brandImage(void) const return _firmwarePlugin->brandImage(this); } +QStringList Vehicle::unhealthySensors(void) const +{ + QStringList sensorList; + + struct sensorInfo_s { + uint32_t bit; + const char* sensorName; + }; + + static const sensorInfo_s rgSensorInfo[] = { + { MAV_SYS_STATUS_SENSOR_3D_GYRO, "Gyro" }, + { MAV_SYS_STATUS_SENSOR_3D_ACCEL, "Accelerometer" }, + { MAV_SYS_STATUS_SENSOR_3D_MAG, "Magnetometer" }, + { MAV_SYS_STATUS_SENSOR_ABSOLUTE_PRESSURE, "Absolute pressure" }, + { MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE, "Differential pressure" }, + { MAV_SYS_STATUS_SENSOR_GPS, "GPS" }, + { MAV_SYS_STATUS_SENSOR_OPTICAL_FLOW, "Optical flow" }, + { MAV_SYS_STATUS_SENSOR_VISION_POSITION, "Computer vision position" }, + { MAV_SYS_STATUS_SENSOR_LASER_POSITION, "Laser based position" }, + { MAV_SYS_STATUS_SENSOR_EXTERNAL_GROUND_TRUTH, "External ground truth" }, + { MAV_SYS_STATUS_SENSOR_ANGULAR_RATE_CONTROL, "Angular rate control" }, + { MAV_SYS_STATUS_SENSOR_ATTITUDE_STABILIZATION, "Attitude stabilization" }, + { MAV_SYS_STATUS_SENSOR_YAW_POSITION, "Yaw position" }, + { MAV_SYS_STATUS_SENSOR_Z_ALTITUDE_CONTROL, "Z/altitude control" }, + { MAV_SYS_STATUS_SENSOR_XY_POSITION_CONTROL, "X/Y position control" }, + { MAV_SYS_STATUS_SENSOR_MOTOR_OUTPUTS, "Motor outputs / control" }, + { MAV_SYS_STATUS_SENSOR_RC_RECEIVER, "RC receiver" }, + { MAV_SYS_STATUS_SENSOR_3D_GYRO2, "Gyro 2" }, + { MAV_SYS_STATUS_SENSOR_3D_ACCEL2, "Accelerometer 2" }, + { MAV_SYS_STATUS_SENSOR_3D_MAG2, "Magnetometer 2" }, + { MAV_SYS_STATUS_GEOFENCE, "GeoFence" }, + { MAV_SYS_STATUS_AHRS, "AHRS" }, + { MAV_SYS_STATUS_TERRAIN, "Terrain" }, + { MAV_SYS_STATUS_REVERSE_MOTOR, "Motors reversed" }, + { MAV_SYS_STATUS_LOGGING, "Logging" }, + }; + + for (size_t i=0; ibit) && !(_onboardControlSensorsHealth & pSensorInfo->bit)) { + sensorList << pSensorInfo->sensorName; + } + } + + return sensorList; +} + + const char* VehicleGPSFactGroup::_hdopFactName = "hdop"; const char* VehicleGPSFactGroup::_vdopFactName = "vdop"; const char* VehicleGPSFactGroup::_courseOverGroundFactName = "courseOverGround"; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 0f6243519..6dbabf76e 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -280,6 +280,7 @@ public: Q_PROPERTY(bool xConfigMotors READ xConfigMotors CONSTANT) Q_PROPERTY(bool isOfflineEditingVehicle READ isOfflineEditingVehicle CONSTANT) Q_PROPERTY(QString brandImage READ brandImage CONSTANT) + Q_PROPERTY(QStringList unhealthySensors READ unhealthySensors NOTIFY unhealthySensorsChanged) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -530,6 +531,7 @@ public: uint32_t customMode () const { return _custom_mode; } bool isOfflineEditingVehicle () const { return _offlineEditingVehicle; } QString brandImage () const; + QStringList unhealthySensors () const; Fact* roll (void) { return &_rollFact; } Fact* heading (void) { return &_headingFact; } @@ -604,6 +606,7 @@ signals: void prearmErrorChanged(const QString& prearmError); void commandLongAck(uint8_t compID, uint16_t command, uint8_t result); void soloFirmwareChanged(bool soloFirmware); + void unhealthySensorsChanged(void); void messagesReceivedChanged (); void messagesSentChanged (); @@ -751,6 +754,10 @@ private: double _rcRSSIstore; bool _autoDisconnect; ///< true: Automatically disconnect vehicle when last connection goes away or lost heartbeat bool _flying; + uint32_t _onboardControlSensorsPresent; + uint32_t _onboardControlSensorsEnabled; + uint32_t _onboardControlSensorsHealth; + uint32_t _onboardControlSensorsUnhealthy; QString _prearmError; QTimer _prearmErrorTimer; -- GitLab From 89b7e11543229c6e34a9f774e610622d4b6e1bf8 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Sat, 12 Nov 2016 21:47:44 -0500 Subject: [PATCH 030/398] travis-ci android setup echo full PATH (#4207) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 37f8ca580..3877ade68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,7 +108,7 @@ install: ./android-ndk-r10e-linux-x86_64.bin > /dev/null && export ANDROID_NDK_ROOT=`pwd`/android-ndk-r10e && export ANDROID_SDK_ROOT=/usr/local/android-sdk && - export PATH=/tmp/Qt/5.5/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH + export PATH=/tmp/Qt/5.5/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH && echo $PATH ; fi -- GitLab From 6210e39d64455f9ff4abe48360e09056f6ad39de Mon Sep 17 00:00:00 2001 From: Peter Barker Date: Mon, 14 Nov 2016 03:57:05 +1100 Subject: [PATCH 031/398] Vagrant: QGC now requires SDL2 (#4208) --- Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index f58cd8766..41756cd15 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -25,7 +25,7 @@ Vagrant.configure(2) do |config| sudo apt-get update -y sudo apt-get dist-upgrade -y sudo apt-get install -y git build-essential - sudo apt-get install -y espeak libespeak-dev libudev-dev libsdl1.2-dev + sudo apt-get install -y espeak libespeak-dev libudev-dev libsdl2-dev sudo apt-get install -y doxygen sudo apt-get install -y gstreamer1.0* libgstreamer1.0* -- GitLab From 1694d46bb57b885e4cf68eeea58ee3a21d7af35e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 14 Nov 2016 08:12:06 -0800 Subject: [PATCH 032/398] No flaps for multi-rotor --- src/AutoPilotPlugins/Common/RadioComponent.qml | 6 +++++- src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/AutoPilotPlugins/Common/RadioComponent.qml b/src/AutoPilotPlugins/Common/RadioComponent.qml index 75e038a5e..2d4bd7fc6 100644 --- a/src/AutoPilotPlugins/Common/RadioComponent.qml +++ b/src/AutoPilotPlugins/Common/RadioComponent.qml @@ -423,7 +423,11 @@ SetupPage { } Repeater { - model: QGroundControl.multiVehicleManager.activeVehicle.px4Firmware ? [ "RC_MAP_FLAPS", "RC_MAP_AUX1", "RC_MAP_AUX2", "RC_MAP_PARAM1", "RC_MAP_PARAM2", "RC_MAP_PARAM3"] : 0 + model: QGroundControl.multiVehicleManager.activeVehicle.px4Firmware ? + (QGroundControl.multiVehicleManager.activeVehicle.multiRotor ? + [ "RC_MAP_AUX1", "RC_MAP_AUX2", "RC_MAP_PARAM1", "RC_MAP_PARAM2", "RC_MAP_PARAM3"] : + [ "RC_MAP_FLAPS", "RC_MAP_AUX1", "RC_MAP_AUX2", "RC_MAP_PARAM1", "RC_MAP_PARAM2", "RC_MAP_PARAM3"]) : + 0 Row { spacing: ScreenTools.defaultFontPixelWidth diff --git a/src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml b/src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml index 17eb6fefb..6704f871f 100644 --- a/src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml @@ -46,8 +46,9 @@ FactPanel { } VehicleSummaryRow { - labelText: qsTr("Flaps:") - valueText: mapFlapsFact ? (mapFlapsFact.value === 0 ? qsTr("Disabled") : mapFlapsFact.valueString) : "" + labelText: qsTr("Flaps:") + valueText: mapFlapsFact ? (mapFlapsFact.value === 0 ? qsTr("Disabled") : mapFlapsFact.valueString) : "" + visible: !controller.vehicle.multiRotor } VehicleSummaryRow { -- GitLab From e1864d0bd35ba4f44124555f5b9e8d88a810ae70 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 14 Nov 2016 08:26:25 -0800 Subject: [PATCH 033/398] Filter out calibration messages --- src/uas/UASMessageHandler.cc | 23 ++++++++++++----------- src/uas/UASMessageHandler.h | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/uas/UASMessageHandler.cc b/src/uas/UASMessageHandler.cc index db0c98db9..51941c9a6 100644 --- a/src/uas/UASMessageHandler.cc +++ b/src/uas/UASMessageHandler.cc @@ -41,7 +41,7 @@ bool UASMessage::severityIsError() UASMessageHandler::UASMessageHandler(QGCApplication* app) : QGCTool(app) - , _activeUAS(NULL) + , _activeVehicle(NULL) , _activeComponent(-1) , _multiComp(false) , _errorCount(0) @@ -87,27 +87,28 @@ void UASMessageHandler::clearMessages() void UASMessageHandler::_activeVehicleChanged(Vehicle* vehicle) { // If we were already attached to an autopilot, disconnect it. - if (_activeUAS) - { - disconnect(_activeUAS, &UASInterface::textMessageReceived, this, &UASMessageHandler::handleTextMessage); - _activeUAS = NULL; + if (_activeVehicle) { + disconnect(_activeVehicle->uas(), &UASInterface::textMessageReceived, this, &UASMessageHandler::handleTextMessage); + _activeVehicle = NULL; clearMessages(); emit textMessageReceived(NULL); } - // And now if there's an autopilot to follow, set up the UI. - if (vehicle) - { - UAS* uas = vehicle->uas(); + // And now if there's an autopilot to follow, set up the UI. + if (vehicle) { // Connect to the new UAS. clearMessages(); - _activeUAS = uas; - connect(uas, &UASInterface::textMessageReceived, this, &UASMessageHandler::handleTextMessage); + _activeVehicle = vehicle; + connect(_activeVehicle->uas(), &UASInterface::textMessageReceived, this, &UASMessageHandler::handleTextMessage); } } void UASMessageHandler::handleTextMessage(int, int compId, int severity, QString text) { + // Hack to prevent calibration messages from cluttering things up + if (_activeVehicle->px4Firmware() && text.startsWith(QStringLiteral("[cal] "))) { + return; + } // Color the output depending on the message severity. We have 3 distinct cases: // 1: If we have an ERROR or worse, make it bigger, bolder, and highlight it red. diff --git a/src/uas/UASMessageHandler.h b/src/uas/UASMessageHandler.h index ed8cefe4b..3b039e22e 100644 --- a/src/uas/UASMessageHandler.h +++ b/src/uas/UASMessageHandler.h @@ -144,7 +144,7 @@ private slots: void _activeVehicleChanged(Vehicle* vehicle); private: - UASInterface* _activeUAS; + Vehicle* _activeVehicle; int _activeComponent; bool _multiComp; QVector _messages; -- GitLab From cba2a722fe5cb0d7b9652feef1d0a15ed3bf0fe7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 15 Nov 2016 09:32:15 -0800 Subject: [PATCH 034/398] Correct altitude usage for Change Altitude --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 934774eef..6510bfd77 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -365,7 +365,7 @@ void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) void PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord) { - if (qIsNaN(vehicle->altitudeRelative()->rawValue().toDouble())) { + if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) { qgcApp()->showMessage(QStringLiteral("Unable to go to location, vehicle position not known.")); return; } @@ -397,8 +397,12 @@ void PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoord void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeRel) { - if (qIsNaN(vehicle->altitudeRelative()->rawValue().toDouble())) { - qgcApp()->showMessage(QStringLiteral("Unable to change altitude, vehicle altitude not known.")); + if (!vehicle->homePositionAvailable()) { + qgcApp()->showMessage(QStringLiteral("Unable to change altitude, home position unknown.")); + return; + } + if (qIsNaN(vehicle->homePosition().altitude())) { + qgcApp()->showMessage(QStringLiteral("Unable to change altitude, home position altitude unknown.")); return; } @@ -413,7 +417,7 @@ void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitu cmd.param4 = NAN; cmd.param5 = NAN; cmd.param6 = NAN; - cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble() + altitudeRel; + cmd.param7 = vehicle->homePosition().altitude() + altitudeRel; cmd.target_system = vehicle->id(); cmd.target_component = vehicle->defaultComponentId(); -- GitLab From 7667e634f0aad3ec5af0758c5250ad0be811b4f5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 15 Nov 2016 09:44:42 -0800 Subject: [PATCH 035/398] Show mission time in minutes --- src/MissionEditor/MissionItemStatus.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionEditor/MissionItemStatus.qml b/src/MissionEditor/MissionItemStatus.qml index fcedf7fd4..9777157df 100644 --- a/src/MissionEditor/MissionItemStatus.qml +++ b/src/MissionEditor/MissionItemStatus.qml @@ -70,7 +70,7 @@ Rectangle { property string _gradientText: _statusValid ? _gradientPercent.toFixed(0) + "%" : " " property string _azimuthText: _statusValid ? Math.round(_azimuth) : " " property string _missionDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_missionDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _missionTimeText: _missionValid ? _missionTime.toFixed(0) + "s" : " " + property string _missionTimeText: _missionValid ? Number(_missionTime / 60).toFixed(1) + " min" : " " property string _missionMaxTelemetryText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_missionMaxTelemetry).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " property string _hoverDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_hoverDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " property string _cruiseDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_cruiseDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " -- GitLab From 0ea21917a56f34e40cccc9ded80bad3f2a3c4056 Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Fri, 11 Nov 2016 11:34:36 +0100 Subject: [PATCH 036/398] reading the message lengthts from the header of the binary log file Conflicts: src/AnalyzeView/GeoTagController.cc --- src/AnalyzeView/GeoTagController.cc | 39 +++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index a367e654c..5527888a5 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -282,14 +282,21 @@ bool GeoTagWorker::parsePX4Log() { // general message header char header[] = {(char)0xA3, (char)0x95, (char)0x00}; + // header for GPOS message header + char gposHeaderHeader[] = {(char)0xA3, (char)0x95, (char)0x80, (char)0x10, (char)0x00}; + int gposHeaderOffset; // header for GPOS message char gposHeader[] = {(char)0xA3, (char)0x95, (char)0x10, (char)0x00}; int gposOffsets[3] = {3, 7, 11}; int gposLengths[3] = {4, 4, 4}; + // header for trigger message header + char triggerHeaderHeader[] = {(char)0xA3, (char)0x95, (char)0x80, (char)0x37, (char)0x00}; + int triggerHeaderOffset; // header for trigger message char triggerHeader[] = {(char)0xA3, (char)0x95, (char)0x37, (char)0x00}; int triggerOffsets[2] = {3, 11}; int triggerLengths[2] = {8, 4}; + // load log QFile file(_logFile); if (!file.open(QIODevice::ReadOnly)) { @@ -299,6 +306,12 @@ bool GeoTagWorker::parsePX4Log() QByteArray log = file.readAll(); file.close(); + // extract header information: message lengths + uint8_t* iptr = reinterpret_cast(log.mid(log.indexOf(gposHeaderHeader) + 4, 1).data()); + gposHeaderOffset = static_cast(qFromLittleEndian(*iptr)); + iptr = reinterpret_cast(log.mid(log.indexOf(triggerHeaderHeader) + 4, 1).data()); + triggerHeaderOffset = static_cast(qFromLittleEndian(*iptr)); + // extract trigger data int index = 1; int sequence = -1; @@ -310,20 +323,24 @@ bool GeoTagWorker::parsePX4Log() } // first extract trigger - int triggerIndex = log.indexOf(triggerHeader, index + 1); + index = log.indexOf(triggerHeader, index + 1); // check for whether last entry has been passed - if (triggerIndex < 0) { + if (index < 0) { break; } - uint64_t* time = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[0], triggerLengths[0]).data()); - double timeDouble = static_cast(qFromLittleEndian(*time)); - uint32_t* seq = reinterpret_cast(log.mid(triggerIndex + triggerOffsets[1], triggerLengths[1]).data()); + + if (log.indexOf(header, index + 1) != index + triggerHeaderOffset) { + continue; + } + uint64_t* time = reinterpret_cast(log.mid(index + triggerOffsets[0], triggerLengths[0]).data()); + double timeDouble = static_cast(qFromLittleEndian(*time)) / 1.0e6; + uint32_t* seq = reinterpret_cast(log.mid(index + triggerOffsets[1], triggerLengths[1]).data()); int seqInt = static_cast(qFromLittleEndian(*seq)); - if (sequence < seqInt && sequence + 20 > seqInt) { // assume that logging has not skipped more than 20 triggers. this prevents wrong header detection - _triggerTime.append(timeDouble/1000000.0); - sequence = seqInt; + if (sequence >= seqInt || sequence + 20 < seqInt) { // assume that logging has not skipped more than 20 triggers. this prevents wrong header detection + continue; } - index = triggerIndex; // extract next entry gpos + _triggerTime.append(timeDouble); + sequence = seqInt; // second extract position bool lookForGpos = true; @@ -339,8 +356,8 @@ bool GeoTagWorker::parsePX4Log() break; } index = gposIndex; - // verify that at an offset of 27 the next log message starts - if (gposIndex + 27 == log.indexOf(header, gposIndex + 1)) { + // verify that at an offset of gposHeaderOffset the next log message starts + if (gposIndex + gposHeaderOffset == log.indexOf(header, gposIndex + 1)) { int32_t* lat = reinterpret_cast(log.mid(gposIndex + gposOffsets[0], gposLengths[0]).data()); double latitude = static_cast(qFromLittleEndian(*lat))/1.0e7; lastCoordinate.setLatitude(latitude); -- GitLab From ff499429df1e72a16da0ab266f61a43cae75db4e Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Wed, 16 Nov 2016 10:48:10 -0800 Subject: [PATCH 037/398] Improve Sub's leak sensor labeling for better clarity. --- src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml b/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml index affae197e..12f810322 100644 --- a/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml +++ b/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml @@ -172,7 +172,7 @@ SetupPage { QGCLabel { id: leakDetectorLabel - text: qsTr("Leak detector") + text: qsTr("Leak Detector") font.family: ScreenTools.demiboldFontFamily } @@ -205,7 +205,7 @@ SetupPage { anchors.margins: _margins anchors.left: parent.left anchors.top: leakPinLabel.bottom - text: qsTr("Logic:") + text: qsTr("Logic (when dry):") } FactComboBox { -- GitLab From 33ee98333d73905a221fee4703b38efd248eb8fa Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 17 Nov 2016 09:32:07 -0800 Subject: [PATCH 038/398] Correct handling of fence visibility --- src/FlightDisplay/FlightDisplayViewMap.qml | 8 +++++--- src/MissionEditor/MissionEditor.qml | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index decd6d79c..2d631ecec 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -110,7 +110,8 @@ FlightMap { MapPolygon { border.color: "#80FF0000" border.width: 3 - path: geoFenceController.polygonSupported ? geoFenceController.polygon.path : undefined + path: geoFenceController.polygon.path + visible: geoFenceController.fenceEnabled && geoFenceController.polygonSupported } // GeoFence circle @@ -118,15 +119,16 @@ FlightMap { border.color: "#80FF0000" border.width: 3 center: missionController.plannedHomePosition - radius: geoFenceController.circleSupported ? geoFenceController.circleRadius : 0 + radius: (geoFenceController.fenceEnabled && geoFenceController.circleSupported) ? geoFenceController.circleRadius : 0 z: QGroundControl.zOrderMapItems + visible: geoFenceController.fenceEnabled && geoFenceController.circleSupported } // GeoFence breach return point MapQuickItem { anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) coordinate: geoFenceController.breachReturnPoint - visible: geoFenceController.breachReturnSupported + visible: geoFenceController.fenceEnabled && geoFenceController.breachReturnSupported sourceItem: MissionItemIndexLabel { label: "F" } z: QGroundControl.zOrderMapItems } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 550cf2461..007234bec 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -786,8 +786,9 @@ QGCView { MapPolygon { border.color: "#80FF0000" border.width: 3 - path: geoFenceController.polygonSupported ? geoFenceController.polygon.path : undefined + path: geoFenceController.polygon.path z: QGroundControl.zOrderMapItems + visible: geoFenceController.polygonSupported } // GeoFence circle @@ -797,6 +798,7 @@ QGCView { center: missionController.plannedHomePosition radius: geoFenceController.circleSupported ? geoFenceController.circleRadius : 0 z: QGroundControl.zOrderMapItems + visible: geoFenceController.circleSupported } // GeoFence breach return point -- GitLab From 88e4692db8a8d4b829508f90744768689195f8b5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 17 Nov 2016 09:33:01 -0800 Subject: [PATCH 039/398] Fix visible usage Change in visibility was causing combo box contents to reset. Visible goes to false even when you move away from Plan view, not just when the combo is unused. --- src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml b/src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml index f0da718fa..480d3213d 100644 --- a/src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml +++ b/src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml @@ -43,6 +43,8 @@ Column { width: editorColumn.width height: textField.height + property bool showCombo: modelData.enumStrings.length > 0 + QGCLabel { id: textFieldLabel anchors.baseline: textField.baseline @@ -55,7 +57,7 @@ Column { width: _editFieldWidth showUnits: true fact: modelData - visible: !comboField.visible + visible: !parent.showCombo } FactComboBox { @@ -63,8 +65,8 @@ Column { anchors.right: parent.right width: _editFieldWidth indexModel: false - fact: visible ? modelData : _nullFact - visible: modelData.enumStrings.length + fact: showCombo ? modelData : _nullFact + visible: parent.showCombo property var _nullFact: Fact { } } -- GitLab From 18ea73bd1ecd23a7898bdafa7223bae155c30b1c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 17 Nov 2016 09:33:21 -0800 Subject: [PATCH 040/398] Correct fence enabled handling --- src/FirmwarePlugin/APM/APMGeoFenceManager.cc | 41 ++++++++++++++------ src/FirmwarePlugin/APM/APMGeoFenceManager.h | 2 + src/MissionManager/GeoFenceController.cc | 9 ++++- src/MissionManager/GeoFenceController.h | 3 ++ src/MissionManager/GeoFenceManager.h | 2 + 5 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc index 0590c11ef..a2562dd27 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc @@ -14,9 +14,9 @@ #include "QGCApplication.h" #include "ParameterManager.h" -const char* APMGeoFenceManager::_fenceTotalParam = "FENCE_TOTAL"; -const char* APMGeoFenceManager::_fenceActionParam = "FENCE_ACTION"; -const char* APMGeoFenceManager::_fenceEnableParam = "FENCE_ENABLE"; +const char* APMGeoFenceManager::_fenceTotalParam = "FENCE_TOTAL"; +const char* APMGeoFenceManager::_fenceActionParam = "FENCE_ACTION"; +const char* APMGeoFenceManager::_fenceEnableParam = "FENCE_ENABLE"; APMGeoFenceManager::APMGeoFenceManager(Vehicle* vehicle) : GeoFenceManager(vehicle) @@ -73,13 +73,6 @@ void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const _breachReturnPoint = breachReturn; _polygon = polygon; - // First thing is to turn off geo fence while we are updating. This prevents the vehicle from going haywire it is in the air. - // Unfortunately the param to do this with differs between plane and copter. - const char* enableParam = _vehicle->fixedWing() ? _fenceActionParam : _fenceEnableParam; - Fact* fenceEnableFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, enableParam); - QVariant savedEnableState = fenceEnableFact->rawValue(); - fenceEnableFact->setRawValue(0); - // Total point count, +1 polygon close in last index, +1 for breach in index 0 _cWriteFencePoints = validatedPolygonCount ? validatedPolygonCount + 1 + 1 : 0; _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceTotalParam)->setRawValue(_cWriteFencePoints); @@ -89,8 +82,6 @@ void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const _sendFencePoint(index); } - fenceEnableFact->setRawValue(savedEnableState); - emit loadComplete(_breachReturnPoint, _polygon); } @@ -241,6 +232,28 @@ bool APMGeoFenceManager::_geoFenceSupported(void) } } +bool APMGeoFenceManager::fenceEnabled(void) const +{ + if (qgcApp()->runningUnitTests()) { + return false; + } + + if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceEnableParam)) { + bool fenceEnabled = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam)->rawValue().toBool(); + qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE available" << fenceEnabled; + return fenceEnabled; + } + + qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE not available"; + return true; +} + +void APMGeoFenceManager::_fenceEnabledRawValueChanged(QVariant value) +{ + qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE changed" << value.toBool(); + emit fenceEnabledChanged(!qgcApp()->runningUnitTests() && value.toBool()); +} + void APMGeoFenceManager::_updateSupportedFlags(void) { bool newCircleSupported = _fenceSupported && _vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 2); @@ -269,6 +282,10 @@ void APMGeoFenceManager::_parametersReady(void) QStringList paramNames; QStringList paramLabels; + if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceEnableParam)) { + connect(_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam), &Fact::rawValueChanged, this, &APMGeoFenceManager::_fenceEnabledRawValueChanged); + } + if (_vehicle->multiRotor()) { _fenceTypeFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_TYPE")); diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.h b/src/FirmwarePlugin/APM/APMGeoFenceManager.h index 33fe15912..8002de668 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.h +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.h @@ -27,6 +27,7 @@ public: void loadFromVehicle (void) final; void sendToVehicle (const QGeoCoordinate& breachReturn, const QList& polygon) final; bool fenceSupported (void) const final { return _fenceSupported; } + bool fenceEnabled (void) const final; bool circleSupported (void) const final; bool polygonSupported (void) const final; bool breachReturnSupported (void) const final { return _breachReturnSupported; } @@ -40,6 +41,7 @@ private slots: void _updateSupportedFlags(void); void _circleRadiusRawValueChanged(QVariant value); void _parametersReady(void); + void _fenceEnabledRawValueChanged(QVariant value); private: void _requestFencePoint(uint8_t pointIndex); diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index 64d78bc13..d331c40e8 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -63,6 +63,7 @@ void GeoFenceController::setBreachReturnPoint(const QGeoCoordinate& breachReturn void GeoFenceController::_signalAll(void) { emit fenceSupportedChanged(fenceSupported()); + emit fenceEnabledChanged(fenceEnabled()); emit circleSupportedChanged(circleSupported()); emit polygonSupportedChanged(polygonSupported()); emit breachReturnSupportedChanged(breachReturnSupported()); @@ -82,9 +83,10 @@ void GeoFenceController::_activeVehicleBeingRemoved(void) void GeoFenceController::_activeVehicleSet(void) { GeoFenceManager* geoFenceManager = _activeVehicle->geoFenceManager(); + connect(geoFenceManager, &GeoFenceManager::fenceSupportedChanged, this, &GeoFenceController::fenceSupportedChanged); + connect(geoFenceManager, &GeoFenceManager::fenceEnabledChanged, this, &GeoFenceController::fenceEnabledChanged); connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::_setDirty); connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::_setDirty); - connect(geoFenceManager, &GeoFenceManager::fenceSupportedChanged, this, &GeoFenceController::fenceSupportedChanged); connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::circleSupportedChanged); connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::polygonSupportedChanged); connect(geoFenceManager, &GeoFenceManager::breachReturnSupportedChanged, this, &GeoFenceController::breachReturnSupportedChanged); @@ -375,6 +377,11 @@ bool GeoFenceController::fenceSupported(void) const return _activeVehicle->geoFenceManager()->fenceSupported(); } +bool GeoFenceController::fenceEnabled(void) const +{ + return _activeVehicle->geoFenceManager()->fenceEnabled(); +} + bool GeoFenceController::circleSupported(void) const { return _activeVehicle->geoFenceManager()->circleSupported(); diff --git a/src/MissionManager/GeoFenceController.h b/src/MissionManager/GeoFenceController.h index 654b933c6..ae5b8c6dc 100644 --- a/src/MissionManager/GeoFenceController.h +++ b/src/MissionManager/GeoFenceController.h @@ -30,6 +30,7 @@ public: ~GeoFenceController(); Q_PROPERTY(bool fenceSupported READ fenceSupported NOTIFY fenceSupportedChanged) + Q_PROPERTY(bool fenceEnabled READ fenceEnabled NOTIFY fenceEnabledChanged) Q_PROPERTY(bool circleSupported READ circleSupported NOTIFY circleSupportedChanged) Q_PROPERTY(bool polygonSupported READ polygonSupported NOTIFY polygonSupportedChanged) Q_PROPERTY(bool breachReturnSupported READ breachReturnSupported NOTIFY breachReturnSupportedChanged) @@ -55,6 +56,7 @@ public: QString fileExtension(void) const final; bool fenceSupported (void) const; + bool fenceEnabled (void) const; bool circleSupported (void) const; bool polygonSupported (void) const; bool breachReturnSupported (void) const; @@ -69,6 +71,7 @@ public: signals: void fenceSupportedChanged (bool fenceSupported); + void fenceEnabledChanged (bool fenceEnabled); void circleSupportedChanged (bool circleSupported); void polygonSupportedChanged (bool polygonSupported); void breachReturnSupportedChanged (bool breachReturnSupported); diff --git a/src/MissionManager/GeoFenceManager.h b/src/MissionManager/GeoFenceManager.h index 20294ed5d..eb2e8f6b6 100644 --- a/src/MissionManager/GeoFenceManager.h +++ b/src/MissionManager/GeoFenceManager.h @@ -40,6 +40,7 @@ public: // Support flags virtual bool fenceSupported (void) const { return false; } + virtual bool fenceEnabled (void) const { return false; } virtual bool circleSupported (void) const { return false; } virtual bool polygonSupported (void) const { return false; } virtual bool breachReturnSupported (void) const { return false; } @@ -64,6 +65,7 @@ public: signals: void loadComplete (const QGeoCoordinate& breachReturn, const QList& polygon); void fenceSupportedChanged (bool fenceSupported); + void fenceEnabledChanged (bool fenceEnabled); void circleSupportedChanged (bool circleSupported); void polygonSupportedChanged (bool polygonSupported); void breachReturnSupportedChanged (bool fenceSupported); -- GitLab From 4c604d0db69420017cca54eae7f54be78f93fe7a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 17 Nov 2016 13:01:25 -0800 Subject: [PATCH 041/398] Harden mission protocol to lost packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retry in all the right/possible places. Don’t freak out about things coming in unknown sequences. --- src/MissionManager/MissionManager.cc | 184 +++++++++++++++-------- src/MissionManager/MissionManager.h | 10 +- src/MissionManager/MissionManagerTest.cc | 155 ++++++++++--------- src/MissionManager/MissionManagerTest.h | 8 +- src/comm/MockLinkMissionItemHandler.cc | 40 +++-- src/comm/MockLinkMissionItemHandler.h | 16 +- 6 files changed, 259 insertions(+), 154 deletions(-) diff --git a/src/MissionManager/MissionManager.cc b/src/MissionManager/MissionManager.cc index fa0ce387b..472dd453f 100644 --- a/src/MissionManager/MissionManager.cc +++ b/src/MissionManager/MissionManager.cc @@ -23,7 +23,7 @@ MissionManager::MissionManager(Vehicle* vehicle) : _vehicle(vehicle) , _dedicatedLink(NULL) , _ackTimeoutTimer(NULL) - , _retryAck(AckNone) + , _expectedAck(AckNone) , _readTransactionInProgress(false) , _writeTransactionInProgress(false) , _currentMissionItem(-1) @@ -81,7 +81,17 @@ void MissionManager::writeMissionItems(const QList& missionItems) for (int i=0; i<_missionItems.count(); i++) { _itemIndicesToWrite << i; } + _writeTransactionInProgress = true; + _retryCount = 0; + emit inProgressChanged(true); + _writeMissionCount(); +} + +/// This begins the write sequence with the vehicle. This may be called during a retry. +void MissionManager::_writeMissionCount(void) +{ + qCDebug(MissionManagerLog) << "_writeMissionCount retry count" << _retryCount; mavlink_message_t message; mavlink_mission_count_t missionCount; @@ -99,7 +109,6 @@ void MissionManager::writeMissionItems(const QList& missionItems) _vehicle->sendMessageOnLink(_dedicatedLink, message); _startAckTimeout(AckMissionRequest); - emit inProgressChanged(true); } void MissionManager::writeArduPilotGuidedMissionItem(const QGeoCoordinate& gotoCoord, bool altChangeOnly) @@ -153,75 +162,128 @@ void MissionManager::requestMissionItems(void) qCDebug(MissionManagerLog) << "requestMissionItems called while transaction in progress"; return; } - + + _retryCount = 0; + emit inProgressChanged(true); + _requestList(); +} + +/// Internal call to request list of mission items. May be called during a retry sequence. +void MissionManager::_requestList(void) +{ + qCDebug(MissionManagerLog) << "_requestList retry count" << _retryCount; + mavlink_message_t message; mavlink_mission_request_list_t request; - - _requestItemRetryCount = 0; + _itemIndicesToRead.clear(); _readTransactionInProgress = true; _clearMissionItems(); - + request.target_system = _vehicle->id(); request.target_component = MAV_COMP_ID_MISSIONPLANNER; - + _dedicatedLink = _vehicle->priorityLink(); mavlink_msg_mission_request_list_encode_chan(qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), _dedicatedLink->mavlinkChannel(), &message, &request); - + _vehicle->sendMessageOnLink(_dedicatedLink, message); _startAckTimeout(AckMissionCount); - emit inProgressChanged(true); } void MissionManager::_ackTimeout(void) { - AckType_t timedOutAck = _retryAck; - - _retryAck = AckNone; - - if (timedOutAck == AckNone) { - qCWarning(MissionManagerLog) << "_ackTimeout timeout with AckNone"; - _sendError(InternalError, "Internal error occurred during Mission Item communication: _ackTimeOut:_retryAck == AckNone"); + if (_expectedAck == AckNone) { return; } - - _sendError(AckTimeoutError, QString("Vehicle did not respond to mission item communication: %1").arg(_ackTypeToString(timedOutAck))); - _finishTransaction(false); + + switch (_expectedAck) { + case AckNone: + qCWarning(MissionManagerLog) << "_ackTimeout timeout with AckNone"; + _sendError(InternalError, "Internal error occurred during Mission Item communication: _ackTimeOut:_expectedAck == AckNone"); + break; + case AckMissionCount: + // MISSION_COUNT message expected + if (_retryCount > _maxRetryCount) { + _sendError(VehicleError, QStringLiteral("Mission request list failed, maximum retries exceeded.")); + _finishTransaction(false); + } else { + _retryCount++; + qCDebug(MissionManagerLog) << "Retrying REQUEST_LIST retry Count" << _retryCount; + _requestList(); + } + break; + case AckMissionItem: + // MISSION_ITEM expected + if (_retryCount > _maxRetryCount) { + _sendError(VehicleError, QStringLiteral("Mission read failed, maximum retries exceeded.")); + _finishTransaction(false); + } else { + _retryCount++; + qCDebug(MissionManagerLog) << "Retrying MISSION_REQUEST retry Count" << _retryCount; + _requestNextMissionItem(); + } + break; + case AckMissionRequest: + // MISSION_REQUEST is expected, or MISSION_ACK to end sequence + if (_itemIndicesToWrite.count() == 0) { + // Vehicle did not send final MISSION_ACK at end of sequence + _sendError(VehicleError, QStringLiteral("Mission write failed, vehicle failed to send final ack.")); + _finishTransaction(false); + } else if (_itemIndicesToWrite[0] == 0) { + // Vehicle did not respond to MISSION_COUNT, try again + if (_retryCount > _maxRetryCount) { + _sendError(VehicleError, QStringLiteral("Mission write mission count failed, maximum retries exceeded.")); + _finishTransaction(false); + } else { + _retryCount++; + qCDebug(MissionManagerLog) << "Retrying MISSION_COUNT retry Count" << _retryCount; + _writeMissionCount(); + } + } else { + // Vehicle did not request all items from ground station + _sendError(AckTimeoutError, QString("Vehicle did not request all items from ground station: %1").arg(_ackTypeToString(_expectedAck))); + _expectedAck = AckNone; + _finishTransaction(false); + } + break; + case AckGuidedItem: + // MISSION_REQUEST is expected, or MISSION_ACK to end sequence + default: + _sendError(AckTimeoutError, QString("Vehicle did not respond to mission item communication: %1").arg(_ackTypeToString(_expectedAck))); + _expectedAck = AckNone; + _finishTransaction(false); + } } void MissionManager::_startAckTimeout(AckType_t ack) { - _retryAck = ack; + _expectedAck = ack; _ackTimeoutTimer->start(); } -bool MissionManager::_stopAckTimeout(AckType_t expectedAck) +/// Checks the received ack against the expected ack. If they match the ack timeout timer will be stopped. +/// @return true: received ack matches expected ack +bool MissionManager::_checkForExpectedAck(AckType_t receivedAck) { - bool success = false; - AckType_t savedRetryAck = _retryAck; - - _retryAck = AckNone; - - _ackTimeoutTimer->stop(); - - if (savedRetryAck != expectedAck) { - if (savedRetryAck == AckNone) { - // Don't annoy the user with warnings about unexpected mission commands, just ignore them; ArduPilot updates home position using + if (receivedAck == _expectedAck) { + _expectedAck = AckNone; + _ackTimeoutTimer->stop(); + return true; + } else { + if (_expectedAck == AckNone) { + // Don't worry about unexpected mission commands, just ignore them; ArduPilot updates home position using // spurious MISSION_ITEMs. } else { - _sendError(ProtocolOrderError, QString("Vehicle responded incorrectly to mission item protocol sequence: %1:%2").arg(_ackTypeToString(savedRetryAck)).arg(_ackTypeToString(expectedAck))); - _finishTransaction(false); + // We just warn in this case, this could be crap left over from a previous transaction or the vehicle going bonkers. + // Whatever it is we let the ack timeout handle any error output to the user. + qCDebug(MissionManagerLog) << QString("Out of sequence ack expected:received %1:%2").arg(_ackTypeToString(_expectedAck)).arg(_ackTypeToString(receivedAck)); } - success = false; - } else { - success = true; + return false; } - - return success; } void MissionManager::_readTransactionComplete(void) @@ -251,9 +313,11 @@ void MissionManager::_handleMissionCount(const mavlink_message_t& message) { mavlink_mission_count_t missionCount; - if (!_stopAckTimeout(AckMissionCount)) { + if (!_checkForExpectedAck(AckMissionCount)) { return; } + + _retryCount = 0; mavlink_msg_mission_count_decode(&message, &missionCount); qCDebug(MissionManagerLog) << "_handleMissionCount count:" << missionCount.count; @@ -271,13 +335,13 @@ void MissionManager::_handleMissionCount(const mavlink_message_t& message) void MissionManager::_requestNextMissionItem(void) { - qCDebug(MissionManagerLog) << "_requestNextMissionItem sequenceNumber:" << _itemIndicesToRead[0]; - if (_itemIndicesToRead.count() == 0) { _sendError(InternalError, "Internal Error: Call to Vehicle _requestNextMissionItem with no more indices to read"); return; } - + + qCDebug(MissionManagerLog) << "_requestNextMissionItem sequenceNumber:retry" << _itemIndicesToRead[0] << _retryCount; + mavlink_message_t message; mavlink_mission_request_t missionRequest; @@ -299,7 +363,7 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message) { mavlink_mission_item_t missionItem; - if (!_stopAckTimeout(AckMissionItem)) { + if (!_checkForExpectedAck(AckMissionItem)) { return; } @@ -308,7 +372,6 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message) qCDebug(MissionManagerLog) << "_handleMissionItem sequenceNumber:" << missionItem.seq; if (_itemIndicesToRead.contains(missionItem.seq)) { - _requestItemRetryCount = 0; _itemIndicesToRead.removeOne(missionItem.seq); MissionItem* item = new MissionItem(missionItem.seq, @@ -333,13 +396,12 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message) _missionItems.append(item); } else { qCDebug(MissionManagerLog) << "_handleMissionItem mission item received item index which was not requested, disregrarding:" << missionItem.seq; - if (++_requestItemRetryCount > _maxRetryCount) { - _sendError(RequestRangeError, QString("Vehicle would not send item %1 after max retries. Read from Vehicle failed.").arg(_itemIndicesToRead[0])); - _finishTransaction(false); - return; - } + // We have to put the ack timeout back since it was removed above + _startAckTimeout(AckMissionItem); + return; } + _retryCount = 0; if (_itemIndicesToRead.count() == 0) { _readTransactionComplete(); } else { @@ -357,7 +419,7 @@ void MissionManager::_handleMissionRequest(const mavlink_message_t& message) { mavlink_mission_request_t missionRequest; - if (!_stopAckTimeout(AckMissionRequest)) { + if (!_checkForExpectedAck(AckMissionRequest)) { return; } @@ -411,14 +473,14 @@ void MissionManager::_handleMissionAck(const mavlink_message_t& message) { mavlink_mission_ack_t missionAck; - // Save the retry ack before calling _stopAckTimeout since we'll need it to determine what + // Save the retry ack before calling _checkForExpectedAck since we'll need it to determine what // type of a protocol sequence we are in. - AckType_t savedRetryAck = _retryAck; + AckType_t savedExpectedAck = _expectedAck; // We can get a MISSION_ACK with an error at any time, so if the Acks don't match it is not - // a protocol sequence error. Call _stopAckTimeout with _retryAck so it will succeed no + // a protocol sequence error. Call _checkForExpectedAck with _retryAck so it will succeed no // matter what. - if (!_stopAckTimeout(_retryAck)) { + if (!_checkForExpectedAck(_expectedAck)) { return; } @@ -426,7 +488,7 @@ void MissionManager::_handleMissionAck(const mavlink_message_t& message) qCDebug(MissionManagerLog) << "_handleMissionAck type:" << _missionResultToString((MAV_MISSION_RESULT)missionAck.type); - switch (savedRetryAck) { + switch (savedExpectedAck) { case AckNone: // State machine is idle. Vehicle is confused. _sendError(VehicleError, QString("Vehicle sent unexpected MISSION_ACK message, error: %1").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type))); @@ -438,7 +500,7 @@ void MissionManager::_handleMissionAck(const mavlink_message_t& message) break; case AckMissionItem: // MISSION_ITEM expected - _sendError(VehicleError, QString("Vehicle returned error: %1. Partial list of mission items may have been returned.").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type))); + _sendError(VehicleError, QString("Vehicle returned error: %1.").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type))); _finishTransaction(false); break; case AckMissionRequest: @@ -459,7 +521,7 @@ void MissionManager::_handleMissionAck(const mavlink_message_t& message) case AckGuidedItem: // MISSION_REQUEST is expected, or MISSION_ACK to end sequence if (missionAck.type == MAV_MISSION_ACCEPTED) { - qCDebug(MissionManagerLog) << "_handleMissionAck guide mode item accepted"; + qCDebug(MissionManagerLog) << "_handleMissionAck guided mode item accepted"; _finishTransaction(true); } else { _sendError(VehicleError, QString("Vehicle returned error: %1. Vehicle did not accept guided item.").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type))); @@ -587,12 +649,14 @@ void MissionManager::_finishTransaction(bool success) emit newMissionItemsAvailable(); } - _readTransactionInProgress = false; - _writeTransactionInProgress = false; _itemIndicesToRead.clear(); _itemIndicesToWrite.clear(); - emit inProgressChanged(false); + if (_readTransactionInProgress || _writeTransactionInProgress) { + _readTransactionInProgress = false; + _writeTransactionInProgress = false; + emit inProgressChanged(false); + } } bool MissionManager::inProgress(void) diff --git a/src/MissionManager/MissionManager.h b/src/MissionManager/MissionManager.h index 6a4602abe..7e3ce2171 100644 --- a/src/MissionManager/MissionManager.h +++ b/src/MissionManager/MissionManager.h @@ -62,7 +62,7 @@ public: } ErrorCode_t; // These values are public so the unit test can set appropriate signal wait times - static const int _ackTimeoutMilliseconds= 2000; + static const int _ackTimeoutMilliseconds = 1000; static const int _maxRetryCount = 5; signals: @@ -85,7 +85,7 @@ private: } AckType_t; void _startAckTimeout(AckType_t ack); - bool _stopAckTimeout(AckType_t expectedAck); + bool _checkForExpectedAck(AckType_t receivedAck); void _readTransactionComplete(void); void _handleMissionCount(const mavlink_message_t& message); void _handleMissionItem(const mavlink_message_t& message); @@ -98,14 +98,16 @@ private: QString _ackTypeToString(AckType_t ackType); QString _missionResultToString(MAV_MISSION_RESULT result); void _finishTransaction(bool success); + void _requestList(void); + void _writeMissionCount(void); private: Vehicle* _vehicle; LinkInterface* _dedicatedLink; QTimer* _ackTimeoutTimer; - AckType_t _retryAck; - int _requestItemRetryCount; + AckType_t _expectedAck; + int _retryCount; bool _readTransactionInProgress; bool _writeTransactionInProgress; diff --git a/src/MissionManager/MissionManagerTest.cc b/src/MissionManager/MissionManagerTest.cc index 35a91d71e..f6e94581c 100644 --- a/src/MissionManager/MissionManagerTest.cc +++ b/src/MissionManager/MissionManagerTest.cc @@ -27,7 +27,7 @@ MissionManagerTest::MissionManagerTest(void) } -void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t failureMode) +void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t failureMode, bool shouldFail) { _mockLink->setMissionItemFailureMode(failureMode); @@ -67,29 +67,9 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f _multiSpyMissionManager->clearAllSignals(); - if (failureMode == MockLinkMissionItemHandler::FailNone) { - // This should be clean run - - // Wait for write sequence to complete. We should get: - // inProgressChanged(false) signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkOnlySignalByMask(inProgressChangedSignalMask), true); - - // Validate inProgressChanged signal value - _checkInProgressValues(false); - - // Validate item count in mission manager - - int expectedCount = (int)_cTestCases; - if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // Home position at position 0 comes from vehicle - expectedCount++; - } - - QCOMPARE(_missionManager->missionItems().count(), expectedCount); - } else { + if (shouldFail) { // This should be a failed run - + setExpectedMessageBox(QMessageBox::Ok); // Wait for write sequence to complete. We should get: @@ -97,10 +77,10 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f // error(errorCode, QString) signal _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); QCOMPARE(_multiSpyMissionManager->checkSignalByMask(inProgressChangedSignalMask | errorSignalMask), true); - + // Validate inProgressChanged signal value _checkInProgressValues(false); - + // Validate error signal values QSignalSpy* spy = _multiSpyMissionManager->getSpyByIndex(errorSignalIndex); QList signalArgs = spy->takeFirst(); @@ -108,14 +88,34 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f qDebug() << signalArgs[1].toString(); checkExpectedMessageBox(); + } else { + // This should be clean run + + // Wait for write sequence to complete. We should get: + // inProgressChanged(false) signal + _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); + QCOMPARE(_multiSpyMissionManager->checkOnlySignalByMask(inProgressChangedSignalMask), true); + + // Validate inProgressChanged signal value + _checkInProgressValues(false); + + // Validate item count in mission manager + + int expectedCount = (int)_cTestCases; + if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { + // Home position at position 0 comes from vehicle + expectedCount++; + } + + QCOMPARE(_missionManager->missionItems().count(), expectedCount); } _multiSpyMissionManager->clearAllSignals(); } -void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode_t failureMode) +void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode_t failureMode, bool shouldFail) { - _writeItems(MockLinkMissionItemHandler::FailNone); + _writeItems(MockLinkMissionItemHandler::FailNone, false); _mockLink->setMissionItemFailureMode(failureMode); @@ -129,19 +129,9 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode _multiSpyMissionManager->clearAllSignals(); - if (failureMode == MockLinkMissionItemHandler::FailNone) { - // This should be clean run - - // Now wait for read sequence to complete. We should get: - // inProgressChanged(false) signal to signal completion - // newMissionItemsAvailable signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true); - _checkInProgressValues(false); - - } else { + if (shouldFail) { // This should be a failed run - + setExpectedMessageBox(QMessageBox::Ok); // Wait for read sequence to complete. We should get: @@ -150,17 +140,26 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode // newMissionItemsAvailable signal _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask | errorSignalMask), true); - + // Validate inProgressChanged signal value _checkInProgressValues(false); - + // Validate error signal values QSignalSpy* spy = _multiSpyMissionManager->getSpyByIndex(errorSignalIndex); QList signalArgs = spy->takeFirst(); QCOMPARE(signalArgs.count(), 2); qDebug() << signalArgs[1].toString(); - + checkExpectedMessageBox(); + } else { + // This should be clean run + + // Now wait for read sequence to complete. We should get: + // inProgressChanged(false) signal to signal completion + // newMissionItemsAvailable signal + _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); + QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true); + _checkInProgressValues(false); } _multiSpyMissionManager->clearAllSignals(); @@ -169,14 +168,14 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode size_t cMissionItemsExpected; - if (failureMode == MockLinkMissionItemHandler::FailNone) { + if (shouldFail) { + cMissionItemsExpected = 0; + } else { cMissionItemsExpected = (int)_cTestCases; if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { // Home position at position 0 comes from vehicle cMissionItemsExpected++; } - } else { - cMissionItemsExpected = 0; } QCOMPARE(_missionManager->missionItems().count(), (int)cMissionItemsExpected); @@ -233,24 +232,27 @@ void MissionManagerTest::_testWriteFailureHandlingWorker(void) typedef struct { const char* failureText; MockLinkMissionItemHandler::FailureMode_t failureMode; - } TestCase_t; + bool shouldFail; + } WriteTestCase_t; - static const TestCase_t rgTestCases[] = { - { "No Failure", MockLinkMissionItemHandler::FailNone }, - { "FailWriteRequest0NoResponse", MockLinkMissionItemHandler::FailWriteRequest0NoResponse }, - { "FailWriteRequest1NoResponse", MockLinkMissionItemHandler::FailWriteRequest1NoResponse }, - { "FailWriteRequest0IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest0IncorrectSequence }, - { "FailWriteRequest1IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest1IncorrectSequence }, - { "FailWriteRequest0ErrorAck", MockLinkMissionItemHandler::FailWriteRequest0ErrorAck }, - { "FailWriteRequest1ErrorAck", MockLinkMissionItemHandler::FailWriteRequest1ErrorAck }, - { "FailWriteFinalAckNoResponse", MockLinkMissionItemHandler::FailWriteFinalAckNoResponse }, - { "FailWriteFinalAckErrorAck", MockLinkMissionItemHandler::FailWriteFinalAckErrorAck }, - { "FailWriteFinalAckMissingRequests", MockLinkMissionItemHandler::FailWriteFinalAckMissingRequests }, + static const WriteTestCase_t rgTestCases[] = { + { "No Failure", MockLinkMissionItemHandler::FailNone, false }, + { "FailWriteMissionCountNoResponse", MockLinkMissionItemHandler::FailWriteMissionCountNoResponse, true }, + { "FailWriteMissionCountFirstResponse", MockLinkMissionItemHandler::FailWriteMissionCountFirstResponse, false }, + { "FailWriteRequest1NoResponse", MockLinkMissionItemHandler::FailWriteRequest1NoResponse, true }, + { "FailWriteRequest0IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest0IncorrectSequence, true }, + { "FailWriteRequest1IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest1IncorrectSequence, true }, + { "FailWriteRequest0ErrorAck", MockLinkMissionItemHandler::FailWriteRequest0ErrorAck, true }, + { "FailWriteRequest1ErrorAck", MockLinkMissionItemHandler::FailWriteRequest1ErrorAck, true }, + { "FailWriteFinalAckNoResponse", MockLinkMissionItemHandler::FailWriteFinalAckNoResponse, true }, + { "FailWriteFinalAckErrorAck", MockLinkMissionItemHandler::FailWriteFinalAckErrorAck, true }, + { "FailWriteFinalAckMissingRequests", MockLinkMissionItemHandler::FailWriteFinalAckMissingRequests, true }, }; for (size_t i=0; ifailureText; + _writeItems(pCase->failureMode, pCase->shouldFail); _mockLink->resetMissionItemHandler(); } } @@ -271,22 +273,31 @@ void MissionManagerTest::_testReadFailureHandlingWorker(void) typedef struct { const char* failureText; MockLinkMissionItemHandler::FailureMode_t failureMode; - } TestCase_t; + bool shouldFail; + } ReadTestCase_t; - static const TestCase_t rgTestCases[] = { - { "No Failure", MockLinkMissionItemHandler::FailNone }, - { "FailReadRequestListNoResponse", MockLinkMissionItemHandler::FailReadRequestListNoResponse }, - { "FailReadRequest0NoResponse", MockLinkMissionItemHandler::FailReadRequest0NoResponse }, - { "FailReadRequest1NoResponse", MockLinkMissionItemHandler::FailReadRequest1NoResponse }, - { "FailReadRequest0IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest0IncorrectSequence }, - { "FailReadRequest1IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest1IncorrectSequence }, - { "FailReadRequest0ErrorAck", MockLinkMissionItemHandler::FailReadRequest0ErrorAck }, - { "FailReadRequest1ErrorAck", MockLinkMissionItemHandler::FailReadRequest1ErrorAck }, + /* + static const ReadTestCase_t rgTestCases[] = { + { "FailReadRequest1FirstResponse", MockLinkMissionItemHandler::FailReadRequest1FirstResponse, false }, + };*/ + + static const ReadTestCase_t rgTestCases[] = { + { "No Failure", MockLinkMissionItemHandler::FailNone, false }, + { "FailReadRequestListNoResponse", MockLinkMissionItemHandler::FailReadRequestListNoResponse, true }, + { "FailReadRequestListFirstResponse", MockLinkMissionItemHandler::FailReadRequestListFirstResponse, false }, + { "FailReadRequest0NoResponse", MockLinkMissionItemHandler::FailReadRequest0NoResponse, true }, + { "FailReadRequest1NoResponse", MockLinkMissionItemHandler::FailReadRequest1NoResponse, true }, + { "FailReadRequest1FirstResponse", MockLinkMissionItemHandler::FailReadRequest1FirstResponse, false }, + { "FailReadRequest0IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest0IncorrectSequence, true }, + { "FailReadRequest1IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest1IncorrectSequence, true }, + { "FailReadRequest0ErrorAck", MockLinkMissionItemHandler::FailReadRequest0ErrorAck, true }, + { "FailReadRequest1ErrorAck", MockLinkMissionItemHandler::FailReadRequest1ErrorAck, true }, }; - + for (size_t i=0; ifailureText; + _roundTripItems(pCase->failureMode, pCase->shouldFail); _mockLink->resetMissionItemHandler(); _multiSpyMissionManager->clearAllSignals(); } diff --git a/src/MissionManager/MissionManagerTest.h b/src/MissionManager/MissionManagerTest.h index 03cfd07ab..e8bd7a597 100644 --- a/src/MissionManager/MissionManagerTest.h +++ b/src/MissionManager/MissionManagerTest.h @@ -33,13 +33,13 @@ private slots: void _testReadFailureHandlingAPM(void); private: - void _roundTripItems(MockLinkMissionItemHandler::FailureMode_t failureMode); - void _writeItems(MockLinkMissionItemHandler::FailureMode_t failureMode); + void _roundTripItems(MockLinkMissionItemHandler::FailureMode_t failureMode, bool shouldFail); + void _writeItems(MockLinkMissionItemHandler::FailureMode_t failureMode, bool shouldFail); void _testWriteFailureHandlingWorker(void); void _testReadFailureHandlingWorker(void); - static const MissionControllerManagerTest::TestCase_t _rgTestCases[]; - static const size_t _cTestCases; + static const TestCase_t _rgTestCases[]; + static const size_t _cTestCases; }; #endif diff --git a/src/comm/MockLinkMissionItemHandler.cc b/src/comm/MockLinkMissionItemHandler.cc index 146e5d7f3..e9a0bff65 100644 --- a/src/comm/MockLinkMissionItemHandler.cc +++ b/src/comm/MockLinkMissionItemHandler.cc @@ -21,6 +21,9 @@ MockLinkMissionItemHandler::MockLinkMissionItemHandler(MockLink* mockLink, MAVLi , _failureMode(FailNone) , _sendHomePositionOnEmptyList(false) , _mavlinkProtocol(mavlinkProtocol) + , _failReadRequestListFirstResponse(true) + , _failReadRequest1FirstResponse(true) + , _failWriteMissionCountFirstResponse(true) { Q_ASSERT(mockLink); } @@ -82,9 +85,17 @@ void MockLinkMissionItemHandler::_handleMissionRequestList(const mavlink_message { qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList read sequence"; - if (_failureMode != FailReadRequestListNoResponse) { + _failReadRequest1FirstResponse = true; + + if (_failureMode == FailReadRequestListNoResponse) { + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList not responding due to failure mode FailReadRequestListNoResponse"; + } else if (_failureMode == FailReadRequestListFirstResponse && _failReadRequestListFirstResponse) { + _failReadRequestListFirstResponse = false; + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList not responding due to failure mode FailReadRequestListFirstResponse"; + } else { mavlink_mission_request_list_t request; + _failReadRequestListFirstResponse = true; mavlink_msg_mission_request_list_decode(&msg, &request); Q_ASSERT(request.target_system == _mockLink->vehicleId()); @@ -104,8 +115,6 @@ void MockLinkMissionItemHandler::_handleMissionRequestList(const mavlink_message msg.compid, // Target is original sender itemCount); // Number of mission items _mockLink->respondWithMavlinkMessage(responseMsg); - } else { - qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList not responding due to failure mode"; } } @@ -120,9 +129,13 @@ void MockLinkMissionItemHandler::_handleMissionRequest(const mavlink_message_t& Q_ASSERT(request.target_system == _mockLink->vehicleId()); Q_ASSERT(request.seq < _missionItems.count()); - if ((_failureMode == FailReadRequest0NoResponse && request.seq == 0) || - (_failureMode == FailReadRequest1NoResponse && request.seq == 1)) { - qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode"; + if (_failureMode == FailReadRequest0NoResponse && request.seq == 0) { + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest0NoResponse"; + } else if (_failureMode == FailReadRequest1NoResponse && request.seq == 1) { + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest1NoResponse"; + } else if (_failureMode == FailReadRequest1FirstResponse && request.seq == 1 && _failReadRequest1FirstResponse) { + _failReadRequest1FirstResponse = false; + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest1FirstResponse"; } else { // FIXME: Track whether all items are requested, or requested in sequence @@ -185,6 +198,16 @@ void MockLinkMissionItemHandler::_handleMissionCount(const mavlink_message_t& ms if (_writeSequenceCount == 0) { _sendAck(MAV_MISSION_ACCEPTED); } else { + if (_failureMode == FailWriteMissionCountNoResponse) { + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionCount not responding due to failure mode FailWriteMissionCountNoResponse"; + return; + } + if (_failureMode == FailWriteMissionCountFirstResponse && _failWriteMissionCountFirstResponse) { + _failWriteMissionCountFirstResponse = false; + qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionCount not responding due to failure mode FailWriteMissionCountNoResponse"; + return; + } + _failWriteMissionCountFirstResponse = true; _writeSequenceIndex = 0; _requestNextMissionItem(_writeSequenceIndex); } @@ -194,9 +217,8 @@ void MockLinkMissionItemHandler::_requestNextMissionItem(int sequenceNumber) { qCDebug(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem write sequence sequenceNumber:" << sequenceNumber << "_failureMode:" << _failureMode; - if ((_failureMode == FailWriteRequest0NoResponse && sequenceNumber == 0) || - (_failureMode == FailWriteRequest1NoResponse && sequenceNumber == 1)) { - qCDebug(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem not responding due to failure mode"; + if (_failureMode == FailWriteRequest1NoResponse && sequenceNumber == 1) { + qCDebug(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem not responding due to failure mode FailWriteRequest1NoResponse"; } else { if (sequenceNumber >= _writeSequenceCount) { qCWarning(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem requested seqeuence number > write count sequenceNumber::_writeSequenceCount" << sequenceNumber << _writeSequenceCount; diff --git a/src/comm/MockLinkMissionItemHandler.h b/src/comm/MockLinkMissionItemHandler.h index 77c9ea238..765058fd6 100644 --- a/src/comm/MockLinkMissionItemHandler.h +++ b/src/comm/MockLinkMissionItemHandler.h @@ -42,18 +42,21 @@ public: typedef enum { FailNone, // No failures FailReadRequestListNoResponse, // Don't send MISSION_COUNT in response to MISSION_REQUEST_LIST + FailReadRequestListFirstResponse, // Don't send MISSION_COUNT in response to first MISSION_REQUEST_LIST, allow subsequent to go through FailReadRequest0NoResponse, // Don't send MISSION_ITEM in response to MISSION_REQUEST item 0 FailReadRequest1NoResponse, // Don't send MISSION_ITEM in response to MISSION_REQUEST item 1 + FailReadRequest1FirstResponse, // Don't send MISSION_ITEM in response to MISSION_REQUEST item 1 on first try, allow subsequent request to go through FailReadRequest0IncorrectSequence, // Respond to MISSION_REQUEST 0 with incorrect sequence number in MISSION_ITEM FailReadRequest1IncorrectSequence, // Respond to MISSION_REQUEST 1 with incorrect sequence number in MISSION_ITEM FailReadRequest0ErrorAck, // Respond to MISSION_REQUEST 0 with MISSION_ACK error FailReadRequest1ErrorAck, // Respond to MISSION_REQUEST 1 bogus MISSION_ACK error - FailWriteRequest0NoResponse, // Don't respond to MISSION_COUNT with MISSION_REQUEST 0 + FailWriteMissionCountNoResponse, // Don't respond to MISSION_COUNT with MISSION_REQUEST 0 + FailWriteMissionCountFirstResponse, // Don't respond to first MISSION_COUNT with MISSION_REQUEST 0, respond to subsequent MISSION_COUNT requests FailWriteRequest1NoResponse, // Don't respond to MISSION_ITEM 0 with MISSION_REQUEST 1 - FailWriteRequest0IncorrectSequence, // Respond to MISSION_COUNT 0 with MISSION_REQUEST with wrong sequence number - FailWriteRequest1IncorrectSequence, // Respond to MISSION_ITEM 0 with MISSION_REQUEST with wrong sequence number - FailWriteRequest0ErrorAck, // Respond to MISSION_COUNT 0 with MISSION_ACK error - FailWriteRequest1ErrorAck, // Respond to MISSION_ITEM 0 with MISSION_ACK error + FailWriteRequest0IncorrectSequence, // Item 0 MISSION_REQUEST sent has wrong sequence number + FailWriteRequest1IncorrectSequence, // Item 1 MISSION_REQUEST sent has wrong sequence number + FailWriteRequest0ErrorAck, // Instead of sending MISSION_REQUEST 0, send MISSION_ACK error + FailWriteRequest1ErrorAck, // Instead of sending MISSION_REQUEST 1, send MISSION_ACK error FailWriteFinalAckNoResponse, // Don't send the final MISSION_ACK FailWriteFinalAckErrorAck, // Send an error as the final MISSION_ACK FailWriteFinalAckMissingRequests, // Send the MISSION_ACK before all items have been requested @@ -102,6 +105,9 @@ private: FailureMode_t _failureMode; bool _sendHomePositionOnEmptyList; MAVLinkProtocol* _mavlinkProtocol; + bool _failReadRequestListFirstResponse; + bool _failReadRequest1FirstResponse; + bool _failWriteMissionCountFirstResponse; }; #endif -- GitLab From 3f659c5279c6bd0419ee114beb6074d5b8f5d2b3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 18 Nov 2016 09:27:17 -0800 Subject: [PATCH 042/398] Only depend on accel 1 for setup complete --- .../APM/APMSensorsComponent.cc | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.cc b/src/AutoPilotPlugins/APM/APMSensorsComponent.cc index e5933b554..0efdf4e6a 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.cc @@ -62,12 +62,6 @@ QStringList APMSensorsComponent::setupCompleteChangedTriggerList(void) const // Accelerometer triggers triggers << "INS_ACCOFFS_X" << "INS_ACCOFFS_Y" << "INS_ACCOFFS_Z"; - if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("INS_USE"))) { - triggers << QStringLiteral("INS_USE") << QStringLiteral("INS_USE2") << QStringLiteral("INS_USE3"); - triggers << QStringLiteral("INS_ACC2OFFS_X") << QStringLiteral("INS_ACC2OFFS_Y") << QStringLiteral("INS_ACC2OFFS_Z"); - triggers << QStringLiteral("INS_ACC3OFFS_X") << QStringLiteral("INS_ACC3OFFS_Y") << QStringLiteral("INS_ACC3OFFS_Z"); - } - return triggers; } @@ -123,39 +117,18 @@ bool APMSensorsComponent::compassSetupNeeded(void) const bool APMSensorsComponent::accelSetupNeeded(void) const { - QStringList rgUse; - QStringList rgOffsets; - QList rgAccels; + QStringList rgOffsets; - // We always at a minimum test the first accel + // The best we can do is test the first accel which will always be there. We don't have enough information to know + // whether any of the other accels are available. rgOffsets << QStringLiteral("INS_ACCOFFS_X") << QStringLiteral("INS_ACCOFFS_Y") << QStringLiteral("INS_ACCOFFS_Z"); - rgAccels << rgOffsets; - rgOffsets.clear(); - - // This parameter is not available in all firmware version. Specifically missing from older Solo firmware. - if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("INS_USE"))) { - rgUse << QStringLiteral("INS_USE") << QStringLiteral("INS_USE2") << QStringLiteral("INS_USE3"); - - // We have usage information for the remaining accels, so we can test them sa well - rgOffsets << QStringLiteral("INS_ACC2OFFS_X") << QStringLiteral("INS_ACC2OFFS_Y") << QStringLiteral("INS_ACC2OFFS_Z"); - rgAccels << rgOffsets; - rgOffsets.clear(); - - rgOffsets << QStringLiteral("INS_ACC3OFFS_X") << QStringLiteral("INS_ACC3OFFS_Y") << QStringLiteral("INS_ACC3OFFS_Z"); - rgAccels << rgOffsets; - rgOffsets.clear(); - } - for (int i=0; iparameterManager()->getParameter(FactSystem::defaultComponentId, rgUse[i])->rawValue().toInt() != 0) { - for (int j=0; jparameterManager()->getParameter(FactSystem::defaultComponentId, rgAccels[i][j])->rawValue().toFloat() == 0.0f) { - return true; - } - } + int zeroCount = 0; + for (int i=0; iparameterManager()->getParameter(FactSystem::defaultComponentId, rgOffsets[i])->rawValue().toFloat() == 0.0f) { + zeroCount++; } } - return false; + return zeroCount == rgOffsets.count(); } - -- GitLab From e3f8676cf82bc37b6ae69f338c10f154a2d44e50 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 18 Nov 2016 10:38:59 -0800 Subject: [PATCH 043/398] Don't let other function mapping lock out usage of those channels for cal --- .../Common/RadioComponentController.cc | 48 +------------------ .../Common/RadioComponentController.h | 13 ----- src/qgcunittest/RadioConfigTest.cc | 40 ++++------------ src/qgcunittest/RadioConfigTest.h | 1 - 4 files changed, 11 insertions(+), 91 deletions(-) diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index a6f69d44f..0f2e4c626 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -58,29 +58,17 @@ const char* RadioComponentController::_settingsGroup = "RadioCalibration"; const char* RadioComponentController::_settingsKeyTransmitterMode = "TransmitterMode"; const struct RadioComponentController::FunctionInfo RadioComponentController::_rgFunctionInfoPX4[RadioComponentController::rcCalFunctionMax] = { - //Parameter required { "RC_MAP_ROLL" }, { "RC_MAP_PITCH" }, { "RC_MAP_YAW" }, - { "RC_MAP_THROTTLE" }, - { "RC_MAP_MODE_SW" }, - { "RC_MAP_POSCTL_SW" }, - { "RC_MAP_LOITER_SW" }, - { "RC_MAP_RETURN_SW" }, - { "RC_MAP_ACRO_SW" }, + { "RC_MAP_THROTTLE" } }; const struct RadioComponentController::FunctionInfo RadioComponentController::_rgFunctionInfoAPM[RadioComponentController::rcCalFunctionMax] = { - //Parameter required { "RCMAP_ROLL" }, { "RCMAP_PITCH" }, { "RCMAP_YAW" }, - { "RCMAP_THROTTLE" }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, + { "RCMAP_THROTTLE" } }; RadioComponentController::RadioComponentController(void) : @@ -572,38 +560,6 @@ void RadioComponentController::_resetInternalCalibrationValues(void) _rgFunctionChannelMapping[i] = _chanMax(); } - if (_px4Vehicle()) { - // Reserve the existing Flight Mode switch settings channels so we don't re-use them - - static const rcCalFunctions rgFlightModeFunctions[] = { - rcCalFunctionModeSwitch, - rcCalFunctionPosCtlSwitch, - rcCalFunctionLoiterSwitch, - rcCalFunctionReturnSwitch }; - static const size_t crgFlightModeFunctions = sizeof(rgFlightModeFunctions) / sizeof(rgFlightModeFunctions[0]); - - for (size_t i=0; i < crgFlightModeFunctions; i++) { - QVariant value; - enum rcCalFunctions curFunction = rgFlightModeFunctions[i]; - - Fact* paramFact = getParameterFact(FactSystem::defaultComponentId, _functionInfo()[curFunction].parameterName); - if (paramFact) { - bool ok; - int switchChannel = paramFact->rawValue().toInt(&ok); - Q_ASSERT(ok); - - // Parameter: 1-based channel, 0=not mapped - // _rgFunctionChannelMapping: 0-based channel, _chanMax=not mapped - - if (switchChannel != 0) { - qCDebug(RadioComponentControllerLog) << "Reserving 0-based switch channel" << switchChannel - 1; - _rgFunctionChannelMapping[curFunction] = switchChannel - 1; - _rgChannelInfo[switchChannel - 1].function = curFunction; - } - } - } - } - _signalAllAttiudeValueChanges(); } diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.h b/src/AutoPilotPlugins/Common/RadioComponentController.h index bd3c056cf..2b6062b7c 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.h +++ b/src/AutoPilotPlugins/Common/RadioComponentController.h @@ -149,20 +149,7 @@ private: rcCalFunctionPitch, rcCalFunctionYaw, rcCalFunctionThrottle, - rcCalFunctionModeSwitch, - rcCalFunctionPosCtlSwitch, - rcCalFunctionLoiterSwitch, - rcCalFunctionReturnSwitch, - rcCalFunctionAcroSwitch, rcCalFunctionMax, - - // Attitude functions are roll/pitch/yaw/throttle - rcCalFunctionFirstAttitudeFunction = rcCalFunctionRoll, - rcCalFunctionLastAttitudeFunction = rcCalFunctionThrottle, - - // Non-Attitude functions are everything else - rcCalFunctionFirstNonAttitudeFunction = rcCalFunctionModeSwitch, - rcCalFunctionLastNonAttitudeFunction = rcCalFunctionAcroSwitch, }; /// @brief The states of the calibration state machine. diff --git a/src/qgcunittest/RadioConfigTest.cc b/src/qgcunittest/RadioConfigTest.cc index 0d8f21c67..63c8ba31c 100644 --- a/src/qgcunittest/RadioConfigTest.cc +++ b/src/qgcunittest/RadioConfigTest.cc @@ -332,28 +332,6 @@ void RadioConfigTest::_switchMinMaxStep(void) QCOMPARE(_controller->_currentStep, saveStep + 1); } -void RadioConfigTest::_switchSelectAutoStep(const char* functionStr, RadioComponentController::rcCalFunctions function) -{ - Q_UNUSED(functionStr); - ////qCDebug(RadioConfigTestLog)() << "_switchSelectAutoStep" << functionStr << "function:" << function; - - int buttonMask = cancelButtonMask; - if (function != RadioComponentController::rcCalFunctionModeSwitch) { - buttonMask |= skipButtonMask; - } - - CHK_BUTTONS(buttonMask); - - int saveStep = _controller->_currentStep; - - // Wiggle stick for channel - int channel = _rgFunctionChannelMap[function]; - _mockLink->emitRemoteControlChannelRawChanged(channel, _testMinValue); - _mockLink->emitRemoteControlChannelRawChanged(channel, _testMaxValue); - - QCOMPARE(_controller->_currentStep, saveStep + 1); -} - void RadioConfigTest::_fullCalibrationWorker(MAV_AUTOPILOT firmwareType) { _init(firmwareType); @@ -409,15 +387,15 @@ void RadioConfigTest::_fullCalibrationWorker(MAV_AUTOPILOT firmwareType) _channelHomePosition(); _controller->nextButtonClicked(); _beginCalibration(); - _stickMoveAutoStep("Throttle", RadioComponentController::rcCalFunctionThrottle, moveToMax, true /* identify step */); - _stickMoveAutoStep("Throttle", RadioComponentController::rcCalFunctionThrottle, moveToMin, false /* not identify step */); - _stickMoveAutoStep("Yaw", RadioComponentController::rcCalFunctionYaw, moveToMax, true /* identify step */); - _stickMoveAutoStep("Yaw", RadioComponentController::rcCalFunctionYaw, moveToMin, false /* not identify step */); - _stickMoveAutoStep("Roll", RadioComponentController::rcCalFunctionRoll, moveToMax, true /* identify step */); - _stickMoveAutoStep("Roll", RadioComponentController::rcCalFunctionRoll, moveToMin, false /* not identify step */); - _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToMax, true /* identify step */); - _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToMin, false /* not identify step */); - _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToCenter, false /* not identify step */); + _stickMoveAutoStep("Throttle", RadioComponentController::rcCalFunctionThrottle, moveToMax, true /* identify step */); + _stickMoveAutoStep("Throttle", RadioComponentController::rcCalFunctionThrottle, moveToMin, false /* not identify step */); + _stickMoveAutoStep("Yaw", RadioComponentController::rcCalFunctionYaw, moveToMax, true /* identify step */); + _stickMoveAutoStep("Yaw", RadioComponentController::rcCalFunctionYaw, moveToMin, false /* not identify step */); + _stickMoveAutoStep("Roll", RadioComponentController::rcCalFunctionRoll, moveToMax, true /* identify step */); + _stickMoveAutoStep("Roll", RadioComponentController::rcCalFunctionRoll, moveToMin, false /* not identify step */); + _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToMax, true /* identify step */); + _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToMin, false /* not identify step */); + _stickMoveAutoStep("Pitch", RadioComponentController::rcCalFunctionPitch, moveToCenter, false /* not identify step */); _switchMinMaxStep(); // One more click and the parameters should get saved diff --git a/src/qgcunittest/RadioConfigTest.h b/src/qgcunittest/RadioConfigTest.h index d66031e58..7791d097c 100644 --- a/src/qgcunittest/RadioConfigTest.h +++ b/src/qgcunittest/RadioConfigTest.h @@ -76,7 +76,6 @@ private: void _stickMoveWaitForSettle(int channel, int value); void _stickMoveAutoStep(const char* functionStr, enum RadioComponentController::rcCalFunctions function, enum MoveToDirection direction, bool identifyStep); void _switchMinMaxStep(void); - void _switchSelectAutoStep(const char* functionStr, RadioComponentController::rcCalFunctions function); bool _px4Vehicle(void) const; const struct RadioComponentController::FunctionInfo* _functionInfo(void) const; const struct ChannelSettings* _channelSettings(void) const; -- GitLab From c6091ed44ed1cf2b5b18c3abb703a0707ef6e22c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 18 Nov 2016 12:00:49 -0800 Subject: [PATCH 044/398] Fix mobile polygon draw --- src/FlightMap/FlightMap.qml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/FlightMap/FlightMap.qml b/src/FlightMap/FlightMap.qml index 2d3b7f2df..96018f1ad 100644 --- a/src/FlightMap/FlightMap.qml +++ b/src/FlightMap/FlightMap.qml @@ -349,8 +349,17 @@ Map { // Add first coordinate polygonPath.push(clickCoordinate) } else { - // Update finalized coordinate - polygonPath[polygonDrawerPolygon.path.length - 1] = clickCoordinate + // Add subsequent coordinate + if (ScreenTools.isMobile) { + // Since mobile has no mouse, the onPositionChangedHandler will not fire. We have to add the coordinate + // here instead. + polygonDrawer.justClicked = false + polygonPath.push(clickCoordinate) + } else { + // The onPositionChanged handler for mouse movement will have already added the coordinate to the array. + // Just update it to the final position + polygonPath[polygonDrawerPolygon.path.length - 1] = clickCoordinate + } } polygonDrawerPolygonSet.path = polygonPath polygonDrawerPolygon.path = polygonPath @@ -360,10 +369,15 @@ Map { } onPositionChanged: { + if (ScreenTools.isMobile) { + // We don't track mouse drag on mobile + return + } if (polygonDrawerPolygon.path.length) { var dragCoordinate = _map.toCoordinate(Qt.point(mouse.x, mouse.y)) var polygonPath = polygonDrawerPolygon.path if (polygonDrawer.justClicked){ + // Add new drag coordinate polygonPath.push(dragCoordinate) polygonDrawer.justClicked = false } -- GitLab From a210d8d4f66c8d7e0bc45d471117b09f6a4eaf2e Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Sat, 19 Nov 2016 08:43:28 +0100 Subject: [PATCH 045/398] casting to unsigned chars --- src/AnalyzeView/ExifParser.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/AnalyzeView/ExifParser.cc b/src/AnalyzeView/ExifParser.cc index 4ef943e3b..4e4087ffa 100644 --- a/src/AnalyzeView/ExifParser.cc +++ b/src/AnalyzeView/ExifParser.cc @@ -15,8 +15,8 @@ ExifParser::~ExifParser() double ExifParser::readTime(QByteArray& buf) { - char tiffHeader[] = {static_cast(0x49),static_cast(0x49),static_cast(0x2A),static_cast(0x00)}; - char createDateHeader[] = {static_cast(0x04),static_cast(0x90),static_cast(0x02),static_cast(0x00)}; + QByteArray tiffHeader("\x49\x49\x2A", 3); + QByteArray createDateHeader("\x04\x90\x02", 3); // find header position uint32_t tiffHeaderIndex = buf.indexOf(tiffHeader); @@ -58,14 +58,12 @@ double ExifParser::readTime(QByteArray& buf) bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) { - QByteArray app1Header; - app1Header.append(0xff); - app1Header.append(0xe1); + QByteArray app1Header("\xff\xe1", 2); uint32_t app1HeaderInd = buf.indexOf(app1Header); uint16_t *conversionPointer = reinterpret_cast(buf.mid(app1HeaderInd + 2, 2).data()); uint16_t app1Size = *conversionPointer; uint16_t app1SizeEndian = qFromBigEndian(app1Size) + 0xa5; // change wrong endian - char tiffHeader[4] = {0x49, 0x49, 0x2A, 0x00}; + QByteArray tiffHeader("\x49\x49\x2A", 3); uint32_t tiffHeaderInd = buf.indexOf(tiffHeader); conversionPointer = reinterpret_cast(buf.mid(tiffHeaderInd + 8, 2).data()); uint16_t numberOfTiffFields = *conversionPointer; @@ -125,7 +123,11 @@ bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) gpsIFDInd.i = nextIfdOffset; // this will stay constant - char gpsInfo[12] = {static_cast(0x25), static_cast(0x88), static_cast(0x04), static_cast(0x00), static_cast(0x01), static_cast(0x00), static_cast(0x00), static_cast(0x00), static_cast(gpsIFDInd.c[0]), static_cast(gpsIFDInd.c[1]), static_cast(gpsIFDInd.c[2]), static_cast(gpsIFDInd.c[3])}; + QByteArray gpsInfo("\x25\x88\x04\x00\x01\x00\x00\x00", 8); + gpsInfo.append(gpsIFDInd.c[0]); + gpsInfo.append(gpsIFDInd.c[1]); + gpsInfo.append(gpsIFDInd.c[2]); + gpsInfo.append(gpsIFDInd.c[3]); // filling values to gpsData uint32_t gpsDataExtInd = gpsIFDInd.i + 2 + sizeof(fields_s); -- GitLab From 498056eb5b6c212e925d779e505371c75d1d817a Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Tue, 1 Nov 2016 16:47:43 +0100 Subject: [PATCH 046/398] open file and directory dialogs in foreground Conflicts: src/MissionManager/GeoFenceController.cc --- src/AnalyzeView/GeoTagController.cc | 7 ++++--- src/MissionManager/GeoFenceController.cc | 5 +++-- src/MissionManager/MissionController.cc | 5 +++-- src/QmlControls/ParameterEditorController.cc | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index 5527888a5..9c49c4f59 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -11,6 +11,7 @@ #include "ExifParser.h" #include "QGCFileDialog.h" #include "QGCLoggingCategory.h" +#include "MainWindow.h" #include #include #include @@ -34,7 +35,7 @@ GeoTagController::~GeoTagController() void GeoTagController::pickLogFile(void) { - QString filename = QGCFileDialog::getOpenFileName(NULL, "Select log file load", QString(), "PX4 log file (*.px4log);;All Files (*.*)"); + QString filename = QGCFileDialog::getOpenFileName(MainWindow::instance(), "Select log file load", QString(), "PX4 log file (*.px4log);;All Files (*.*)"); if (!filename.isEmpty()) { _worker.setLogFile(filename); emit logFileChanged(filename); @@ -43,7 +44,7 @@ void GeoTagController::pickLogFile(void) void GeoTagController::pickImageDirectory(void) { - QString dir = QGCFileDialog::getExistingDirectory(NULL, "Select image directory"); + QString dir = QGCFileDialog::getExistingDirectory(MainWindow::instance(), "Select image directory"); if (!dir.isEmpty()) { _worker.setImageDirectory(dir); emit imageDirectoryChanged(dir); @@ -52,7 +53,7 @@ void GeoTagController::pickImageDirectory(void) void GeoTagController::pickSaveDirectory(void) { - QString dir = QGCFileDialog::getExistingDirectory(NULL, "Select save directory"); + QString dir = QGCFileDialog::getExistingDirectory(MainWindow::instance(), "Select save directory"); if (!dir.isEmpty()) { _worker.setSaveDirectory(dir); emit saveDirectoryChanged(dir); diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index d331c40e8..fc20278c6 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -20,6 +20,7 @@ #include "JsonHelper.h" #ifndef __mobile__ +#include "MainWindow.h" #include "QGCFileDialog.h" #endif @@ -245,7 +246,7 @@ void GeoFenceController::loadFromFile(const QString& filename) void GeoFenceController::loadFromFilePicker(void) { #ifndef __mobile__ - QString filename = QGCFileDialog::getOpenFileName(NULL, "Select GeoFence File to load", QString(), "Fence file (*.fence);;All Files (*.*)"); + QString filename = QGCFileDialog::getOpenFileName(MainWindow::instance(), "Select GeoFence File to load", QString(), "Fence file (*.fence);;All Files (*.*)"); if (filename.isEmpty()) { return; @@ -308,7 +309,7 @@ void GeoFenceController::saveToFile(const QString& filename) void GeoFenceController::saveToFilePicker(void) { #ifndef __mobile__ - QString filename = QGCFileDialog::getSaveFileName(NULL, "Select file to save GeoFence to", QString(), "Fence file (*.fence);;All Files (*.*)"); + QString filename = QGCFileDialog::getSaveFileName(MainWindow::instance(), "Select file to save GeoFence to", QString(), "Fence file (*.fence);;All Files (*.*)"); if (filename.isEmpty()) { return; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index b6178a6e1..1e19e2e89 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -21,6 +21,7 @@ #include "QGroundControlQmlGlobal.h" #ifndef __mobile__ +#include "MainWindow.h" #include "QGCFileDialog.h" #endif @@ -469,7 +470,7 @@ void MissionController::loadFromFile(const QString& filename) void MissionController::loadFromFilePicker(void) { #ifndef __mobile__ - QString filename = QGCFileDialog::getOpenFileName(NULL, "Select Mission File to load", QString(), "Mission file (*.mission);;All Files (*.*)"); + QString filename = QGCFileDialog::getOpenFileName(MainWindow::instance(), "Select Mission File to load", QString(), "Mission file (*.mission);;All Files (*.*)"); if (filename.isEmpty()) { return; @@ -553,7 +554,7 @@ void MissionController::saveToFile(const QString& filename) void MissionController::saveToFilePicker(void) { #ifndef __mobile__ - QString filename = QGCFileDialog::getSaveFileName(NULL, "Select file to save mission to", QString(), "Mission file (*.mission);;All Files (*.*)"); + QString filename = QGCFileDialog::getSaveFileName(MainWindow::instance(), "Select file to save mission to", QString(), "Mission file (*.mission);;All Files (*.*)"); if (filename.isEmpty()) { return; diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc index 16eafae33..b9b1de3db 100644 --- a/src/QmlControls/ParameterEditorController.cc +++ b/src/QmlControls/ParameterEditorController.cc @@ -113,7 +113,7 @@ void ParameterEditorController::saveToFile(const QString& filename) void ParameterEditorController::saveToFilePicker(void) { #ifndef __mobile__ - QString fileName = QGCFileDialog::getSaveFileName(NULL, + QString fileName = QGCFileDialog::getSaveFileName(MainWindow::instance(), "Save Parameters", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "Parameter Files (*.params)", @@ -153,7 +153,7 @@ void ParameterEditorController::loadFromFile(const QString& filename) void ParameterEditorController::loadFromFilePicker(void) { #ifndef __mobile__ - QString fileName = QGCFileDialog::getOpenFileName(NULL, + QString fileName = QGCFileDialog::getOpenFileName(MainWindow::instance(), "Load Parameters", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "Parameter Files (*.params);;All Files (*)"); -- GitLab From cb2892bc88868113f90ae4881e03f9049fd09834 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 22 Nov 2016 17:16:47 -0800 Subject: [PATCH 047/398] One heartbeat per-link, not per-vehicle --- src/Vehicle/MultiVehicleManager.cc | 35 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 139955c62..79d335309 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -303,20 +303,27 @@ void MultiVehicleManager::setGcsHeartbeatEnabled(bool gcsHeartBeatEnabled) void MultiVehicleManager::_sendGCSHeartbeat(void) { - for (int i=0; i< _vehicles.count(); i++) { - Vehicle* vehicle = qobject_cast(_vehicles[i]); - - mavlink_message_t message; - mavlink_msg_heartbeat_pack_chan(_mavlinkProtocol->getSystemId(), - _mavlinkProtocol->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &message, - MAV_TYPE_GCS, // MAV_TYPE - MAV_AUTOPILOT_INVALID, // MAV_AUTOPILOT - MAV_MODE_MANUAL_ARMED, // MAV_MODE - 0, // custom mode - MAV_STATE_ACTIVE); // MAV_STATE - vehicle->sendMessageOnLink(vehicle->priorityLink(), message); + // Send a heartbeat out on each link + QmlObjectListModel* links = _toolbox->linkManager()->links(); + for (int i=0; icount(); i++) { + LinkInterface* link = links->value(i); + if (link->isConnected()) { + mavlink_message_t message; + mavlink_msg_heartbeat_pack_chan(_mavlinkProtocol->getSystemId(), + _mavlinkProtocol->getComponentId(), + link->mavlinkChannel(), + &message, + MAV_TYPE_GCS, // MAV_TYPE + MAV_AUTOPILOT_INVALID, // MAV_AUTOPILOT + MAV_MODE_MANUAL_ARMED, // MAV_MODE + 0, // custom mode + MAV_STATE_ACTIVE); // MAV_STATE + + uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; + int len = mavlink_msg_to_send_buffer(buffer, &message); + + link->writeBytesSafe((const char*)buffer, len); + } } } -- GitLab From da5971a540078c7ea54f450f681d9b9123c4712a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 23 Nov 2016 15:41:34 -0800 Subject: [PATCH 048/398] Better logging, plus some retry fixes --- src/FactSystem/ParameterManager.cc | 200 +++++++++++++++++------------ src/FactSystem/ParameterManager.h | 17 ++- 2 files changed, 127 insertions(+), 90 deletions(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index f286e54eb..e27729f3f 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -32,7 +32,8 @@ typedef QPair ParamTypeVal; typedef QPair NamedParam; typedef QMap MapID2NamedParam; -QGC_LOGGING_CATEGORY(ParameterManagerVerboseLog, "ParameterManagerVerboseLog") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "ParameterManagerVerbose1Log") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "ParameterManagerVerbose2Log") Fact ParameterManager::_defaultFact; @@ -58,6 +59,7 @@ ParameterManager::ParameterManager(Vehicle* vehicle) , _prevWaitingReadParamNameCount(0) , _prevWaitingWriteParamNameCount(0) , _initialRequestRetryCount(0) + , _disableAllRetries(false) , _totalParamCount(0) { _versionParam = vehicle->firmwarePlugin()->getVersionParam(); @@ -69,21 +71,18 @@ ParameterManager::ParameterManager(Vehicle* vehicle) _mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - // We signal this to ouselves in order to start timer on our thread - connect(this, &ParameterManager::restartWaitingParamTimer, this, &ParameterManager::_restartWaitingParamTimer); - _initialRequestTimeoutTimer.setSingleShot(true); - _initialRequestTimeoutTimer.setInterval(6000); + _initialRequestTimeoutTimer.setInterval(20000); connect(&_initialRequestTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_initialRequestTimeout); _waitingParamTimeoutTimer.setSingleShot(true); - _waitingParamTimeoutTimer.setInterval(1000); + _waitingParamTimeoutTimer.setInterval(3000); connect(&_waitingParamTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_waitingParamTimeout); connect(_vehicle->uas(), &UASInterface::parameterUpdate, this, &ParameterManager::_parameterUpdate); _defaultComponentIdParam = vehicle->firmwarePlugin()->getDefaultComponentIdParam(); - qCDebug(ParameterManagerLog) << "Default component param" << _defaultComponentIdParam; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Default component param" << _defaultComponentIdParam; // Ensure the cache directory exists QFileInfo(QSettings().fileName()).dir().mkdir("ParamCache"); @@ -103,28 +102,16 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString return; } - _initialRequestTimeoutTimer.stop(); + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << + "_parameterUpdate" << + "name:" << parameterName << + "count:" << parameterCount << + "index:" << parameterId << + "mavType:" << mavType << + "value:" << value << + ")"; - if (_initialLoadComplete) { - qCDebug(ParameterManagerLog) << "_parameterUpdate (id:" << vehicleId << - "componentId:" << componentId << - "name:" << parameterName << - "count:" << parameterCount << - "index:" << parameterId << - "mavType:" << mavType << - "value:" << value << - ")"; - } else { - // This is too noisy during initial load - qCDebug(ParameterManagerVerboseLog) << "_parameterUpdate (id:" << vehicleId << - "componentId:" << componentId << - "name:" << parameterName << - "count:" << parameterCount << - "index:" << parameterId << - "mavType:" << mavType << - "value:" << value << - ")"; - } + _initialRequestTimeoutTimer.stop(); #if 0 // Handy for testing retry logic @@ -147,6 +134,14 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _tryCacheHashLoad(vehicleId, componentId, value); return; } + + if (parameterId >= parameterCount) { + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Discarding bogus update name:id:count" << parameterName << parameterId << parameterCount; + return; + } + + _initialRequestTimeoutTimer.stop(); + _dataMutex.lock(); // Update our total parameter counts @@ -169,12 +164,12 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _waitingReadParamNameMap[componentId] = QMap(); _waitingWriteParamNameMap[componentId] = QMap(); - qCDebug(ParameterManagerLog) << "Seeing component for first time, id:" << componentId << "parameter count:" << parameterCount; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Seeing component for first time - paramcount:" << parameterCount; } // Determine default component id if (!_defaultComponentIdParam.isEmpty() && _defaultComponentIdParam == parameterName) { - qCDebug(ParameterManagerLog) << "Default component id determined" << componentId; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Default component id determined"; _defaultComponentId = componentId; } @@ -189,16 +184,25 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _waitingWriteParamNameMap[componentId].contains(parameterName)) { // We were waiting for this parameter, restart wait timer. Otherwise it is a spurious parameter update which // means we should not reset the wait timer. + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer (valid param received)"; _waitingParamTimeoutTimer.start(); + } else { + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Unrequested param update" << parameterName; } // Remove this parameter from the waiting lists _waitingReadParamIndexMap[componentId].remove(parameterId); _waitingReadParamNameMap[componentId].remove(parameterName); _waitingWriteParamNameMap[componentId].remove(parameterName); - qCDebug(ParameterManagerVerboseLog) << "_waitingReadParamIndexMap:" << _waitingReadParamIndexMap[componentId]; - qCDebug(ParameterManagerLog) << "_waitingReadParamNameMap" << _waitingReadParamNameMap[componentId]; - qCDebug(ParameterManagerLog) << "_waitingWriteParamNameMap" << _waitingWriteParamNameMap[componentId]; + if (_waitingReadParamIndexMap[componentId].count()) { + qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamIndexMap:" << _waitingReadParamIndexMap[componentId]; + } + if (_waitingReadParamNameMap[componentId].count()) { + qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamNameMap" << _waitingReadParamNameMap[componentId]; + } + if (_waitingWriteParamNameMap[componentId].count()) { + qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingWriteParamNameMap" << _waitingWriteParamNameMap[componentId]; + } // Track how many parameters we are still waiting for @@ -210,31 +214,31 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString waitingReadParamIndexCount += _waitingReadParamIndexMap[waitingComponentId].count(); } if (waitingReadParamIndexCount) { - qCDebug(ParameterManagerLog) << "waitingReadParamIndexCount:" << waitingReadParamIndexCount; + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamIndexCount:" << waitingReadParamIndexCount; } - foreach(int waitingComponentId, _waitingReadParamNameMap.keys()) { waitingReadParamNameCount += _waitingReadParamNameMap[waitingComponentId].count(); } if (waitingReadParamNameCount) { - qCDebug(ParameterManagerLog) << "waitingReadParamNameCount:" << waitingReadParamNameCount; + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamNameCount:" << waitingReadParamNameCount; } foreach(int waitingComponentId, _waitingWriteParamNameMap.keys()) { waitingWriteParamNameCount += _waitingWriteParamNameMap[waitingComponentId].count(); } if (waitingWriteParamNameCount) { - qCDebug(ParameterManagerLog) << "waitingWriteParamNameCount:" << waitingWriteParamNameCount; + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingWriteParamNameCount:" << waitingWriteParamNameCount; } int readWaitingParamCount = waitingReadParamIndexCount + waitingReadParamNameCount; int totalWaitingParamCount = readWaitingParamCount + waitingWriteParamNameCount; if (totalWaitingParamCount) { - qCDebug(ParameterManagerLog) << "totalWaitingParamCount:" << totalWaitingParamCount; + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "totalWaitingParamCount:" << totalWaitingParamCount; } else if (_defaultComponentId != MAV_COMP_ID_ALL) { // No more parameters to wait for, stop the timeout. Be careful to not stop timer if we don't have the default // component yet. + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Stopping _waitingParamTimeoutTimer (all requests satisfied)"; _waitingParamTimeoutTimer.stop(); } @@ -255,7 +259,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString } if (!_mapParameterName2Variant.contains(componentId) || !_mapParameterName2Variant[componentId].contains(parameterName)) { - qCDebug(ParameterManagerLog) << "Adding new fact"; + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "Adding new fact" << parameterName; FactMetaData::ValueType_t factType; switch (mavType) { @@ -339,6 +343,8 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString // Don't fail initial load complete if default component isn't found yet. That will be handled in wait timeout check. _checkInitialLoadComplete(false /* failIfNoDefaultComponent */); + + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "_parameterUpdate complete"; } /// Connected to Fact::valueUpdated @@ -363,14 +369,14 @@ void ParameterManager::_valueUpdated(const QVariant& value) _dataMutex.unlock(); _writeParameterRaw(componentId, fact->name(), value); - qCDebug(ParameterManagerLog) << "Set parameter (componentId:" << componentId << "name:" << name << value << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Set parameter - name:" << name << value << "(_waitingParamTimeoutTimer started)"; if (fact->rebootRequired() && !qgcApp()->runningUnitTests()) { qgcApp()->showMessage(QStringLiteral("Change of parameter %1 requires a Vehicle reboot to take effect").arg(name)); } } -void ParameterManager::refreshAllParameters(uint8_t componentID) +void ParameterManager::refreshAllParameters(uint8_t componentId) { _dataMutex.lock(); @@ -381,7 +387,7 @@ void ParameterManager::refreshAllParameters(uint8_t componentID) // Reset index wait lists foreach (int cid, _paramCountMap.keys()) { // Add/Update all indices to the wait list, parameter index is 0-based - if(componentID != MAV_COMP_ID_ALL && componentID != cid) + if(componentId != MAV_COMP_ID_ALL && componentId != cid) continue; for (int waitingIndex = 0; waitingIndex < _paramCountMap[cid]; waitingIndex++) { // This will add a new waiting index if needed and set the retry count for that index to 0 @@ -400,11 +406,11 @@ void ParameterManager::refreshAllParameters(uint8_t componentID) _vehicle->priorityLink()->mavlinkChannel(), &msg, _vehicle->id(), - componentID); + componentId); _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); - QString what = (componentID == MAV_COMP_ID_ALL) ? "MAV_COMP_ID_ALL" : QString::number(componentID); - qCDebug(ParameterManagerLog) << "Request to refresh all parameters for component ID:" << what; + QString what = (componentId == MAV_COMP_ID_ALL) ? "MAV_COMP_ID_ALL" : QString::number(componentId); + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Request to refresh all parameters for component ID:" << what; } void ParameterManager::_determineDefaultComponentId(void) @@ -424,7 +430,7 @@ void ParameterManager::_determineDefaultComponentId(void) } if (_defaultComponentId == MAV_COMP_ID_ALL) { - qWarning() << "All parameters missing, unable to determine default componet id"; + qWarning() << _logVehiclePrefix() << "All parameters missing, unable to determine default component id"; } } } @@ -435,7 +441,7 @@ int ParameterManager::_actualComponentId(int componentId) if (componentId == FactSystem::defaultComponentId) { componentId = _defaultComponentId; if (componentId == FactSystem::defaultComponentId) { - qWarning() << "Default component id not set"; + qWarning() << _logVehiclePrefix() << "Default component id not set"; } } @@ -445,7 +451,7 @@ int ParameterManager::_actualComponentId(int componentId) void ParameterManager::refreshParameter(int componentId, const QString& name) { componentId = _actualComponentId(componentId); - qCDebug(ParameterManagerLog) << "refreshParameter (component id:" << componentId << "name:" << name << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParameter - name:" << name << ")"; _dataMutex.lock(); @@ -456,7 +462,8 @@ void ParameterManager::refreshParameter(int componentId, const QString& name) _waitingReadParamNameMap[componentId].remove(mappedParamName); // Remove old wait entry if there _waitingReadParamNameMap[componentId][mappedParamName] = 0; // Add new wait entry and update retry count - emit restartWaitingParamTimer(); + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "restarting _waitingParamTimeout"; + _waitingParamTimeoutTimer.start(); } _dataMutex.unlock(); @@ -467,7 +474,7 @@ void ParameterManager::refreshParameter(int componentId, const QString& name) void ParameterManager::refreshParametersPrefix(int componentId, const QString& namePrefix) { componentId = _actualComponentId(componentId); - qCDebug(ParameterManagerLog) << "refreshParametersPrefix (component id:" << componentId << "name:" << namePrefix << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParametersPrefix - name:" << namePrefix << ")"; foreach(const QString &name, _mapParameterName2Variant[componentId].keys()) { if (name.startsWith(namePrefix)) { @@ -536,25 +543,32 @@ void ParameterManager::_waitingParamTimeout(void) const int maxBatchSize = 10; int batchCount = 0; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_waitingParamTimeout"; + // First check for any missing parameters from the initial index based load + batchCount = 0; foreach(int componentId, _waitingReadParamIndexMap.keys()) { - foreach(int paramIndex, _waitingReadParamIndexMap[componentId].keys()) { + if (_waitingReadParamIndexMap[componentId].count()) { + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_waitingReadParamIndexMap" << _waitingReadParamIndexMap[componentId]; + } + + foreach(int paramIndex, _waitingReadParamIndexMap[componentId].keys()) { + if (++batchCount > maxBatchSize) { + goto Out; + } + _waitingReadParamIndexMap[componentId][paramIndex]++; // Bump retry count - if (_waitingReadParamIndexMap[componentId][paramIndex] > _maxInitialLoadRetrySingleParam) { + if (_disableAllRetries || _waitingReadParamIndexMap[componentId][paramIndex] > _maxInitialLoadRetrySingleParam) { // Give up on this index _failedReadParamIndexMap[componentId] << paramIndex; - qCDebug(ParameterManagerLog) << "Giving up on (componentId:" << componentId << "paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Giving up on (paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")"; _waitingReadParamIndexMap[componentId].remove(paramIndex); } else { // Retry again paramsRequested = true; _readParameterRaw(componentId, "", paramIndex); - qCDebug(ParameterManagerLog) << "Read re-request for (componentId:" << componentId << "paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")"; - - if (++batchCount > maxBatchSize) { - goto Out; - } + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")"; } } } @@ -562,6 +576,7 @@ void ParameterManager::_waitingParamTimeout(void) if (!paramsRequested && _defaultComponentId == MAV_COMP_ID_ALL && !_waitingForDefaultComponent) { // Initial load is complete but we still don't have default component params. Wait one more cycle to see if the // default component finally shows up. + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer - still don't have default component id"; _waitingParamTimeoutTimer.start(); _waitingForDefaultComponent = true; return; @@ -578,14 +593,16 @@ void ParameterManager::_waitingParamTimeout(void) _waitingWriteParamNameMap[componentId][paramName]++; // Bump retry count if (_waitingWriteParamNameMap[componentId][paramName] <= _maxReadWriteRetry) { _writeParameterRaw(componentId, paramName, getParameter(componentId, paramName)->rawValue()); - qCDebug(ParameterManagerLog) << "Write resend for (componentId:" << componentId << "paramName:" << paramName << "retryCount:" << _waitingWriteParamNameMap[componentId][paramName] << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Write resend for (paramName:" << paramName << "retryCount:" << _waitingWriteParamNameMap[componentId][paramName] << ")"; if (++batchCount > maxBatchSize) { goto Out; } } else { // Exceeded max retry count, notify user _waitingWriteParamNameMap[componentId].remove(paramName); - qgcApp()->showMessage(tr("Parameter write failed: comp:%1 param:%2").arg(componentId).arg(paramName)); + QString errorMsg = tr("Parameter write failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); } } } @@ -598,14 +615,16 @@ void ParameterManager::_waitingParamTimeout(void) _waitingReadParamNameMap[componentId][paramName]++; // Bump retry count if (_waitingReadParamNameMap[componentId][paramName] <= _maxReadWriteRetry) { _readParameterRaw(componentId, paramName, -1); - qCDebug(ParameterManagerLog) << "Read re-request for (componentId:" << componentId << "paramName:" << paramName << "retryCount:" << _waitingReadParamNameMap[componentId][paramName] << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramName:" << paramName << "retryCount:" << _waitingReadParamNameMap[componentId][paramName] << ")"; if (++batchCount > maxBatchSize) { goto Out; } } else { // Exceeded max retry count, notify user _waitingReadParamNameMap[componentId].remove(paramName); - qgcApp()->showMessage(tr("Parameter read failed: comp:%1 param:%2").arg(componentId).arg(paramName)); + QString errorMsg = tr("Parameter read failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); } } } @@ -613,6 +632,7 @@ void ParameterManager::_waitingParamTimeout(void) Out: if (paramsRequested) { + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer - re-request"; _waitingParamTimeoutTimer.start(); } } @@ -809,9 +829,9 @@ void ParameterManager::_saveToEEPROM(void) 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 1, -1, -1, -1, 0, 0, 0); _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); - qCDebug(ParameterManagerLog) << "_saveToEEPROM"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM"; } else { - qCDebug(ParameterManagerLog) << "_saveToEEPROM skipped due to FirmwarePlugin::isCapable"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM skipped due to FirmwarePlugin::isCapable"; } } } @@ -941,11 +961,6 @@ FactMetaData::ValueType_t ParameterManager::_mavTypeToFactType(MAV_PARAM_TYPE ma } } -void ParameterManager::_restartWaitingParamTimer(void) -{ - _waitingParamTimeoutTimer.start(); -} - void ParameterManager::_addMetaDataToDefaultComponent(void) { if (_defaultComponentId == MAV_COMP_ID_ALL) { @@ -1002,6 +1017,8 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) // We aren't waiting for any more initial parameter updates, initial parameter loading is complete _initialLoadComplete = true; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Initial load complete"; + // Check for index based load failures QString indexList; bool initialLoadFailures = false; @@ -1012,29 +1029,33 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) } indexList += QString("%1").arg(paramIndex); initialLoadFailures = true; - qCDebug(ParameterManagerLog) << "Gave up on initial load after max retries (componentId:" << componentId << "paramIndex:" << paramIndex << ")"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Gave up on initial load after max retries (paramIndex:" << paramIndex << ")"; } } _missingParameters = false; if (initialLoadFailures) { _missingParameters = true; - qgcApp()->showMessage("QGroundControl was unable to retrieve the full set of parameters from the vehicle. " + QString errorMsg = tr("QGroundControl was unable to retrieve the full set of parameters from vehicle %1. " "This will cause QGroundControl to be unable to display its full user interface. " "If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. " - "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue."); + "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue.").arg(_vehicle->id()); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); if (!qgcApp()->runningUnitTests()) { - qCWarning(ParameterManagerLog) << "The following parameter indices could not be loaded after the maximum number of retries: " << indexList; + qCWarning(ParameterManagerLog) << _logVehiclePrefix() << "The following parameter indices could not be loaded after the maximum number of retries: " << indexList; } } else if (_defaultComponentId == FactSystem::defaultComponentId && !_defaultComponentIdParam.isEmpty()) { // Missing default component when we should have one _missingParameters = true; - qgcApp()->showMessage("QGroundControl did not receive parameters from the default component. " + QString errorMsg = tr("QGroundControl did not receive parameters from the default component for vehicle %1. " "This will cause QGroundControl to be unable to display its full user interface. " "If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. " - "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue."); + "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue.").arg(_vehicle->id()); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); if (!qgcApp()->runningUnitTests()) { - qCWarning(ParameterManagerLog) << "Default component was never found, param:" << _defaultComponentIdParam; + qCWarning(ParameterManagerLog) << _logVehiclePrefix() << "Default component was never found, param:" << _defaultComponentIdParam; } } @@ -1048,13 +1069,22 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) void ParameterManager::_initialRequestTimeout(void) { - if (!_vehicle->genericFirmware()) { - // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user - qgcApp()->showMessage("Vehicle did not respond to request for parameters, retrying"); - } - if (++_initialRequestRetryCount <= _maxInitialRequestListRetry) { + if (!_disableAllRetries && ++_initialRequestRetryCount <= _maxInitialRequestListRetry) { + if (!_vehicle->genericFirmware()) { + // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user + QString errorMsg = tr("Vehicle %1 did not respond to request for parameters, retrying").arg(_vehicle->id()); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); + } refreshAllParameters(); _initialRequestTimeoutTimer.start(); + } else { + if (!_vehicle->genericFirmware()) { + // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user + QString errorMsg = tr("Vehicle %1 did not respond to request for parameters, failing after maximum number of retries").arg(_vehicle->id()); + qCDebug(ParameterManagerLog) << errorMsg; + qgcApp()->showMessage(errorMsg); + } } } @@ -1444,3 +1474,11 @@ void ParameterManager::resetAllParametersToDefaults(void) _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); } +QString ParameterManager::_logVehiclePrefix(int componentId) +{ + if (componentId == -1) { + return QString("V:%1").arg(_vehicle->id()); + } else { + return QString("V:%1 C:%2").arg(_vehicle->id()).arg(componentId); + } +} diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 2f46da4b4..dc91aad2d 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -28,7 +28,8 @@ /// @file /// @author Don Gagne -Q_DECLARE_LOGGING_CATEGORY(ParameterManagerVerboseLog) +Q_DECLARE_LOGGING_CATEGORY(ParameterManagerVerbose1Log) +Q_DECLARE_LOGGING_CATEGORY(ParameterManagerVerbose2Log) /// Connects to Parameter Manager to load/update Facts class ParameterManager : public QObject @@ -123,9 +124,6 @@ signals: /// Signalled to update progress of full parameter list request void parameterListProgress(float value); - - /// Signalled to ourselves in order to get call on our own thread - void restartWaitingParamTimer(void); protected: Vehicle* _vehicle; @@ -133,7 +131,6 @@ protected: void _parameterUpdate(int vehicleId, int componentId, QString parameterName, int parameterCount, int parameterId, int mavType, QVariant value); void _valueUpdated(const QVariant& value); - void _restartWaitingParamTimer(void); void _waitingParamTimeout(void); void _tryCacheLookup(void); void _initialRequestTimeout(void); @@ -150,6 +147,7 @@ private: void _addMetaDataToDefaultComponent(void); QString _remapParamNameToVersion(const QString& paramName); void _loadOfflineEditingParams(void); + QString _logVehiclePrefix(int componentId = -1); MAV_PARAM_TYPE _factTypeToMavType(FactMetaData::ValueType_t factType); FactMetaData::ValueType_t _mavTypeToFactType(MAV_PARAM_TYPE mavType); @@ -183,10 +181,11 @@ private: int _prevWaitingWriteParamNameCount; - static const int _maxInitialRequestListRetry = 4; ///< Maximum retries for request list - int _initialRequestRetryCount; ///< Current retry count for request list - static const int _maxInitialLoadRetrySingleParam = 10; ///< Maximum retries for initial index based load of a single param - static const int _maxReadWriteRetry = 5; ///< Maximum retries read/write + static const int _maxInitialRequestListRetry = 4; ///< Maximum retries for request list + int _initialRequestRetryCount; ///< Current retry count for request list + static const int _maxInitialLoadRetrySingleParam = 5; ///< Maximum retries for initial index based load of a single param + static const int _maxReadWriteRetry = 5; ///< Maximum retries read/write + bool _disableAllRetries; ///< true: Don't retry any requests (used for testing) QMap _paramCountMap; ///< Key: Component id, Value: count of parameters in this component QMap > _waitingReadParamIndexMap; ///< Key: Component id, Value: Map { Key: parameter index still waiting for, Value: retry count } -- GitLab From 1b29384e1910af9d8941644bde507b22f099f2ff Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 23 Nov 2016 16:38:49 -0800 Subject: [PATCH 049/398] RallyPoint support should be optional --- src/FirmwarePlugin/APM/APMRallyPointManager.cc | 9 +++++++-- src/FirmwarePlugin/APM/APMRallyPointManager.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/FirmwarePlugin/APM/APMRallyPointManager.cc b/src/FirmwarePlugin/APM/APMRallyPointManager.cc index 76eb2a876..f25aaf3cc 100644 --- a/src/FirmwarePlugin/APM/APMRallyPointManager.cc +++ b/src/FirmwarePlugin/APM/APMRallyPointManager.cc @@ -31,7 +31,7 @@ APMRallyPointManager::~APMRallyPointManager() void APMRallyPointManager::sendToVehicle(const QList& rgPoints) { - if (_vehicle->isOfflineEditingVehicle()) { + if (_vehicle->isOfflineEditingVehicle() || !rallyPointsSupported()) { return; } @@ -53,7 +53,7 @@ void APMRallyPointManager::sendToVehicle(const QList& rgPoints) void APMRallyPointManager::loadFromVehicle(void) { - if (_vehicle->isOfflineEditingVehicle() || _readTransactionInProgress) { + if (_vehicle->isOfflineEditingVehicle() || !rallyPointsSupported() || _readTransactionInProgress) { return; } @@ -144,3 +144,8 @@ bool APMRallyPointManager::inProgress(void) const { return _readTransactionInProgress || _writeTransactionInProgress; } + +bool APMRallyPointManager::rallyPointsSupported(void) const +{ + return _vehicle->parameterManager()->parameterExists(_vehicle->defaultComponentId(), _rallyTotalParam); +} diff --git a/src/FirmwarePlugin/APM/APMRallyPointManager.h b/src/FirmwarePlugin/APM/APMRallyPointManager.h index 80dc758c4..4a5ac2067 100644 --- a/src/FirmwarePlugin/APM/APMRallyPointManager.h +++ b/src/FirmwarePlugin/APM/APMRallyPointManager.h @@ -25,7 +25,7 @@ public: bool inProgress (void) const final; void loadFromVehicle (void) final; void sendToVehicle (const QList& rgPoints) final; - bool rallyPointsSupported (void) const final { return true; } + bool rallyPointsSupported (void) const final; QString editorQml(void) const final { return QStringLiteral("qrc:/FirmwarePlugin/APM/APMRallyPointEditor.qml"); } -- GitLab From 5505a66e259b5bb11122af4b94c337bd701e441f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 23 Nov 2016 17:07:36 -0800 Subject: [PATCH 050/398] Don't change active vehicle for additional vehicles --- src/Vehicle/MultiVehicleManager.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 79d335309..53ea7a1ae 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -109,7 +109,11 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle emit vehicleAdded(vehicle); - setActiveVehicle(vehicle); + if (_vehicles.count() > 1) { + qgcApp()->showMessage(tr("Connected to Vehicle %1").arg(vehicleId)); + } else { + setActiveVehicle(vehicle); + } // Mark link as active link->setActive(true); -- GitLab From 9a30008fcb760e3eb17a3140ac98c518a4bdfcac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beat=20K=C3=BCng?= Date: Thu, 24 Nov 2016 10:03:55 +0100 Subject: [PATCH 051/398] MAVLinkLogManager: avoid returning too early, use mavlink_logging_data_acked_t message The return lead to data being dropped. Some of this data could have been part of the ulog header, which lead to corrupt files. --- src/Vehicle/MAVLinkLogManager.cc | 1 - src/Vehicle/Vehicle.cc | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index ea153d396..72e2d6e6e 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -223,7 +223,6 @@ MAVLinkLogProcessor::_writeUlogMessage(QByteArray& data) break; _writeData(data.data(), message_length); data.remove(0, message_length); - return data; } return data; } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index a3eaa1de6..11572f8c1 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2090,8 +2090,8 @@ Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) { - mavlink_logging_data_t log; - mavlink_msg_logging_data_decode(&message, &log); + mavlink_logging_data_acked_t log; + mavlink_msg_logging_data_acked_decode(&message, &log); _ackMavlinkLogData(log.sequence); emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, log.first_message_offset, QByteArray((const char*)log.data, log.length), true); -- GitLab From 7fed3dcc6146558dca0f4778486211bb91e49c2d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 12:17:33 -0800 Subject: [PATCH 052/398] removeAll call before prompt --- src/MissionEditor/MissionEditor.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 007234bec..9641df47a 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -1191,7 +1191,6 @@ QGCView { Layout.fillWidth: true onClicked: { syncButton.hideDropDown() - _syncDropDownController.removeAll() qgcView.showDialog(removeAllPromptDialog, qsTr("Remove all"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.No) } } -- GitLab From 7cdaddd333e95fe3a0fbd040535f84a94b8a77f2 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 12:17:45 -0800 Subject: [PATCH 053/398] Fix positioning --- src/QmlControls/DropButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QmlControls/DropButton.qml b/src/QmlControls/DropButton.qml index f7eae40a5..956115e60 100644 --- a/src/QmlControls/DropButton.qml +++ b/src/QmlControls/DropButton.qml @@ -111,7 +111,7 @@ Item { dropItemHolderRect.y = 0 if (dropDirection == dropLeft) { - dropDownItem.x = dropDownItem.width + _dropMargin + dropDownItem.x = -(dropDownItem.width + _dropMargin) dropItemHolderRect.x = 0 } else { -- GitLab From cbc37c9e7529b64877f5bf132d4249c942b57bd9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 12:29:50 -0800 Subject: [PATCH 054/398] Tone down opacity so you can see what is behind when n/a --- src/FlightMap/Widgets/VibrationWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightMap/Widgets/VibrationWidget.qml b/src/FlightMap/Widgets/VibrationWidget.qml index 96cba71db..e4ebade92 100644 --- a/src/FlightMap/Widgets/VibrationWidget.qml +++ b/src/FlightMap/Widgets/VibrationWidget.qml @@ -157,7 +157,7 @@ QGCFlickable { Rectangle { anchors.fill: parent color: backgroundColor - opacity: 0.95 + opacity: 0.75 visible: _activeVehicle ? isNaN(_activeVehicle.vibration.xAxis.value) : false QGCLabel { -- GitLab From 40bb2f19ab0a68be52fdf6375a0fb9ea7209358b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 12:30:02 -0800 Subject: [PATCH 055/398] Click instead of swipe to change pages --- src/FlightMap/Widgets/InstrumentSwipeView.qml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/FlightMap/Widgets/InstrumentSwipeView.qml b/src/FlightMap/Widgets/InstrumentSwipeView.qml index 0a31ffb25..8a52ea4f8 100644 --- a/src/FlightMap/Widgets/InstrumentSwipeView.qml +++ b/src/FlightMap/Widgets/InstrumentSwipeView.qml @@ -67,7 +67,7 @@ Item { Rectangle { height: radius * 2 width: radius * 2 - radius: 2.5 + radius: ScreenTools.defaultFontPixelWidth / 3 border.color: textColor border.width: 1 color: _currentPage == index ? textColor : "transparent" @@ -75,6 +75,21 @@ Item { } } + MouseArea { + anchors.fill: parent + + onClicked: { + if (_currentPage == _maxPage) { + _currentPage = 0 + } else { + _currentPage++ + } + showPage(_currentPage) + } + } + + /* + Switching from swipe to click to change pages. Keeping swipe code for now in case of change back. MouseArea { anchors.fill: parent @@ -104,4 +119,5 @@ Item { showPage(_currentPage) } } + */ } -- GitLab From a66732b4821fdd08ff28fb378cece9927fa58f2e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 14:25:07 -0800 Subject: [PATCH 056/398] Meta data for ArduPlane 3.7 --- qgcresources.qrc | 1 + .../APMParameterFactMetaData.Plane.3.7.xml | 7137 +++++++++++++++++ 2 files changed, 7138 insertions(+) create mode 100644 src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml diff --git a/qgcresources.qrc b/qgcresources.qrc index 0ac994c11..b8ce22efe 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -273,6 +273,7 @@ src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.3.xml src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.5.xml + src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.4.xml src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.0.xml diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml new file mode 100644 index 000000000..07622b617 --- /dev/null +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml @@ -0,0 +1,7137 @@ + + + + + + + + +True + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +1 10 +1 + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0 1 +0.01 + + +0 5 +0.01 + + +0 15 +0.1 +Degrees + + +0 1000 +1 +meters + + +0 100 +1 +meters + + + +Disabled +FBWMixing +DirectMixing + + + + +Disabled +Enabled + + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s/s + + +0 127 +1 +0.1 seconds + + +-100 100 +1 +Percent + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 10 +0.5 +seconds + + +0 127 +1 +percent + + +0 100 +Percent + + + + +0 45 +1 +degrees + + +0 5 +0.5 +meters + + +0 90 +0.1 +degrees + + +centi-Degrees + + +0.1 +meters + + +0.1 +seconds + + +0 30 +0.1 +meters + + +0 10 +0.1 +seconds + + +0 30 +0.1 +m/s + + +0:AUTO_ALWAYS,1:AUTO_LAND,2:AUTO_LOITER_TO_ALT,3:AUTO_LOITER_ALL,4:AUTO_WAYPOINTS,5:LOITER,6:RTL,7:CIRCLE,8:CRUISE,9:FBWB,10:GUIDED + +Disabled +AlwaysAllowedInAuto +Auto_LandApproach +Auto_LoiterToAlt +Auto_Loiter +Auto_Waypoint +Loiter +RTL +Circle +Cruise +FBWB +Guided + + + +0 127 +1 +seconds + + + +Disabled +Servos to Neutral +Servos to Zero PWM + + + + +Disabled +Enabled + + + + +Default +L1Controller + + + +0 1 +0.1 +Percent + + + +Automatic + + + +-32767 32767 +1 +Meters + + +1 32767 +1 +Meters + + +0 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + + +None +GuidedMode +ReportOnly +GuidedModeThrPass +RTL_Mode + + + + + + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + + +NoAutoEnable +AutoEnable +AutoEnableDisableFloorOnly + + + + +FenceReturnPoint +NearestRallyPoint + + + + +Disabled +Enabled + + + +5 100 +1 +m/s + + +5 100 +1 +m/s + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 10000 +meters + + +1 10 +0.1 +m/s + + +-100 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 127 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +925 2200 +1 + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +CIRCLE/no change(if already in AUTO|GUIDED|LOITER) +CIRCLE +FBWA + + + +1 100 +0.5 +seconds + + + +Continue +ReturnToLaunch +Glide +Deploy Parachute + + + +1 300 +0.5 +seconds + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Heartbeat +HeartbeatAndREMRSSI +HeartbeatAndAUTO + + + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + +0 9000 +1 +centi-Degrees + + +0 9000 +1 +centi-Degrees + + +-9000 0 +1 +centi-Degrees + + +10 500 +1 +degrees/second + + +10 500 +1 +degrees/second + + + +Disabled +Enabled + + + +-100 100 +0.1 +Meters + + +10 360 +1 +degrees/second + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +UpUp +UpDown +DownUp +DownDown + + + + +Disabled +UpUp +UpDown +DownUp +DownDown + + + +0.5 1.2 + + + +Disabled +Enabled + + + +-1000 1000 +percent + + +-1000 1000 +percent + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +APM2-Default +PX4/Pixhawk-Default + + + + + + + +cm/s + + +m/s + + +cm/s + + +centi-Degrees + + +centimeters + + +centimeters + + + +Disabled +Enabled + + + + + + +Disabled +UpUp +UpDown +DownUp +DownDown + + + +0 100 +Percent + + +0 100 +1 +m/s + + +0 100 +Percent + + +0 100 +1 +m/s + + +0 100 +Percent + + + + + + + +Disabled +Channel1 +Channel2 +Channel3 +Channel4 +Channel5 +Channel6 +Channel7 +Channel8 + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 90 +0.1 +degrees + + + +Disable +Enable - go HOME then land +Enable - go directly to landing sequence + + + + +Disable +Enable + + + +10 127 +m/s/s + + +0:Disarm + +Disabled +Disarm + + + + + + +Disabled +Enabled + + + + + +True + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + + +Mission Planner and DroidPlanner + AP Planner 2 + + + + +Disabled +Enabled + + + +0 10 +.5 +Hz + + +0.0 1000.0 +10 +Centimeters + + +0.0 500.0 +10 + + +0:Feedback from mid stick,1:High throttle cancels landing,2:Disarm on land detection + +None +Feedback from mid stick +High throttle cancels landing +Disarm on land detection + + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0 8000 +1 +Centimeters + + +0.5 10.0 + +Disabled +Shallow +Steep + +.1 + + +0 2000 +50 +cm/s + + +0.01 2.0 +0.01 + + + +Disabled +Land +RTL + + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode + + + +100 900 + + + +Disabled +Enabled + + + + +Disabled +Mode1 +Mode2 +Mode1+2 +Mode3 +Mode1+3 +Mode2+3 +Mode1+2+3 +Mode4 +Mode1+4 +Mode2+4 +Mode1+2+4 +Mode3+4 +Mode1+3+4 +Mode2+3+4 +Mode1+2+3+4 +Mode5 +Mode1+5 +Mode2+5 +Mode1+2+5 +Mode3+5 +Mode1+3+5 +Mode2+3+5 +Mode1+2+3+5 +Mode4+5 +Mode1+4+5 +Mode2+4+5 +Mode1+2+4+5 +Mode3+4+5 +Mode1+3+4+5 +Mode2+3+4+5 +Mode1+2+3+4+5 +Mode6 +Mode1+6 +Mode2+6 +Mode1+2+6 +Mode3+6 +Mode1+3+6 +Mode2+3+6 +Mode1+2+3+6 +Mode4+6 +Mode1+4+6 +Mode2+4+6 +Mode1+2+4+6 +Mode3+4+6 +Mode1+3+4+6 +Mode2+3+4+6 +Mode1+2+3+4+6 +Mode5+6 +Mode1+5+6 +Mode2+5+6 +Mode1+2+5+6 +Mode3+5+6 +Mode1+3+5+6 +Mode2+3+5+6 +Mode1+2+3+5+6 +Mode4+5+6 +Mode1+4+5+6 +Mode2+4+5+6 +Mode1+2+4+5+6 +Mode3+4+5+6 +Mode1+3+4+5+6 +Mode2+3+4+5+6 +Mode1+2+3+4+5+6 + + + +-1 1000 +1 +Centimeters + + +0 3000 +10 +Centimeters + + + +Never change yaw +Face next waypoint +Face next waypoint except RTL +Face along GPS course + + + +0 60000 +1000 +ms + + +30 200 +10 +cm/s + + +0 500 +10 +cm/s + + +50 500 +10 +Centimeters/Second + + +50 500 +10 +cm/s/s + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode +Enabled always LAND + + + +925 1100 +1 +pwm + + +0 300 +1 +pwm + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW + +Default +Default+RCIN +Default+IMU +Default+Motors +NearlyAll-AC315 +NearlyAll +All+FastATT +All+MotBatt +All+FastIMU +All+FastIMU+PID +All+FullIMU +Disabled + + + + +Normal Start-up +Start-up in ESC Calibration mode if throttle high +Start-up in ESC Calibration mode regardless of throttle +Start-up and automatically calibrate ESCs +Disabled + + + + +None +Stab Roll/Pitch kP +Rate Roll/Pitch kP +Rate Roll/Pitch kI +Rate Roll/Pitch kD +Stab Yaw kP +Rate Yaw kP +Rate Yaw kD +Altitude Hold kP +Throttle Rate kP +Throttle Accel kP +Throttle Accel kI +Throttle Accel kD +Loiter Speed +Loiter Pos kP +Velocity XY kP +Velocity XY kI +WP Speed +Acro RollPitch kP +Acro Yaw kP +Heli Ext Gyro +OF Loiter kP +OF Loiter kI +OF Loiter kD +Declination +Circle Rate +RangeFinder Gain +Rate Pitch kP +Rate Pitch kI +Rate Pitch kD +Rate Roll kP +Rate Roll kI +Rate Roll kD +Rate Pitch FF +Rate Roll FF +Rate Yaw FF + + + +0 32767 + + +0 32767 + + + +Plus +X +V +H +V-Tail +A-Tail +Y6B (New) + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +EPM +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance + + + +0:All,1:Baro,2:Compass,3:GPS,4:INS,5:Parameters+Rangefinder,6:RC,7:Voltage + +Disabled +Enabled +Skip Baro +Skip Compass +Skip GPS +Skip INS +Skip Params/Rangefinder +Skip RC +Skip Voltage + + + +0 127 +Seconds + + +1000 8000 +Centi-degrees + + +0 100 + +Very Soft +Soft +Medium +Crisp +Very Crisp + +10 + + +4 12 +deg/sec + + +2000 4500 +Centi-degrees + + + +No repositioning +Repositioning + + + + +Land +AltHold +Land even in Stabilize + + + +0.6:Strict, 0.8:Default, 1.0:Relaxed + + + +Disabled +Enabled + + + +50 490 +1 +Hz + + +1 10 + + +1 10 + + +0 3 +0.1 + + +0 3 +0.1 + + + +Disabled +Leveling +Leveling and Limited + + + + +Disabled +Very Low +Low +Medium +High +Very High + + + +0.1 6.0 +0.1 + + +0.02 1.00 +0.01 + + +0 4500 +10 +cm/s/s + + +1.000 8.000 + + +0.500 1.500 +0.05 + + +0.000 3.000 + + +0 1000 +Percent*10 + + +0.000 0.400 + + +1.000 100.000 +Hz + + +1.000 3.000 + + +0.500 2.000 + + +0:Roll,1:Pitch,2:Yaw + +All +Roll Only +Pitch Only +Yaw Only +Roll and Pitch +Roll and Yaw +Pitch and Yaw + + + +0.05 0.10 + + +0.001 0.006 + + + +Stopped +Running + + + + +Do Not Use in RTL and Land +Use in RTL and Land + + + +0 5 + + + +Auto +Guided +RTL +Land +Brake +Throw + + + + +Upward Throw +Drop + + + + +Disabled +Enabled + + + + + + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 20 +0.1 +seconds + + +0 20 +0.1 +seconds + + +0 100 +1 +degrees/second + + +0 20 +1 +seconds + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10 +0.1 +seconds + + + +Position +OnOff +ContinuousRotation + + + + +Position +OnOff +ContinuousRotation + + + +0 50 +0.1 +degrees/second + + +0 50 +0.1 +degrees/second + + +0 2 +0.01 +seconds + + +0 2 +0.01 +seconds + + +-10 10 +0.1 +degrees + + +-10 10 +0.1 +degrees + + +0 360 +0.1 +degrees + + +0 100 +1 +meters + + + +Barometer +GPS +GPS vehicle only + + + +1 10 +1 +Hz + + +-90 0 +1 +Degrees + + +0 90 +1 +Degrees + + +0:ATTITUDE,1:GPS,2:RCIN,3:IMU,4:RCOUT,5:COMPASS + +Default +Disabled + + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +1 255 + + + + + + +True + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +APM2-Default +PX4/Pixhawk-Default + + + + + + + + +MANUAL +LEARNING +STEERING +HOLD +AUTO +RTL +GUIDED + + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 30 +1 +seconds + + +0:Steering,1:Throttle + +None +Steering +Throttle + + + + +Disabled +Enabled + + + + +Disabled +APM TriggerPin + Pixhawk TriggerPin + + + +0 20 +0.1 +m/s/s + + +0 100 +0.1 +m/s + + +0 100 +1 +percent + + +0 100 +0.1 +meters + + +0 100 +1 +percent + + +0 100 +1 +m/s + + +0 360 +1 +degrees + + + +Nothing +LearnWaypoint + + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +SkidSteeringOutput + + + + +Disabled +SkidSteeringInput + + + + +Nothing +RTL +HOLD + + + +seconds + + + +Disabled +Enabled + + + +925 1100 +1 + + + +Disabled +Enabled + + + +0 1000 +1 +centimeters + + +-45 45 +1 +centimeters + + +0 100 +0.1 +seconds + + +1 100 +1 + + + + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + +0 1000 +0.1 +meters + + +0.2 10 +0.1 +gravities + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +meters +0.1 + + + +FirstBaro +2ndBaro +3rdBaro + + + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF + +True + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF + +True + + + +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G + + + + +Disabled +Enabled + + + + +Any +FloatRTK +IntegerRTK + +True + + + +Disabled +Enabled +NoChange + + + +-100 90 +Degrees + + + +send to first GPS +send to 2nd GPS +send to all + + + + +None +All +External only + + + + +Disabled +log every sample +log every 5 samples + +True + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Do not save config +Save config +Save only when needed + + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Disables automatic configuration +Enable automatic configuration + + + + + + +Servo +Relay + + + +0 50 +seconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 +meters + + + +Low +High + + + +0 10000 +milliseconds + + +0 180 +Degrees + + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + + + + +TriggerLow +TriggerHigh + + + + + + +Disabled +THR_MIN PWM when disarmed +0 PWM when disarmed + + + +0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration + +None +All +Barometer +Compass +GPS Lock +INS(INertial Sensors - accels & gyros) +Parameters(unused) +RC Failsafe +Board voltage +Battery Level +Airspeed +LoggingAvailable +Hardware safety switch +GPS configuration + + + +0.25 3.0 +m/s/s + + +0.1 +Volts + + +0.1 +Volts + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + + +Disabled +Enabled + + + + +First Relay +Second Relay +Third Relay +Fourth Relay +Servo + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + +0 32000 +1 +Meters + + +0 5000 +1 +Milliseconds + + + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +0 127 +1 +centimeters + + +0 127 +1 + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +meters/Volt +0.001 + + +Volts +0.001 + + + +Linear +Inverted +Hyperbolic + + + +centimeters +1 + + +centimeters +1 + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +milliseconds +1 + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +meters/Volt +0.001 + + +Volts +0.001 + + + +Linear +Inverted +Hyperbolic + + + +centimeters +1 + + +centimeters +1 + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +milliseconds +1 + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + + + + +Disable +Enable + + + +meters +1 + + + + + +Disabled +Enabled + + + +1 100 + + +1 100000 + + +-1 16777215 + + + +NoInfo +Light +Small +Large +HighVortexlarge +Heavy +HighlyManuv +Rotocraft +RESERVED +Glider +LightAir +Parachute +UltraLight +RESERVED +UAV +Space +RESERVED +EmergencySurface +ServiceSurface +PointObstacle + + + + +NO_DATA +L15W23 +L25W28P5 +L25W34 +L35W33 +L35W38 +L45W39P5 +L45W45 +L55W45 +L55W52 +L65W59P5 +L65W67 +L75W72P5 +L75W80 +L85W80 +L85W90 + + + + +NoData +Left2m +Left4m +Left6m +Center +Right2m +Right4m +Right6m + + + + +NO_DATA +AppliedBySensor + + + + +Disabled +Rx-Only +Tx-Only +Rx and Tx Enabled + + + + + + +Disabled +Enabled + + + + +Remain in AVOID_ADSB +Resume previous flight mode +RTL +Resume if AUTO else Loiter + + + + + + + + + + + + + + + + + + + + +Disable +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +900 2100 + + +900 2100 + + + +Disable +Chan1 +Chan3 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + + + + +Disable +Enable + + + +0 1 + + + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Reversed +Normal + + + +0 200 +pwm + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle + + + + + +0.4 1.0 +0.1 +seconds + + +0.1 4.0 +0.1 + + +0 0.1 +0.01 + + +0 1.0 +0.05 + + +0 180 +1 +degrees/second + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0.4 1.0 +0.1 +seconds + + +0.1 3.0 +0.1 + + +0 0.1 +0.01 + + +0 0.5 +0.05 + + +0 100 +1 +degrees/second + + +0 100 +1 +degrees/second + + +0.7 1.5 +0.05 + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0 4 +0.25 + + +0 2 +0.25 + + +0 2 +0.25 + + +0.8 1.2 +0.05 + + +0 4500 +1 + + + + +0.4 1.0 +0.1 +seconds + + +0.1 10.0 +0.1 + + +0 1.0 +0.05 + + +0 0.1 +0.01 + + +0 4500 +1 + + +0 5 +0.1 +m/s + + +0.0 10.0 +0.1 + + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-3.142 3.142 +0.01 +Radians + + + +Disabled +Internal-Learning +EKF-Learning + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Use Throttle +Use Current + + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +FirstCompass +SecondCompass +ThirdCompass + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + + + + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 20 +0.1 + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + +Unknown +unused +unused +unused +SITL +PX4v1 +PX4v2 +unused +Linux + + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0.05 50 + + + +Never +Start-up only + + + + +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle + + + + +IMU 1 +IMU 2 +IMU 3 + + + + + +0.0 1.0 +.01 + + + +Disabled +Enabled + + + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 + + + +0.001 0.5 +.01 + + +0 10 +1 + + + +Disabled +Enabled +Enable EKF2 + + + + + + +Disable +Enable + + + + +Use +Don't Use + + + +0.1 + + +0.1 + + + + + + + + + +Disable +Enable + + + + + + + +1 60 +1 +seconds + + +0.6 1.0 +0.05 + + +0 0.1 +0.01 + + + + +0.1 20.0 +0.1 + + +0.1 10.0 +0.1 + + +3.0 10.0 +0.2 + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +1.0 10.0 +0.5 + + +1.0 5.0 +0.05 + + +0.5 2.0 +0.05 + + +5.0 30.0 +1.0 + + +0.0 2.0 +0.1 + + +0.1 1.0 +0.1 + + +0.0 20.0 +0.1 + + +-1 127 +1 + + +-1 100 +0.1 + + +-1.0 2.0 +0.1 + + +0 45 +1 + + +-45 0 +1 + + +0.0 2.0 +0.1 + + +1.0 5.0 +0.2 + + +0.1 1.0 +0.1 + + +-5 40 +1 + + +0.0 20.0 +0.1 +m/s + + +-2.0 2.0 +0.1 +m/s/m + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +0.0 0.5 +0.02 + + +0.1 1.0 +0.1 + + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0 100 +1 + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + + + + + + +None +File +MAVLink +BothFileAndMAVLink + + + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +SMBus +Bebop + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Watts + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +SMBus +Bebop + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Amps + + + + + +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled + +True + + + +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz + +True + + +-32767 32768 + + + +Disabled +Enabled + + + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8 + +Disabled +Enabled + +True + + +degreesC +-1 80 + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + + + + + + + + + + + + + + + + +meters + + +meters + + +millibar + + + + + + + + + + + + + + +seconds + + + + + +Disabled +Enabled + + + +-200 +200 +1 + + +-200 +200 +1 + + +-18000 +18000 +1 + + + + +0 32766 +1 + + + +Resume Mission +Restart Mission + + + + + + + +0.1 +kilometers + + + +DoNotIncludeHome +IncludeHome + + + + + + +Disabled +Enabled + + + +0.05 5.0 +0.05 + + +0.05 5.0 +0.05 + + +0.1 10.0 +0.1 +meters + + +0.1 10.0 +0.1 +meters + + +0.01 0.5 +0.01 + + +0.5 5.0 +0.1 +m/s + + +0.01 1.0 +0.1 + + +0.0 1.0 +0.1 + + +0.001 0.05 +0.001 +rad/s + + +0.05 1.0 +0.01 +m/s/s + + +0.0000001 0.00001 +rad/s + + +0.00001 0.001 +m/s/s + + +0.0001 0.01 +gauss/s + + +0.0001 0.01 +gauss/s + + +0 500 +10 +milliseconds + + +0 500 +10 +milliseconds + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS use optical flow + + + +1 100 +1 + + +1 100 +1 + + +1 100 +1 + + +1 100 +1 + + +1 100 +1 + + + +Speed and Height +Acceleration +Never +Always + + + +100 500 +50 + + +10 50 +5 +meters + + +1 50 +1 + + +0.05 1.0 +0.05 +rad/s + + +1 100 +1 + + +0 500 +10 +milliseconds + + +1 100 +1 + + +1.0 4.0 +0.1 + + + +Trust EKF more +Trust DCM more + + + + +Use Baro +Use Range Finder + + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS use optical flow + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +msec + + + +Use Baro +Use Range Finder +Use GPS + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +msec + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +1 127 + + +50 200 +% + + +0.5 50.0 +m/s + + + +Disabled +FirstIMU +FirstAndSecondIMU +AllIMUs + + + +0.05 1.0 +0.05 +gauss + + +100 1000 +25 + + +10 50 +5 + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +AnalogPin +RCChannelPwmValue + + + + +APM2 A0 +APM2 A1 +APM2 A13 +Pixracer +Pixhawk ADC4 +Pixhawk ADC3 + Pixhawk ADC6 +Pixhawk SBUS + + + +0 5.0 +0.01 +Volt + + +0 5.0 +0.01 +Volt + + + + + +0 2000 +Microseconds + + +0 2000 +Microseconds + + + + + +Off +Low +Medium +High + + + + +Disable +Enable + + + + +Disable +Enable + + + + + + +Disabled +Enabled + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +0 3600 + + + + + +Disabled +Enabled + + + + +None +Chan1 +Chan2 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +0.1 5 +Seconds + + +1 10 +Seconds + + +100 100000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + + +None +RPM1 +RPM2 + + + +0 100 + + + + + +Disabled +Enabled + + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +0 255 + + +0 255 + + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + + + +0 500 +1 +Percent*10 + + +0 500 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + + +Disabled +Very Low +Low +Medium +High +Very High + + + + + +0 2000 +50 +cm/s + + +100 1000 +1 +cm + + +0 1000 +50 +cm/s + + +0 500 +10 +cm/s + + +0 2000 +50 +cm/s + + +50 500 +10 +cm/s/s + + +50 500 +10 +cm/s/s + + +500 5000 +1 +cm/s/s/s + + +100 981 +1 +cm/s/s + + +25 250 +1 +cm/s/s + + + +Disable +Enable + + + + + +0 10000 +100 +cm + + +-90 90 +1 +deg/s + + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +3.000 12.000 + + +3.000 12.000 + + +3.000 6.000 + + +0.5 10.0 + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + + + +0.5 5 +0.1 +Hz + + + + + +Disabled +Enabled + + + +0 100 +percentage + + +1000 2000 +ms + + +0 1000 +cm/s + + +0 100 +percentage + + + + + +Disabled +Enabled + + + +0:Altitude,1:Circle,2:Polygon + +None +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All + + + + +Report Only +RTL or Land + + + +10 1000 +1 +Meters + + +30 10000 +Meters + + +1 10 +Meters + + +1 20 + + + + + +None +StopAtFence + + + + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + + +Servo only +Servo with ExtGyro +DirectDrive VarPitch +DirectDrive FixedPitch + + + + +3-Servo CCPM +H1 Mechanical Mixing + + + +0 1000 +1 +PWM + + +-90 90 +1 +Degrees + + +-10 10 +0.1 + + + +NoFlybar +Flybar + + + +0 1000 +1 +PWM + + +0 1000 +1 +PWM + + +0 2000 + + +0 2000 + + + +Reversed +Normal + + + + + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 + + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + + + +Disabled +Enabled Always Land +Enabled Strict + + + + +None +CompanionComputer +IRLock + + + + -- GitLab From b861154299744f4bee63352ac85db5d44971b846 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 14:34:56 -0800 Subject: [PATCH 057/398] Fix combo selection bug --- src/AutoPilotPlugins/PX4/AirframeComponent.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/PX4/AirframeComponent.qml b/src/AutoPilotPlugins/PX4/AirframeComponent.qml index 048b045df..13a3f3b8f 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponent.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponent.qml @@ -208,6 +208,7 @@ Your vehicle will also be restarted in order to complete the process.") onCheckedChanged: { if (checked && combo.currentIndex != -1) { + console.log("check box change", combo.currentIndex) controller.autostartId = modelData.airframes[combo.currentIndex].autostartId } } @@ -230,8 +231,9 @@ Your vehicle will also be restarted in order to complete the process.") onActivated: { applyButton.primary = true - controller.autostartId = modelData.airframes[index].autostartId airframeCheckBox.checked = true; + console.log("combo change", index) + controller.autostartId = modelData.airframes[index].autostartId } } } -- GitLab From ab81dc7c2b231b90a26d7770f5e9b9c0da53434c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 14:40:28 -0800 Subject: [PATCH 058/398] Bump image size --- QGCInstaller.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGCInstaller.pri b/QGCInstaller.pri index cce214a04..e47938565 100644 --- a/QGCInstaller.pri +++ b/QGCInstaller.pri @@ -29,7 +29,7 @@ installer { QMAKE_POST_LINK += && mkdir -p $${DESTDIR}/package QMAKE_POST_LINK += && cd $${DESTDIR} && $$dirname(QMAKE_QMAKE)/macdeployqt QGroundControl.app -appstore-compliant -verbose=2 -qmldir=$${BASEDIR}/src QMAKE_POST_LINK += && cd $${OUT_PWD} - QMAKE_POST_LINK += && hdiutil create -verbose -stretch 2g -layout SPUD -srcfolder $${DESTDIR}/QGroundControl.app -volname QGroundControl $${DESTDIR}/package/QGroundControl.dmg + QMAKE_POST_LINK += && hdiutil create -verbose -stretch 3g -layout SPUD -srcfolder $${DESTDIR}/QGroundControl.app -volname QGroundControl $${DESTDIR}/package/QGroundControl.dmg } WindowsBuild { # The pdb moving command are commented out for now since we are including the .pdb in the installer. This makes it much -- GitLab From 0daae906ed5f9aa8f79b71b6cdb19fb2ebb9a350 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 24 Nov 2016 17:59:31 -0800 Subject: [PATCH 059/398] Per-Vehicle parameter load progess Also removed intermediate param load retry message --- src/FactSystem/ParameterManager.cc | 24 +++++++++++++----------- src/FactSystem/ParameterManager.h | 9 ++++++--- src/ui/toolbar/MainToolBar.qml | 2 +- src/ui/toolbar/MainToolBarController.cc | 9 --------- src/ui/toolbar/MainToolBarController.h | 4 ---- 5 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index e27729f3f..6c1197cac 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -47,6 +47,7 @@ ParameterManager::ParameterManager(Vehicle* vehicle) : QObject(vehicle) , _vehicle(vehicle) , _mavlink(NULL) + , _loadProgress(0.0) , _parametersReady(false) , _missingParameters(false) , _initialLoadComplete(false) @@ -247,10 +248,10 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString // We are no longer waiting for any reads to complete if (_prevWaitingReadParamIndexCount + _prevWaitingReadParamNameCount != 0) { // Set progress to 0 if not already there - emit parameterListProgress(0); + _setLoadProgress(0.0); } } else { - emit parameterListProgress((float)(_totalParamCount - readWaitingParamCount) / (float)_totalParamCount); + _setLoadProgress((double)(_totalParamCount - readWaitingParamCount) / (double)_totalParamCount); } // Get parameter set version @@ -801,13 +802,13 @@ void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, QVarian ani->setDuration(750); connect(ani, &QVariantAnimation::valueChanged, [this](const QVariant &value) { - emit parameterListProgress(value.toFloat()); + _setLoadProgress(value.toDouble()); }); // Hide 500ms after animation finishes connect(ani, &QVariantAnimation::finished, [this](){ QTimer::singleShot(500, [this]() { - emit parameterListProgress(0); + _setLoadProgress(0); }); }); @@ -1070,18 +1071,13 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) void ParameterManager::_initialRequestTimeout(void) { if (!_disableAllRetries && ++_initialRequestRetryCount <= _maxInitialRequestListRetry) { - if (!_vehicle->genericFirmware()) { - // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user - QString errorMsg = tr("Vehicle %1 did not respond to request for parameters, retrying").arg(_vehicle->id()); - qCDebug(ParameterManagerLog) << errorMsg; - qgcApp()->showMessage(errorMsg); - } refreshAllParameters(); _initialRequestTimeoutTimer.start(); } else { if (!_vehicle->genericFirmware()) { // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user - QString errorMsg = tr("Vehicle %1 did not respond to request for parameters, failing after maximum number of retries").arg(_vehicle->id()); + QString errorMsg = tr("Vehicle %1 did not respond to request for parameters" + "This will cause QGroundControl to be unable to display its full user interface.").arg(_vehicle->id()); qCDebug(ParameterManagerLog) << errorMsg; qgcApp()->showMessage(errorMsg); } @@ -1482,3 +1478,9 @@ QString ParameterManager::_logVehiclePrefix(int componentId) return QString("V:%1 C:%2").arg(_vehicle->id()).arg(componentId); } } + +void ParameterManager::_setLoadProgress(double loadProgress) +{ + _loadProgress = loadProgress; + emit loadProgressChanged(loadProgress); +} diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index dc91aad2d..18ea33e4e 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -49,6 +49,9 @@ public: Q_PROPERTY(bool missingParameters READ missingParameters NOTIFY missingParametersChanged) bool missingParameters(void) { return _missingParameters; } + Q_PROPERTY(double loadProgress READ loadProgress NOTIFY loadProgressChanged) + double loadProgress(void) const { return _loadProgress; } + /// @return Directory of parameter caches static QDir parameterCacheDir(); @@ -121,9 +124,7 @@ public: signals: void parametersReadyChanged(bool parametersReady); void missingParametersChanged(bool missingParameters); - - /// Signalled to update progress of full parameter list request - void parameterListProgress(float value); + void loadProgressChanged(float value); protected: Vehicle* _vehicle; @@ -148,6 +149,7 @@ private: QString _remapParamNameToVersion(const QString& paramName); void _loadOfflineEditingParams(void); QString _logVehiclePrefix(int componentId = -1); + void _setLoadProgress(double loadProgress); MAV_PARAM_TYPE _factTypeToMavType(FactMetaData::ValueType_t factType); FactMetaData::ValueType_t _mavTypeToFactType(MAV_PARAM_TYPE mavType); @@ -164,6 +166,7 @@ private: /// Second mapping is group name, to Fact QMap > _mapGroup2ParameterName; + double _loadProgress; ///< Parameter load progess, [0.0,1.0] bool _parametersReady; ///< true: parameter load complete bool _missingParameters; ///< true: parameter missing from initial load bool _initialLoadComplete; ///< true: Initial load of all parameters complete, whether successful or not diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index c86037067..84dd08811 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -439,7 +439,7 @@ Rectangle { id: progressBar anchors.bottom: parent.bottom height: toolBar.height * 0.05 - width: parent.width * _controller.progressBarValue + width: activeVehicle ? activeVehicle.parameterManager.loadProgress * parent.width : 0 color: colorGreen } diff --git a/src/ui/toolbar/MainToolBarController.cc b/src/ui/toolbar/MainToolBarController.cc index 50a6d298c..5001be8e3 100644 --- a/src/ui/toolbar/MainToolBarController.cc +++ b/src/ui/toolbar/MainToolBarController.cc @@ -30,7 +30,6 @@ MainToolBarController::MainToolBarController(QObject* parent) : QObject(parent) , _vehicle(NULL) , _mav(NULL) - , _progressBarValue(0.0f) , _telemetryRRSSI(0) , _telemetryLRSSI(0) { @@ -48,7 +47,6 @@ void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle) { // Disconnect the previous one (if any) if (_vehicle) { - disconnect(_vehicle->parameterManager(), &ParameterManager::parameterListProgress, this, &MainToolBarController::_setProgressBarValue); _mav = NULL; _vehicle = NULL; } @@ -58,7 +56,6 @@ void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle) { _vehicle = vehicle; _mav = vehicle->uas(); - connect(_vehicle->parameterManager(), &ParameterManager::parameterListProgress, this, &MainToolBarController::_setProgressBarValue); } } @@ -93,9 +90,3 @@ void MainToolBarController::_telemetryChanged(LinkInterface*, unsigned rxerrors, emit telemetryRNoiseChanged(_telemetryRNoise); } } - -void MainToolBarController::_setProgressBarValue(float value) -{ - _progressBarValue = value; - emit progressBarValueChanged(value); -} diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h index 99b8f232f..19fb78e0b 100644 --- a/src/ui/toolbar/MainToolBarController.h +++ b/src/ui/toolbar/MainToolBarController.h @@ -38,7 +38,6 @@ public: ~MainToolBarController(); Q_PROPERTY(double height MEMBER _toolbarHeight NOTIFY heightChanged) - Q_PROPERTY(float progressBarValue MEMBER _progressBarValue NOTIFY progressBarValueChanged) Q_PROPERTY(int telemetryRRSSI READ telemetryRRSSI NOTIFY telemetryRRSSIChanged) Q_PROPERTY(int telemetryLRSSI READ telemetryLRSSI NOTIFY telemetryLRSSIChanged) Q_PROPERTY(unsigned int telemetryRXErrors READ telemetryRXErrors NOTIFY telemetryRXErrorsChanged) @@ -58,7 +57,6 @@ public: unsigned int telemetryRNoise () { return _telemetryRNoise; } signals: - void progressBarValueChanged (float value); void telemetryRRSSIChanged (int value); void telemetryLRSSIChanged (int value); void heightChanged (double height); @@ -70,13 +68,11 @@ signals: private slots: void _activeVehicleChanged (Vehicle* vehicle); - void _setProgressBarValue (float value); void _telemetryChanged (LinkInterface* link, unsigned rxerrors, unsigned fixed, int rssi, int remrssi, unsigned txbuf, unsigned noise, unsigned remnoise); private: Vehicle* _vehicle; UASInterface* _mav; - float _progressBarValue; double _remoteRSSIstore; int _telemetryRRSSI; int _telemetryLRSSI; -- GitLab From 0ce6d773bb61554bef969ff49df2bcf0d6ec04f6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 26 Nov 2016 18:54:10 -0800 Subject: [PATCH 060/398] Allows QGC plugins to be optional or custom --- QGCCommon.pri | 8 - qgroundcontrol.pro | 371 ++++++++++-------- .../APM/APMAirframeComponentController.cc | 1 - .../APM/APMAutoPilotPlugin.cc | 1 - .../APM/APMFlightModesComponentController.cc | 1 - .../AutoPilotPluginManager.cc | 29 -- src/AutoPilotPlugins/AutoPilotPluginManager.h | 37 -- .../Common/ESP8266ComponentController.cc | 1 - src/AutoPilotPlugins/Common/MotorComponent.cc | 3 - .../Common/RadioComponentController.cc | 1 - .../PX4/AirframeComponentController.cc | 1 - .../PX4/PX4AdvancedFlightModesController.cc | 1 - .../PX4/PX4AutoPilotPlugin.cc | 1 - .../PX4/PX4SimpleFlightModesController.cc | 1 - src/FactSystem/ParameterManager.cc | 29 +- src/FactSystem/ParameterManager.h | 2 +- src/FactSystem/ParameterManagerTest.cc | 8 +- src/FactSystem/ParameterManagerTest.h | 3 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 18 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.h | 45 ++- .../APM/APMFirmwarePluginFactory.cc | 71 ++++ .../APM/APMFirmwarePluginFactory.h | 37 ++ src/FirmwarePlugin/FirmwarePlugin.cc | 25 +- src/FirmwarePlugin/FirmwarePlugin.h | 41 +- src/FirmwarePlugin/FirmwarePluginManager.cc | 83 +--- src/FirmwarePlugin/FirmwarePluginManager.h | 18 +- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 23 +- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 3 +- .../PX4/PX4FirmwarePluginFactory.cc | 44 +++ .../PX4/PX4FirmwarePluginFactory.h | 31 ++ src/FollowMe/FollowMe.cc | 6 +- src/QGCApplication.cc | 24 -- src/QGCApplication.h | 1 - src/QGCFileDialog.cc | 22 +- src/QGCMessageBox.h | 11 +- src/QGCQuickWidget.cc | 1 - src/QGCToolbox.cc | 5 - src/QGCToolbox.h | 3 - src/QmlControls/ParameterEditorController.cc | 1 - src/Vehicle/MultiVehicleManager.cc | 4 +- src/Vehicle/MultiVehicleManager.h | 2 - src/Vehicle/Vehicle.cc | 34 +- src/Vehicle/Vehicle.h | 11 +- src/comm/MockLink.cc | 10 +- src/main.cc | 11 +- 45 files changed, 602 insertions(+), 482 deletions(-) delete mode 100644 src/AutoPilotPlugins/AutoPilotPluginManager.cc delete mode 100644 src/AutoPilotPlugins/AutoPilotPluginManager.h create mode 100644 src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc create mode 100644 src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h create mode 100644 src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc create mode 100644 src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h diff --git a/QGCCommon.pri b/QGCCommon.pri index e10d5ea85..4a8074318 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -239,11 +239,3 @@ ReleaseBuild { QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO += /OPT:ICF } } - -# -# Unit Test specific configuration goes here -# - -DebugBuild { - DEFINES += UNITTEST_BUILD -} diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 8d2ea11d0..d9902929f 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -70,6 +70,20 @@ CONFIG += qt \ thread \ c++11 \ +# +# Plugin configuration +# +# This allows you to build custom versions of QGC which only include your specific vehicle plugin. To remove support for +# a firmware type completely remove both the Plugin and PluginFactory entries. To include custom support for an existing +# plugin type remove PluginFactory only. Then provide you own implementation of FirmwarePluginFactory and use the +# FirmwarePlugin and AutoPilotPlugin classes as the base clase for your derived plugin implementation. +# +CONFIG += \ + APMFirmwarePlugin \ + PX4FirmwarePlugin \ + APMFirmwarePluginFactory \ + PX4FirmwarePluginFactory \ + contains(DEFINES, ENABLE_VERBOSE_OUTPUT) { message("Enable verbose compiler output (manual override from command line)") } else:exists(user_config.pri):infile(user_config.pri, DEFINES, ENABLE_VERBOSE_OUTPUT) { @@ -267,6 +281,74 @@ FORMS += \ src/ui/uas/UASQuickViewItemSelect.ui \ } +# +# Unit Test specific configuration goes here (requires full debug build with all plugins) +# + +DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { APMFirmwarePluginFactory { !MobileBuild { + DEFINES += UNITTEST_BUILD + + INCLUDEPATH += \ + src/qgcunittest + + HEADERS += \ + src/AnalyzeView/LogDownloadTest.h \ + src/FactSystem/FactSystemTestBase.h \ + src/FactSystem/FactSystemTestGeneric.h \ + src/FactSystem/FactSystemTestPX4.h \ + src/FactSystem/ParameterManagerTest.h \ + src/MissionManager/ComplexMissionItemTest.h \ + src/MissionManager/MissionCommandTreeTest.h \ + src/MissionManager/MissionControllerTest.h \ + src/MissionManager/MissionControllerManagerTest.h \ + src/MissionManager/MissionItemTest.h \ + src/MissionManager/MissionManagerTest.h \ + src/MissionManager/SimpleMissionItemTest.h \ + src/qgcunittest/GeoTest.h \ + src/qgcunittest/FileDialogTest.h \ + src/qgcunittest/FileManagerTest.h \ + src/qgcunittest/FlightGearTest.h \ + src/qgcunittest/LinkManagerTest.h \ + src/qgcunittest/MainWindowTest.h \ + src/qgcunittest/MavlinkLogTest.h \ + src/qgcunittest/MessageBoxTest.h \ + src/qgcunittest/MultiSignalSpy.h \ + src/qgcunittest/RadioConfigTest.h \ + src/qgcunittest/TCPLinkTest.h \ + src/qgcunittest/TCPLoopBackServer.h \ + src/qgcunittest/UnitTest.h \ + + SOURCES += \ + src/AnalyzeView/LogDownloadTest.cc \ + src/FactSystem/FactSystemTestBase.cc \ + src/FactSystem/FactSystemTestGeneric.cc \ + src/FactSystem/FactSystemTestPX4.cc \ + src/FactSystem/ParameterManagerTest.cc \ + src/MissionManager/ComplexMissionItemTest.cc \ + src/MissionManager/MissionCommandTreeTest.cc \ + src/MissionManager/MissionControllerTest.cc \ + src/MissionManager/MissionControllerManagerTest.cc \ + src/MissionManager/MissionItemTest.cc \ + src/MissionManager/MissionManagerTest.cc \ + src/MissionManager/SimpleMissionItemTest.cc \ + src/qgcunittest/GeoTest.cc \ + src/qgcunittest/FileDialogTest.cc \ + src/qgcunittest/FileManagerTest.cc \ + src/qgcunittest/FlightGearTest.cc \ + src/qgcunittest/LinkManagerTest.cc \ + src/qgcunittest/MainWindowTest.cc \ + src/qgcunittest/MavlinkLogTest.cc \ + src/qgcunittest/MessageBoxTest.cc \ + src/qgcunittest/MultiSignalSpy.cc \ + src/qgcunittest/RadioConfigTest.cc \ + src/qgcunittest/TCPLinkTest.cc \ + src/qgcunittest/TCPLoopBackServer.cc \ + src/qgcunittest/UnitTest.cc \ + src/qgcunittest/UnitTestList.cc \ +} } } } } } + +# Main QGC Headers and Source files + HEADERS += \ src/audio/QGCAudioWorker.h \ src/CmdLineOptParser.h \ @@ -278,6 +360,7 @@ HEADERS += \ src/comm/QGCMAVLink.h \ src/comm/TCPLink.h \ src/comm/UDPLink.h \ + src/FirmwarePlugin/PX4/px4_custom_mode.h \ src/FlightDisplay/VideoManager.h \ src/FlightMap/FlightMapSettings.h \ src/FlightMap/Widgets/ValuesWidgetController.h \ @@ -336,8 +419,6 @@ HEADERS += \ src/uas/UASMessageHandler.h \ src/Vehicle/MAVLinkLogManager.h \ src/ui/toolbar/MainToolBarController.h \ - src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ - src/AutoPilotPlugins/APM/APMAirframeLoader.h \ src/QmlControls/QGCImageProvider.h \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ src/PositionManager/PositionManager.h \ @@ -502,8 +583,6 @@ SOURCES += \ src/uas/UASMessageHandler.cc \ src/Vehicle/MAVLinkLogManager.cc \ src/ui/toolbar/MainToolBarController.cc \ - src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ - src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ src/QmlControls/QGCImageProvider.cc \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ src/PositionManager/SimulatedPosition.cc \ @@ -578,143 +657,31 @@ SOURCES += \ src/ViewWidgets/ViewWidgetController.cc } -# -# Unit Test specific configuration goes here -# - +# Palette test widget in debug builds DebugBuild { - -HEADERS += src/QmlControls/QmlTestWidget.h -SOURCES += src/QmlControls/QmlTestWidget.cc - -!MobileBuild { - -INCLUDEPATH += \ - src/qgcunittest - -HEADERS += \ - src/AnalyzeView/LogDownloadTest.h \ - src/FactSystem/FactSystemTestBase.h \ - src/FactSystem/FactSystemTestGeneric.h \ - src/FactSystem/FactSystemTestPX4.h \ - src/FactSystem/ParameterManagerTest.h \ - src/MissionManager/ComplexMissionItemTest.h \ - src/MissionManager/MissionCommandTreeTest.h \ - src/MissionManager/MissionControllerTest.h \ - src/MissionManager/MissionControllerManagerTest.h \ - src/MissionManager/MissionItemTest.h \ - src/MissionManager/MissionManagerTest.h \ - src/MissionManager/SimpleMissionItemTest.h \ - src/qgcunittest/GeoTest.h \ - src/qgcunittest/FileDialogTest.h \ - src/qgcunittest/FileManagerTest.h \ - src/qgcunittest/FlightGearTest.h \ - src/qgcunittest/LinkManagerTest.h \ - src/qgcunittest/MainWindowTest.h \ - src/qgcunittest/MavlinkLogTest.h \ - src/qgcunittest/MessageBoxTest.h \ - src/qgcunittest/MultiSignalSpy.h \ - src/qgcunittest/RadioConfigTest.h \ - src/qgcunittest/TCPLinkTest.h \ - src/qgcunittest/TCPLoopBackServer.h \ - src/qgcunittest/UnitTest.h \ - -SOURCES += \ - src/AnalyzeView/LogDownloadTest.cc \ - src/FactSystem/FactSystemTestBase.cc \ - src/FactSystem/FactSystemTestGeneric.cc \ - src/FactSystem/FactSystemTestPX4.cc \ - src/FactSystem/ParameterManagerTest.cc \ - src/MissionManager/ComplexMissionItemTest.cc \ - src/MissionManager/MissionCommandTreeTest.cc \ - src/MissionManager/MissionControllerTest.cc \ - src/MissionManager/MissionControllerManagerTest.cc \ - src/MissionManager/MissionItemTest.cc \ - src/MissionManager/MissionManagerTest.cc \ - src/MissionManager/SimpleMissionItemTest.cc \ - src/qgcunittest/GeoTest.cc \ - src/qgcunittest/FileDialogTest.cc \ - src/qgcunittest/FileManagerTest.cc \ - src/qgcunittest/FlightGearTest.cc \ - src/qgcunittest/LinkManagerTest.cc \ - src/qgcunittest/MainWindowTest.cc \ - src/qgcunittest/MavlinkLogTest.cc \ - src/qgcunittest/MessageBoxTest.cc \ - src/qgcunittest/MultiSignalSpy.cc \ - src/qgcunittest/RadioConfigTest.cc \ - src/qgcunittest/TCPLinkTest.cc \ - src/qgcunittest/TCPLoopBackServer.cc \ - src/qgcunittest/UnitTest.cc \ - src/qgcunittest/UnitTestList.cc \ -} # !MobileBuild -} # DebugBuild + HEADERS += src/QmlControls/QmlTestWidget.h + SOURCES += src/QmlControls/QmlTestWidget.cc +} # # Firmware Plugin Support # INCLUDEPATH += \ - src/AutoPilotPlugins/APM \ src/AutoPilotPlugins/Common \ - src/AutoPilotPlugins/PX4 \ src/FirmwarePlugin \ - src/FirmwarePlugin/APM \ - src/FirmwarePlugin/PX4 \ src/Vehicle \ src/VehicleSetup \ HEADERS+= \ src/AutoPilotPlugins/AutoPilotPlugin.h \ - src/AutoPilotPlugins/AutoPilotPluginManager.h \ - src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h \ - src/AutoPilotPlugins/APM/APMAirframeComponent.h \ - src/AutoPilotPlugins/APM/APMAirframeComponentController.h \ - src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.h \ - src/AutoPilotPlugins/APM/APMCameraComponent.h \ - src/AutoPilotPlugins/APM/APMLightsComponent.h \ - src/AutoPilotPlugins/APM/APMCompassCal.h \ - src/AutoPilotPlugins/APM/APMFlightModesComponent.h \ - src/AutoPilotPlugins/APM/APMFlightModesComponentController.h \ - src/AutoPilotPlugins/APM/APMPowerComponent.h \ - src/AutoPilotPlugins/APM/APMRadioComponent.h \ - src/AutoPilotPlugins/APM/APMSafetyComponent.h \ - src/AutoPilotPlugins/APM/APMSensorsComponent.h \ - src/AutoPilotPlugins/APM/APMSensorsComponentController.h \ - src/AutoPilotPlugins/APM/APMTuningComponent.h \ src/AutoPilotPlugins/Common/MotorComponent.h \ src/AutoPilotPlugins/Common/RadioComponentController.h \ src/AutoPilotPlugins/Common/ESP8266ComponentController.h \ src/AutoPilotPlugins/Common/ESP8266Component.h \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.h \ - src/AutoPilotPlugins/PX4/AirframeComponent.h \ - src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h \ - src/AutoPilotPlugins/PX4/AirframeComponentController.h \ - src/AutoPilotPlugins/PX4/FlightModesComponent.h \ - src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.h \ - src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h \ - src/AutoPilotPlugins/PX4/PowerComponent.h \ - src/AutoPilotPlugins/PX4/PowerComponentController.h \ - src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h \ - src/AutoPilotPlugins/PX4/PX4RadioComponent.h \ - src/AutoPilotPlugins/PX4/CameraComponent.h \ - src/AutoPilotPlugins/PX4/SafetyComponent.h \ - src/AutoPilotPlugins/PX4/SensorsComponent.h \ - src/AutoPilotPlugins/PX4/SensorsComponentController.h \ - src/AutoPilotPlugins/PX4/PX4TuningComponent.h \ src/FirmwarePlugin/FirmwarePluginManager.h \ src/FirmwarePlugin/FirmwarePlugin.h \ - src/FirmwarePlugin/APM/APMFirmwarePlugin.h \ - src/FirmwarePlugin/APM/APMGeoFenceManager.h \ - src/FirmwarePlugin/APM/APMParameterMetaData.h \ - src/FirmwarePlugin/APM/APMRallyPointManager.h \ - src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h \ - src/FirmwarePlugin/PX4/px4_custom_mode.h \ - src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h \ - src/FirmwarePlugin/PX4/PX4GeoFenceManager.h \ - src/FirmwarePlugin/PX4/PX4ParameterMetaData.h \ src/Vehicle/MultiVehicleManager.h \ src/Vehicle/Vehicle.h \ src/VehicleSetup/VehicleComponent.h \ @@ -730,66 +697,144 @@ HEADERS += \ SOURCES += \ src/AutoPilotPlugins/AutoPilotPlugin.cc \ - src/AutoPilotPlugins/AutoPilotPluginManager.cc \ - src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc \ - src/AutoPilotPlugins/APM/APMAirframeComponent.cc \ - src/AutoPilotPlugins/APM/APMAirframeComponentController.cc \ - src/AutoPilotPlugins/APM/APMCameraComponent.cc \ - src/AutoPilotPlugins/APM/APMLightsComponent.cc \ - src/AutoPilotPlugins/APM/APMCompassCal.cc \ - src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \ - src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc \ - src/AutoPilotPlugins/APM/APMPowerComponent.cc \ - src/AutoPilotPlugins/APM/APMRadioComponent.cc \ - src/AutoPilotPlugins/APM/APMSafetyComponent.cc \ - src/AutoPilotPlugins/APM/APMSensorsComponent.cc \ - src/AutoPilotPlugins/APM/APMSensorsComponentController.cc \ - src/AutoPilotPlugins/APM/APMTuningComponent.cc \ src/AutoPilotPlugins/Common/MotorComponent.cc \ src/AutoPilotPlugins/Common/RadioComponentController.cc \ src/AutoPilotPlugins/Common/ESP8266ComponentController.cc \ src/AutoPilotPlugins/Common/ESP8266Component.cc \ - src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.cc \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.cc \ - src/AutoPilotPlugins/PX4/AirframeComponent.cc \ - src/AutoPilotPlugins/PX4/AirframeComponentAirframes.cc \ - src/AutoPilotPlugins/PX4/AirframeComponentController.cc \ - src/AutoPilotPlugins/PX4/FlightModesComponent.cc \ - src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc \ - src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc \ - src/AutoPilotPlugins/PX4/PowerComponent.cc \ - src/AutoPilotPlugins/PX4/PowerComponentController.cc \ - src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc \ - src/AutoPilotPlugins/PX4/PX4RadioComponent.cc \ - src/AutoPilotPlugins/PX4/CameraComponent.cc \ - src/AutoPilotPlugins/PX4/SafetyComponent.cc \ - src/AutoPilotPlugins/PX4/SensorsComponent.cc \ - src/AutoPilotPlugins/PX4/SensorsComponentController.cc \ - src/AutoPilotPlugins/PX4/PX4TuningComponent.cc \ - src/FirmwarePlugin/APM/APMFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/APMGeoFenceManager.cc \ - src/FirmwarePlugin/APM/APMParameterMetaData.cc \ - src/FirmwarePlugin/APM/APMRallyPointManager.cc \ - src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc \ src/FirmwarePlugin/FirmwarePlugin.cc \ src/FirmwarePlugin/FirmwarePluginManager.cc \ - src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc \ - src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc \ - src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc \ src/Vehicle/MultiVehicleManager.cc \ src/Vehicle/Vehicle.cc \ src/VehicleSetup/VehicleComponent.cc \ !MobileBuild { -SOURCES += \ + SOURCES += \ src/VehicleSetup/FirmwareUpgradeController.cc \ src/VehicleSetup/Bootloader.cc \ src/VehicleSetup/PX4FirmwareUpgradeThread.cc \ src/VehicleSetup/FirmwareImage.cc \ + +} + +# ArduPilot FirmwarePlugin +APMFirmwarePlugin { + INCLUDEPATH += \ + src/AutoPilotPlugins/APM \ + src/FirmwarePlugin/APM \ + + HEADERS += \ + src/FirmwarePlugin/APM/APMFirmwarePlugin.h \ + src/FirmwarePlugin/APM/APMGeoFenceManager.h \ + src/FirmwarePlugin/APM/APMParameterMetaData.h \ + src/FirmwarePlugin/APM/APMRallyPointManager.h \ + src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h \ + src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h \ + src/AutoPilotPlugins/APM/APMAirframeComponent.h \ + src/AutoPilotPlugins/APM/APMAirframeComponentController.h \ + src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.h \ + src/AutoPilotPlugins/APM/APMAirframeLoader.h \ + src/AutoPilotPlugins/APM/APMCameraComponent.h \ + src/AutoPilotPlugins/APM/APMLightsComponent.h \ + src/AutoPilotPlugins/APM/APMCompassCal.h \ + src/AutoPilotPlugins/APM/APMFlightModesComponent.h \ + src/AutoPilotPlugins/APM/APMFlightModesComponentController.h \ + src/AutoPilotPlugins/APM/APMPowerComponent.h \ + src/AutoPilotPlugins/APM/APMRadioComponent.h \ + src/AutoPilotPlugins/APM/APMSafetyComponent.h \ + src/AutoPilotPlugins/APM/APMSensorsComponent.h \ + src/AutoPilotPlugins/APM/APMSensorsComponentController.h \ + src/AutoPilotPlugins/APM/APMTuningComponent.h \ + + SOURCES += \ + src/FirmwarePlugin/APM/APMFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/APMGeoFenceManager.cc \ + src/FirmwarePlugin/APM/APMParameterMetaData.cc \ + src/FirmwarePlugin/APM/APMRallyPointManager.cc \ + src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc \ + src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc \ + src/AutoPilotPlugins/APM/APMAirframeComponent.cc \ + src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.cc \ + src/AutoPilotPlugins/APM/APMAirframeComponentController.cc \ + src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ + src/AutoPilotPlugins/APM/APMCameraComponent.cc \ + src/AutoPilotPlugins/APM/APMLightsComponent.cc \ + src/AutoPilotPlugins/APM/APMCompassCal.cc \ + src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \ + src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc \ + src/AutoPilotPlugins/APM/APMPowerComponent.cc \ + src/AutoPilotPlugins/APM/APMRadioComponent.cc \ + src/AutoPilotPlugins/APM/APMSafetyComponent.cc \ + src/AutoPilotPlugins/APM/APMSensorsComponent.cc \ + src/AutoPilotPlugins/APM/APMSensorsComponentController.cc \ + src/AutoPilotPlugins/APM/APMTuningComponent.cc \ +} + +APMFirmwarePluginFactory { + HEADERS += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h + SOURCES += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc +} + +# PX4 FirmwarePlugin + +PX4FirmwarePlugin { + INCLUDEPATH += \ + src/AutoPilotPlugins/PX4 \ + src/FirmwarePlugin/PX4 \ + + HEADERS+= \ + src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h \ + src/FirmwarePlugin/PX4/PX4GeoFenceManager.h \ + src/FirmwarePlugin/PX4/PX4ParameterMetaData.h \ + src/AutoPilotPlugins/PX4/AirframeComponent.h \ + src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h \ + src/AutoPilotPlugins/PX4/AirframeComponentController.h \ + src/AutoPilotPlugins/PX4/FlightModesComponent.h \ + src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ + src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.h \ + src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h \ + src/AutoPilotPlugins/PX4/PowerComponent.h \ + src/AutoPilotPlugins/PX4/PowerComponentController.h \ + src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h \ + src/AutoPilotPlugins/PX4/PX4RadioComponent.h \ + src/AutoPilotPlugins/PX4/CameraComponent.h \ + src/AutoPilotPlugins/PX4/SafetyComponent.h \ + src/AutoPilotPlugins/PX4/SensorsComponent.h \ + src/AutoPilotPlugins/PX4/SensorsComponentController.h \ + src/AutoPilotPlugins/PX4/PX4TuningComponent.h \ + + SOURCES += \ + src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc \ + src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc \ + src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc \ + src/AutoPilotPlugins/PX4/AirframeComponent.cc \ + src/AutoPilotPlugins/PX4/AirframeComponentAirframes.cc \ + src/AutoPilotPlugins/PX4/AirframeComponentController.cc \ + src/AutoPilotPlugins/PX4/FlightModesComponent.cc \ + src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc \ + src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ + src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc \ + src/AutoPilotPlugins/PX4/PowerComponent.cc \ + src/AutoPilotPlugins/PX4/PowerComponentController.cc \ + src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc \ + src/AutoPilotPlugins/PX4/PX4RadioComponent.cc \ + src/AutoPilotPlugins/PX4/CameraComponent.cc \ + src/AutoPilotPlugins/PX4/SafetyComponent.cc \ + src/AutoPilotPlugins/PX4/SensorsComponent.cc \ + src/AutoPilotPlugins/PX4/SensorsComponentController.cc \ + src/AutoPilotPlugins/PX4/PX4TuningComponent.cc \ +} + +PX4FirmwarePluginFactory { + HEADERS += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h + SOURCES += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc } # Fact System code diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc index 3f488f217..cc298091a 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc @@ -15,7 +15,6 @@ #include "APMAirframeComponentAirframes.h" #include "QGCMAVLink.h" #include "MultiVehicleManager.h" -#include "AutoPilotPluginManager.h" #include "QGCApplication.h" #include "QGCFileDownload.h" #include "ParameterManager.h" diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 9b8b70245..618e2a7ab 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -9,7 +9,6 @@ #include "APMAutoPilotPlugin.h" -#include "AutoPilotPluginManager.h" #include "UAS.h" #include "FirmwarePlugin/APM/APMParameterMetaData.h" // FIXME: Hack #include "FirmwarePlugin/APM/APMFirmwarePlugin.h" // FIXME: Hack diff --git a/src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc b/src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc index 970d9fced..58410af77 100644 --- a/src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc @@ -10,7 +10,6 @@ #include "APMFlightModesComponentController.h" #include "QGCMAVLink.h" -#include "AutoPilotPluginManager.h" #include #include diff --git a/src/AutoPilotPlugins/AutoPilotPluginManager.cc b/src/AutoPilotPlugins/AutoPilotPluginManager.cc deleted file mode 100644 index b45391758..000000000 --- a/src/AutoPilotPlugins/AutoPilotPluginManager.cc +++ /dev/null @@ -1,29 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/// @file -/// @author Don Gagne - -#include "AutoPilotPluginManager.h" -#include "PX4/PX4AutoPilotPlugin.h" -#include "APM/APMAutoPilotPlugin.h" -#include "Generic/GenericAutoPilotPlugin.h" - -AutoPilotPlugin* AutoPilotPluginManager::newAutopilotPluginForVehicle(Vehicle* vehicle) -{ - switch (vehicle->firmwareType()) { - case MAV_AUTOPILOT_PX4: - return new PX4AutoPilotPlugin(vehicle, vehicle); - case MAV_AUTOPILOT_ARDUPILOTMEGA: - return new APMAutoPilotPlugin(vehicle, vehicle); - default: - return new GenericAutoPilotPlugin(vehicle, vehicle); - } -} diff --git a/src/AutoPilotPlugins/AutoPilotPluginManager.h b/src/AutoPilotPlugins/AutoPilotPluginManager.h deleted file mode 100644 index 4927cfbc2..000000000 --- a/src/AutoPilotPlugins/AutoPilotPluginManager.h +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/// @file -/// @author Don Gagne - -#ifndef AUTOPILOTPLUGINMANAGER_H -#define AUTOPILOTPLUGINMANAGER_H - -#include -#include -#include - -#include "AutoPilotPlugin.h" -#include "Vehicle.h" -#include "QGCToolbox.h" - -class QGCApplication; - -class AutoPilotPluginManager : public QGCTool -{ - Q_OBJECT - -public: - AutoPilotPluginManager(QGCApplication* app) : QGCTool(app) { } - - AutoPilotPlugin* newAutopilotPluginForVehicle(Vehicle* vehicle); -}; - -#endif diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc index bfe76f384..206b54bcd 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc @@ -13,7 +13,6 @@ /// @author Gus Grubba #include "ESP8266ComponentController.h" -#include "AutoPilotPluginManager.h" #include "QGCApplication.h" #include "UAS.h" #include "ParameterManager.h" diff --git a/src/AutoPilotPlugins/Common/MotorComponent.cc b/src/AutoPilotPlugins/Common/MotorComponent.cc index fe7b611d2..972d243ff 100644 --- a/src/AutoPilotPlugins/Common/MotorComponent.cc +++ b/src/AutoPilotPlugins/Common/MotorComponent.cc @@ -7,10 +7,7 @@ * ****************************************************************************/ - #include "MotorComponent.h" -#include "APMAutoPilotPlugin.h" -#include "APMAirframeComponent.h" MotorComponent::MotorComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : VehicleComponent(vehicle, autopilot, parent), diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index 0f2e4c626..46a27dbef 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -13,7 +13,6 @@ /// @author Don Gagne diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index 2048101a0..d656a998c 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -15,7 +15,6 @@ #include "AirframeComponentAirframes.h" #include "QGCMAVLink.h" #include "MultiVehicleManager.h" -#include "AutoPilotPluginManager.h" #include "QGCApplication.h" #include diff --git a/src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc b/src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc index e8044064e..57818a6de 100644 --- a/src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc +++ b/src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc @@ -13,7 +13,6 @@ #include "PX4AdvancedFlightModesController.h" #include "QGCMAVLink.h" -#include "AutoPilotPluginManager.h" #include #include diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index af9d98d3c..0b96501b3 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -9,7 +9,6 @@ #include "PX4AutoPilotPlugin.h" -#include "AutoPilotPluginManager.h" #include "PX4AirframeLoader.h" #include "PX4AdvancedFlightModesController.h" #include "AirframeComponentController.h" diff --git a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc index 8f0bebfd5..65ae2e7f1 100644 --- a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc +++ b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc @@ -10,7 +10,6 @@ #include "PX4SimpleFlightModesController.h" #include "QGCMAVLink.h" -#include "AutoPilotPluginManager.h" #include #include diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 6c1197cac..1adfafe65 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -17,7 +17,6 @@ #include "QGCApplication.h" #include "UASMessageHandler.h" #include "FirmwarePlugin.h" -#include "APMFirmwarePlugin.h" #include "UAS.h" #include "JsonHelper.h" @@ -236,7 +235,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString int totalWaitingParamCount = readWaitingParamCount + waitingWriteParamNameCount; if (totalWaitingParamCount) { qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "totalWaitingParamCount:" << totalWaitingParamCount; - } else if (_defaultComponentId != MAV_COMP_ID_ALL) { + } else if (_defaultComponentId != MAV_COMP_ID_ALL || _defaultComponentIdParam.isEmpty()) { // No more parameters to wait for, stop the timeout. Be careful to not stop timer if we don't have the default // component yet. qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Stopping _waitingParamTimeoutTimer (all requests satisfied)"; @@ -574,7 +573,7 @@ void ParameterManager::_waitingParamTimeout(void) } } - if (!paramsRequested && _defaultComponentId == MAV_COMP_ID_ALL && !_waitingForDefaultComponent) { + if (!paramsRequested && _defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty() && !_waitingForDefaultComponent) { // Initial load is complete but we still don't have default component params. Wait one more cycle to see if the // default component finally shows up. qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer - still don't have default component id"; @@ -975,16 +974,10 @@ void ParameterManager::_addMetaDataToDefaultComponent(void) QString metaDataFile; int majorVersion, minorVersion; - if (_vehicle->firmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // Parameter versioning is still not really figured out correctly. We need to handle ArduPilot specially based on vehicle version. - // The current three version are hardcoded in. - metaDataFile = ((APMFirmwarePlugin*)_vehicle->firmwarePlugin())->getParameterMetaDataFile(_vehicle); - qCDebug(ParameterManagerLog) << "Adding meta data to Vehicle file:major:minor" << metaDataFile; - } else { - // Load best parameter meta data set - metaDataFile = parameterMetaDataFile(_vehicle->firmwareType(), _parameterSetMajorVersion, majorVersion, minorVersion); - qCDebug(ParameterManagerLog) << "Adding meta data to Vehicle file:major:minor" << metaDataFile << majorVersion << minorVersion; - } + + // Load best parameter meta data set + metaDataFile = parameterMetaDataFile(_vehicle, _vehicle->firmwareType(), _parameterSetMajorVersion, majorVersion, minorVersion); + qCDebug(ParameterManagerLog) << "Adding meta data to Vehicle file:major:minor" << metaDataFile << majorVersion << minorVersion; _parameterMetaData = _vehicle->firmwarePlugin()->loadParameterMetaData(metaDataFile); @@ -1010,7 +1003,7 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) } } - if (!failIfNoDefaultComponent && _defaultComponentId == MAV_COMP_ID_ALL) { + if (!failIfNoDefaultComponent && _defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { // We are still waiting for default component to show up return; } @@ -1046,7 +1039,7 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) if (!qgcApp()->runningUnitTests()) { qCWarning(ParameterManagerLog) << _logVehiclePrefix() << "The following parameter indices could not be loaded after the maximum number of retries: " << indexList; } - } else if (_defaultComponentId == FactSystem::defaultComponentId && !_defaultComponentIdParam.isEmpty()) { + } else if (_defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { // Missing default component when we should have one _missingParameters = true; QString errorMsg = tr("QGroundControl did not receive parameters from the default component for vehicle %1. " @@ -1084,7 +1077,7 @@ void ParameterManager::_initialRequestTimeout(void) } } -QString ParameterManager::parameterMetaDataFile(MAV_AUTOPILOT firmwareType, int wantedMajorVersion, int& majorVersion, int& minorVersion) +QString ParameterManager::parameterMetaDataFile(Vehicle* vehicle, MAV_AUTOPILOT firmwareType, int wantedMajorVersion, int& majorVersion, int& minorVersion) { bool cacheHit = false; FirmwarePlugin* plugin = qgcApp()->toolbox()->firmwarePluginManager()->firmwarePluginForAutopilot(firmwareType, MAV_TYPE_QUADROTOR); @@ -1141,7 +1134,7 @@ QString ParameterManager::parameterMetaDataFile(MAV_AUTOPILOT firmwareType, int } int internalMinorVersion, internalMajorVersion; - QString internalMetaDataFile = plugin->internalParameterMetaDataFile(); + QString internalMetaDataFile = plugin->internalParameterMetaDataFile(vehicle); plugin->getParameterMetaDataVersionInfo(internalMetaDataFile, internalMajorVersion, internalMinorVersion); qCDebug(ParameterManagerLog) << "Internal meta data file:major:minor" << internalMetaDataFile << internalMajorVersion << internalMinorVersion; if (cacheHit) { @@ -1190,7 +1183,7 @@ void ParameterManager::cacheMetaDataFile(const QString& metaDataFile, MAV_AUTOPI // Find the cache hit closest to this new file int cacheMajorVersion, cacheMinorVersion; - QString cacheHit = ParameterManager::parameterMetaDataFile(firmwareType, newMajorVersion, cacheMajorVersion, cacheMinorVersion); + QString cacheHit = ParameterManager::parameterMetaDataFile(NULL, firmwareType, newMajorVersion, cacheMajorVersion, cacheMinorVersion); qCDebug(ParameterManagerLog) << "ParameterManager::cacheMetaDataFile cacheHit file:firmware:major;minor" << cacheHit << cacheMajorVersion << cacheMinorVersion; bool cacheNewFile = false; diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 18ea33e4e..106426277 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -99,7 +99,7 @@ public: /// @param[out] majorVersion Major version for found meta data /// @param[out] minorVersion Minor version for found meta data /// @return Meta data file name of best match, emptyString is none found - static QString parameterMetaDataFile(MAV_AUTOPILOT firmwareType, int wantedMajorVersion, int& majorVersion, int& minorVersion); + static QString parameterMetaDataFile(Vehicle* vehicle, MAV_AUTOPILOT firmwareType, int wantedMajorVersion, int& majorVersion, int& minorVersion); /// If this file is newer than anything in the cache, cache it as the latest version static void cacheMetaDataFile(const QString& metaDataFile, MAV_AUTOPILOT firmwareType); diff --git a/src/FactSystem/ParameterManagerTest.cc b/src/FactSystem/ParameterManagerTest.cc index c47d8bed3..ae362f278 100644 --- a/src/FactSystem/ParameterManagerTest.cc +++ b/src/FactSystem/ParameterManagerTest.cc @@ -34,7 +34,7 @@ void ParameterManagerTest::_noFailureWorker(MockConfiguration::FailureMode_t fai QVERIFY(vehicle); // We should get progress bar updates during load - QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(parameterListProgress(float))); + QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(loadProgressChanged(float))); QCOMPARE(spyProgress.wait(2000), true); arguments = spyProgress.takeFirst(); QCOMPARE(arguments.count(), 1); @@ -64,6 +64,7 @@ void ParameterManagerTest::_requestListMissingParamSuccess(void) _noFailureWorker(MockConfiguration::FailMissingParamOnInitialReqest); } +#if 0 // Test no response to param_request_list void ParameterManagerTest::_requestListNoResponse(void) { @@ -85,7 +86,7 @@ void ParameterManagerTest::_requestListNoResponse(void) QVERIFY(vehicle); QSignalSpy spyParamsReady(vehicleMgr, SIGNAL(parameterReadyVehicleAvailableChanged(bool))); - QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(parameterListProgress(float))); + QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(loadProgressChanged(float))); // We should not get any progress bar updates, nor a parameter ready signal QCOMPARE(spyProgress.wait(500), false); @@ -94,6 +95,7 @@ void ParameterManagerTest::_requestListNoResponse(void) // User should have been notified checkMultipleExpectedMessageBox(5); } +#endif // MockLink will fail to send a param on initial request, it will also fail to send it on subsequent // param_read requests. @@ -120,7 +122,7 @@ void ParameterManagerTest::_requestListMissingParamFail(void) QVERIFY(vehicle); QSignalSpy spyParamsReady(vehicleMgr, SIGNAL(parameterReadyVehicleAvailableChanged(bool))); - QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(parameterListProgress(float))); + QSignalSpy spyProgress(vehicle->parameterManager(), SIGNAL(loadProgressChanged(float))); // We will get progress bar updates, since it will fail after getting partially through the request QCOMPARE(spyProgress.wait(2000), true); diff --git a/src/FactSystem/ParameterManagerTest.h b/src/FactSystem/ParameterManagerTest.h index 374eebba5..7d732169f 100644 --- a/src/FactSystem/ParameterManagerTest.h +++ b/src/FactSystem/ParameterManagerTest.h @@ -22,7 +22,8 @@ class ParameterManagerTest : public UnitTest private slots: void _noFailure(void); - void _requestListNoResponse(void); + // FIXME: Hack to work around changed no reponse handling + //void _requestListNoResponse(void); void _requestListMissingParamSuccess(void); void _requestListMissingParamFail(void); diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 526df49bc..7b3d2ced1 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -12,9 +12,12 @@ /// @author Don Gagne #include "APMFirmwarePlugin.h" -#include "AutoPilotPlugins/APM/APMAutoPilotPlugin.h" // FIXME: Hack +#include "APMAutoPilotPlugin.h" #include "QGCMAVLink.h" #include "QGCApplication.h" +#include "APMFlightModesComponentController.h" +#include "APMAirframeComponentController.h" +#include "APMSensorsComponentController.h" #include @@ -140,7 +143,14 @@ APMFirmwarePlugin::APMFirmwarePlugin(void) : _coaxialMotors(false) , _textSeverityAdjustmentNeeded(false) { + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController"); +} +AutoPilotPlugin* APMFirmwarePlugin::autopilotPlugin(Vehicle* vehicle) +{ + return new APMAutoPilotPlugin(vehicle, vehicle); } bool APMFirmwarePlugin::isCapable(const Vehicle* /*vehicle*/, FirmwareCapabilities capabilities) @@ -654,8 +664,8 @@ QList APMFirmwarePlugin::supportedMissionCommands(void) << MAV_CMD_DO_AUTOTUNE_ENABLE << MAV_CMD_NAV_VTOL_TAKEOFF << MAV_CMD_NAV_VTOL_LAND << MAV_CMD_DO_VTOL_TRANSITION; #if 0 - // Waiting for module update - << MAV_CMD_DO_SET_REVERSE; + // Waiting for module update + << MAV_CMD_DO_SET_REVERSE; #endif return list; @@ -723,7 +733,7 @@ void APMFirmwarePlugin::_artooSocketError(QAbstractSocket::SocketError socketErr qgcApp()->showMessage(tr("Error during Solo video link setup: %1").arg(socketError)); } -QString APMFirmwarePlugin::getParameterMetaDataFile(Vehicle* vehicle) +QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) { switch (vehicle->vehicleType()) { case MAV_TYPE_QUADROTOR: diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index f9ca107ca..7306599c4 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -75,29 +75,28 @@ public: QList componentsForVehicle(AutoPilotPlugin* vehicle) final; QList supportedMissionCommands(void) final; - bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities); - QStringList flightModes (Vehicle* vehicle) final; - QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; - bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) final; - bool isGuidedMode (const Vehicle* vehicle) const final; - void pauseVehicle (Vehicle* vehicle); - int manualControlReservedButtonCount(void); - bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) final; - void adjustOutgoingMavlinkMessage (Vehicle* vehicle, LinkInterface* outgoingLink, mavlink_message_t* message) final; - void initializeVehicle (Vehicle* vehicle) final; - bool sendHomePositionToVehicle (void) final; - void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; - QString getDefaultComponentIdParam (void) const final { return QString("SYSID_SW_TYPE"); } - QString missionCommandOverrides (MAV_TYPE vehicleType) const; - QString getVersionParam (void) final { return QStringLiteral("SYSID_SW_MREV"); } - QString internalParameterMetaDataFile (void) final { return QString(":/FirmwarePlugin/APM/APMParameterFactMetaData.xml"); } - void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { APMParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } - QObject* loadParameterMetaData (const QString& metaDataFile); - GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new APMGeoFenceManager(vehicle); } - RallyPointManager* newRallyPointManager (Vehicle* vehicle) { return new APMRallyPointManager(vehicle); } - QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } - - QString getParameterMetaDataFile(Vehicle* vehicle); + AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) final; + bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities); + QStringList flightModes (Vehicle* vehicle) final; + QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; + bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) final; + bool isGuidedMode (const Vehicle* vehicle) const final; + void pauseVehicle (Vehicle* vehicle); + int manualControlReservedButtonCount(void); + bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) final; + void adjustOutgoingMavlinkMessage (Vehicle* vehicle, LinkInterface* outgoingLink, mavlink_message_t* message) final; + void initializeVehicle (Vehicle* vehicle) final; + bool sendHomePositionToVehicle (void) final; + void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; + QString getDefaultComponentIdParam (void) const final { return QString("SYSID_SW_TYPE"); } + QString missionCommandOverrides (MAV_TYPE vehicleType) const; + QString getVersionParam (void) final { return QStringLiteral("SYSID_SW_MREV"); } + QString internalParameterMetaDataFile (Vehicle* vehicle) final; + void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { APMParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } + QObject* loadParameterMetaData (const QString& metaDataFile); + GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new APMGeoFenceManager(vehicle); } + RallyPointManager* newRallyPointManager (Vehicle* vehicle) { return new APMRallyPointManager(vehicle); } + QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } protected: /// All access to singleton is through stack specific implementation diff --git a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc new file mode 100644 index 000000000..e82523e8d --- /dev/null +++ b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc @@ -0,0 +1,71 @@ +/**************************************************************************** + * + * (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 "APMFirmwarePluginFactory.h" +#include "APM/ArduCopterFirmwarePlugin.h" +#include "APM/ArduPlaneFirmwarePlugin.h" +#include "APM/ArduRoverFirmwarePlugin.h" +#include "APM/ArduSubFirmwarePlugin.h" + +APMFirmwarePluginFactory APMFirmwarePluginFactory; + +APMFirmwarePluginFactory::APMFirmwarePluginFactory(void) + : _arduCopterPluginInstance(NULL) + , _arduPlanePluginInstance(NULL) + , _arduRoverPluginInstance(NULL) + , _arduSubPluginInstance(NULL) +{ + +} + +QList APMFirmwarePluginFactory::knownFirmwareTypes(void) const +{ + QList list; + + list.append(MAV_AUTOPILOT_ARDUPILOTMEGA); + return list; +} + +FirmwarePlugin* APMFirmwarePluginFactory::firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) +{ + if (autopilotType == MAV_AUTOPILOT_ARDUPILOTMEGA) { + switch (vehicleType) { + case MAV_TYPE_QUADROTOR: + case MAV_TYPE_HEXAROTOR: + case MAV_TYPE_OCTOROTOR: + case MAV_TYPE_TRICOPTER: + case MAV_TYPE_COAXIAL: + case MAV_TYPE_HELICOPTER: + if (!_arduCopterPluginInstance) { + _arduCopterPluginInstance = new ArduCopterFirmwarePlugin; + } + return _arduCopterPluginInstance; + case MAV_TYPE_FIXED_WING: + if (!_arduPlanePluginInstance) { + _arduPlanePluginInstance = new ArduPlaneFirmwarePlugin; + } + return _arduPlanePluginInstance; + case MAV_TYPE_GROUND_ROVER: + case MAV_TYPE_SURFACE_BOAT: + if (!_arduRoverPluginInstance) { + _arduRoverPluginInstance = new ArduRoverFirmwarePlugin; + } + return _arduRoverPluginInstance; + case MAV_TYPE_SUBMARINE: + if (!_arduSubPluginInstance) { + _arduSubPluginInstance = new ArduSubFirmwarePlugin; + } + return _arduSubPluginInstance; + default: + break; + } + } + + return NULL; +} diff --git a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h new file mode 100644 index 000000000..7ca1e0f03 --- /dev/null +++ b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (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 APMFirmwarePluginFactory_H +#define APMFirmwarePluginFactory_H + +#include "FirmwarePlugin.h" + +class ArduCopterFirmwarePlugin; +class ArduPlaneFirmwarePlugin; +class ArduRoverFirmwarePlugin; +class ArduSubFirmwarePlugin; + +class APMFirmwarePluginFactory : public FirmwarePluginFactory +{ + Q_OBJECT + +public: + APMFirmwarePluginFactory(void); + + QList knownFirmwareTypes (void) const final; + FirmwarePlugin* firmwarePluginForAutopilot (MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) final; + +private: + ArduCopterFirmwarePlugin* _arduCopterPluginInstance; + ArduPlaneFirmwarePlugin* _arduPlanePluginInstance; + ArduRoverFirmwarePlugin* _arduRoverPluginInstance; + ArduSubFirmwarePlugin* _arduSubPluginInstance; +}; + +#endif diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index bbc330969..5a19fc777 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -7,14 +7,37 @@ * ****************************************************************************/ - #include "FirmwarePlugin.h" #include "QGCApplication.h" +#include "Generic/GenericAutoPilotPlugin.h" #include +static FirmwarePluginFactoryRegister* _instance = NULL; + const char* guided_mode_not_supported_by_vehicle = "Guided mode not supported by Vehicle."; +const char* FirmwarePlugin::px4FollowMeFlightMode = "Follow Me"; + +FirmwarePluginFactory::FirmwarePluginFactory(void) +{ + FirmwarePluginFactoryRegister::instance()->registerPluginFactory(this); +} + +FirmwarePluginFactoryRegister* FirmwarePluginFactoryRegister::instance(void) +{ + if (!_instance) { + _instance = new FirmwarePluginFactoryRegister; + } + + return _instance; +} + +AutoPilotPlugin* FirmwarePlugin::autopilotPlugin(Vehicle* vehicle) +{ + return new GenericAutoPilotPlugin(vehicle, vehicle); +} + bool FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) { Q_UNUSED(vehicle); diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 6038748b6..640b491c8 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -61,6 +61,9 @@ public: /// value: remapParamNameMinorVersionRemapMap_t entry typedef QMap remapParamNameMajorVersionMap_t; + /// @return The AutoPilotPlugin associated with this firmware plugin. Must be overriden. + virtual AutoPilotPlugin* autopilotPlugin(Vehicle* vehicle); + /// Called when Vehicle is first created to perform any firmware specific setup. virtual void initializeVehicle(Vehicle* vehicle); @@ -181,7 +184,7 @@ public: virtual void getParameterMetaDataVersionInfo(const QString& metaDataFile, int& majorVersion, int& minorVersion); /// Returns the internal resource parameter meta date file. - virtual QString internalParameterMetaDataFile(void) { return QString(); } + virtual QString internalParameterMetaDataFile(Vehicle* vehicle) { Q_UNUSED(vehicle); return QString(); } /// Loads the specified parameter meta data file. /// @return Opaque parameter meta data information which must be stored with Vehicle. Vehicle is responsible to @@ -225,6 +228,42 @@ public: /// Return the resource file which contains the brand image for the vehicle. virtual QString brandImage(const Vehicle* vehicle) const { Q_UNUSED(vehicle) return QString(); } + + // FIXME: Hack workaround for non pluginize FollowMe support + static const char* px4FollowMeFlightMode; +}; + +class FirmwarePluginFactory : public QObject +{ + Q_OBJECT + +public: + FirmwarePluginFactory(void); + + /// Returns appropriate plugin for autopilot type. + /// @param autopilotType Type of autopilot to return plugin for. + /// @param vehicleType Vehicle type of autopilot to return plugin for. + /// @return Singleton FirmwarePlugin instance for the specified MAV_AUTOPILOT. + virtual FirmwarePlugin* firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) = 0; + + /// @return List of autopilot types this plugin supports. + virtual QList knownFirmwareTypes(void) const = 0; +}; + +class FirmwarePluginFactoryRegister : public QObject +{ + Q_OBJECT + +public: + static FirmwarePluginFactoryRegister* instance(void); + + /// Registers the specified logging category to the system. + void registerPluginFactory(FirmwarePluginFactory* pluginFactory) { _factoryList.append(pluginFactory); } + + QList pluginFactories(void) const { return _factoryList; } + +private: + QList _factoryList; }; #endif diff --git a/src/FirmwarePlugin/FirmwarePluginManager.cc b/src/FirmwarePlugin/FirmwarePluginManager.cc index b7ade237b..b2ec571cd 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.cc +++ b/src/FirmwarePlugin/FirmwarePluginManager.cc @@ -12,91 +12,50 @@ /// @author Don Gagne #include "FirmwarePluginManager.h" -#include "APM/ArduCopterFirmwarePlugin.h" -#include "APM/ArduPlaneFirmwarePlugin.h" -#include "APM/ArduRoverFirmwarePlugin.h" -#include "APM/ArduSubFirmwarePlugin.h" -#include "PX4/PX4FirmwarePlugin.h" +#include "FirmwarePlugin.h" FirmwarePluginManager::FirmwarePluginManager(QGCApplication* app) : QGCTool(app) - , _arduCopterFirmwarePlugin(NULL) - , _arduPlaneFirmwarePlugin(NULL) - , _arduRoverFirmwarePlugin(NULL) - , _arduSubFirmwarePlugin(NULL) , _genericFirmwarePlugin(NULL) - , _px4FirmwarePlugin(NULL) { } FirmwarePluginManager::~FirmwarePluginManager() { - delete _arduCopterFirmwarePlugin; - delete _arduPlaneFirmwarePlugin; - delete _arduRoverFirmwarePlugin; - delete _arduSubFirmwarePlugin; delete _genericFirmwarePlugin; - delete _px4FirmwarePlugin; } -QList FirmwarePluginManager::knownFirmwareTypes(void) const +QList FirmwarePluginManager::knownFirmwareTypes(void) { - QList list; - list << MAV_AUTOPILOT_GENERIC << MAV_AUTOPILOT_PX4 << MAV_AUTOPILOT_ARDUPILOTMEGA; - return list; + if (_knownFirmwareTypes.isEmpty()) { + QList factoryList = FirmwarePluginFactoryRegister::instance()->pluginFactories(); + + for (int i=0; iknownFirmwareTypes()); + } + } + + _knownFirmwareTypes.append(MAV_AUTOPILOT_GENERIC); + + return _knownFirmwareTypes; } FirmwarePlugin* FirmwarePluginManager::firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) { - switch (autopilotType) { - case MAV_AUTOPILOT_ARDUPILOTMEGA: - switch (vehicleType) { - case MAV_TYPE_QUADROTOR: - case MAV_TYPE_HEXAROTOR: - case MAV_TYPE_OCTOROTOR: - case MAV_TYPE_TRICOPTER: - case MAV_TYPE_COAXIAL: - case MAV_TYPE_HELICOPTER: - if (!_arduCopterFirmwarePlugin) { - _arduCopterFirmwarePlugin = new ArduCopterFirmwarePlugin; - } - return _arduCopterFirmwarePlugin; - case MAV_TYPE_FIXED_WING: - if (!_arduPlaneFirmwarePlugin) { - _arduPlaneFirmwarePlugin = new ArduPlaneFirmwarePlugin; - } - return _arduPlaneFirmwarePlugin; - case MAV_TYPE_GROUND_ROVER: - case MAV_TYPE_SURFACE_BOAT: - if (!_arduRoverFirmwarePlugin) { - _arduRoverFirmwarePlugin = new ArduRoverFirmwarePlugin; - } - return _arduRoverFirmwarePlugin; - case MAV_TYPE_SUBMARINE: - if (!_arduSubFirmwarePlugin) { - _arduSubFirmwarePlugin = new ArduSubFirmwarePlugin; - } - return _arduSubFirmwarePlugin; - default: - break; - } - case MAV_AUTOPILOT_PX4: - if (!_px4FirmwarePlugin) { - _px4FirmwarePlugin = new PX4FirmwarePlugin; + FirmwarePlugin* _plugin = NULL; + QList factoryList = FirmwarePluginFactoryRegister::instance()->pluginFactories(); + + // Find the plugin which supports this vehicle + for (int i=0; ifirmwarePluginForAutopilot(autopilotType, vehicleType))) { + return _plugin; } - return _px4FirmwarePlugin; - default: - break; } + // Default plugin fallback if (!_genericFirmwarePlugin) { _genericFirmwarePlugin = new FirmwarePlugin; } return _genericFirmwarePlugin; } - -void FirmwarePluginManager::clearSettings(void) -{ - // FIXME: NYI -} diff --git a/src/FirmwarePlugin/FirmwarePluginManager.h b/src/FirmwarePlugin/FirmwarePluginManager.h index efdbb07c5..0e5f1a4f0 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.h +++ b/src/FirmwarePlugin/FirmwarePluginManager.h @@ -21,11 +21,6 @@ #include "QGCToolbox.h" class QGCApplication; -class ArduCopterFirmwarePlugin; -class ArduPlaneFirmwarePlugin; -class ArduRoverFirmwarePlugin; -class ArduSubFirmwarePlugin; -class PX4FirmwarePlugin; /// FirmwarePluginManager is a singleton which is used to return the correct FirmwarePlugin for a MAV_AUTOPILOT type. @@ -37,7 +32,7 @@ public: FirmwarePluginManager(QGCApplication* app); ~FirmwarePluginManager(); - QList knownFirmwareTypes(void) const; + QList knownFirmwareTypes(void); /// Returns appropriate plugin for autopilot type. /// @param autopilotType Type of autopilot to return plugin for. @@ -45,16 +40,9 @@ public: /// @return Singleton FirmwarePlugin instance for the specified MAV_AUTOPILOT. FirmwarePlugin* firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType); - /// Clears settings from all firmware plugins. - void clearSettings(void); - private: - ArduCopterFirmwarePlugin* _arduCopterFirmwarePlugin; - ArduPlaneFirmwarePlugin* _arduPlaneFirmwarePlugin; - ArduRoverFirmwarePlugin* _arduRoverFirmwarePlugin; - ArduSubFirmwarePlugin* _arduSubFirmwarePlugin; - FirmwarePlugin* _genericFirmwarePlugin; - PX4FirmwarePlugin* _px4FirmwarePlugin; + FirmwarePlugin* _genericFirmwarePlugin; + QList _knownFirmwareTypes; }; #endif diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 6510bfd77..9644e8a83 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -14,7 +14,13 @@ #include "PX4FirmwarePlugin.h" #include "PX4ParameterMetaData.h" #include "QGCApplication.h" -#include "AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h" // FIXME: Hack +#include "PX4AutoPilotPlugin.h" +#include "PX4AdvancedFlightModesController.h" +#include "PX4SimpleFlightModesController.h" +#include "AirframeComponentController.h" +#include "SensorsComponentController.h" +#include "PowerComponentController.h" +#include "RadioComponentController.h" #include @@ -72,7 +78,17 @@ static const struct Modes2Name rgModes2Name[] = { PX4FirmwarePlugin::PX4FirmwarePlugin(void) : _versionNotified(false) { + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PX4AdvancedFlightModesController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PX4SimpleFlightModesController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "AirframeComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "SensorsComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PowerComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "RadioComponentController"); +} +AutoPilotPlugin* PX4FirmwarePlugin::autopilotPlugin(Vehicle* vehicle) +{ + return new PX4AutoPilotPlugin(vehicle, vehicle); } QList PX4FirmwarePlugin::componentsForVehicle(AutoPilotPlugin* vehicle) @@ -177,10 +193,11 @@ bool PX4FirmwarePlugin::supportsManualControl(void) bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) { - if(vehicle->multiRotor()) { + if (vehicle->multiRotor()) { return (capabilities & (MavCmdPreflightStorageCapability | GuidedModeCapability | SetFlightModeCapability | PauseVehicleCapability | OrbitModeCapability)) == capabilities; + } else { + return (capabilities & (MavCmdPreflightStorageCapability | GuidedModeCapability | SetFlightModeCapability | PauseVehicleCapability)) == capabilities; } - return (capabilities & (MavCmdPreflightStorageCapability | GuidedModeCapability | SetFlightModeCapability | PauseVehicleCapability)) == capabilities; } void PX4FirmwarePlugin::initializeVehicle(Vehicle* vehicle) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index 47f963e6d..c4a340f2d 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -31,6 +31,7 @@ public: QList componentsForVehicle(AutoPilotPlugin* vehicle) final; QList supportedMissionCommands(void) final; + AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) final; bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities) final; QStringList flightModes (Vehicle* vehicle) final; QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; @@ -52,7 +53,7 @@ public: QString getDefaultComponentIdParam (void) const final { return QString("SYS_AUTOSTART"); } QString missionCommandOverrides (MAV_TYPE vehicleType) const final; QString getVersionParam (void) final { return QString("SYS_PARAM_VER"); } - QString internalParameterMetaDataFile (void) final { return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } + QString internalParameterMetaDataFile (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { PX4ParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } QObject* loadParameterMetaData (const QString& metaDataFile); bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message); diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc new file mode 100644 index 000000000..4b742f90b --- /dev/null +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc @@ -0,0 +1,44 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +/// @file +/// @author Don Gagne + +#include "PX4FirmwarePluginFactory.h" +#include "PX4/PX4FirmwarePlugin.h" +PX4FirmwarePluginFactory PX4FirmwarePluginFactory; + +PX4FirmwarePluginFactory::PX4FirmwarePluginFactory(void) + : _pluginInstance(NULL) +{ + +} + +QList PX4FirmwarePluginFactory::knownFirmwareTypes(void) const +{ + QList list; + + list.append(MAV_AUTOPILOT_PX4); + return list; +} + +FirmwarePlugin* PX4FirmwarePluginFactory::firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) +{ + Q_UNUSED(vehicleType); + + if (autopilotType == MAV_AUTOPILOT_PX4) { + if (!_pluginInstance) { + _pluginInstance = new PX4FirmwarePlugin; + } + return _pluginInstance; + } + + return NULL; +} diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h new file mode 100644 index 000000000..254cf7b64 --- /dev/null +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * + * (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 PX4FirmwarePluginFactory_H +#define PX4FirmwarePluginFactory_H + +#include "FirmwarePlugin.h" + +class PX4FirmwarePlugin; + +class PX4FirmwarePluginFactory : public FirmwarePluginFactory +{ + Q_OBJECT + +public: + PX4FirmwarePluginFactory(void); + + QList knownFirmwareTypes (void) const final; + FirmwarePlugin* firmwarePluginForAutopilot (MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) final; + +private: + PX4FirmwarePlugin* _pluginInstance; +}; + +#endif diff --git a/src/FollowMe/FollowMe.cc b/src/FollowMe/FollowMe.cc index 196e3223e..1bb1e1880 100644 --- a/src/FollowMe/FollowMe.cc +++ b/src/FollowMe/FollowMe.cc @@ -11,7 +11,7 @@ #include #include "MultiVehicleManager.h" -#include "PX4FirmwarePlugin.h" +#include "FirmwarePlugin.h" #include "MAVLinkProtocol.h" #include "FollowMe.h" #include "Vehicle.h" @@ -38,7 +38,7 @@ void FollowMe::followMeHandleManager(const QString&) for (int i=0; i< vehicles.count(); i++) { Vehicle* vehicle = qobject_cast(vehicles[i]); - if (vehicle->px4Firmware() && vehicle->flightMode().compare(PX4FirmwarePlugin::followMeFlightMode, Qt::CaseInsensitive) == 0) { + if (vehicle->px4Firmware() && vehicle->flightMode().compare(FirmwarePlugin::px4FollowMeFlightMode, Qt::CaseInsensitive) == 0) { _enable(); return; } @@ -138,7 +138,7 @@ void FollowMe::_sendGCSMotionReport(void) for (int i=0; i< vehicles.count(); i++) { Vehicle* vehicle = qobject_cast(vehicles[i]); - if(vehicle->flightMode().compare(PX4FirmwarePlugin::followMeFlightMode, Qt::CaseInsensitive) == 0) { + if(vehicle->flightMode().compare(FirmwarePlugin::px4FollowMeFlightMode, Qt::CaseInsensitive) == 0) { mavlink_message_t message; mavlink_msg_follow_target_encode_chan(mavlinkProtocol->getSystemId(), mavlinkProtocol->getComponentId(), diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 137c5806b..c9608f75f 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -41,7 +41,6 @@ #include "LinkManager.h" #include "HomePositionManager.h" #include "UASMessageHandler.h" -#include "AutoPilotPluginManager.h" #include "QGCTemporaryFile.h" #include "QGCPalette.h" #include "QGCMapPalette.h" @@ -49,14 +48,6 @@ #include "ViewWidgetController.h" #include "ParameterEditorController.h" #include "CustomCommandWidgetController.h" -#include "PX4AdvancedFlightModesController.h" -#include "PX4SimpleFlightModesController.h" -#include "APMFlightModesComponentController.h" -#include "AirframeComponentController.h" -#include "SensorsComponentController.h" -#include "APMSensorsComponentController.h" -#include "PowerComponentController.h" -#include "RadioComponentController.h" #include "ESP8266ComponentController.h" #include "ScreenToolsController.h" #include "QGCMobileFileDialogController.h" @@ -65,11 +56,6 @@ #include "VehicleComponent.h" #include "FirmwarePluginManager.h" #include "MultiVehicleManager.h" -#include "APM/ArduCopterFirmwarePlugin.h" -#include "APM/ArduPlaneFirmwarePlugin.h" -#include "APM/ArduRoverFirmwarePlugin.h" -#include "APM/APMAirframeComponentController.h" -#include "PX4/PX4FirmwarePlugin.h" #include "Vehicle.h" #include "MavlinkQmlSingleton.h" #include "JoystickConfigController.h" @@ -88,7 +74,6 @@ #include "VideoSurface.h" #include "VideoReceiver.h" #include "LogDownloadController.h" -#include "PX4AirframeLoader.h" #include "ValuesWidgetController.h" #include "AppMessages.h" #include "SimulatedPosition.h" @@ -393,15 +378,6 @@ void QGCApplication::_initCommon(void) qmlRegisterUncreatableType ("QGroundControl.FlightMap", 1, 0, "QGCMapPolygon", "Reference only"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ParameterEditorController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMFlightModesComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PX4AdvancedFlightModesController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PX4SimpleFlightModesController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMAirframeComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "AirframeComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "APMSensorsComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "SensorsComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PowerComponentController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "RadioComponentController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ScreenToolsController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "MainToolBarController"); diff --git a/src/QGCApplication.h b/src/QGCApplication.h index a5c9e7643..ddb14cca0 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -32,7 +32,6 @@ #include "MultiVehicleManager.h" #include "JoystickManager.h" #include "GAudioOutput.h" -#include "AutoPilotPluginManager.h" #include "UASMessageHandler.h" #include "FactSystem.h" diff --git a/src/QGCFileDialog.cc b/src/QGCFileDialog.cc index 9e82eb166..59ff5db45 100644 --- a/src/QGCFileDialog.cc +++ b/src/QGCFileDialog.cc @@ -12,10 +12,8 @@ #include "QGCApplication.h" #include "MainWindow.h" -#ifdef QT_DEBUG -#ifndef __mobile__ -#include "UnitTest.h" -#endif +#ifdef UNITTEST_BUILD + #include "UnitTest.h" #endif #include @@ -30,12 +28,10 @@ QString QGCFileDialog::getExistingDirectory( { _validate(options); -#ifdef QT_DEBUG -#ifndef __mobile__ +#ifdef UNITTEST_BUILD if (qgcApp()->runningUnitTests()) { return UnitTest::_getExistingDirectory(parent, caption, dir, options); } else -#endif #endif { return QFileDialog::getExistingDirectory(parent, caption, dir, options); @@ -51,12 +47,10 @@ QString QGCFileDialog::getOpenFileName( { _validate(options); -#ifdef QT_DEBUG -#ifndef __mobile__ +#ifdef UNITTEST_BUILD if (qgcApp()->runningUnitTests()) { return UnitTest::_getOpenFileName(parent, caption, dir, filter, options); } else -#endif #endif { return QFileDialog::getOpenFileName(parent, caption, dir, filter, NULL, options); @@ -72,12 +66,10 @@ QStringList QGCFileDialog::getOpenFileNames( { _validate(options); -#ifdef QT_DEBUG -#ifndef __mobile__ +#ifdef UNITTEST_BUILD if (qgcApp()->runningUnitTests()) { return UnitTest::_getOpenFileNames(parent, caption, dir, filter, options); } else -#endif #endif { return QFileDialog::getOpenFileNames(parent, caption, dir, filter, NULL, options); @@ -95,12 +87,10 @@ QString QGCFileDialog::getSaveFileName( { _validate(options); -#ifdef QT_DEBUG -#ifndef __mobile__ +#ifdef UNITTEST_BUILD if (qgcApp()->runningUnitTests()) { return UnitTest::_getSaveFileName(parent, caption, dir, filter, defaultSuffix, options); } else -#endif #endif { QString defaultSuffixCopy(defaultSuffix); diff --git a/src/QGCMessageBox.h b/src/QGCMessageBox.h index 8ecc44743..7cc2d8111 100644 --- a/src/QGCMessageBox.h +++ b/src/QGCMessageBox.h @@ -20,11 +20,8 @@ #include "MainWindow.h" #include "QGCApplication.h" -#ifdef QT_DEBUG -#ifndef __mobile__ -#include "UnitTest.h" -#endif - +#ifdef UNITTEST_BUILD + #include "UnitTest.h" #endif /// @file @@ -99,12 +96,10 @@ private: qDebug() << "QGCMessageBox (unit testing)" << title << text; -#ifdef QT_DEBUG -#ifndef __mobile__ +#ifdef UNITTEST_BUILD if (qgcApp()->runningUnitTests()) { return UnitTest::_messageBox(icon, title, text, buttons, defaultButton); } else -#endif #endif { #ifdef __macos__ diff --git a/src/QGCQuickWidget.cc b/src/QGCQuickWidget.cc index a3628bc6d..2c77673c5 100644 --- a/src/QGCQuickWidget.cc +++ b/src/QGCQuickWidget.cc @@ -9,7 +9,6 @@ #include "QGCQuickWidget.h" -#include "AutoPilotPluginManager.h" #include "MultiVehicleManager.h" #include "JoystickManager.h" #include "QGCApplication.h" diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 1d80469cd..3e625d66a 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -8,7 +8,6 @@ ****************************************************************************/ -#include "AutoPilotPluginManager.h" #include "FactSystem.h" #include "FirmwarePluginManager.h" #include "FlightMapSettings.h" @@ -32,7 +31,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) : _audioOutput(NULL) - , _autopilotPluginManager(NULL) , _factSystem(NULL) , _firmwarePluginManager(NULL) , _flightMapSettings(NULL) @@ -54,7 +52,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) , _mavlinkLogManager(NULL) { _audioOutput = new GAudioOutput(app); - _autopilotPluginManager = new AutoPilotPluginManager(app); _factSystem = new FactSystem(app); _firmwarePluginManager = new FirmwarePluginManager(app); _flightMapSettings = new FlightMapSettings(app); @@ -79,7 +76,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) void QGCToolbox::setChildToolboxes(void) { _audioOutput->setToolbox(this); - _autopilotPluginManager->setToolbox(this); _factSystem->setToolbox(this); _firmwarePluginManager->setToolbox(this); _flightMapSettings->setToolbox(this); @@ -106,7 +102,6 @@ QGCToolbox::~QGCToolbox() delete _videoManager; delete _mavlinkLogManager; delete _audioOutput; - delete _autopilotPluginManager; delete _factSystem; delete _firmwarePluginManager; delete _flightMapSettings; diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index cb36a06e6..8948fda3a 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -13,7 +13,6 @@ #include -class AutoPilotPluginManager; class FactSystem; class FirmwarePluginManager; class FlightMapSettings; @@ -41,7 +40,6 @@ public: QGCToolbox(QGCApplication* app); ~QGCToolbox(); - AutoPilotPluginManager* autopilotPluginManager(void) { return _autopilotPluginManager; } FirmwarePluginManager* firmwarePluginManager(void) { return _firmwarePluginManager; } FlightMapSettings* flightMapSettings(void) { return _flightMapSettings; } GAudioOutput* audioOutput(void) { return _audioOutput; } @@ -67,7 +65,6 @@ private: void setChildToolboxes(void); GAudioOutput* _audioOutput; - AutoPilotPluginManager* _autopilotPluginManager; FactSystem* _factSystem; FirmwarePluginManager* _firmwarePluginManager; FlightMapSettings* _flightMapSettings; diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc index b9b1de3db..6fe546c04 100644 --- a/src/QmlControls/ParameterEditorController.cc +++ b/src/QmlControls/ParameterEditorController.cc @@ -12,7 +12,6 @@ /// @author Don Gagne #include "ParameterEditorController.h" -#include "AutoPilotPluginManager.h" #include "QGCApplication.h" #include "ParameterManager.h" diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 53ea7a1ae..9bac096f6 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -33,7 +33,6 @@ MultiVehicleManager::MultiVehicleManager(QGCApplication* app) , _activeVehicle(NULL) , _offlineEditingVehicle(NULL) , _firmwarePluginManager(NULL) - , _autopilotPluginManager(NULL) , _joystickManager(NULL) , _mavlinkProtocol(NULL) , _gcsHeartbeatEnabled(true) @@ -55,7 +54,6 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); _firmwarePluginManager = _toolbox->firmwarePluginManager(); - _autopilotPluginManager = _toolbox->autopilotPluginManager(); _joystickManager = _toolbox->joystickManager(); _mavlinkProtocol = _toolbox->mavlinkProtocol(); @@ -98,7 +96,7 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle // return; // } - Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _autopilotPluginManager, _joystickManager); + Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _joystickManager); connect(vehicle, &Vehicle::allLinksInactive, this, &MultiVehicleManager::_deleteVehiclePhase1); connect(vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &MultiVehicleManager::_vehicleParametersReadyChanged); diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 5c6a8e3ee..8dc19950f 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -21,7 +21,6 @@ #include "QGCLoggingCategory.h" class FirmwarePluginManager; -class AutoPilotPluginManager; class FollowMe; class JoystickManager; class QGCApplication; @@ -113,7 +112,6 @@ private: QmlObjectListModel _vehicles; FirmwarePluginManager* _firmwarePluginManager; - AutoPilotPluginManager* _autopilotPluginManager; JoystickManager* _joystickManager; MAVLinkProtocol* _mavlinkProtocol; diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index a3eaa1de6..7b2f2815e 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -13,7 +13,6 @@ #include "FirmwarePluginManager.h" #include "LinkManager.h" #include "FirmwarePlugin.h" -#include "AutoPilotPluginManager.h" #include "UAS.h" #include "JoystickManager.h" #include "MissionManager.h" @@ -61,7 +60,6 @@ Vehicle::Vehicle(LinkInterface* link, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, - AutoPilotPluginManager* autopilotPluginManager, JoystickManager* joystickManager) : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json") , _id(vehicleId) @@ -70,6 +68,7 @@ Vehicle::Vehicle(LinkInterface* link, , _firmwareType(firmwareType) , _vehicleType(vehicleType) , _firmwarePlugin(NULL) + , _firmwarePluginInstanceData(NULL) , _autopilotPlugin(NULL) , _mavlink(NULL) , _soloFirmware(false) @@ -114,7 +113,6 @@ Vehicle::Vehicle(LinkInterface* link, , _custom_mode(0) , _nextSendMessageMultipleIndex(0) , _firmwarePluginManager(firmwarePluginManager) - , _autopilotPluginManager(autopilotPluginManager) , _joystickManager(joystickManager) , _flowImageIndex(0) , _allLinksInactiveSent(false) @@ -161,7 +159,7 @@ Vehicle::Vehicle(LinkInterface* link, connect(this, &Vehicle::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged); _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); - _autopilotPlugin = _autopilotPluginManager->newAutopilotPluginForVehicle(this); + _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this); // connect this vehicle to the follow me handle manager connect(this, &Vehicle::flightModeChanged,qgcApp()->toolbox()->followMe(), &FollowMe::followMeHandleManager); @@ -277,6 +275,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _firmwareType(firmwareType) , _vehicleType(vehicleType) , _firmwarePlugin(NULL) + , _firmwarePluginInstanceData(NULL) , _autopilotPlugin(NULL) , _joystickMode(JoystickModeRC) , _joystickEnabled(false) @@ -319,7 +318,6 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _custom_mode(0) , _nextSendMessageMultipleIndex(0) , _firmwarePluginManager(firmwarePluginManager) - , _autopilotPluginManager(NULL) , _joystickManager(NULL) , _flowImageIndex(0) , _allLinksInactiveSent(false) @@ -2044,23 +2042,17 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject* parent) _courseOverGroundFact.setRawValue(std::numeric_limits::quiet_NaN()); } -//----------------------------------------------------------------------------- -void -Vehicle::startMavlinkLog() +void Vehicle::startMavlinkLog() { doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_START); } -//----------------------------------------------------------------------------- -void -Vehicle::stopMavlinkLog() +void Vehicle::stopMavlinkLog() { doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_STOP); } -//----------------------------------------------------------------------------- -void -Vehicle::_ackMavlinkLogData(uint16_t sequence) +void Vehicle::_ackMavlinkLogData(uint16_t sequence) { mavlink_message_t msg; mavlink_logging_ack_t ack; @@ -2076,9 +2068,7 @@ Vehicle::_ackMavlinkLogData(uint16_t sequence) sendMessageOnLink(priorityLink(), msg); } -//----------------------------------------------------------------------------- -void -Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) +void Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) { mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); @@ -2086,9 +2076,7 @@ Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) log.first_message_offset, QByteArray((const char*)log.data, log.length), false); } -//----------------------------------------------------------------------------- -void -Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) +void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) { mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); @@ -2097,6 +2085,12 @@ Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) log.first_message_offset, QByteArray((const char*)log.data, log.length), true); } +void Vehicle::setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData) +{ + firmwarePluginInstanceData->setParent(this); + _firmwarePluginInstanceData = firmwarePluginInstanceData; +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 6dbabf76e..1ee3d62f6 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -31,7 +31,6 @@ class UASInterface; class FirmwarePlugin; class FirmwarePluginManager; class AutoPilotPlugin; -class AutoPilotPluginManager; class MissionManager; class GeoFenceManager; class RallyPointManager; @@ -214,7 +213,6 @@ public: MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, - AutoPilotPluginManager* autopilotPluginManager, JoystickManager* joystickManager); // The following is used to create a disconnected Vehicle for use while offline editing. @@ -579,6 +577,13 @@ public: /// @return true: X confiuration, false: Plus configuration bool xConfigMotors(void); + /// @return Firmware plugin instance data associated with this Vehicle + QObject* firmwarePluginInstanceData(void) { return _firmwarePluginInstanceData; } + + /// Sets the firmware plugin instance data associated with this Vehicle. This object will be parented to the Vehicle + /// and destroyed when the vehicle goes away. + void setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData); + public slots: void setLatitude(double latitude); void setLongitude(double longitude); @@ -717,6 +722,7 @@ private: MAV_AUTOPILOT _firmwareType; MAV_TYPE _vehicleType; FirmwarePlugin* _firmwarePlugin; + QObject* _firmwarePluginInstanceData; AutoPilotPlugin* _autopilotPlugin; MAVLinkProtocol* _mavlink; bool _soloFirmware; @@ -806,7 +812,6 @@ private: // Toolbox references FirmwarePluginManager* _firmwarePluginManager; - AutoPilotPluginManager* _autopilotPluginManager; JoystickManager* _joystickManager; int _flowImageIndex; diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index 5dbec5851..4509928d7 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -11,8 +11,9 @@ #include "MockLink.h" #include "QGCLoggingCategory.h" #include "QGCApplication.h" -#ifndef __mobile__ -#include "UnitTest.h" + +#ifdef UNITTEST_BUILD + #include "UnitTest.h" #endif #include @@ -21,7 +22,8 @@ #include -#include "px4_custom_mode.h" +// FIXME: Hack to work around clean headers +#include "FirmwarePlugin/PX4/px4_custom_mode.h" QGC_LOGGING_CATEGORY(MockLinkLog, "MockLinkLog") QGC_LOGGING_CATEGORY(MockLinkVerboseLog, "MockLinkVerboseLog") @@ -1159,7 +1161,7 @@ void MockLink::_handleLogRequestData(const mavlink_message_t& msg) mavlink_msg_log_request_data_decode(&msg, &request); if (_logDownloadFilename.isEmpty()) { - #ifndef __mobile__ + #ifdef UNITTEST_BUILD _logDownloadFilename = UnitTest::createRandomFile(_logDownloadFileSize); #endif } diff --git a/src/main.cc b/src/main.cc index 5a582ce66..eb8319cdf 100644 --- a/src/main.cc +++ b/src/main.cc @@ -33,10 +33,11 @@ #include "QGCSerialPortInfo.h" #endif +#ifdef UNITTEST_BUILD + #include "UnitTest.h" +#endif + #ifdef QT_DEBUG - #ifndef __mobile__ - #include "UnitTest.h" - #endif #include "CmdLineOptParser.h" #ifdef Q_OS_WIN #include @@ -229,8 +230,7 @@ int main(int argc, char *argv[]) int exitCode = 0; -#ifndef __mobile__ -#ifdef QT_DEBUG +#ifdef UNITTEST_BUILD if (runUnitTests) { for (int i=0; i < (stressUnitTests ? 20 : 1); i++) { if (!app->_initForUnitTests()) { @@ -249,7 +249,6 @@ int main(int argc, char *argv[]) } } } else -#endif #endif { if (!app->_initForNormalAppBoot()) { -- GitLab From 5693e8623a4a01fc4fc6e956e70d0ec642e1c48f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 26 Nov 2016 19:23:26 -0800 Subject: [PATCH 061/398] Allow changes of log saving if not armed with reboot --- src/comm/MAVLinkProtocol.cc | 16 ++++++---------- src/comm/MAVLinkProtocol.h | 9 +-------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 4d9be09c4..49c1a1a38 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -57,7 +57,7 @@ MAVLinkProtocol::MAVLinkProtocol(QGCApplication* app) #ifndef __mobile__ , _logSuspendError(false) , _logSuspendReplay(false) - , _logPromptForSave(false) + , _vehicleWasArmed(false) , _tempLogFile(QString("%2.%3").arg(_tempLogFileTemplate).arg(_logFileExtension)) #endif , _linkMgr(NULL) @@ -283,11 +283,11 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) } // Check for the vehicle arming going by. This is used to trigger log save. - if (!_logPromptForSave && message.msgid == MAVLINK_MSG_ID_HEARTBEAT) { + if (!_vehicleWasArmed && message.msgid == MAVLINK_MSG_ID_HEARTBEAT) { mavlink_heartbeat_t state; mavlink_msg_heartbeat_decode(&message, &state); if (state.base_mode & MAV_MODE_FLAG_DECODE_POSITION_SAFETY) { - _logPromptForSave = true; + _vehicleWasArmed = true; } } } @@ -429,11 +429,7 @@ void MAVLinkProtocol::_startLogging(void) return; } - if (_app->promptFlightDataSaveNotArmed()) { - _logPromptForSave = true; - } - - qDebug() << "Temp log" << _tempLogFile.fileName() << _logPromptForSave; + qDebug() << "Temp log" << _tempLogFile.fileName(); _logSuspendError = false; } @@ -444,13 +440,13 @@ void MAVLinkProtocol::_stopLogging(void) { if (_closeLogFile()) { // If the signals are not connected it means we are running a unit test. In that case just delete log files - if (_logPromptForSave && _app->promptFlightDataSave()) { + if ((_vehicleWasArmed || _app->promptFlightDataSaveNotArmed()) && _app->promptFlightDataSave()) { emit saveTempFlightDataLog(_tempLogFile.fileName()); } else { QFile::remove(_tempLogFile.fileName()); } } - _logPromptForSave = false; + _vehicleWasArmed = false; } /// @brief Checks the temp directory for log files which may have been left there. diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 4a78d7f99..346de17fe 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -7,13 +7,6 @@ * ****************************************************************************/ - -/** - * @file - * @brief Definition of class MAVLinkProtocol - * @author Lorenz Meier - */ - #ifndef MAVLINKPROTOCOL_H_ #define MAVLINKPROTOCOL_H_ @@ -179,7 +172,7 @@ private: bool _logSuspendError; ///< true: Logging suspended due to error bool _logSuspendReplay; ///< true: Logging suspended due to replay - bool _logPromptForSave; ///< true: Prompt for log save when appropriate + bool _vehicleWasArmed; ///< true: Vehicle was armed during log sequence QGCTemporaryFile _tempLogFile; ///< File to log to static const char* _tempLogFileTemplate; ///< Template for temporary log file -- GitLab From dce4379c943dfbc74797e1a174578a7a20b09c2b Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 27 Nov 2016 07:53:48 -0500 Subject: [PATCH 062/398] Work on QGC plugin support --- android.pri | 26 ++ api/IQGCApplication.h | 24 + api/IQGCCorePlugin.h | 33 ++ api/IQGCOptions.h | 52 +++ api/IQGCQMLSource.h | 17 + apmresources.qrc | 40 ++ px4resources.qrc | 23 + qgroundcontrol.pro | 456 ++++++++++--------- qgroundcontrol.qrc | 119 ++--- src/FactSystem/FactControls/FactCheckBox.qml | 4 +- src/QGCApplication.cc | 61 ++- src/QGCApplication.h | 20 +- src/dummy.h | 2 + 13 files changed, 579 insertions(+), 298 deletions(-) create mode 100644 android.pri create mode 100644 api/IQGCApplication.h create mode 100644 api/IQGCCorePlugin.h create mode 100644 api/IQGCOptions.h create mode 100644 api/IQGCQMLSource.h create mode 100644 apmresources.qrc create mode 100644 px4resources.qrc create mode 100644 src/dummy.h diff --git a/android.pri b/android.pri new file mode 100644 index 000000000..eef0ae3f9 --- /dev/null +++ b/android.pri @@ -0,0 +1,26 @@ +include($$PWD/libs/qtandroidserialport/src/qtandroidserialport.pri) +message("Adding Serial Java Classes") +QT += androidextras +ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android +OTHER_FILES += \ + $$PWD/android/AndroidManifest.xml \ + $$PWD/android/res/xml/device_filter.xml \ + $$PWD/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbId.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \ + $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \ + $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java \ + $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbIoManager.java + +DISTFILES += \ + $$PWD/android/gradle/wrapper/gradle-wrapper.jar \ + $$PWD/android/gradlew \ + $$PWD/android/res/values/libs.xml \ + $$PWD/android/build.gradle \ + $$PWD/android/gradle/wrapper/gradle-wrapper.properties \ + $$PWD/android/gradlew.bat diff --git a/api/IQGCApplication.h b/api/IQGCApplication.h new file mode 100644 index 000000000..79edce901 --- /dev/null +++ b/api/IQGCApplication.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +/** + * @brief QGC Main Application Interface (used for Dynamic Loaded plugins) + * @author Gus Grubba + */ + +#pragma once + +class IQGCApplication +{ +public: + IQGCApplication() {} + virtual ~IQGCApplication() {} + //-- Not yet implemented +}; diff --git a/api/IQGCCorePlugin.h b/api/IQGCCorePlugin.h new file mode 100644 index 000000000..6bd0fa859 --- /dev/null +++ b/api/IQGCCorePlugin.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl +/// @author Gus Grubba + +// Work In Progress + +class QGCApplication; +class IQGCApplication; +class IQGCOptions; +class IQGCQMLSource; + +class IQGCCorePlugin +{ +public: + IQGCCorePlugin(QObject*) {} + virtual ~IQGCCorePlugin() {} + +#if defined (QGC_DYNAMIC_PLUGIN) + virtual bool init (IQGCApplication* pApp) = 0; +#else + virtual bool init (QGCApplication* pApp) = 0; +#endif + virtual IQGCOptions* uiOptions () { return NULL; } + virtual IQGCQMLSource* settingsQML () { return NULL; } +}; + +#if defined (QGC_DYNAMIC_PLUGIN) +Q_DECLARE_INTERFACE(IQGCCorePlugin, "org.qgroundcontrol.qgccoreplugin") +#endif diff --git a/api/IQGCOptions.h b/api/IQGCOptions.h new file mode 100644 index 000000000..f28544617 --- /dev/null +++ b/api/IQGCOptions.h @@ -0,0 +1,52 @@ +#pragma once + +/// @file +/// @brief Core Plugin Interface for QGroundControl +/// @author Gus Grubba + +class IQGCOptions +{ +public: + IQGCOptions() {} + virtual ~IQGCOptions() {} + //! Should QGC colapse its settings menu into one single menu (Settings and Vehicle Setup)? + /*! + @return true if QGC should consolidate both menus into one. + */ + virtual bool colapseSettings () { return false; } + //! Should QGC use Maps as its default main view? + /*! + @return true if QGC should use Maps by default or false to show Video by default. + */ + virtual bool mainViewIsMap () { return true; } + //! Should QGC use virtual Joysticks? + /*! + @return false to disable Virtual Joysticks. + */ + virtual bool enableVirtualJoystick () { return true; } + //! Should QGC allow setting auto-connect options? + /*! + @return false to disable auto-connect options. + */ + virtual bool enableAutoConnectOptions () { return true; } + //! Should QGC allow setting video source options? + /*! + @return false to disable video source options. + */ + virtual bool enableVideoSourceOptions () { return true; } + //! Does your plugin defines its on video source? + /*! + @return true to define your own video source. + */ + virtual bool definesVideo () { return false; } + //! UDP port to use for (RTP) video source. + /*! + @return UDP Port to use. Return 0 to disable UDP RTP. + */ + virtual uint16_t videoUDPPort () { return 0; } + //! RTSP URL to use for video source. + /*! + @return RTSP url to use. Return "" to disable RTSP. + */ + virtual QString videoRSTPUrl () { return QString(); } +}; diff --git a/api/IQGCQMLSource.h b/api/IQGCQMLSource.h new file mode 100644 index 000000000..5e59c2169 --- /dev/null +++ b/api/IQGCQMLSource.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl +/// @author Gus Grubba + +class IQGCQMLSource +{ +public: + IQGCQMLSource() {} + virtual ~IQGCQMLSource() {} + virtual QString pageUrl () { return QString(); } + virtual QString pageTitle () { return QString(); } + virtual QString pageIconUrl () { return QString(); } +}; diff --git a/apmresources.qrc b/apmresources.qrc new file mode 100644 index 000000000..8c0ffc0f6 --- /dev/null +++ b/apmresources.qrc @@ -0,0 +1,40 @@ + + + src/comm/APMArduCopterMockLink.params + src/comm/APMArduPlaneMockLink.params + src/comm/APMArduSubMockLink.params + + + src/AutoPilotPlugins/APM/APMAirframeComponent.qml + src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml + src/AutoPilotPlugins/APM/APMCameraComponent.qml + src/AutoPilotPlugins/APM/APMCameraComponentSummary.qml + src/AutoPilotPlugins/APM/APMFlightModesComponent.qml + src/AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml + src/AutoPilotPlugins/APM/APMLightsComponent.qml + src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml + src/AutoPilotPlugins/APM/APMNotSupported.qml + src/AutoPilotPlugins/APM/APMPowerComponent.qml + src/AutoPilotPlugins/APM/APMPowerComponentSummary.qml + src/AutoPilotPlugins/APM/APMRadioComponentSummary.qml + src/AutoPilotPlugins/APM/APMSafetyComponentCopter.qml + src/AutoPilotPlugins/APM/APMSafetyComponentPlane.qml + src/AutoPilotPlugins/APM/APMSafetyComponentRover.qml + src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml + src/AutoPilotPlugins/APM/APMSafetyComponentSummaryCopter.qml + src/AutoPilotPlugins/APM/APMSafetyComponentSummaryPlane.qml + src/AutoPilotPlugins/APM/APMSafetyComponentSummaryRover.qml + src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml + src/AutoPilotPlugins/APM/APMSensorsComponent.qml + src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml + src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml + + + src/FirmwarePlugin/APM/MavCmdInfoCommon.json + src/FirmwarePlugin/APM/MavCmdInfoFixedWing.json + src/FirmwarePlugin/APM/MavCmdInfoMultiRotor.json + src/FirmwarePlugin/APM/MavCmdInfoRover.json + src/FirmwarePlugin/APM/MavCmdInfoSub.json + src/FirmwarePlugin/APM/MavCmdInfoVTOL.json + + diff --git a/px4resources.qrc b/px4resources.qrc new file mode 100644 index 000000000..a25e79221 --- /dev/null +++ b/px4resources.qrc @@ -0,0 +1,23 @@ + + + src/comm/PX4MockLink.params + + + src/AutoPilotPlugins/PX4/PX4AdvancedFlightModes.qml + src/AutoPilotPlugins/PX4/PX4FlightModes.qml + src/VehicleSetup/PX4FlowSensor.qml + src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml + src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml + src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml + src/AutoPilotPlugins/PX4/PX4TuningComponentPlane.qml + src/AutoPilotPlugins/PX4/PX4TuningComponentVTOL.qml + + + src/FirmwarePlugin/PX4/MavCmdInfoCommon.json + src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json + src/FirmwarePlugin/PX4/MavCmdInfoMultiRotor.json + src/FirmwarePlugin/PX4/MavCmdInfoRover.json + src/FirmwarePlugin/PX4/MavCmdInfoSub.json + src/FirmwarePlugin/PX4/MavCmdInfoVTOL.json + + diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index d9902929f..3652d7529 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -34,6 +34,69 @@ exists(user_config.pri):infile(user_config.pri, CONFIG) { message($$sprintf("Using user-supplied additional config: '%1' specified in user_config.pri", $$fromfile(user_config.pri, CONFIG))) } +# +# Plugin configuration +# +# This allows you to build custom versions of QGC which only includes your +# specific vehicle plugin. To remove support for a firmware type completely, +# disable both the Plugin and PluginFactory entries. To include custom support +# for an existing plugin type disable PluginFactory only. Then provide you own +# implementation of FirmwarePluginFactory and use the FirmwarePlugin and +# AutoPilotPlugin classes as the base clase for your derived plugin +# implementation. + +contains (CONFIG, QGC_DISABLE_APM_PLUGIN) { + message("Disable APM Plugin") +} else { + CONFIG += APMFirmwarePlugin +} + +contains (CONFIG, QGC_DISABLE_APM_PLUGIN_FACTORY) { + message("Disable APM Plugin Factory") +} else { + CONFIG += APMFirmwarePluginFactory +} + +contains (CONFIG, QGC_DISABLE_PX4_PLUGIN) { + message("Disable PX4 Plugin") +} else { + CONFIG += PX4FirmwarePlugin +} + +contains (CONFIG, QGC_DISABLE_PX4_PLUGIN_FACTORY) { + message("Disable PX4 Plugin Factory") +} else { + CONFIG += PX4FirmwarePluginFactory +} + +# +# Custom Build +# +# QGC will create a "CUSTOMCLASS" object (exposed by your custom build +# and derived from IQGCCorePlugin) and call its IQGCCorePlugin::init() method. +# This is the start of allowing custom Plugins, which will eventually use a +# more defined runtime plugin architecture and not require a QGC project +# file you would have to keep in sync with the upstream repo. +# + +# This allows you to ignore the custom build even if the custom build +# is present. It's useful to run "regular" builds to make sure you didn't +# break anything. + +contains (CONFIG, QGC_DISABLE_CUSTOM_BUILD) { + message("Disable custom build override") +} else { + exists($$PWD/custom/custom.pri) { + message("Found custom build") + CONFIG += CustomBuild + DEFINES += QGC_CUSTOM_BUILD + # custom.pri must define: + # CUSTOMCLASS = YourIQGCCorePluginDerivation + # CUSTOMHEADER = \"\\\"YourIQGCCorePluginDerivation.h\\\"\" + include($$PWD/custom/custom.pri) + } +} + # Bluetooth contains (DEFINES, QGC_DISABLE_BLUETOOTH) { message("Skipping support for Bluetooth (manual override from command line)") @@ -70,20 +133,6 @@ CONFIG += qt \ thread \ c++11 \ -# -# Plugin configuration -# -# This allows you to build custom versions of QGC which only include your specific vehicle plugin. To remove support for -# a firmware type completely remove both the Plugin and PluginFactory entries. To include custom support for an existing -# plugin type remove PluginFactory only. Then provide you own implementation of FirmwarePluginFactory and use the -# FirmwarePlugin and AutoPilotPlugin classes as the base clase for your derived plugin implementation. -# -CONFIG += \ - APMFirmwarePlugin \ - PX4FirmwarePlugin \ - APMFirmwarePluginFactory \ - PX4FirmwarePluginFactory \ - contains(DEFINES, ENABLE_VERBOSE_OUTPUT) { message("Enable verbose compiler output (manual override from command line)") } else:exists(user_config.pri):infile(user_config.pri, DEFINES, ENABLE_VERBOSE_OUTPUT) { @@ -224,23 +273,29 @@ DEPENDPATH += \ INCLUDEPATH += . INCLUDEPATH += \ + api \ include/ui \ src \ src/AnalyzeView \ - src/audio \ src/AutoPilotPlugins \ - src/comm \ src/FlightDisplay \ src/FlightMap \ src/FlightMap/Widgets \ - src/input \ - src/Joystick \ src/FollowMe \ src/GPS \ - src/lib/qmapcontrol \ + src/Joystick \ src/MissionEditor \ src/MissionManager \ + src/PositionManager \ src/QmlControls \ + src/QtLocationPlugin \ + src/QtLocationPlugin/QMLControl \ + src/VehicleSetup \ + src/ViewWidgets \ + src/audio \ + src/comm \ + src/input \ + src/lib/qmapcontrol \ src/uas \ src/ui \ src/ui/linechart \ @@ -250,11 +305,6 @@ INCLUDEPATH += \ src/ui/px4_configuration \ src/ui/toolbar \ src/ui/uas \ - src/VehicleSetup \ - src/ViewWidgets \ - src/QtLocationPlugin \ - src/QtLocationPlugin/QMLControl \ - src/PositionManager \ FORMS += \ src/ui/MainWindow.ui \ @@ -262,8 +312,6 @@ FORMS += \ !MobileBuild { FORMS += \ - src/ui/uas/QGCUnconnectedInfoWidget.ui \ - src/ui/uas/UASMessageView.ui \ src/ui/Linechart.ui \ src/ui/MultiVehicleDockWidget.ui \ src/ui/QGCDataPlot2D.ui \ @@ -271,16 +319,28 @@ FORMS += \ src/ui/QGCHilFlightGearConfiguration.ui \ src/ui/QGCHilJSBSimConfiguration.ui \ src/ui/QGCHilXPlaneConfiguration.ui \ - src/ui/QGCMapRCToParamDialog.ui \ src/ui/QGCMAVLinkInspector.ui \ src/ui/QGCMAVLinkLogPlayer.ui \ + src/ui/QGCMapRCToParamDialog.ui \ src/ui/QGCTabbedInfoView.ui \ src/ui/QGCUASFileView.ui \ src/ui/QGCUASFileViewMulti.ui \ + src/ui/uas/QGCUnconnectedInfoWidget.ui \ + src/ui/uas/UASMessageView.ui \ src/ui/uas/UASQuickView.ui \ src/ui/uas/UASQuickViewItemSelect.ui \ } +# +# Plugin API +# + +HEADERS += \ + api/IQGCApplication.h \ + api/IQGCCorePlugin.h \ + api/IQGCOptions.h \ + api/IQGCQMLSource.h \ + # # Unit Test specific configuration goes here (requires full debug build with all plugins) # @@ -299,15 +359,15 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/FactSystem/ParameterManagerTest.h \ src/MissionManager/ComplexMissionItemTest.h \ src/MissionManager/MissionCommandTreeTest.h \ - src/MissionManager/MissionControllerTest.h \ src/MissionManager/MissionControllerManagerTest.h \ + src/MissionManager/MissionControllerTest.h \ src/MissionManager/MissionItemTest.h \ src/MissionManager/MissionManagerTest.h \ src/MissionManager/SimpleMissionItemTest.h \ - src/qgcunittest/GeoTest.h \ src/qgcunittest/FileDialogTest.h \ src/qgcunittest/FileManagerTest.h \ src/qgcunittest/FlightGearTest.h \ + src/qgcunittest/GeoTest.h \ src/qgcunittest/LinkManagerTest.h \ src/qgcunittest/MainWindowTest.h \ src/qgcunittest/MavlinkLogTest.h \ @@ -326,15 +386,15 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/FactSystem/ParameterManagerTest.cc \ src/MissionManager/ComplexMissionItemTest.cc \ src/MissionManager/MissionCommandTreeTest.cc \ - src/MissionManager/MissionControllerTest.cc \ src/MissionManager/MissionControllerManagerTest.cc \ + src/MissionManager/MissionControllerTest.cc \ src/MissionManager/MissionItemTest.cc \ src/MissionManager/MissionManagerTest.cc \ src/MissionManager/SimpleMissionItemTest.cc \ - src/qgcunittest/GeoTest.cc \ src/qgcunittest/FileDialogTest.cc \ src/qgcunittest/FileManagerTest.cc \ src/qgcunittest/FlightGearTest.cc \ + src/qgcunittest/GeoTest.cc \ src/qgcunittest/LinkManagerTest.cc \ src/qgcunittest/MainWindowTest.cc \ src/qgcunittest/MavlinkLogTest.cc \ @@ -350,27 +410,17 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { # Main QGC Headers and Source files HEADERS += \ - src/audio/QGCAudioWorker.h \ + src/AnalyzeView/ExifParser.h \ src/CmdLineOptParser.h \ - src/comm/LinkConfiguration.h \ - src/comm/LinkInterface.h \ - src/comm/LinkManager.h \ - src/comm/MAVLinkProtocol.h \ - src/comm/ProtocolInterface.h \ - src/comm/QGCMAVLink.h \ - src/comm/TCPLink.h \ - src/comm/UDPLink.h \ src/FirmwarePlugin/PX4/px4_custom_mode.h \ src/FlightDisplay/VideoManager.h \ src/FlightMap/FlightMapSettings.h \ src/FlightMap/Widgets/ValuesWidgetController.h \ + src/FollowMe/FollowMe.h \ src/GAudioOutput.h \ src/HomePositionManager.h \ src/Joystick/Joystick.h \ src/Joystick/JoystickManager.h \ - src/VehicleSetup/JoystickConfigController.h \ - src/FollowMe/FollowMe.h \ - src/PositionManager/SimulatedPosition.h \ src/JsonHelper.h \ src/LogCompressor.h \ src/MG.h \ @@ -391,6 +441,8 @@ HEADERS += \ src/MissionManager/SimpleMissionItem.h \ src/MissionManager/SurveyMissionItem.h \ src/MissionManager/VisualMissionItem.h \ + src/PositionManager/PositionManager.h \ + src/PositionManager/SimulatedPosition.h \ src/QGC.h \ src/QGCApplication.h \ src/QGCComboBox.h \ @@ -410,23 +462,27 @@ HEADERS += \ src/QmlControls/CoordinateVector.h \ src/QmlControls/MavlinkQmlSingleton.h \ src/QmlControls/ParameterEditorController.h \ - src/QmlControls/RCChannelMonitorController.h \ - src/QmlControls/ScreenToolsController.h \ + src/QmlControls/QGCImageProvider.h \ src/QmlControls/QGroundControlQmlGlobal.h \ src/QmlControls/QmlObjectListModel.h \ + src/QmlControls/RCChannelMonitorController.h \ + src/QmlControls/ScreenToolsController.h \ + src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ + src/Vehicle/MAVLinkLogManager.h \ + src/VehicleSetup/JoystickConfigController.h \ + src/audio/QGCAudioWorker.h \ + src/comm/LinkConfiguration.h \ + src/comm/LinkInterface.h \ + src/comm/LinkManager.h \ + src/comm/MAVLinkProtocol.h \ + src/comm/ProtocolInterface.h \ + src/comm/QGCMAVLink.h \ + src/comm/TCPLink.h \ + src/comm/UDPLink.h \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ - src/Vehicle/MAVLinkLogManager.h \ src/ui/toolbar/MainToolBarController.h \ - src/QmlControls/QGCImageProvider.h \ - src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ - src/PositionManager/PositionManager.h \ - src/AnalyzeView/ExifParser.h - -AndroidBuild { -HEADERS += \ -} DebugBuild { HEADERS += \ @@ -457,56 +513,56 @@ HEADERS += \ HEADERS += \ src/AnalyzeView/GeoTagController.h \ src/AnalyzeView/LogDownloadController.h \ + src/GPS/Drivers/src/gps_helper.h \ + src/GPS/Drivers/src/ubx.h \ + src/GPS/GPSManager.h \ + src/GPS/GPSPositionMessage.h \ + src/GPS/GPSProvider.h \ + src/GPS/RTCM/RTCMMavlink.h \ + src/GPS/definitions.h \ + src/GPS/satellite_info.h \ + src/GPS/vehicle_gps_position.h \ + src/Joystick/JoystickSDL.h \ + src/QGCFileDialog.h \ + src/QGCMessageBox.h \ + src/ViewWidgets/CustomCommandWidget.h \ + src/ViewWidgets/CustomCommandWidgetController.h \ + src/ViewWidgets/ViewWidgetController.h \ src/comm/LogReplayLink.h \ src/comm/QGCFlightGearLink.h \ src/comm/QGCHilLink.h \ src/comm/QGCJSBSimLink.h \ src/comm/QGCXPlaneLink.h \ - src/Joystick/JoystickSDL.h \ - src/QGCFileDialog.h \ - src/QGCMessageBox.h \ src/uas/FileManager.h \ src/ui/HILDockWidget.h \ - src/ui/linechart/ChartPlot.h \ - src/ui/linechart/IncrementalPlot.h \ - src/ui/linechart/LinechartPlot.h \ - src/ui/linechart/Linecharts.h \ - src/ui/linechart/LinechartWidget.h \ - src/ui/linechart/Scrollbar.h \ - src/ui/linechart/ScrollZoomer.h \ - src/ui/MainWindow.h \ src/ui/MAVLinkDecoder.h \ + src/ui/MainWindow.h \ src/ui/MultiVehicleDockWidget.h \ - src/ui/QGCMAVLinkLogPlayer.h \ - src/ui/QGCMapRCToParamDialog.h \ - src/ui/uas/UASMessageView.h \ - src/ui/uas/QGCUnconnectedInfoWidget.h \ src/ui/QGCDataPlot2D.h \ src/ui/QGCHilConfiguration.h \ src/ui/QGCHilFlightGearConfiguration.h \ src/ui/QGCHilJSBSimConfiguration.h \ src/ui/QGCHilXPlaneConfiguration.h \ src/ui/QGCMAVLinkInspector.h \ + src/ui/QGCMAVLinkLogPlayer.h \ + src/ui/QGCMapRCToParamDialog.h \ src/ui/QGCTabbedInfoView.h \ src/ui/QGCUASFileView.h \ src/ui/QGCUASFileViewMulti.h \ + src/ui/linechart/ChartPlot.h \ + src/ui/linechart/IncrementalPlot.h \ + src/ui/linechart/LinechartPlot.h \ + src/ui/linechart/LinechartWidget.h \ + src/ui/linechart/Linecharts.h \ + src/ui/linechart/ScrollZoomer.h \ + src/ui/linechart/Scrollbar.h \ + src/ui/uas/QGCUnconnectedInfoWidget.h \ + src/ui/uas/UASMessageView.h \ src/ui/uas/UASQuickView.h \ src/ui/uas/UASQuickViewGaugeItem.h \ src/ui/uas/UASQuickViewItem.h \ src/ui/uas/UASQuickViewItemSelect.h \ src/ui/uas/UASQuickViewTextItem.h \ - src/GPS/Drivers/src/gps_helper.h \ - src/GPS/Drivers/src/ubx.h \ - src/GPS/definitions.h \ - src/GPS/vehicle_gps_position.h \ - src/GPS/satellite_info.h \ - src/GPS/RTCM/RTCMMavlink.h \ - src/GPS/GPSManager.h \ - src/GPS/GPSPositionMessage.h \ - src/GPS/GPSProvider.h \ - src/ViewWidgets/CustomCommandWidget.h \ - src/ViewWidgets/CustomCommandWidgetController.h \ - src/ViewWidgets/ViewWidgetController.h \ } iOSBuild { @@ -519,28 +575,19 @@ AndroidBuild { SOURCES += src/MobileScreenMgr.cc \ } - SOURCES += \ - src/audio/QGCAudioWorker.cpp \ + src/AnalyzeView/ExifParser.cc \ src/CmdLineOptParser.cc \ - src/comm/LinkConfiguration.cc \ - src/comm/LinkManager.cc \ - src/comm/MAVLinkProtocol.cc \ - src/comm/QGCMAVLink.cc \ - src/comm/TCPLink.cc \ - src/comm/UDPLink.cc \ src/FlightDisplay/VideoManager.cc \ src/FlightMap/FlightMapSettings.cc \ src/FlightMap/Widgets/ValuesWidgetController.cc \ + src/FollowMe/FollowMe.cc \ src/GAudioOutput.cc \ src/HomePositionManager.cc \ src/Joystick/Joystick.cc \ src/Joystick/JoystickManager.cc \ - src/VehicleSetup/JoystickConfigController.cc \ src/JsonHelper.cc \ - src/FollowMe/FollowMe.cc \ src/LogCompressor.cc \ - src/main.cc \ src/MissionManager/ComplexMissionItem.cc \ src/MissionManager/GeoFenceController.cc \ src/MissionManager/GeoFenceManager.cc \ @@ -558,36 +605,44 @@ SOURCES += \ src/MissionManager/SimpleMissionItem.cc \ src/MissionManager/SurveyMissionItem.cc \ src/MissionManager/VisualMissionItem.cc \ + src/PositionManager/PositionManager.cpp \ + src/PositionManager/SimulatedPosition.cc \ src/QGC.cc \ src/QGCApplication.cc \ src/QGCComboBox.cc \ src/QGCDockWidget.cc \ src/QGCFileDownload.cc \ + src/QGCGeo.cc \ src/QGCLoggingCategory.cc \ src/QGCMapPalette.cc \ src/QGCMobileFileDialogController.cc \ src/QGCPalette.cc \ - src/QGCQuickWidget.cc \ src/QGCQmlWidgetHolder.cpp \ + src/QGCQuickWidget.cc \ src/QGCTemporaryFile.cc \ src/QGCToolbox.cc \ - src/QGCGeo.cc \ src/QmlControls/AppMessages.cc \ src/QmlControls/CoordinateVector.cc \ src/QmlControls/ParameterEditorController.cc \ - src/QmlControls/RCChannelMonitorController.cc \ - src/QmlControls/ScreenToolsController.cc \ + src/QmlControls/QGCImageProvider.cc \ src/QmlControls/QGroundControlQmlGlobal.cc \ src/QmlControls/QmlObjectListModel.cc \ + src/QmlControls/RCChannelMonitorController.cc \ + src/QmlControls/ScreenToolsController.cc \ + src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ + src/Vehicle/MAVLinkLogManager.cc \ + src/VehicleSetup/JoystickConfigController.cc \ + src/audio/QGCAudioWorker.cpp \ + src/comm/LinkConfiguration.cc \ + src/comm/LinkManager.cc \ + src/comm/MAVLinkProtocol.cc \ + src/comm/QGCMAVLink.cc \ + src/comm/TCPLink.cc \ + src/comm/UDPLink.cc \ + src/main.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ - src/Vehicle/MAVLinkLogManager.cc \ src/ui/toolbar/MainToolBarController.cc \ - src/QmlControls/QGCImageProvider.cc \ - src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ - src/PositionManager/SimulatedPosition.cc \ - src/PositionManager/PositionManager.cpp \ - src/AnalyzeView/ExifParser.cc DebugBuild { SOURCES += \ @@ -611,26 +666,23 @@ contains(DEFINES, QGC_ENABLE_BLUETOOTH) { SOURCES += \ src/AnalyzeView/GeoTagController.cc \ src/AnalyzeView/LogDownloadController.cc \ - src/ui/uas/UASMessageView.cc \ - src/uas/FileManager.cc \ - src/ui/uas/QGCUnconnectedInfoWidget.cc \ - src/ui/MAVLinkDecoder.cc \ - src/ui/QGCMapRCToParamDialog.cpp \ - src/comm/LogReplayLink.cc \ + src/GPS/Drivers/src/gps_helper.cpp \ + src/GPS/Drivers/src/ubx.cpp \ + src/GPS/GPSManager.cc \ + src/GPS/GPSProvider.cc \ + src/GPS/RTCM/RTCMMavlink.cc \ + src/Joystick/JoystickSDL.cc \ src/QGCFileDialog.cc \ - src/ui/QGCMAVLinkLogPlayer.cc \ + src/ViewWidgets/CustomCommandWidget.cc \ + src/ViewWidgets/CustomCommandWidgetController.cc \ + src/ViewWidgets/ViewWidgetController.cc \ + src/comm/LogReplayLink.cc \ src/comm/QGCFlightGearLink.cc \ src/comm/QGCJSBSimLink.cc \ src/comm/QGCXPlaneLink.cc \ - src/Joystick/JoystickSDL.cc \ + src/uas/FileManager.cc \ src/ui/HILDockWidget.cc \ - src/ui/linechart/ChartPlot.cc \ - src/ui/linechart/IncrementalPlot.cc \ - src/ui/linechart/LinechartPlot.cc \ - src/ui/linechart/Linecharts.cc \ - src/ui/linechart/LinechartWidget.cc \ - src/ui/linechart/Scrollbar.cc \ - src/ui/linechart/ScrollZoomer.cc \ + src/ui/MAVLinkDecoder.cc \ src/ui/MainWindow.cc \ src/ui/MultiVehicleDockWidget.cc \ src/ui/QGCDataPlot2D.cc \ @@ -639,22 +691,25 @@ SOURCES += \ src/ui/QGCHilJSBSimConfiguration.cc \ src/ui/QGCHilXPlaneConfiguration.cc \ src/ui/QGCMAVLinkInspector.cc \ + src/ui/QGCMAVLinkLogPlayer.cc \ + src/ui/QGCMapRCToParamDialog.cpp \ src/ui/QGCTabbedInfoView.cpp \ src/ui/QGCUASFileView.cc \ src/ui/QGCUASFileViewMulti.cc \ + src/ui/linechart/ChartPlot.cc \ + src/ui/linechart/IncrementalPlot.cc \ + src/ui/linechart/LinechartPlot.cc \ + src/ui/linechart/LinechartWidget.cc \ + src/ui/linechart/Linecharts.cc \ + src/ui/linechart/ScrollZoomer.cc \ + src/ui/linechart/Scrollbar.cc \ + src/ui/uas/QGCUnconnectedInfoWidget.cc \ + src/ui/uas/UASMessageView.cc \ src/ui/uas/UASQuickView.cc \ src/ui/uas/UASQuickViewGaugeItem.cc \ src/ui/uas/UASQuickViewItem.cc \ src/ui/uas/UASQuickViewItemSelect.cc \ src/ui/uas/UASQuickViewTextItem.cc \ - src/GPS/Drivers/src/gps_helper.cpp \ - src/GPS/Drivers/src/ubx.cpp \ - src/GPS/RTCM/RTCMMavlink.cc \ - src/GPS/GPSManager.cc \ - src/GPS/GPSProvider.cc \ - src/ViewWidgets/CustomCommandWidget.cc \ - src/ViewWidgets/CustomCommandWidgetController.cc \ - src/ViewWidgets/ViewWidgetController.cc } # Palette test widget in debug builds @@ -675,32 +730,31 @@ INCLUDEPATH += \ HEADERS+= \ src/AutoPilotPlugins/AutoPilotPlugin.h \ + src/AutoPilotPlugins/Common/ESP8266Component.h \ + src/AutoPilotPlugins/Common/ESP8266ComponentController.h \ src/AutoPilotPlugins/Common/MotorComponent.h \ src/AutoPilotPlugins/Common/RadioComponentController.h \ - src/AutoPilotPlugins/Common/ESP8266ComponentController.h \ - src/AutoPilotPlugins/Common/ESP8266Component.h \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.h \ - src/FirmwarePlugin/FirmwarePluginManager.h \ src/FirmwarePlugin/FirmwarePlugin.h \ + src/FirmwarePlugin/FirmwarePluginManager.h \ src/Vehicle/MultiVehicleManager.h \ src/Vehicle/Vehicle.h \ src/VehicleSetup/VehicleComponent.h \ !MobileBuild { -HEADERS += \ - src/VehicleSetup/FirmwareUpgradeController.h \ - src/VehicleSetup/Bootloader.h \ - src/VehicleSetup/PX4FirmwareUpgradeThread.h \ - src/VehicleSetup/FirmwareImage.h \ - + HEADERS += \ + src/VehicleSetup/Bootloader.h \ + src/VehicleSetup/FirmwareImage.h \ + src/VehicleSetup/FirmwareUpgradeController.h \ + src/VehicleSetup/PX4FirmwareUpgradeThread.h \ } SOURCES += \ src/AutoPilotPlugins/AutoPilotPlugin.cc \ + src/AutoPilotPlugins/Common/ESP8266Component.cc \ + src/AutoPilotPlugins/Common/ESP8266ComponentController.cc \ src/AutoPilotPlugins/Common/MotorComponent.cc \ src/AutoPilotPlugins/Common/RadioComponentController.cc \ - src/AutoPilotPlugins/Common/ESP8266ComponentController.cc \ - src/AutoPilotPlugins/Common/ESP8266Component.cc \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.cc \ src/FirmwarePlugin/FirmwarePlugin.cc \ src/FirmwarePlugin/FirmwarePluginManager.cc \ @@ -710,131 +764,136 @@ SOURCES += \ !MobileBuild { SOURCES += \ - src/VehicleSetup/FirmwareUpgradeController.cc \ - src/VehicleSetup/Bootloader.cc \ - src/VehicleSetup/PX4FirmwareUpgradeThread.cc \ - src/VehicleSetup/FirmwareImage.cc \ - + src/VehicleSetup/Bootloader.cc \ + src/VehicleSetup/FirmwareImage.cc \ + src/VehicleSetup/FirmwareUpgradeController.cc \ + src/VehicleSetup/PX4FirmwareUpgradeThread.cc \ } # ArduPilot FirmwarePlugin APMFirmwarePlugin { + RESOURCES *= apmresources.qrc + INCLUDEPATH += \ src/AutoPilotPlugins/APM \ src/FirmwarePlugin/APM \ HEADERS += \ - src/FirmwarePlugin/APM/APMFirmwarePlugin.h \ - src/FirmwarePlugin/APM/APMGeoFenceManager.h \ - src/FirmwarePlugin/APM/APMParameterMetaData.h \ - src/FirmwarePlugin/APM/APMRallyPointManager.h \ - src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h \ - src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h \ - src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h \ src/AutoPilotPlugins/APM/APMAirframeComponent.h \ - src/AutoPilotPlugins/APM/APMAirframeComponentController.h \ src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.h \ + src/AutoPilotPlugins/APM/APMAirframeComponentController.h \ src/AutoPilotPlugins/APM/APMAirframeLoader.h \ + src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h \ src/AutoPilotPlugins/APM/APMCameraComponent.h \ - src/AutoPilotPlugins/APM/APMLightsComponent.h \ src/AutoPilotPlugins/APM/APMCompassCal.h \ src/AutoPilotPlugins/APM/APMFlightModesComponent.h \ src/AutoPilotPlugins/APM/APMFlightModesComponentController.h \ + src/AutoPilotPlugins/APM/APMLightsComponent.h \ src/AutoPilotPlugins/APM/APMPowerComponent.h \ src/AutoPilotPlugins/APM/APMRadioComponent.h \ src/AutoPilotPlugins/APM/APMSafetyComponent.h \ src/AutoPilotPlugins/APM/APMSensorsComponent.h \ src/AutoPilotPlugins/APM/APMSensorsComponentController.h \ src/AutoPilotPlugins/APM/APMTuningComponent.h \ + src/FirmwarePlugin/APM/APMFirmwarePlugin.h \ + src/FirmwarePlugin/APM/APMGeoFenceManager.h \ + src/FirmwarePlugin/APM/APMParameterMetaData.h \ + src/FirmwarePlugin/APM/APMRallyPointManager.h \ + src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h \ + src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h \ SOURCES += \ - src/FirmwarePlugin/APM/APMFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/APMGeoFenceManager.cc \ - src/FirmwarePlugin/APM/APMParameterMetaData.cc \ - src/FirmwarePlugin/APM/APMRallyPointManager.cc \ - src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc \ - src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc \ - src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc \ src/AutoPilotPlugins/APM/APMAirframeComponent.cc \ src/AutoPilotPlugins/APM/APMAirframeComponentAirframes.cc \ src/AutoPilotPlugins/APM/APMAirframeComponentController.cc \ src/AutoPilotPlugins/APM/APMAirframeLoader.cc \ + src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc \ src/AutoPilotPlugins/APM/APMCameraComponent.cc \ - src/AutoPilotPlugins/APM/APMLightsComponent.cc \ src/AutoPilotPlugins/APM/APMCompassCal.cc \ src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \ src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc \ + src/AutoPilotPlugins/APM/APMLightsComponent.cc \ src/AutoPilotPlugins/APM/APMPowerComponent.cc \ src/AutoPilotPlugins/APM/APMRadioComponent.cc \ src/AutoPilotPlugins/APM/APMSafetyComponent.cc \ src/AutoPilotPlugins/APM/APMSensorsComponent.cc \ src/AutoPilotPlugins/APM/APMSensorsComponentController.cc \ src/AutoPilotPlugins/APM/APMTuningComponent.cc \ + src/FirmwarePlugin/APM/APMFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/APMGeoFenceManager.cc \ + src/FirmwarePlugin/APM/APMParameterMetaData.cc \ + src/FirmwarePlugin/APM/APMRallyPointManager.cc \ + src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc \ + src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc \ } APMFirmwarePluginFactory { - HEADERS += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h - SOURCES += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc + RESOURCES *= apmresources.qrc + HEADERS += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h + SOURCES += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc } # PX4 FirmwarePlugin PX4FirmwarePlugin { + RESOURCES *= px4resources.qrc + INCLUDEPATH += \ src/AutoPilotPlugins/PX4 \ src/FirmwarePlugin/PX4 \ HEADERS+= \ - src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h \ - src/FirmwarePlugin/PX4/PX4GeoFenceManager.h \ - src/FirmwarePlugin/PX4/PX4ParameterMetaData.h \ src/AutoPilotPlugins/PX4/AirframeComponent.h \ src/AutoPilotPlugins/PX4/AirframeComponentAirframes.h \ src/AutoPilotPlugins/PX4/AirframeComponentController.h \ + src/AutoPilotPlugins/PX4/CameraComponent.h \ src/AutoPilotPlugins/PX4/FlightModesComponent.h \ - src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.h \ + src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ + src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h \ + src/AutoPilotPlugins/PX4/PX4RadioComponent.h \ src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.h \ + src/AutoPilotPlugins/PX4/PX4TuningComponent.h \ src/AutoPilotPlugins/PX4/PowerComponent.h \ src/AutoPilotPlugins/PX4/PowerComponentController.h \ - src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h \ - src/AutoPilotPlugins/PX4/PX4RadioComponent.h \ - src/AutoPilotPlugins/PX4/CameraComponent.h \ src/AutoPilotPlugins/PX4/SafetyComponent.h \ src/AutoPilotPlugins/PX4/SensorsComponent.h \ src/AutoPilotPlugins/PX4/SensorsComponentController.h \ - src/AutoPilotPlugins/PX4/PX4TuningComponent.h \ + src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h \ + src/FirmwarePlugin/PX4/PX4GeoFenceManager.h \ + src/FirmwarePlugin/PX4/PX4ParameterMetaData.h \ SOURCES += \ - src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc \ - src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc \ - src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc \ src/AutoPilotPlugins/PX4/AirframeComponent.cc \ src/AutoPilotPlugins/PX4/AirframeComponentAirframes.cc \ src/AutoPilotPlugins/PX4/AirframeComponentController.cc \ + src/AutoPilotPlugins/PX4/CameraComponent.cc \ src/AutoPilotPlugins/PX4/FlightModesComponent.cc \ src/AutoPilotPlugins/PX4/PX4AdvancedFlightModesController.cc \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ + src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc \ + src/AutoPilotPlugins/PX4/PX4RadioComponent.cc \ src/AutoPilotPlugins/PX4/PX4SimpleFlightModesController.cc \ + src/AutoPilotPlugins/PX4/PX4TuningComponent.cc \ src/AutoPilotPlugins/PX4/PowerComponent.cc \ src/AutoPilotPlugins/PX4/PowerComponentController.cc \ - src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc \ - src/AutoPilotPlugins/PX4/PX4RadioComponent.cc \ - src/AutoPilotPlugins/PX4/CameraComponent.cc \ src/AutoPilotPlugins/PX4/SafetyComponent.cc \ src/AutoPilotPlugins/PX4/SensorsComponent.cc \ src/AutoPilotPlugins/PX4/SensorsComponentController.cc \ - src/AutoPilotPlugins/PX4/PX4TuningComponent.cc \ + src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc \ + src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc \ + src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc \ } PX4FirmwarePluginFactory { - HEADERS += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h - SOURCES += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc + RESOURCES *= px4resources.qrc + HEADERS += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h + SOURCES += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc } # Fact System code @@ -845,8 +904,8 @@ INCLUDEPATH += \ HEADERS += \ src/FactSystem/Fact.h \ - src/FactSystem/FactGroup.h \ src/FactSystem/FactControls/FactPanelController.h \ + src/FactSystem/FactGroup.h \ src/FactSystem/FactMetaData.h \ src/FactSystem/FactSystem.h \ src/FactSystem/FactValidator.h \ @@ -855,8 +914,8 @@ HEADERS += \ SOURCES += \ src/FactSystem/Fact.cc \ - src/FactSystem/FactGroup.cc \ src/FactSystem/FactControls/FactPanelController.cc \ + src/FactSystem/FactGroup.cc \ src/FactSystem/FactMetaData.cc \ src/FactSystem/FactSystem.cc \ src/FactSystem/FactValidator.cc \ @@ -895,32 +954,11 @@ contains (CONFIG, DISABLE_VIDEOSTREAMING) { # Android AndroidBuild { - include($$PWD/libs/qtandroidserialport/src/qtandroidserialport.pri) - message("Adding Serial Java Classes") - QT += androidextras - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android - OTHER_FILES += \ - $$PWD/android/AndroidManifest.xml \ - $$PWD/android/res/xml/device_filter.xml \ - $$PWD/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/UsbId.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \ - $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \ - $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbDeviceJNI.java \ - $$PWD/android/src/org/qgroundcontrol/qgchelper/UsbIoManager.java - - DISTFILES += \ - android/gradle/wrapper/gradle-wrapper.jar \ - android/gradlew \ - android/res/values/libs.xml \ - android/build.gradle \ - android/gradle/wrapper/gradle-wrapper.properties \ - android/gradlew.bat + contains (CONFIG, DISABLE_BUILTIN_ANDROID) { + message("Skipping builtin support for Android") + } else { + include(android.pri) + } } #------------------------------------------------------------------------------------- diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 96cdbe10d..c1938e297 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -1,52 +1,45 @@ - src/comm/PX4MockLink.params - src/comm/APMArduCopterMockLink.params - src/comm/APMArduPlaneMockLink.params - src/comm/APMArduSubMockLink.params src/FactSystem/FactSystemTest.qml src/AutoPilotPlugins/PX4/AirframeComponent.qml src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml - src/AutoPilotPlugins/APM/APMAirframeComponent.qml - src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml + src/AnalyzeView/AnalyzeView.qml + src/ui/AppSettings.qml + src/ui/preferences/BluetoothSettings.qml + src/AutoPilotPlugins/PX4/CameraComponent.qml + src/AutoPilotPlugins/PX4/CameraComponentSummary.qml src/ViewWidgets/CustomCommandWidget.qml + src/ui/preferences/DebugWindow.qml + src/AutoPilotPlugins/Common/ESP8266Component.qml + src/AutoPilotPlugins/Common/ESP8266ComponentSummary.qml src/VehicleSetup/FirmwareUpgrade.qml src/FlightDisplay/FlightDisplayView.qml - src/FlightDisplay/FlightDisplayViewUVC.qml src/FlightDisplay/FlightDisplayViewDummy.qml - src/AutoPilotPlugins/PX4/PX4FlightModes.qml - src/AutoPilotPlugins/PX4/PX4AdvancedFlightModes.qml - src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml + src/FlightDisplay/FlightDisplayViewUVC.qml src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml - src/AutoPilotPlugins/APM/APMFlightModesComponent.qml - src/AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml - src/ui/preferences/BluetoothSettings.qml - src/ui/preferences/DebugWindow.qml src/ui/preferences/GeneralSettings.qml + src/MissionEditor/GeoFenceEditor.qml + src/AnalyzeView/GeoTagPage.qml + src/VehicleSetup/JoystickConfig.qml src/ui/preferences/LinkSettings.qml + src/AnalyzeView/LogDownloadPage.qml src/ui/preferences/LogReplaySettings.qml - src/ui/preferences/MavlinkSettings.qml - src/ui/preferences/MockLink.qml - src/ui/preferences/MockLinkSettings.qml - src/QtLocationPlugin/QMLControl/OfflineMap.qml - src/ui/preferences/SerialSettings.qml - src/ui/preferences/TcpSettings.qml - src/ui/preferences/UdpSettings.qml - src/VehicleSetup/JoystickConfig.qml src/ui/toolbar/MainToolBar.qml src/ui/MainWindowHybrid.qml src/ui/MainWindowInner.qml src/ui/MainWindowNative.qml - src/ui/AppSettings.qml + src/ui/preferences/MavlinkSettings.qml src/MissionEditor/MissionEditor.qml + src/ui/preferences/MockLink.qml + src/ui/preferences/MockLinkSettings.qml src/AutoPilotPlugins/Common/MotorComponent.qml + src/QtLocationPlugin/QMLControl/OfflineMap.qml src/AutoPilotPlugins/PX4/PowerComponent.qml src/AutoPilotPlugins/PX4/PowerComponentSummary.qml - src/VehicleSetup/PX4FlowSensor.qml - src/QmlControls/QGroundControl.Controls.qmldir src/AnalyzeView/AnalyzePage.qml + src/QmlControls/AppMessages.qml src/QmlControls/ClickableColor.qml src/QmlControls/DropButton.qml src/QmlControls/ExclusiveGroupItem.qml @@ -55,23 +48,22 @@ src/QmlControls/JoystickThumbPad.qml src/ui/toolbar/MainToolBar.qml src/ui/toolbar/MainToolBarIndicators.qml + src/QmlControls/MissionCommandDialog.qml src/MissionEditor/MissionItemEditor.qml src/QmlControls/MissionItemIndexLabel.qml src/MissionEditor/MissionItemStatus.qml - src/QmlControls/MissionCommandDialog.qml - src/QmlControls/MultiRotorMotorDisplay.qml src/QmlControls/ModeSwitchDisplay.qml + src/QmlControls/MultiRotorMotorDisplay.qml + src/QmlControls/OfflineMapButton.qml src/QmlControls/ParameterEditor.qml src/QmlControls/ParameterEditorDialog.qml - src/MissionEditor/RallyPointItemEditor.qml - src/QmlControls/RCChannelMonitor.qml src/QmlControls/QGCButton.qml src/QmlControls/QGCCheckBox.qml src/QmlControls/QGCColoredImage.qml src/QmlControls/QGCComboBox.qml src/QmlControls/QGCFlickable.qml - src/QmlControls/QGCFlickableVerticalIndicator.qml src/QmlControls/QGCFlickableHorizontalIndicator.qml + src/QmlControls/QGCFlickableVerticalIndicator.qml src/QmlControls/QGCLabel.qml src/QmlControls/QGCMobileFileDialog.qml src/QmlControls/QGCMovableItem.qml @@ -84,7 +76,10 @@ src/QmlControls/QGCViewDialog.qml src/QmlControls/QGCViewMessage.qml src/QmlControls/QGCViewPanel.qml + src/QmlControls/QGroundControl.Controls.qmldir src/MissionEditor/RallyPointEditorHeader.qml + src/MissionEditor/RallyPointItemEditor.qml + src/QmlControls/RCChannelMonitor.qml src/QmlControls/RoundButton.qml src/AutoPilotPlugins/Common/SetupPage.qml src/ui/toolbar/SignalStrength.qml @@ -92,11 +87,7 @@ src/QmlControls/SubMenuButton.qml src/QmlControls/VehicleRotationCal.qml src/QmlControls/VehicleSummaryRow.qml - src/QmlControls/AppMessages.qml src/ViewWidgets/ViewWidget.qml - src/MissionEditor/SimpleItemEditor.qml - src/MissionEditor/SurveyItemEditor.qml - src/MissionEditor/GeoFenceEditor.qml src/FactSystem/FactControls/FactBitmask.qml src/FactSystem/FactControls/FactCheckBox.qml src/FactSystem/FactControls/FactComboBox.qml @@ -106,15 +97,13 @@ src/FactSystem/FactControls/FactTextFieldGrid.qml src/FactSystem/FactControls/FactTextFieldRow.qml src/FactSystem/FactControls/qmldir - src/FlightDisplay/qmldir src/FlightDisplay/FlightDisplayView.qml src/FlightDisplay/FlightDisplayViewMap.qml src/FlightDisplay/FlightDisplayViewVideo.qml src/FlightDisplay/FlightDisplayViewWidgets.qml - src/FlightDisplay/VirtualJoystick.qml - src/FlightMap/qmldir + src/FlightDisplay/qmldir src/FlightMap/FlightMap.qml - src/MissionEditor/QGCMapPolygonControls.qml + src/FlightMap/Widgets/InstrumentSwipeView.qml src/FlightMap/MapScale.qml src/FlightMap/MapItems/MissionItemIndicator.qml src/FlightMap/MapItems/MissionItemView.qml @@ -125,57 +114,33 @@ src/FlightMap/Widgets/QGCCompassWidget.qml src/FlightMap/Widgets/QGCInstrumentWidget.qml src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml + src/MissionEditor/QGCMapPolygonControls.qml src/FlightMap/Widgets/QGCPitchIndicator.qml src/FlightMap/QGCVideoBackground.qml + src/FlightMap/qmldir src/FlightMap/Widgets/ValuesWidget.qml src/FlightMap/Widgets/VehicleHealthWidget.qml - src/FlightMap/Widgets/VibrationWidget.qml src/FlightMap/MapItems/VehicleMapItem.qml - src/FlightMap/Widgets/InstrumentSwipeView.qml + src/FlightMap/Widgets/VibrationWidget.qml src/QmlControls/QGroundControl.ScreenTools.qmldir src/QmlControls/ScreenTools.qml src/QmlControls/QmlTest.qml src/AutoPilotPlugins/Common/RadioComponent.qml - src/AutoPilotPlugins/Common/ESP8266Component.qml - src/AutoPilotPlugins/Common/ESP8266ComponentSummary.qml - src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentPlane.qml - src/AutoPilotPlugins/APM/APMNotSupported.qml - src/AutoPilotPlugins/APM/APMCameraComponent.qml - src/AutoPilotPlugins/APM/APMCameraComponentSummary.qml - src/AutoPilotPlugins/APM/APMLightsComponent.qml - src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml - src/AutoPilotPlugins/APM/APMPowerComponent.qml - src/AutoPilotPlugins/APM/APMPowerComponentSummary.qml - src/AutoPilotPlugins/APM/APMRadioComponentSummary.qml - src/AutoPilotPlugins/APM/APMSafetyComponentCopter.qml - src/AutoPilotPlugins/APM/APMSafetyComponentPlane.qml - src/AutoPilotPlugins/APM/APMSafetyComponentRover.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryCopter.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryPlane.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryRover.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml - src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml - src/AutoPilotPlugins/PX4/CameraComponent.qml - src/AutoPilotPlugins/PX4/CameraComponentSummary.qml src/AutoPilotPlugins/PX4/SafetyComponent.qml src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml src/AutoPilotPlugins/PX4/SensorsComponent.qml src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml - src/AutoPilotPlugins/APM/APMSensorsComponent.qml - src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml + src/ui/preferences/SerialSettings.qml src/VehicleSetup/SetupParameterEditor.qml src/VehicleSetup/SetupView.qml + src/MissionEditor/SimpleItemEditor.qml + src/MissionEditor/SurveyItemEditor.qml + src/ui/preferences/TcpSettings.qml src/test.qml + src/ui/preferences/UdpSettings.qml src/VehicleSetup/VehicleSummary.qml - src/QmlControls/OfflineMapButton.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentVTOL.qml - src/AnalyzeView/AnalyzeView.qml - src/AnalyzeView/GeoTagPage.qml - src/AnalyzeView/LogDownloadPage.qml + src/FlightDisplay/VirtualJoystick.qml src/MissionManager/MavCmdInfoCommon.json @@ -184,18 +149,6 @@ src/MissionManager/MavCmdInfoRover.json src/MissionManager/MavCmdInfoSub.json src/MissionManager/MavCmdInfoVTOL.json - src/FirmwarePlugin/APM/MavCmdInfoCommon.json - src/FirmwarePlugin/APM/MavCmdInfoFixedWing.json - src/FirmwarePlugin/APM/MavCmdInfoMultiRotor.json - src/FirmwarePlugin/APM/MavCmdInfoRover.json - src/FirmwarePlugin/APM/MavCmdInfoSub.json - src/FirmwarePlugin/APM/MavCmdInfoVTOL.json - src/FirmwarePlugin/PX4/MavCmdInfoCommon.json - src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json - src/FirmwarePlugin/PX4/MavCmdInfoMultiRotor.json - src/FirmwarePlugin/PX4/MavCmdInfoRover.json - src/FirmwarePlugin/PX4/MavCmdInfoSub.json - src/FirmwarePlugin/PX4/MavCmdInfoVTOL.json src/Vehicle/VehicleFact.json src/Vehicle/BatteryFact.json src/Vehicle/GPSFact.json diff --git a/src/FactSystem/FactControls/FactCheckBox.qml b/src/FactSystem/FactControls/FactCheckBox.qml index cd421ad5e..5dc7788a4 100644 --- a/src/FactSystem/FactControls/FactCheckBox.qml +++ b/src/FactSystem/FactControls/FactCheckBox.qml @@ -11,8 +11,8 @@ QGCCheckBox { property variant checkedValue: 1 property variant uncheckedValue: 0 - partiallyCheckedEnabled: fact.value != checkedValue && fact.value != uncheckedValue - checkedState: fact.value == checkedValue ? Qt.Checked : (fact.value == uncheckedValue ? Qt.Unchecked : Qt.PartiallyChecked) + partiallyCheckedEnabled: fact ? fact.value !== checkedValue && fact.value !== uncheckedValue : false + checkedState: fact ? fact.value === checkedValue ? Qt.Checked : (fact.value === uncheckedValue ? Qt.Unchecked : Qt.PartiallyChecked) : false text: qsTr("Label") diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index c9608f75f..278c0a06c 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -83,6 +83,10 @@ #include "QGCMapPolygon.h" #include "ParameterManager.h" +#if defined(QGC_CUSTOM_BUILD) +#include CUSTOMHEADER +#endif + #ifndef __ios__ #include "SerialLink.h" #endif @@ -184,10 +188,15 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) , _toolbox(NULL) , _bluetoothAvailable(false) , _lastKnownHomePosition(37.803784, -122.462276, 0.0) + , _pQGCOptions(NULL) + , _pCorePlugin(NULL) { Q_ASSERT(_app == NULL); _app = this; + //-- Scan and load plugins + _scanAndLoadPlugins(); + // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings #ifndef __android__ setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); @@ -349,6 +358,9 @@ QGCApplication::~QGCApplication() #endif shutdownVideoStreaming(); delete _toolbox; + if(_pCorePlugin) { + delete _pCorePlugin; + } } void QGCApplication::_initCommon(void) @@ -445,6 +457,13 @@ bool QGCApplication::_initForNormalAppBoot(void) settings.sync(); + //-- Initialize Core Plugin (if any) + if(_pCorePlugin) { + if(!_pCorePlugin->init(this)) { + return false; + } + } + return true; } @@ -564,7 +583,7 @@ void QGCApplication::setStyle(bool styleIsDark) emit styleChanged(_styleIsDark); } -void QGCApplication::_loadCurrentStyle(void) +void QGCApplication::_loadCurrentStyle() { #ifndef __mobile__ bool success = true; @@ -626,7 +645,7 @@ void QGCApplication::_missingParamsDisplay(void) showMessage(QString("Parameters missing from firmware: %1. You may be running an older version of firmware QGC does not work correctly with or your firmware has a bug in it.").arg(params)); } -QObject* QGCApplication::_rootQmlObject(void) +QObject* QGCApplication::_rootQmlObject() { #ifdef __mobile__ return _qmlAppEngine->rootObjects()[0]; @@ -689,3 +708,41 @@ void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosit settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude()); _lastKnownHomePosition = lastKnownHomePosition; } + +IQGCOptions* QGCApplication::qgcOptions() +{ + return _pQGCOptions; +} + +void QGCApplication::_scanAndLoadPlugins() +{ +#if defined (QGC_DYNAMIC_PLUGIN) + //-- Look for plugins (Dynamic) + QString filter = "*.core.so"; + QString path = QCoreApplication::applicationDirPath(); + QDirIterator it(path, QStringList() << filter, QDir::Files); + while(it.hasNext()) { + QString pluginFile = it.next(); + QPluginLoader loader(pluginFile); + QObject *plugin = loader.instance(); + if(plugin) { + _pCorePlugin = qobject_cast(plugin); + if(_pCorePlugin) { + _pQGCOptions = _pCorePlugin->uiOptions(); + return; + } + } else { + qWarning() << "Plugin" << pluginFile << " not loaded:" << loader.errorString(); + } + } +#elif defined (QGC_CUSTOM_BUILD) + //-- Create custom plugin (Static) + _pCorePlugin = (IQGCCorePlugin*) new CUSTOMCLASS(this); + if(_pCorePlugin) { + _pQGCOptions = _pCorePlugin->uiOptions(); + return; + } +#endif + //-- No plugins found, use default options + _pQGCOptions = new IQGCOptions; +} diff --git a/src/QGCApplication.h b/src/QGCApplication.h index ddb14cca0..6a9dbf966 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -35,6 +35,12 @@ #include "UASMessageHandler.h" #include "FactSystem.h" +//-- Plugin Architecture +#include "IQGCApplication.h" +#include "IQGCCorePlugin.h" +#include "IQGCOptions.h" +#include "IQGCQMLSource.h" + #ifdef QGC_RTLAB_ENABLED #include "OpalLink.h" #endif @@ -117,6 +123,11 @@ public: QGeoCoordinate lastKnownHomePosition(void) { return _lastKnownHomePosition; } void setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition); + /// Options (can be overwriten by a core plugin) + IQGCOptions* qgcOptions(); + /// Custom core plugin (NULL if none) + IQGCCorePlugin* customCorePlugin() { return _pCorePlugin; } + public slots: /// You can connect to this slot to show an information message box from a different thread. void informationMessageBoxOnMainThread(const QString& title, const QString& msg); @@ -166,8 +177,9 @@ private slots: void _missingParamsDisplay(void); private: - void _loadCurrentStyle(void); - QObject* _rootQmlObject(void); + void _loadCurrentStyle (); + QObject* _rootQmlObject (); + void _scanAndLoadPlugins (); #ifdef __mobile__ QQmlApplicationEngine* _qmlAppEngine; @@ -205,6 +217,10 @@ private: /// Unit Test have access to creating and destroying singletons friend class UnitTest; + + //-- Plugin Architecture + IQGCOptions* _pQGCOptions; + IQGCCorePlugin* _pCorePlugin; }; /// @brief Returns the QGCApplication object singleton. diff --git a/src/dummy.h b/src/dummy.h new file mode 100644 index 000000000..668d46204 --- /dev/null +++ b/src/dummy.h @@ -0,0 +1,2 @@ +// Dummy Header +#pragma once -- GitLab From 33297c60e312e682773e91c964f7f829fcb9359a Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 27 Nov 2016 08:06:14 -0500 Subject: [PATCH 063/398] Removing unused file. --- src/dummy.h | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 src/dummy.h diff --git a/src/dummy.h b/src/dummy.h deleted file mode 100644 index 668d46204..000000000 --- a/src/dummy.h +++ /dev/null @@ -1,2 +0,0 @@ -// Dummy Header -#pragma once -- GitLab From 0920b97983e6168fe7bd625966eddb4778806481 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 27 Nov 2016 10:19:51 -0500 Subject: [PATCH 064/398] Custom plugin work --- QGCCommon.pri | 4 +- qgcresources.qrc | 1 + qgroundcontrol.pro | 4 +- resources/waves.svg | 21 ++++++ src/FlightDisplay/FlightDisplayView.qml | 2 +- .../FlightDisplayViewWidgets.qml | 8 +-- src/FlightDisplay/VideoManager.cc | 19 +++++- src/QGCApplication.cc | 2 +- src/QmlControls/QGroundControlQmlGlobal.cc | 65 +++++++++++++++++++ src/QmlControls/QGroundControlQmlGlobal.h | 28 ++++++-- src/Vehicle/Vehicle.cc | 2 +- src/VehicleSetup/SetupView.qml | 27 ++++++++ src/comm/LinkConfiguration.cc | 8 +-- src/comm/LinkConfiguration.h | 2 +- src/comm/LinkManager.cc | 28 ++++---- src/comm/LinkManager.h | 10 +-- src/main.cc | 4 +- src/uas/UAS.cc | 2 +- src/ui/MainWindow.cc | 2 +- src/ui/MainWindow.h | 14 ---- src/ui/QGCMAVLinkLogPlayer.cc | 30 ++++----- src/ui/preferences/GeneralSettings.qml | 5 ++ src/ui/toolbar/MainToolBar.qml | 1 + 23 files changed, 215 insertions(+), 74 deletions(-) create mode 100644 resources/waves.svg diff --git a/QGCCommon.pri b/QGCCommon.pri index 4a8074318..2fb1d8016 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -38,7 +38,6 @@ linux { equals(ANDROID_TARGET_ARCH, x86) { CONFIG += Androidx86Build DEFINES += __androidx86__ - DEFINES += QGC_DISABLE_UVC message("Android x86 build") } else { message("Android Arm build") @@ -77,9 +76,10 @@ linux { error("Unsupported Qt version, 5.5.x or greater is required for iOS") } message("iOS build") - CONFIG += iOSBuild MobileBuild app_bundle + CONFIG += iOSBuild MobileBuild app_bundle NoSerialBuild DEFINES += __ios__ DEFINES += QGC_NO_GOOGLE_MAPS + DEFINES += NO_SERIAL_LINK QMAKE_IOS_DEPLOYMENT_TARGET = 8.0 QMAKE_IOS_TARGETED_DEVICE_FAMILY = 1,2 # Universal QMAKE_LFLAGS += -Wl,-no_pie diff --git a/qgcresources.qrc b/qgcresources.qrc index b8ce22efe..51a35405d 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -181,6 +181,7 @@ resources/TrashDelete.svg resources/XDelete.svg resources/XDeleteBlack.svg + resources/waves.svg resources/icons/qgroundcontrol.ico diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 3652d7529..28a09d033 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -503,7 +503,7 @@ contains(DEFINES, QGC_ENABLE_BLUETOOTH) { src/comm/BluetoothLink.h \ } -!iOSBuild { +!NoSerialBuild { HEADERS += \ src/comm/QGCSerialPortInfo.h \ src/comm/SerialLink.h \ @@ -651,7 +651,7 @@ SOURCES += \ src/comm/MockLinkMissionItemHandler.cc \ } -!iOSBuild { +!NoSerialBuild { SOURCES += \ src/comm/QGCSerialPortInfo.cc \ src/comm/SerialLink.cc \ diff --git a/resources/waves.svg b/resources/waves.svg new file mode 100644 index 000000000..fbca7c781 --- /dev/null +++ b/resources/waves.svg @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 85faa00b0..c739d322c 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -34,7 +34,7 @@ QGCView { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, true) : true + property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, QGroundControl.mainViewIsMap) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false property real _roll: _activeVehicle ? _activeVehicle.roll.value : _defaultRoll diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index c37074955..094a3fa25 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -140,10 +140,10 @@ Item { } QGCLabel { - id: flyLabel - text: qsTr("Fly") - color: mapPal.text - visible: !ScreenTools.isShortScreen + id: flyLabel + text: qsTr("Fly") + color: mapPal.text + visible: !ScreenTools.isShortScreen && _mainIsMap anchors.topMargin: _toolButtonTopMargin anchors.horizontalCenter: toolColumn.horizontalCenter anchors.top: parent.top diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 02c325fab..73a200d77 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -43,9 +43,24 @@ VideoManager::VideoManager(QGCApplication* app) { //-- Get saved settings QSettings settings; +#if defined(NO_UDP_VIDEO) && defined(QGC_GST_STREAMING) + setVideoSource(settings.value(kVideoSourceKey, kRTSPStream).toString()); +#else setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); - setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); - setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL +#endif + //-- Check if core plugin defines its own video requirements + if(qgcApp()->qgcOptions()->definesVideo()) { + if(qgcApp()->qgcOptions()->videoUDPPort()) { + setUdpPort(qgcApp()->qgcOptions()->videoUDPPort()); + setVideoSource(kUDPStream); + } else { + setVideoSource(kRTSPStream); + setRtspURL(qgcApp()->qgcOptions()->videoRSTPUrl()); + } + } else { + setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); + setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL + } _init = true; #if defined(QGC_GST_STREAMING) _updateVideo(); diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 278c0a06c..9847f4b8e 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -87,7 +87,7 @@ #include CUSTOMHEADER #endif -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index c249191bd..90a67453b 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -353,3 +353,68 @@ QMap& QGroundControlQmlGlobal::nameToMetaDataMap(void) { return map; } + +bool QGroundControlQmlGlobal::colapseSettings() +{ + return qgcApp()->qgcOptions()->colapseSettings(); +} + +bool QGroundControlQmlGlobal::mainViewIsMap() +{ + return qgcApp()->qgcOptions()->mainViewIsMap(); +} + +bool QGroundControlQmlGlobal::enableVirtualJoystick() +{ + return qgcApp()->qgcOptions()->enableVirtualJoystick(); +} + +bool QGroundControlQmlGlobal::enableAutoConnectOptions() +{ + return qgcApp()->qgcOptions()->enableAutoConnectOptions(); +} + +bool QGroundControlQmlGlobal::enableVideoSourceOptions() +{ + return qgcApp()->qgcOptions()->enableVideoSourceOptions(); +} + +bool QGroundControlQmlGlobal::hasCustomSettings() +{ + if(qgcApp()->customCorePlugin()) { + if(qgcApp()->customCorePlugin()->settingsQML()) { + return !qgcApp()->customCorePlugin()->settingsQML()->pageUrl().isEmpty(); + } + } + return false; +} + +QString QGroundControlQmlGlobal::customSettingsURL() +{ + if(qgcApp()->customCorePlugin()) { + if(qgcApp()->customCorePlugin()->settingsQML()) { + return qgcApp()->customCorePlugin()->settingsQML()->pageUrl(); + } + } + return QString(); +} + +QString QGroundControlQmlGlobal::customSettingsTitle() +{ + if(qgcApp()->customCorePlugin()) { + if(qgcApp()->customCorePlugin()->settingsQML()) { + return qgcApp()->customCorePlugin()->settingsQML()->pageTitle(); + } + } + return QString(); +} + +QString QGroundControlQmlGlobal::customSettingsLogoUrl() +{ + if(qgcApp()->customCorePlugin()) { + if(qgcApp()->customCorePlugin()->settingsQML()) { + return qgcApp()->customCorePlugin()->settingsQML()->pageIconUrl(); + } + } + return QString(); +} diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 8f3ef8777..d56642587 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -79,7 +79,6 @@ public: Q_PROPERTY(qreal zOrderMapItems READ zOrderMapItems CONSTANT) ///< z order value for map items, for example: mission item indicators // Various QGC settings exposed to Qml - Q_PROPERTY(bool isAdvancedMode READ isAdvancedMode CONSTANT) ///< Global "Advance Mode" preference. Certain UI elements and features are different based on this. Q_PROPERTY(bool isDarkStyle READ isDarkStyle WRITE setIsDarkStyle NOTIFY isDarkStyleChanged) // TODO: Should be in ScreenTools? Q_PROPERTY(bool isAudioMuted READ isAudioMuted WRITE setIsAudioMuted NOTIFY isAudioMutedChanged) Q_PROPERTY(bool isSaveLogPrompt READ isSaveLogPrompt WRITE setIsSaveLogPrompt NOTIFY isSaveLogPromptChanged) @@ -87,6 +86,20 @@ public: Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) + //------------------------------------------------------------------------- + //-- Options that can be set by plugins + Q_PROPERTY(bool colapseSettings READ colapseSettings CONSTANT) + Q_PROPERTY(bool mainViewIsMap READ mainViewIsMap CONSTANT) + Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) + Q_PROPERTY(bool enableAutoConnectOptions READ enableAutoConnectOptions CONSTANT) + Q_PROPERTY(bool enableVideoSourceOptions READ enableVideoSourceOptions CONSTANT) + + Q_PROPERTY(bool hasCustomSettings READ hasCustomSettings CONSTANT) + Q_PROPERTY(QString customSettingsURL READ customSettingsURL CONSTANT) + Q_PROPERTY(QString customSettingsTitle READ customSettingsTitle CONSTANT) + Q_PROPERTY(QString customSettingsLogoUrl READ customSettingsLogoUrl CONSTANT) + + //------------------------------------------------------------------------- // MavLink Protocol Q_PROPERTY(bool isVersionCheckEnabled READ isVersionCheckEnabled WRITE setIsVersionCheckEnabled NOTIFY isVersionCheckEnabledChanged) Q_PROPERTY(int mavlinkSystemID READ mavlinkSystemID WRITE setMavlinkSystemID NOTIFY mavlinkSystemIDChanged) @@ -185,6 +198,16 @@ public: bool isVersionCheckEnabled () { return _toolbox->mavlinkProtocol()->versionCheckEnabled(); } int mavlinkSystemID () { return _toolbox->mavlinkProtocol()->getSystemId(); } + bool colapseSettings (); + bool mainViewIsMap (); + bool enableVirtualJoystick (); + bool enableAutoConnectOptions(); + bool enableVideoSourceOptions(); + bool hasCustomSettings (); + QString customSettingsTitle (); + QString customSettingsURL (); + QString customSettingsLogoUrl (); + QGeoCoordinate lastKnownHomePosition() { return qgcApp()->lastKnownHomePosition(); } static Fact* offlineEditingFirmwareType (void); @@ -196,9 +219,6 @@ public: static Fact* speedUnits (void); static Fact* batteryPercentRemainingAnnounce(void); - //-- TODO: Make this into an actual preference. - bool isAdvancedMode () { return false; } - void setIsDarkStyle (bool dark); void setIsAudioMuted (bool muted); void setIsSaveLogPrompt (bool prompt); diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 4537f0f35..90ad8003d 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -933,7 +933,7 @@ void Vehicle::_sendMessageOnLink(LinkInterface* link, mavlink_message_t message) /// @return Direct usb connection link to board if one, NULL if none LinkInterface* Vehicle::priorityLink(void) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK foreach (LinkInterface* link, _links) { if (link->isConnected()) { SerialLink* pSerialLink = qobject_cast(link); diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index 77b678179..89389ee40 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -256,6 +256,33 @@ Rectangle { visible: !ScreenTools.isShortScreen } + SubMenuButton { + imageResource: "/res/gear-white.svg" + setupIndicator: false + exclusiveGroup: setupButtonGroup + text: "General" + visible: QGroundControl.colapseSettings + onClicked: panelLoader.setSource("GeneralSettings.qml") + } + + SubMenuButton { + imageResource: QGroundControl.customSettingsLogoUrl + setupIndicator: false + exclusiveGroup: setupButtonGroup + text: QGroundControl.customSettingsTitle + visible: QGroundControl.colapseSettings && QGroundControl.hasCustomSettings + onClicked: panelLoader.setSource(QGroundControl.customSettingsURL) + } + + SubMenuButton { + imageResource: "/res/waves.svg" + setupIndicator: false + exclusiveGroup: setupButtonGroup + text: "MAVLink" + visible: QGroundControl.colapseSettings + onClicked: panelLoader.setSource("MavlinkSettings.qml") + } + SubMenuButton { id: summaryButton imageResource: "/qmlimages/VehicleSummaryIcon.png" diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc index 367051b73..915c20cda 100644 --- a/src/comm/LinkConfiguration.cc +++ b/src/comm/LinkConfiguration.cc @@ -15,12 +15,12 @@ */ #include "LinkConfiguration.h" -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif #include "UDPLink.h" #include "TCPLink.h" -#ifndef __mobile__ +#if !defined(__mobile__) #include "LogReplayLink.h" #endif #ifdef QGC_ENABLE_BLUETOOTH @@ -79,7 +79,7 @@ LinkConfiguration* LinkConfiguration::createSettings(int type, const QString& na { LinkConfiguration* config = NULL; switch(type) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: config = new SerialConfiguration(name); break; @@ -117,7 +117,7 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc { LinkConfiguration* dupe = NULL; switch(source->type()) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK case TypeSerial: dupe = new SerialConfiguration(dynamic_cast(source)); break; diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 0158cc4a5..59fa8f36f 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -46,7 +46,7 @@ public: /// The link types supported by QGC /// Any changes here MUST be reflected in LinkManager::linkTypeStrings() enum LinkType { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK TypeSerial, ///< Serial Link #endif TypeUdp, ///< UDP Link diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 7aef4cad9..34a0320fd 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -21,7 +21,7 @@ #include #include -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "QGCSerialPortInfo.h" #endif @@ -85,7 +85,7 @@ LinkManager::LinkManager(QGCApplication* app) _autoconnectRTKGPS = settings.value(_autoconnectRTKGPSKey, true).toBool(); _autoconnectLibrePilot = settings.value(_autoconnectLibrePilotKey, true).toBool(); -#ifndef __ios__ +#ifndef NO_SERIAL_LINK _activeLinkCheckTimer.setInterval(_activeLinkCheckTimeoutMSecs); _activeLinkCheckTimer.setSingleShot(false); connect(&_activeLinkCheckTimer, &QTimer::timeout, this, &LinkManager::_activeLinkCheck); @@ -113,7 +113,7 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) Q_ASSERT(config); LinkInterface* pLink = NULL; switch(config->type()) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: { SerialConfiguration* serialConfig = dynamic_cast(config); @@ -373,7 +373,7 @@ void LinkManager::loadLinkConfigurationList() LinkConfiguration* pLink = NULL; bool autoConnect = settings.value(root + "/auto").toBool(); switch((LinkConfiguration::LinkType)type) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: pLink = (LinkConfiguration*)new SerialConfiguration(name); break; @@ -443,7 +443,7 @@ void LinkManager::loadLinkConfigurationList() _configurationsLoaded = true; } -#ifndef __ios__ +#ifndef NO_SERIAL_LINK SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName) { QString searchPort = portName.trimmed(); @@ -488,7 +488,7 @@ void LinkManager::_updateAutoConnectLinks(void) emit linkConfigurationsChanged(); } -#ifndef __ios__ +#ifndef NO_SERIAL_LINK QStringList currentPorts; QList portList = QGCSerialPortInfo::availablePorts(); @@ -633,7 +633,7 @@ void LinkManager::_updateAutoConnectLinks(void) } delete pDeleteConfig; } -#endif // __ios__ +#endif // NO_SERIAL_LINK } void LinkManager::shutdown(void) @@ -704,7 +704,7 @@ QStringList LinkManager::linkTypeStrings(void) const static QStringList list; if(!list.size()) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK list += "Serial"; #endif list += "UDP"; @@ -727,7 +727,7 @@ void LinkManager::_updateSerialPorts() { _commPortList.clear(); _commPortDisplayList.clear(); -#ifndef __ios__ +#ifndef NO_SERIAL_LINK QList portList = QSerialPortInfo::availablePorts(); foreach (const QSerialPortInfo &info, portList) { @@ -758,7 +758,7 @@ QStringList LinkManager::serialPorts(void) QStringList LinkManager::serialBaudRates(void) { -#ifdef __ios__ +#ifdef NO_SERIAL_LINK QStringList foo; return foo; #else @@ -791,7 +791,7 @@ bool LinkManager::endCreateConfiguration(LinkConfiguration* config) LinkConfiguration* LinkManager::createConfiguration(int type, const QString& name) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK if((LinkConfiguration::LinkType)type == LinkConfiguration::TypeSerial) _updateSerialPorts(); #endif @@ -801,7 +801,7 @@ LinkConfiguration* LinkManager::createConfiguration(int type, const QString& nam LinkConfiguration* LinkManager::startConfigurationEditing(LinkConfiguration* config) { Q_ASSERT(config != NULL); -#ifndef __ios__ +#ifndef NO_SERIAL_LINK if(config->type() == LinkConfiguration::TypeSerial) _updateSerialPorts(); #endif @@ -815,7 +815,7 @@ void LinkManager::_fixUnnamed(LinkConfiguration* config) //-- Check for "Unnamed" if (config->name() == "Unnamed") { switch(config->type()) { -#ifndef __ios__ +#ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: { QString tname = dynamic_cast(config)->portName(); #ifdef Q_OS_WIN @@ -895,7 +895,7 @@ bool LinkManager::isBluetoothAvailable(void) return qgcApp()->isBluetoothAvailable(); } -#ifndef __ios__ +#ifndef NO_SERIAL_LINK void LinkManager::_activeLinkCheck(void) { SerialLink* link = NULL; diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 330572033..23cddcbdf 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -24,12 +24,12 @@ #include "QGCToolbox.h" #include "ProtocolInterface.h" #include "MAVLinkProtocol.h" -#ifndef __mobile__ +#if !defined(__mobile__) #include "LogReplayLink.h" #endif #include "QmlObjectListModel.h" -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif @@ -199,7 +199,7 @@ private slots: void _linkConnected(void); void _linkDisconnected(void); void _linkConnectionRemoved(LinkInterface* link); -#ifndef __ios__ +#ifndef NO_SERIAL_LINK void _activeLinkCheck(void); #endif @@ -210,7 +210,7 @@ private: void _fixUnnamed(LinkConfiguration* config); bool _setAutoconnectWorker(bool& currentAutoconnect, bool newAutoconnect, const char* autoconnectKey); -#ifndef __ios__ +#ifndef NO_SERIAL_LINK SerialConfiguration* _autoconnectConfigurationsContainsPort(const QString& portName); #endif @@ -237,7 +237,7 @@ private: bool _autoconnectPX4Flow; bool _autoconnectRTKGPS; bool _autoconnectLibrePilot; -#ifndef __ios__ +#ifndef NO_SERIAL_LINK QTimer _activeLinkCheckTimer; ///< Timer which checks for a vehicle showing up on a usb direct link QList _activeLinkCheckList; ///< List of links we are waiting for a vehicle to show up on static const int _activeLinkCheckTimeoutMSecs = 15000; ///< Amount of time to wait for a heatbeat. Keep in mind ArduPilot stack heartbeat is slow to come. diff --git a/src/main.cc b/src/main.cc index eb8319cdf..80000cd33 100644 --- a/src/main.cc +++ b/src/main.cc @@ -74,7 +74,7 @@ int WindowsCrtReportHook(int reportType, char* message, int* returnValue) #endif -#ifdef __android__ +#if defined(__android__) && !defined(NO_SERIAL_LINK) #include #include "qserialport.h" @@ -151,7 +151,7 @@ int main(int argc, char *argv[]) // that we use these types in signals, and without calling qRegisterMetaType we can't queue // these signals. In general we don't queue these signals, but we do what the warning says // anyway to silence the debug output. -#ifndef __ios__ +#ifndef NO_SERIAL_LINK qRegisterMetaType(); #endif #ifdef QGC_ENABLE_BLUETOOTH diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 840bf7c3a..e313e87aa 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -36,7 +36,7 @@ #include "MAVLinkProtocol.h" #include "QGCMAVLink.h" #include "LinkManager.h" -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif #include "FirmwarePluginManager.h" diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 6cd378e1f..cd40b1927 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -54,7 +54,7 @@ #include "AppMessages.h" #endif -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 3f7c31d38..a8851c7e2 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -33,9 +33,7 @@ #include "UASInterface.h" #include "LogCompressor.h" #include "QGCMAVLinkInspector.h" -#ifndef __mobile__ #include "QGCMAVLinkLogPlayer.h" -#endif #include "MAVLinkDecoder.h" #include "Vehicle.h" #include "QGCDockWidget.h" @@ -117,12 +115,10 @@ signals: #endif //QGC_MOUSE_ENABLED_LINUX public: -#ifndef __mobile__ QGCMAVLinkLogPlayer* getLogPlayer() { return logPlayer; } -#endif protected: void connectCommonActions(); @@ -133,9 +129,7 @@ protected: QSettings settings; QPointer mavlinkDecoder; -#ifndef __mobile__ QGCMAVLinkLogPlayer* logPlayer; -#endif #ifdef QGC_MOUSE_ENABLED_WIN /** @brief 3d Mouse support (WIN only) */ Mouse3DInput* mouseInput; ///< 3dConnexion 3dMouse SDK @@ -164,10 +158,7 @@ protected: private slots: void _closeWindow(void) { close(); } void _vehicleAdded(Vehicle* vehicle); - -#ifndef __mobile__ void _showDockWidgetAction(bool show); -#endif #ifdef UNITTEST_BUILD void _showQmlTestWidget(void); @@ -179,22 +170,17 @@ private: void _openUrl(const QString& url, const QString& errorMessage); -#ifndef __mobile__ QMap _mapName2DockWidget; QMap _mapName2Action; -#endif void _storeCurrentViewState(void); void _loadCurrentViewState(void); - -#ifndef __mobile__ bool _createInnerDockWidget(const QString& widgetName); void _buildCommonWidgets(void); void _hideAllDockWidgets(void); void _showDockWidget(const QString &name, bool show); void _loadVisibleWidgetsSettings(void); void _storeVisibleWidgetsSettings(void); -#endif bool _lowPowerMode; ///< If enabled, QGC reduces the update rates of all widgets bool _showStatusBar; diff --git a/src/ui/QGCMAVLinkLogPlayer.cc b/src/ui/QGCMAVLinkLogPlayer.cc index e3242e1c3..22267f376 100644 --- a/src/ui/QGCMAVLinkLogPlayer.cc +++ b/src/ui/QGCMAVLinkLogPlayer.cc @@ -2,7 +2,7 @@ #include #include "MainWindow.h" -#ifndef __ios__ +#ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif #include "QGCMAVLinkLogPlayer.h" @@ -17,7 +17,7 @@ QGCMAVLinkLogPlayer::QGCMAVLinkLogPlayer(QWidget *parent) : QWidget(parent), _replayLink(NULL), _ui(new Ui::QGCMAVLinkLogPlayer) -{ +{ _ui->setupUi(this); _ui->horizontalLayout->setAlignment(Qt::AlignTop); @@ -26,7 +26,7 @@ QGCMAVLinkLogPlayer::QGCMAVLinkLogPlayer(QWidget *parent) : connect(_ui->playButton, &QPushButton::clicked, this, &QGCMAVLinkLogPlayer::_playPauseToggle); connect(_ui->positionSlider, &QSlider::valueChanged, this, &QGCMAVLinkLogPlayer::_setPlayheadFromSlider); connect(_ui->positionSlider, &QSlider::sliderPressed, this, &QGCMAVLinkLogPlayer::_pause); - + #if 0 // Speed slider is removed from 3.0 release. Too broken to fix. connect(_ui->speedSlider, &QSlider::valueChanged, this, &QGCMAVLinkLogPlayer::_setAccelerationFromSlider); @@ -36,10 +36,10 @@ QGCMAVLinkLogPlayer::QGCMAVLinkLogPlayer(QWidget *parent) : #endif _enablePlaybackControls(false); - + _ui->positionSlider->setMinimum(0); _ui->positionSlider->setMaximum(100); - + } QGCMAVLinkLogPlayer::~QGCMAVLinkLogPlayer() @@ -68,7 +68,7 @@ void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void) QGCMessageBox::information(tr("Log Replay"), tr("You must close all connections prior to replaying a log.")); return; } - + QString logFilename = QGCFileDialog::getOpenFileName( this, tr("Load MAVLink Log File"), @@ -78,21 +78,21 @@ void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void) if (logFilename.isEmpty()) { return; } - + LinkInterface* createConnectedLink(LinkConfiguration* config); - + LogReplayLinkConfiguration* linkConfig = new LogReplayLinkConfiguration(QString("Log Replay")); linkConfig->setLogFilename(logFilename); linkConfig->setName(linkConfig->logFilenameShort()); _ui->logFileNameLabel->setText(linkConfig->logFilenameShort()); _replayLink = (LogReplayLink*)qgcApp()->toolbox()->linkManager()->createConnectedLink(linkConfig); - + connect(_replayLink, &LogReplayLink::logFileStats, this, &QGCMAVLinkLogPlayer::_logFileStats); connect(_replayLink, &LogReplayLink::playbackStarted, this, &QGCMAVLinkLogPlayer::_playbackStarted); connect(_replayLink, &LogReplayLink::playbackPaused, this, &QGCMAVLinkLogPlayer::_playbackPaused); connect(_replayLink, &LogReplayLink::playbackPercentCompleteChanged, this, &QGCMAVLinkLogPlayer::_playbackPercentCompleteChanged); connect(_replayLink, &LogReplayLink::disconnected, this, &QGCMAVLinkLogPlayer::_replayLinkDisconnected); - + _ui->positionSlider->setValue(0); #if 0 _ui->speedSlider->setValue(0); @@ -123,9 +123,9 @@ void QGCMAVLinkLogPlayer::_logFileStats(bool logTimestamped, ///< tru { Q_UNUSED(logTimestamped); Q_UNUSED(binaryBaudRate); - + _logDurationSeconds = logDurationSeconds; - + _ui->logStatsLabel->setText(_secondsToHMS(logDurationSeconds)); } @@ -174,9 +174,9 @@ void QGCMAVLinkLogPlayer::_setAccelerationFromSlider(int value) if (_replayLink) { _replayLink->setAccelerationFactor(value); } - + // Factor: -100: 0.01x, 0: 1.0x, 100: 100x - + float accelerationFactor; if (value < 0) { accelerationFactor = 0.01f; @@ -189,7 +189,7 @@ void QGCMAVLinkLogPlayer::_setAccelerationFromSlider(int value) } else { accelerationFactor = 1.0f; } - + _ui->speedLabel->setText(QString("Speed: %1X").arg(accelerationFactor, 5, 'f', 2, '0')); } #endif diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 324c5af6a..ff6fbd443 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -364,6 +364,7 @@ QGCView { text: qsTr("Virtual Joystick") checked: QGroundControl.virtualTabletJoystick onClicked: QGroundControl.virtualTabletJoystick = checked + visible: QGroundControl.enableVirtualJoystick } //----------------------------------------------------------------- //-- Map Providers @@ -433,6 +434,7 @@ QGCView { height: autoConnectLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.enableAutoConnectOptions QGCLabel { id: autoConnectLabel text: qsTr("Autoconnect to the following devices:") @@ -443,6 +445,7 @@ QGCView { height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) width: qgcView.width * 0.8 color: qgcPal.windowShade + visible: QGroundControl.enableAutoConnectOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter Column { @@ -494,6 +497,7 @@ QGCView { Item { width: qgcView.width * 0.8 height: videoLabel.height + visible: QGroundControl.enableVideoSourceOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter QGCLabel { @@ -506,6 +510,7 @@ QGCView { height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) width: qgcView.width * 0.8 color: qgcPal.windowShade + visible: QGroundControl.enableVideoSourceOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter Column { diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 84dd08811..41c6cdb40 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -338,6 +338,7 @@ Rectangle { source: "/res/QGCLogoWhite" logo: true onClicked: toolBar.showSettingsView() + visible: !QGroundControl.colapseSettings } QGCToolBarButton { -- GitLab From 3f3bfafae831934f3f1eb994352e75d7d07c1d48 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 27 Nov 2016 11:05:18 -0500 Subject: [PATCH 065/398] Build fix for when there is no video streaming support. --- src/FlightDisplay/VideoManager.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 73a200d77..86582d05d 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -24,8 +24,8 @@ static const char* kVideoSourceKey = "VideoSource"; static const char* kVideoUDPPortKey = "VideoUDPPort"; static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; -static const char* kUDPStream = "UDP Video Stream"; #if defined(QGC_GST_STREAMING) +static const char* kUDPStream = "UDP Video Stream"; static const char* kRTSPStream = "RTSP Video Stream"; #endif static const char* kNoVideo = "No Video Available"; @@ -42,8 +42,9 @@ VideoManager::VideoManager(QGCApplication* app) , _init(false) { //-- Get saved settings +#if defined(QGC_GST_STREAMING) QSettings settings; -#if defined(NO_UDP_VIDEO) && defined(QGC_GST_STREAMING) +#if defined(NO_UDP_VIDEO) setVideoSource(settings.value(kVideoSourceKey, kRTSPStream).toString()); #else setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); @@ -61,6 +62,7 @@ VideoManager::VideoManager(QGCApplication* app) setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL } +#endif _init = true; #if defined(QGC_GST_STREAMING) _updateVideo(); -- GitLab From 3d221ef9e175c4b3ab24a67fefb74cf9624d64b9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 27 Nov 2016 11:52:38 -0800 Subject: [PATCH 066/398] Bad reference fixes --- src/MissionEditor/MissionEditor.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 9641df47a..6b3faeaaf 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -131,7 +131,7 @@ QGCView { function addMissionItemCoordsForFit(coordList) { for (var i=1; i Date: Mon, 28 Nov 2016 13:09:54 -0500 Subject: [PATCH 067/398] Made QGCCorePlugin derive from QGCToolBox and expose everything through the same mechanism as everything else. --- api/IQGCCorePlugin.h | 33 ---- api/IQGCOptions.h | 52 ------ api/IQGCQMLSource.h | 17 -- qgroundcontrol.pro | 70 ++++---- src/FlightDisplay/FlightDisplayView.qml | 2 +- src/FlightDisplay/VideoManager.cc | 59 +++---- src/QGCApplication.cc | 58 ------- src/QGCApplication.h | 15 -- src/QGCToolbox.cc | 43 +++++ src/QGCToolbox.h | 5 + src/QmlControls/QGroundControlQmlGlobal.cc | 67 +------- src/QmlControls/QGroundControlQmlGlobal.h | 26 +-- src/Vehicle/MultiVehicleManager.cc | 6 +- src/VehicleSetup/SetupView.qml | 36 ++--- src/api/QGCCorePlugin.cc | 151 ++++++++++++++++++ src/api/QGCCorePlugin.h | 54 +++++++ .../api/QGCOptions.cc | 19 +-- src/api/QGCOptions.h | 92 +++++++++++ src/api/QGCSettings.cc | 21 +++ src/api/QGCSettings.h | 37 +++++ src/ui/AppSettings.qml | 102 +++--------- src/ui/preferences/GeneralSettings.qml | 10 +- src/ui/toolbar/MainToolBar.qml | 2 +- 23 files changed, 522 insertions(+), 455 deletions(-) delete mode 100644 api/IQGCCorePlugin.h delete mode 100644 api/IQGCOptions.h delete mode 100644 api/IQGCQMLSource.h create mode 100644 src/api/QGCCorePlugin.cc create mode 100644 src/api/QGCCorePlugin.h rename api/IQGCApplication.h => src/api/QGCOptions.cc (58%) create mode 100644 src/api/QGCOptions.h create mode 100644 src/api/QGCSettings.cc create mode 100644 src/api/QGCSettings.h diff --git a/api/IQGCCorePlugin.h b/api/IQGCCorePlugin.h deleted file mode 100644 index 6bd0fa859..000000000 --- a/api/IQGCCorePlugin.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -/// @file -/// @brief Core Plugin Interface for QGroundControl -/// @author Gus Grubba - -// Work In Progress - -class QGCApplication; -class IQGCApplication; -class IQGCOptions; -class IQGCQMLSource; - -class IQGCCorePlugin -{ -public: - IQGCCorePlugin(QObject*) {} - virtual ~IQGCCorePlugin() {} - -#if defined (QGC_DYNAMIC_PLUGIN) - virtual bool init (IQGCApplication* pApp) = 0; -#else - virtual bool init (QGCApplication* pApp) = 0; -#endif - virtual IQGCOptions* uiOptions () { return NULL; } - virtual IQGCQMLSource* settingsQML () { return NULL; } -}; - -#if defined (QGC_DYNAMIC_PLUGIN) -Q_DECLARE_INTERFACE(IQGCCorePlugin, "org.qgroundcontrol.qgccoreplugin") -#endif diff --git a/api/IQGCOptions.h b/api/IQGCOptions.h deleted file mode 100644 index f28544617..000000000 --- a/api/IQGCOptions.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -/// @file -/// @brief Core Plugin Interface for QGroundControl -/// @author Gus Grubba - -class IQGCOptions -{ -public: - IQGCOptions() {} - virtual ~IQGCOptions() {} - //! Should QGC colapse its settings menu into one single menu (Settings and Vehicle Setup)? - /*! - @return true if QGC should consolidate both menus into one. - */ - virtual bool colapseSettings () { return false; } - //! Should QGC use Maps as its default main view? - /*! - @return true if QGC should use Maps by default or false to show Video by default. - */ - virtual bool mainViewIsMap () { return true; } - //! Should QGC use virtual Joysticks? - /*! - @return false to disable Virtual Joysticks. - */ - virtual bool enableVirtualJoystick () { return true; } - //! Should QGC allow setting auto-connect options? - /*! - @return false to disable auto-connect options. - */ - virtual bool enableAutoConnectOptions () { return true; } - //! Should QGC allow setting video source options? - /*! - @return false to disable video source options. - */ - virtual bool enableVideoSourceOptions () { return true; } - //! Does your plugin defines its on video source? - /*! - @return true to define your own video source. - */ - virtual bool definesVideo () { return false; } - //! UDP port to use for (RTP) video source. - /*! - @return UDP Port to use. Return 0 to disable UDP RTP. - */ - virtual uint16_t videoUDPPort () { return 0; } - //! RTSP URL to use for video source. - /*! - @return RTSP url to use. Return "" to disable RTSP. - */ - virtual QString videoRSTPUrl () { return QString(); } -}; diff --git a/api/IQGCQMLSource.h b/api/IQGCQMLSource.h deleted file mode 100644 index 5e59c2169..000000000 --- a/api/IQGCQMLSource.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -/// @file -/// @brief Core Plugin Interface for QGroundControl -/// @author Gus Grubba - -class IQGCQMLSource -{ -public: - IQGCQMLSource() {} - virtual ~IQGCQMLSource() {} - virtual QString pageUrl () { return QString(); } - virtual QString pageTitle () { return QString(); } - virtual QString pageIconUrl () { return QString(); } -}; diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 28a09d033..f05699945 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -34,6 +34,34 @@ exists(user_config.pri):infile(user_config.pri, CONFIG) { message($$sprintf("Using user-supplied additional config: '%1' specified in user_config.pri", $$fromfile(user_config.pri, CONFIG))) } +# +# Custom Build +# +# QGC will create a "CUSTOMCLASS" object (exposed by your custom build +# and derived from QGCCorePlugin). +# This is the start of allowing custom Plugins, which will eventually use a +# more defined runtime plugin architecture and not require a QGC project +# file you would have to keep in sync with the upstream repo. +# + +# This allows you to ignore the custom build even if the custom build +# is present. It's useful to run "regular" builds to make sure you didn't +# break anything. + +contains (CONFIG, QGC_DISABLE_CUSTOM_BUILD) { + message("Disable custom build override") +} else { + exists($$PWD/custom/custom.pri) { + message("Found custom build") + CONFIG += CustomBuild + DEFINES += QGC_CUSTOM_BUILD + # custom.pri must define: + # CUSTOMCLASS = YourIQGCCorePluginDerivation + # CUSTOMHEADER = \"\\\"YourIQGCCorePluginDerivation.h\\\"\" + include($$PWD/custom/custom.pri) + } +} + # # Plugin configuration # @@ -69,34 +97,6 @@ contains (CONFIG, QGC_DISABLE_PX4_PLUGIN_FACTORY) { CONFIG += PX4FirmwarePluginFactory } -# -# Custom Build -# -# QGC will create a "CUSTOMCLASS" object (exposed by your custom build -# and derived from IQGCCorePlugin) and call its IQGCCorePlugin::init() method. -# This is the start of allowing custom Plugins, which will eventually use a -# more defined runtime plugin architecture and not require a QGC project -# file you would have to keep in sync with the upstream repo. -# - -# This allows you to ignore the custom build even if the custom build -# is present. It's useful to run "regular" builds to make sure you didn't -# break anything. - -contains (CONFIG, QGC_DISABLE_CUSTOM_BUILD) { - message("Disable custom build override") -} else { - exists($$PWD/custom/custom.pri) { - message("Found custom build") - CONFIG += CustomBuild - DEFINES += QGC_CUSTOM_BUILD - # custom.pri must define: - # CUSTOMCLASS = YourIQGCCorePluginDerivation - # CUSTOMHEADER = \"\\\"YourIQGCCorePluginDerivation.h\\\"\" - include($$PWD/custom/custom.pri) - } -} - # Bluetooth contains (DEFINES, QGC_DISABLE_BLUETOOTH) { message("Skipping support for Bluetooth (manual override from command line)") @@ -273,9 +273,9 @@ DEPENDPATH += \ INCLUDEPATH += . INCLUDEPATH += \ - api \ include/ui \ src \ + src/api \ src/AnalyzeView \ src/AutoPilotPlugins \ src/FlightDisplay \ @@ -336,10 +336,14 @@ FORMS += \ # HEADERS += \ - api/IQGCApplication.h \ - api/IQGCCorePlugin.h \ - api/IQGCOptions.h \ - api/IQGCQMLSource.h \ + src/api/QGCCorePlugin.h \ + src/api/QGCOptions.h \ + src/api/QGCSettings.h \ + +SOURCES += \ + src/api/QGCCorePlugin.cc \ + src/api/QGCOptions.cc \ + src/api/QGCSettings.cc \ # # Unit Test specific configuration goes here (requires full debug build with all plugins) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index c739d322c..3eb92fd5e 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -34,7 +34,7 @@ QGCView { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, QGroundControl.mainViewIsMap) : true + property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, QGroundControl.corePlugin.options.mainViewIsMap) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false property real _roll: _activeVehicle ? _activeVehicle.roll.value : _defaultRoll diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 86582d05d..a68e880aa 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -20,6 +20,9 @@ #include "ScreenToolsController.h" #include "VideoManager.h" +#include "QGCToolbox.h" +#include "QGCCorePlugin.h" +#include "QGCOptions.h" static const char* kVideoSourceKey = "VideoSource"; static const char* kVideoUDPPortKey = "VideoUDPPort"; @@ -41,34 +44,6 @@ VideoManager::VideoManager(QGCApplication* app) , _udpPort(5600) //-- Defalut Port 5600 == Solo UDP Port , _init(false) { - //-- Get saved settings -#if defined(QGC_GST_STREAMING) - QSettings settings; -#if defined(NO_UDP_VIDEO) - setVideoSource(settings.value(kVideoSourceKey, kRTSPStream).toString()); -#else - setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); -#endif - //-- Check if core plugin defines its own video requirements - if(qgcApp()->qgcOptions()->definesVideo()) { - if(qgcApp()->qgcOptions()->videoUDPPort()) { - setUdpPort(qgcApp()->qgcOptions()->videoUDPPort()); - setVideoSource(kUDPStream); - } else { - setVideoSource(kRTSPStream); - setRtspURL(qgcApp()->qgcOptions()->videoRSTPUrl()); - } - } else { - setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); - setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL - } -#endif - _init = true; -#if defined(QGC_GST_STREAMING) - _updateVideo(); - connect(&_frameTimer, &QTimer::timeout, this, &VideoManager::_updateTimer); - _frameTimer.start(1000); -#endif } //----------------------------------------------------------------------------- @@ -84,6 +59,34 @@ VideoManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.VideoManager", 1, 0, "VideoManager", "Reference only"); + //-- Get saved settings +#if defined(QGC_GST_STREAMING) + QSettings settings; +#if defined(NO_UDP_VIDEO) + setVideoSource(settings.value(kVideoSourceKey, kRTSPStream).toString()); +#else + setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); +#endif + //-- Check if core plugin defines its own video requirements + if(qgcApp()->toolbox()->corePlugin()->options()->definesVideo()) { + if(qgcApp()->toolbox()->corePlugin()->options()->videoUDPPort()) { + setUdpPort(qgcApp()->toolbox()->corePlugin()->options()->videoUDPPort()); + setVideoSource(kUDPStream); + } else { + setVideoSource(kRTSPStream); + setRtspURL(qgcApp()->toolbox()->corePlugin()->options()->videoRSTPUrl()); + } + } else { + setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); + setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL + } +#endif + _init = true; +#if defined(QGC_GST_STREAMING) + _updateVideo(); + connect(&_frameTimer, &QTimer::timeout, this, &VideoManager::_updateTimer); + _frameTimer.start(1000); +#endif } //----------------------------------------------------------------------------- diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 9847f4b8e..578634b6d 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -83,10 +83,6 @@ #include "QGCMapPolygon.h" #include "ParameterManager.h" -#if defined(QGC_CUSTOM_BUILD) -#include CUSTOMHEADER -#endif - #ifndef NO_SERIAL_LINK #include "SerialLink.h" #endif @@ -188,15 +184,10 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) , _toolbox(NULL) , _bluetoothAvailable(false) , _lastKnownHomePosition(37.803784, -122.462276, 0.0) - , _pQGCOptions(NULL) - , _pCorePlugin(NULL) { Q_ASSERT(_app == NULL); _app = this; - //-- Scan and load plugins - _scanAndLoadPlugins(); - // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings #ifndef __android__ setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); @@ -358,9 +349,6 @@ QGCApplication::~QGCApplication() #endif shutdownVideoStreaming(); delete _toolbox; - if(_pCorePlugin) { - delete _pCorePlugin; - } } void QGCApplication::_initCommon(void) @@ -456,14 +444,6 @@ bool QGCApplication::_initForNormalAppBoot(void) } settings.sync(); - - //-- Initialize Core Plugin (if any) - if(_pCorePlugin) { - if(!_pCorePlugin->init(this)) { - return false; - } - } - return true; } @@ -708,41 +688,3 @@ void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosit settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude()); _lastKnownHomePosition = lastKnownHomePosition; } - -IQGCOptions* QGCApplication::qgcOptions() -{ - return _pQGCOptions; -} - -void QGCApplication::_scanAndLoadPlugins() -{ -#if defined (QGC_DYNAMIC_PLUGIN) - //-- Look for plugins (Dynamic) - QString filter = "*.core.so"; - QString path = QCoreApplication::applicationDirPath(); - QDirIterator it(path, QStringList() << filter, QDir::Files); - while(it.hasNext()) { - QString pluginFile = it.next(); - QPluginLoader loader(pluginFile); - QObject *plugin = loader.instance(); - if(plugin) { - _pCorePlugin = qobject_cast(plugin); - if(_pCorePlugin) { - _pQGCOptions = _pCorePlugin->uiOptions(); - return; - } - } else { - qWarning() << "Plugin" << pluginFile << " not loaded:" << loader.errorString(); - } - } -#elif defined (QGC_CUSTOM_BUILD) - //-- Create custom plugin (Static) - _pCorePlugin = (IQGCCorePlugin*) new CUSTOMCLASS(this); - if(_pCorePlugin) { - _pQGCOptions = _pCorePlugin->uiOptions(); - return; - } -#endif - //-- No plugins found, use default options - _pQGCOptions = new IQGCOptions; -} diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 6a9dbf966..002c88e39 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -35,12 +35,6 @@ #include "UASMessageHandler.h" #include "FactSystem.h" -//-- Plugin Architecture -#include "IQGCApplication.h" -#include "IQGCCorePlugin.h" -#include "IQGCOptions.h" -#include "IQGCQMLSource.h" - #ifdef QGC_RTLAB_ENABLED #include "OpalLink.h" #endif @@ -123,11 +117,6 @@ public: QGeoCoordinate lastKnownHomePosition(void) { return _lastKnownHomePosition; } void setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition); - /// Options (can be overwriten by a core plugin) - IQGCOptions* qgcOptions(); - /// Custom core plugin (NULL if none) - IQGCCorePlugin* customCorePlugin() { return _pCorePlugin; } - public slots: /// You can connect to this slot to show an information message box from a different thread. void informationMessageBoxOnMainThread(const QString& title, const QString& msg); @@ -179,7 +168,6 @@ private slots: private: void _loadCurrentStyle (); QObject* _rootQmlObject (); - void _scanAndLoadPlugins (); #ifdef __mobile__ QQmlApplicationEngine* _qmlAppEngine; @@ -218,9 +206,6 @@ private: /// Unit Test have access to creating and destroying singletons friend class UnitTest; - //-- Plugin Architecture - IQGCOptions* _pQGCOptions; - IQGCCorePlugin* _pCorePlugin; }; /// @brief Returns the QGCApplication object singleton. diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 3e625d66a..213c31996 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -28,6 +28,12 @@ #include "PositionManager.h" #include "VideoManager.h" #include "MAVLinkLogManager.h" +#include "QGCCorePlugin.h" +#include "QGCOptions.h" + +#if defined(QGC_CUSTOM_BUILD) +#include CUSTOMHEADER +#endif QGCToolbox::QGCToolbox(QGCApplication* app) : _audioOutput(NULL) @@ -50,7 +56,10 @@ QGCToolbox::QGCToolbox(QGCApplication* app) , _qgcPositionManager(NULL) , _videoManager(NULL) , _mavlinkLogManager(NULL) + , _corePlugin(NULL) { + //-- Scan and load plugins + _scanAndLoadPlugins(app); _audioOutput = new GAudioOutput(app); _factSystem = new FactSystem(app); _firmwarePluginManager = new FirmwarePluginManager(app); @@ -75,6 +84,7 @@ QGCToolbox::QGCToolbox(QGCApplication* app) void QGCToolbox::setChildToolboxes(void) { + _corePlugin->setToolbox(this); _audioOutput->setToolbox(this); _factSystem->setToolbox(this); _firmwarePluginManager->setToolbox(this); @@ -115,6 +125,39 @@ QGCToolbox::~QGCToolbox() delete _uasMessageHandler; delete _followMe; delete _qgcPositionManager; + delete _corePlugin; +} + +void QGCToolbox::_scanAndLoadPlugins(QGCApplication* app) +{ +#if defined (QGC_DYNAMIC_PLUGIN) + //-- Look for plugins (Dynamic) + QString filter = "*.core.so"; + QString path = QCoreApplication::applicationDirPath(); + QDirIterator it(path, QStringList() << filter, QDir::Files); + while(it.hasNext()) { + QString pluginFile = it.next(); + QPluginLoader loader(pluginFile); + QObject *plugin = loader.instance(); + if(plugin) { + _pCorePlugin = qobject_cast(plugin); + if(_pCorePlugin) { + _pQGCOptions = _pCorePlugin->uiOptions(); + return; + } + } else { + qWarning() << "Plugin" << pluginFile << " not loaded:" << loader.errorString(); + } + } +#elif defined (QGC_CUSTOM_BUILD) + //-- Create custom plugin (Static) + _corePlugin = (QGCCorePlugin*) new CUSTOMCLASS(app); + if(_corePlugin) { + return; + } +#endif + //-- No plugins found, use default instance + _corePlugin = new QGCCorePlugin(app); } QGCTool::QGCTool(QGCApplication* app) diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 8948fda3a..607ab0a66 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -32,6 +32,7 @@ class UASMessageHandler; class QGCPositionManager; class VideoManager; class MAVLinkLogManager; +class QGCCorePlugin; /// This is used to manage all of our top level services/tools class QGCToolbox { @@ -56,6 +57,7 @@ public: QGCPositionManager* qgcPositionManager(void) { return _qgcPositionManager; } VideoManager* videoManager(void) { return _videoManager; } MAVLinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; } + QGCCorePlugin* corePlugin(void) { return _corePlugin; } #ifndef __mobile__ GPSManager* gpsManager(void) { return _gpsManager; } @@ -63,6 +65,8 @@ public: private: void setChildToolboxes(void); + void _scanAndLoadPlugins(QGCApplication *app); + GAudioOutput* _audioOutput; FactSystem* _factSystem; @@ -84,6 +88,7 @@ private: QGCPositionManager* _qgcPositionManager; VideoManager* _videoManager; MAVLinkLogManager* _mavlinkLogManager; + QGCCorePlugin* _corePlugin; friend class QGCApplication; }; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 90a67453b..2a8168db3 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -45,6 +45,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _missionCommandTree(NULL) , _videoManager(NULL) , _mavlinkLogManager(NULL) + , _corePlugin(NULL) , _virtualTabletJoystick(false) , _baseFontPointSize(0.0) { @@ -73,6 +74,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _missionCommandTree = toolbox->missionCommandTree(); _videoManager = toolbox->videoManager(); _mavlinkLogManager = toolbox->mavlinkLogManager(); + _corePlugin = toolbox->corePlugin(); } void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) @@ -353,68 +355,3 @@ QMap& QGroundControlQmlGlobal::nameToMetaDataMap(void) { return map; } - -bool QGroundControlQmlGlobal::colapseSettings() -{ - return qgcApp()->qgcOptions()->colapseSettings(); -} - -bool QGroundControlQmlGlobal::mainViewIsMap() -{ - return qgcApp()->qgcOptions()->mainViewIsMap(); -} - -bool QGroundControlQmlGlobal::enableVirtualJoystick() -{ - return qgcApp()->qgcOptions()->enableVirtualJoystick(); -} - -bool QGroundControlQmlGlobal::enableAutoConnectOptions() -{ - return qgcApp()->qgcOptions()->enableAutoConnectOptions(); -} - -bool QGroundControlQmlGlobal::enableVideoSourceOptions() -{ - return qgcApp()->qgcOptions()->enableVideoSourceOptions(); -} - -bool QGroundControlQmlGlobal::hasCustomSettings() -{ - if(qgcApp()->customCorePlugin()) { - if(qgcApp()->customCorePlugin()->settingsQML()) { - return !qgcApp()->customCorePlugin()->settingsQML()->pageUrl().isEmpty(); - } - } - return false; -} - -QString QGroundControlQmlGlobal::customSettingsURL() -{ - if(qgcApp()->customCorePlugin()) { - if(qgcApp()->customCorePlugin()->settingsQML()) { - return qgcApp()->customCorePlugin()->settingsQML()->pageUrl(); - } - } - return QString(); -} - -QString QGroundControlQmlGlobal::customSettingsTitle() -{ - if(qgcApp()->customCorePlugin()) { - if(qgcApp()->customCorePlugin()->settingsQML()) { - return qgcApp()->customCorePlugin()->settingsQML()->pageTitle(); - } - } - return QString(); -} - -QString QGroundControlQmlGlobal::customSettingsLogoUrl() -{ - if(qgcApp()->customCorePlugin()) { - if(qgcApp()->customCorePlugin()->settingsQML()) { - return qgcApp()->customCorePlugin()->settingsQML()->pageIconUrl(); - } - } - return QString(); -} diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index d56642587..b0b01d092 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -73,6 +73,7 @@ public: Q_PROPERTY(MissionCommandTree* missionCommandTree READ missionCommandTree CONSTANT) Q_PROPERTY(VideoManager* videoManager READ videoManager CONSTANT) Q_PROPERTY(MAVLinkLogManager* mavlinkLogManager READ mavlinkLogManager CONSTANT) + Q_PROPERTY(QGCCorePlugin* corePlugin READ corePlugin 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 @@ -86,19 +87,6 @@ public: Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) - //------------------------------------------------------------------------- - //-- Options that can be set by plugins - Q_PROPERTY(bool colapseSettings READ colapseSettings CONSTANT) - Q_PROPERTY(bool mainViewIsMap READ mainViewIsMap CONSTANT) - Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) - Q_PROPERTY(bool enableAutoConnectOptions READ enableAutoConnectOptions CONSTANT) - Q_PROPERTY(bool enableVideoSourceOptions READ enableVideoSourceOptions CONSTANT) - - Q_PROPERTY(bool hasCustomSettings READ hasCustomSettings CONSTANT) - Q_PROPERTY(QString customSettingsURL READ customSettingsURL CONSTANT) - Q_PROPERTY(QString customSettingsTitle READ customSettingsTitle CONSTANT) - Q_PROPERTY(QString customSettingsLogoUrl READ customSettingsLogoUrl CONSTANT) - //------------------------------------------------------------------------- // MavLink Protocol Q_PROPERTY(bool isVersionCheckEnabled READ isVersionCheckEnabled WRITE setIsVersionCheckEnabled NOTIFY isVersionCheckEnabledChanged) @@ -183,6 +171,7 @@ public: MissionCommandTree* missionCommandTree () { return _missionCommandTree; } VideoManager* videoManager () { return _videoManager; } MAVLinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } + QGCCorePlugin* corePlugin () { return _corePlugin; } qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } @@ -198,16 +187,6 @@ public: bool isVersionCheckEnabled () { return _toolbox->mavlinkProtocol()->versionCheckEnabled(); } int mavlinkSystemID () { return _toolbox->mavlinkProtocol()->getSystemId(); } - bool colapseSettings (); - bool mainViewIsMap (); - bool enableVirtualJoystick (); - bool enableAutoConnectOptions(); - bool enableVideoSourceOptions(); - bool hasCustomSettings (); - QString customSettingsTitle (); - QString customSettingsURL (); - QString customSettingsLogoUrl (); - QGeoCoordinate lastKnownHomePosition() { return qgcApp()->lastKnownHomePosition(); } static Fact* offlineEditingFirmwareType (void); @@ -264,6 +243,7 @@ private: MissionCommandTree* _missionCommandTree; VideoManager* _videoManager; MAVLinkLogManager* _mavlinkLogManager; + QGCCorePlugin* _corePlugin; bool _virtualTabletJoystick; qreal _baseFontPointSize; diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 9bac096f6..5baa4fab6 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -16,7 +16,7 @@ #include "QGroundControlQmlGlobal.h" #include "ParameterManager.h" -#ifdef __mobile__ +#if defined (__ios__) || defined(__android__) #include "MobileScreenMgr.h" #endif @@ -116,7 +116,7 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle // Mark link as active link->setActive(true); -#ifdef __mobile__ +#if defined (__ios__) || defined(__android__) if(_vehicles.count() == 1) { //-- Once a vehicle is connected, keep screen from going off qCDebug(MultiVehicleManagerLog) << "QAndroidJniObject::keepScreenOn"; @@ -157,7 +157,7 @@ void MultiVehicleManager::_deleteVehiclePhase1(Vehicle* vehicle) emit parameterReadyVehicleAvailableChanged(false); emit vehicleRemoved(vehicle); -#ifdef __mobile__ +#if defined (__ios__) || defined(__android__) if(_vehicles.count() == 0) { //-- Once no vehicles are connected, we no longer need to keep screen from going off qCDebug(MultiVehicleManagerLog) << "QAndroidJniObject::restoreScreenOn"; diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index 89389ee40..54e0e0ffc 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -256,31 +256,17 @@ Rectangle { visible: !ScreenTools.isShortScreen } - SubMenuButton { - imageResource: "/res/gear-white.svg" - setupIndicator: false - exclusiveGroup: setupButtonGroup - text: "General" - visible: QGroundControl.colapseSettings - onClicked: panelLoader.setSource("GeneralSettings.qml") - } - - SubMenuButton { - imageResource: QGroundControl.customSettingsLogoUrl - setupIndicator: false - exclusiveGroup: setupButtonGroup - text: QGroundControl.customSettingsTitle - visible: QGroundControl.colapseSettings && QGroundControl.hasCustomSettings - onClicked: panelLoader.setSource(QGroundControl.customSettingsURL) - } - - SubMenuButton { - imageResource: "/res/waves.svg" - setupIndicator: false - exclusiveGroup: setupButtonGroup - text: "MAVLink" - visible: QGroundControl.colapseSettings - onClicked: panelLoader.setSource("MavlinkSettings.qml") + Repeater { + model: QGroundControl.corePlugin.settings + visible: QGroundControl.corePlugin.options.combineSettingsAndSetup + SubMenuButton { + imageResource: modelData.icon + setupIndicator: false + exclusiveGroup: setupButtonGroup + text: modelData.title + visible: QGroundControl.corePlugin.options.combineSettingsAndSetup + onClicked: panelLoader.setSource(modelData.url) + } } SubMenuButton { diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc new file mode 100644 index 000000000..605b7ef1e --- /dev/null +++ b/src/api/QGCCorePlugin.cc @@ -0,0 +1,151 @@ +/**************************************************************************** + * + * (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 "QGCCorePlugin.h" +#include "QGCOptions.h" +#include "QGCSettings.h" + +#include +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl - Default Implementation +/// @author Gus Grubba + +class QGCCorePlugin_p +{ +public: + QGCCorePlugin_p() + : pGeneral(NULL) + , pCommLinks(NULL) + , pOfflineMaps(NULL) + , pMAVLink(NULL) + , pConsole(NULL) +#if defined(QT_DEBUG) + , pMockLink(NULL) + , pDebug(NULL) +#endif + , defaultOptions(NULL) + { + } + ~QGCCorePlugin_p() + { + if(pGeneral) + delete pGeneral; + if(pCommLinks) + delete pCommLinks; + if(pOfflineMaps) + delete pOfflineMaps; + if(pMAVLink) + delete pMAVLink; + if(pConsole) + delete pConsole; +#if defined(QT_DEBUG) + if(pMockLink) + delete pMockLink; + if(pDebug) + delete pDebug; +#endif + if(defaultOptions) + delete defaultOptions; + } + QGCSettings* pGeneral; + QGCSettings* pCommLinks; + QGCSettings* pOfflineMaps; + QGCSettings* pMAVLink; + QGCSettings* pConsole; +#if defined(QT_DEBUG) + QGCSettings* pMockLink; + QGCSettings* pDebug; +#endif + QVariantList settingsList; + QGCOptions* defaultOptions; +}; + +QGCCorePlugin::~QGCCorePlugin() +{ + if(_p) { + delete _p; + } +} + +QGCCorePlugin::QGCCorePlugin(QGCApplication *app) + : QGCTool(app) +{ + _p = new QGCCorePlugin_p; +} + +void QGCCorePlugin::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.QGCCorePlugin", 1, 0, "QGCCorePlugin", "Reference only"); + qmlRegisterUncreatableType("QGroundControl.QGCOptions", 1, 0, "QGCOptions", "Reference only"); +} + +QVariantList &QGCCorePlugin::settings() +{ + //-- If this hasn't been overridden, create default set of settings + if(!_p->pGeneral) { + QGCOptions* pOptions = options(); + //-- Default Settings + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_GENERAL) { + _p->pGeneral = new QGCSettings(tr("General"), + QUrl::fromUserInput("qrc:/qml/GeneralSettings.qml"), + QUrl::fromUserInput("qrc:/res/gear-white.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pGeneral)); + } + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_COMM_LINKS) { + _p->pCommLinks = new QGCSettings(tr("Comm Links"), + QUrl::fromUserInput("qrc:/qml/LinkSettings.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pCommLinks)); + } + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_OFFLINE_MAPS) { + _p->pOfflineMaps = new QGCSettings(tr("Offline Maps"), + QUrl::fromUserInput("qrc:/qml/OfflineMap.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pOfflineMaps)); + } + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_MAVLINK) { + _p->pMAVLink = new QGCSettings(tr("MAVLink"), + QUrl::fromUserInput("qrc:/qml/MavlinkSettings.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMAVLink)); + } + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_CONSOLE) { + _p->pConsole = new QGCSettings(tr("Console"), + QUrl::fromUserInput("qrc:/QGroundControl/Controls/AppMessages.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pConsole)); + } + #if defined(QT_DEBUG) + //-- These are always present on Debug builds + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_MOCKLINK) { + _p->pMockLink = new QGCSettings(tr("Mock Link"), + QUrl::fromUserInput("qrc:/qml/MockLink.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMockLink)); + } + if(pOptions->enabledSettings() & QGCOptions::SETTINGS_DEBUG) { + _p->pDebug = new QGCSettings(tr("Debug"), + QUrl::fromUserInput("qrc:/qml/DebugWindow.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pDebug)); + } + #endif + } + return _p->settingsList; +} + +QGCOptions* QGCCorePlugin::options() +{ + if(!_p->defaultOptions) { + _p->defaultOptions = new QGCOptions(); + } + return _p->defaultOptions; +} + diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h new file mode 100644 index 000000000..9d1a1afa0 --- /dev/null +++ b/src/api/QGCCorePlugin.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +#pragma once + +#include "QGCToolbox.h" + +#include +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl +/// @author Gus Grubba + +// Work In Progress + +class QGCApplication; +class QGCOptions; +class QGCSettings; +class QGCCorePlugin_p; + +class QGCCorePlugin : public QGCTool +{ + Q_OBJECT +public: + QGCCorePlugin(QGCApplication* app); + ~QGCCorePlugin(); + + Q_PROPERTY(QVariantList settings READ settings CONSTANT) + Q_PROPERTY(QGCOptions* options READ options CONSTANT) + + //! The list of settings under the Settings Menu + /*! + @return A list of QGCSettings + */ + virtual QVariantList& settings (); + + //! Global options + /*! + @return An instance of QGCOptions + */ + virtual QGCOptions* options (); + + // Override from QGCTool + void setToolbox (QGCToolbox *toolbox); +private: + QGCCorePlugin_p* _p; +}; diff --git a/api/IQGCApplication.h b/src/api/QGCOptions.cc similarity index 58% rename from api/IQGCApplication.h rename to src/api/QGCOptions.cc index 79edce901..3b598fe3d 100644 --- a/api/IQGCApplication.h +++ b/src/api/QGCOptions.cc @@ -7,18 +7,13 @@ * ****************************************************************************/ +#include "QGCOptions.h" -/** - * @brief QGC Main Application Interface (used for Dynamic Loaded plugins) - * @author Gus Grubba - */ +/// @file +/// @brief Core Plugin Interface for QGroundControl - Application Options +/// @author Gus Grubba -#pragma once - -class IQGCApplication +QGCOptions::QGCOptions(QObject* parent) + : QObject(parent) { -public: - IQGCApplication() {} - virtual ~IQGCApplication() {} - //-- Not yet implemented -}; +} diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h new file mode 100644 index 000000000..b329472ed --- /dev/null +++ b/src/api/QGCOptions.h @@ -0,0 +1,92 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +#pragma once + +#include +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl - Application Options +/// @author Gus Grubba + +class QGCOptions : public QObject +{ + Q_OBJECT +public: + QGCOptions(QObject* parent = NULL); + + enum BaseSettings { + SETTINGS_GENERAL = 1 << 1, + SETTINGS_COMM_LINKS = 1 << 2, + SETTINGS_OFFLINE_MAPS = 1 << 3, + SETTINGS_MAVLINK = 1 << 4, + SETTINGS_CONSOLE = 1 << 5, + SETTINGS_MOCKLINK = 1 << 6, + SETTINGS_DEBUG = 1 << 7, + }; + + Q_ENUMS(BaseSettings) + + Q_PROPERTY(bool combineSettingsAndSetup READ combineSettingsAndSetup CONSTANT) + Q_PROPERTY(bool mainViewIsMap READ mainViewIsMap CONSTANT) + Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) + Q_PROPERTY(bool enableAutoConnectOptions READ enableAutoConnectOptions CONSTANT) + Q_PROPERTY(bool enableVideoSourceOptions READ enableVideoSourceOptions CONSTANT) + Q_PROPERTY(int enabledSettings READ enabledSettings CONSTANT) + Q_PROPERTY(bool definesVideo READ definesVideo CONSTANT) + Q_PROPERTY(uint16_t videoUDPPort READ videoUDPPort CONSTANT) + Q_PROPERTY(QString videoRSTPUrl READ videoRSTPUrl CONSTANT) + + //! Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? + /*! + @return true if QGC should consolidate both menus into one. + */ + virtual bool combineSettingsAndSetup () { return false; } + //! Should QGC use Maps as its default main view? + /*! + @return true if QGC should use Maps by default or false to show Video by default. + */ + virtual bool mainViewIsMap () { return true; } + //! Should QGC use virtual Joysticks? + /*! + @return false to disable Virtual Joysticks. + */ + virtual bool enableVirtualJoystick () { return true; } + //! Should QGC allow setting auto-connect options? + /*! + @return false to disable auto-connect options. + */ + virtual bool enableAutoConnectOptions () { return true; } + //! Should QGC allow setting video source options? + /*! + @return false to disable video source options. + */ + virtual bool enableVideoSourceOptions () { return true; } + //! Which settings are enabled? + /*! + @return BaseSettings bitmap of enabled settings + */ + virtual int enabledSettings () { return SETTINGS_GENERAL | SETTINGS_COMM_LINKS | SETTINGS_MAVLINK | SETTINGS_CONSOLE | SETTINGS_MOCKLINK | SETTINGS_DEBUG; } + //! Does your plugin defines its on video source? + /*! + @return true to define your own video source. + */ + virtual bool definesVideo () { return false; } + //! UDP port to use for (RTP) video source. + /*! + @return UDP Port to use. Return 0 to disable UDP RTP. + */ + virtual uint16_t videoUDPPort () { return 0; } + //! RTSP URL to use for video source. + /*! + @return RTSP url to use. Return "" to disable RTSP. + */ + virtual QString videoRSTPUrl () { return QString(); } +}; diff --git a/src/api/QGCSettings.cc b/src/api/QGCSettings.cc new file mode 100644 index 000000000..6fbe9dbe3 --- /dev/null +++ b/src/api/QGCSettings.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (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 "QGCSettings.h" + +/// @file +/// @brief Core Plugin Interface for QGroundControl. Settings element. +/// @author Gus Grubba + +QGCSettings::QGCSettings(QString title, QUrl url, QUrl icon) + : _title(title) + , _url(url) + , _icon(icon) +{ +} diff --git a/src/api/QGCSettings.h b/src/api/QGCSettings.h new file mode 100644 index 000000000..682364cb2 --- /dev/null +++ b/src/api/QGCSettings.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +#pragma once + +#include +#include + +/// @file +/// @brief Core Plugin Interface for QGroundControl. Settings element. +/// @author Gus Grubba + +class QGCSettings : public QObject +{ + Q_OBJECT +public: + QGCSettings(QString title, QUrl url, QUrl icon = QUrl()); + + Q_PROPERTY(QString title READ title CONSTANT) + Q_PROPERTY(QUrl url READ url CONSTANT) + Q_PROPERTY(QUrl icon READ icon CONSTANT) + + virtual QString title () { return _title; } + virtual QUrl url () { return _url; } + virtual QUrl icon () { return _icon; } + +protected: + QString _title; + QUrl _url; + QUrl _icon; +}; diff --git a/src/ui/AppSettings.qml b/src/ui/AppSettings.qml index e807c815d..337aac725 100644 --- a/src/ui/AppSettings.qml +++ b/src/ui/AppSettings.qml @@ -30,13 +30,13 @@ Rectangle { readonly property real _buttonHeight: ScreenTools.isTinyScreen ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight * 2 readonly property real _buttonWidth: ScreenTools.defaultFontPixelWidth * 10 + property bool _first: true + QGCPalette { id: qgcPal } Component.onCompleted: { //-- Default to General Settings __rightPanel.source = "GeneralSettings.qml" - _generalButton.checked = true - panelActionGroup.current = _generalButton } QGCFlickable { @@ -81,90 +81,24 @@ Rectangle { visible: !ScreenTools.isShortScreen } - QGCButton { - id: _generalButton - height: _buttonHeight - text: qsTr("General") - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "GeneralSettings.qml") { - __rightPanel.source = "GeneralSettings.qml" - } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("Comm Links") - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "LinkSettings.qml") { - __rightPanel.source = "LinkSettings.qml" - } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("Offline Maps") - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "OfflineMap.qml") { - __rightPanel.source = "OfflineMap.qml" - } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("MAVLink") - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "MavlinkSettings.qml") { - __rightPanel.source = "MavlinkSettings.qml" - } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("Console") - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "QGroundControl/Controls/AppMessages.qml") { - __rightPanel.source = "QGroundControl/Controls/AppMessages.qml" - } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("Mock Link") - visible: ScreenTools.isDebug - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "MockLink.qml") { - __rightPanel.source = "MockLink.qml" + Repeater { + model: QGroundControl.corePlugin.settings + QGCButton { + height: _buttonHeight + text: modelData.title + exclusiveGroup: panelActionGroup + onClicked: { + if(__rightPanel.source !== modelData.url) { + __rightPanel.source = modelData.url + } + checked = true } - checked = true - } - } - - QGCButton { - height: _buttonHeight - text: qsTr("Debug") - visible: ScreenTools.isDebug - exclusiveGroup: panelActionGroup - onClicked: { - if(__rightPanel.source != "DebugWindow.qml") { - __rightPanel.source = "DebugWindow.qml" + Component.onCompleted: { + if(_first) { + _first = false + checked = true + } } - checked = true } } } diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index ff6fbd443..1d81a84a1 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -364,7 +364,7 @@ QGCView { text: qsTr("Virtual Joystick") checked: QGroundControl.virtualTabletJoystick onClicked: QGroundControl.virtualTabletJoystick = checked - visible: QGroundControl.enableVirtualJoystick + visible: QGroundControl.corePlugin.options.enableVirtualJoystick } //----------------------------------------------------------------- //-- Map Providers @@ -434,7 +434,7 @@ QGCView { height: autoConnectLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter - visible: QGroundControl.enableAutoConnectOptions + visible: QGroundControl.corePlugin.options.enableAutoConnectOptions QGCLabel { id: autoConnectLabel text: qsTr("Autoconnect to the following devices:") @@ -445,7 +445,7 @@ QGCView { height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) width: qgcView.width * 0.8 color: qgcPal.windowShade - visible: QGroundControl.enableAutoConnectOptions + visible: QGroundControl.corePlugin.options.enableAutoConnectOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter Column { @@ -497,7 +497,7 @@ QGCView { Item { width: qgcView.width * 0.8 height: videoLabel.height - visible: QGroundControl.enableVideoSourceOptions + visible: QGroundControl.corePlugin.options.enableVideoSourceOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter QGCLabel { @@ -510,7 +510,7 @@ QGCView { height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) width: qgcView.width * 0.8 color: qgcPal.windowShade - visible: QGroundControl.enableVideoSourceOptions + visible: QGroundControl.corePlugin.options.enableVideoSourceOptions anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter Column { diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 41c6cdb40..2d5670039 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -338,7 +338,7 @@ Rectangle { source: "/res/QGCLogoWhite" logo: true onClicked: toolBar.showSettingsView() - visible: !QGroundControl.colapseSettings + visible: !QGroundControl.corePlugin.options.combineSettingsAndSetup } QGCToolBarButton { -- GitLab From 2c7705a5887025430859cda80504309e753bcc87 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 28 Nov 2016 17:44:14 -0500 Subject: [PATCH 068/398] Done with this for now. --- src/FlightDisplay/FlightDisplayView.qml | 2 +- src/QGCToolbox.cc | 21 +------- src/api/QGCCorePlugin.cc | 65 ++++++++++--------------- src/api/QGCOptions.h | 24 --------- 4 files changed, 27 insertions(+), 85 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 3eb92fd5e..85faa00b0 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -34,7 +34,7 @@ QGCView { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, QGroundControl.corePlugin.options.mainViewIsMap) : true + property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, true) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false property real _roll: _activeVehicle ? _activeVehicle.roll.value : _defaultRoll diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 213c31996..24408eafd 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -130,26 +130,7 @@ QGCToolbox::~QGCToolbox() void QGCToolbox::_scanAndLoadPlugins(QGCApplication* app) { -#if defined (QGC_DYNAMIC_PLUGIN) - //-- Look for plugins (Dynamic) - QString filter = "*.core.so"; - QString path = QCoreApplication::applicationDirPath(); - QDirIterator it(path, QStringList() << filter, QDir::Files); - while(it.hasNext()) { - QString pluginFile = it.next(); - QPluginLoader loader(pluginFile); - QObject *plugin = loader.instance(); - if(plugin) { - _pCorePlugin = qobject_cast(plugin); - if(_pCorePlugin) { - _pQGCOptions = _pCorePlugin->uiOptions(); - return; - } - } else { - qWarning() << "Plugin" << pluginFile << " not loaded:" << loader.errorString(); - } - } -#elif defined (QGC_CUSTOM_BUILD) +#if defined (QGC_CUSTOM_BUILD) //-- Create custom plugin (Static) _corePlugin = (QGCCorePlugin*) new CUSTOMCLASS(app); if(_corePlugin) { diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 605b7ef1e..a530547dd 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -93,49 +93,34 @@ QVariantList &QGCCorePlugin::settings() { //-- If this hasn't been overridden, create default set of settings if(!_p->pGeneral) { - QGCOptions* pOptions = options(); //-- Default Settings - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_GENERAL) { - _p->pGeneral = new QGCSettings(tr("General"), - QUrl::fromUserInput("qrc:/qml/GeneralSettings.qml"), - QUrl::fromUserInput("qrc:/res/gear-white.svg")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pGeneral)); - } - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_COMM_LINKS) { - _p->pCommLinks = new QGCSettings(tr("Comm Links"), - QUrl::fromUserInput("qrc:/qml/LinkSettings.qml"), - QUrl::fromUserInput("qrc:/res/waves.svg")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pCommLinks)); - } - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_OFFLINE_MAPS) { - _p->pOfflineMaps = new QGCSettings(tr("Offline Maps"), - QUrl::fromUserInput("qrc:/qml/OfflineMap.qml"), - QUrl::fromUserInput("qrc:/res/waves.svg")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pOfflineMaps)); - } - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_MAVLINK) { - _p->pMAVLink = new QGCSettings(tr("MAVLink"), - QUrl::fromUserInput("qrc:/qml/MavlinkSettings.qml"), - QUrl::fromUserInput("qrc:/res/waves.svg")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMAVLink)); - } - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_CONSOLE) { - _p->pConsole = new QGCSettings(tr("Console"), - QUrl::fromUserInput("qrc:/QGroundControl/Controls/AppMessages.qml")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pConsole)); - } + _p->pGeneral = new QGCSettings(tr("General"), + QUrl::fromUserInput("qrc:/qml/GeneralSettings.qml"), + QUrl::fromUserInput("qrc:/res/gear-white.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pGeneral)); + _p->pCommLinks = new QGCSettings(tr("Comm Links"), + QUrl::fromUserInput("qrc:/qml/LinkSettings.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pCommLinks)); + _p->pOfflineMaps = new QGCSettings(tr("Offline Maps"), + QUrl::fromUserInput("qrc:/qml/OfflineMap.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pOfflineMaps)); + _p->pMAVLink = new QGCSettings(tr("MAVLink"), + QUrl::fromUserInput("qrc:/qml/MavlinkSettings.qml"), + QUrl::fromUserInput("qrc:/res/waves.svg")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMAVLink)); + _p->pConsole = new QGCSettings(tr("Console"), + QUrl::fromUserInput("qrc:/qml/QGroundControl/Controls/AppMessages.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pConsole)); #if defined(QT_DEBUG) //-- These are always present on Debug builds - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_MOCKLINK) { - _p->pMockLink = new QGCSettings(tr("Mock Link"), - QUrl::fromUserInput("qrc:/qml/MockLink.qml")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMockLink)); - } - if(pOptions->enabledSettings() & QGCOptions::SETTINGS_DEBUG) { - _p->pDebug = new QGCSettings(tr("Debug"), - QUrl::fromUserInput("qrc:/qml/DebugWindow.qml")); - _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pDebug)); - } + _p->pMockLink = new QGCSettings(tr("Mock Link"), + QUrl::fromUserInput("qrc:/qml/MockLink.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pMockLink)); + _p->pDebug = new QGCSettings(tr("Debug"), + QUrl::fromUserInput("qrc:/qml/DebugWindow.qml")); + _p->settingsList.append(QVariant::fromValue((QGCSettings*)_p->pDebug)); #endif } return _p->settingsList; diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index b329472ed..e1e913512 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -22,24 +22,10 @@ class QGCOptions : public QObject public: QGCOptions(QObject* parent = NULL); - enum BaseSettings { - SETTINGS_GENERAL = 1 << 1, - SETTINGS_COMM_LINKS = 1 << 2, - SETTINGS_OFFLINE_MAPS = 1 << 3, - SETTINGS_MAVLINK = 1 << 4, - SETTINGS_CONSOLE = 1 << 5, - SETTINGS_MOCKLINK = 1 << 6, - SETTINGS_DEBUG = 1 << 7, - }; - - Q_ENUMS(BaseSettings) - Q_PROPERTY(bool combineSettingsAndSetup READ combineSettingsAndSetup CONSTANT) - Q_PROPERTY(bool mainViewIsMap READ mainViewIsMap CONSTANT) Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) Q_PROPERTY(bool enableAutoConnectOptions READ enableAutoConnectOptions CONSTANT) Q_PROPERTY(bool enableVideoSourceOptions READ enableVideoSourceOptions CONSTANT) - Q_PROPERTY(int enabledSettings READ enabledSettings CONSTANT) Q_PROPERTY(bool definesVideo READ definesVideo CONSTANT) Q_PROPERTY(uint16_t videoUDPPort READ videoUDPPort CONSTANT) Q_PROPERTY(QString videoRSTPUrl READ videoRSTPUrl CONSTANT) @@ -49,11 +35,6 @@ public: @return true if QGC should consolidate both menus into one. */ virtual bool combineSettingsAndSetup () { return false; } - //! Should QGC use Maps as its default main view? - /*! - @return true if QGC should use Maps by default or false to show Video by default. - */ - virtual bool mainViewIsMap () { return true; } //! Should QGC use virtual Joysticks? /*! @return false to disable Virtual Joysticks. @@ -69,11 +50,6 @@ public: @return false to disable video source options. */ virtual bool enableVideoSourceOptions () { return true; } - //! Which settings are enabled? - /*! - @return BaseSettings bitmap of enabled settings - */ - virtual int enabledSettings () { return SETTINGS_GENERAL | SETTINGS_COMM_LINKS | SETTINGS_MAVLINK | SETTINGS_CONSOLE | SETTINGS_MOCKLINK | SETTINGS_DEBUG; } //! Does your plugin defines its on video source? /*! @return true to define your own video source. -- GitLab From c80fac51c9da5fe477ace9b8739ecfcd97f6a606 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 28 Nov 2016 19:24:50 -0500 Subject: [PATCH 069/398] Bogus change to force new build. --- src/QGCToolbox.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 24408eafd..28e1afbe2 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -146,7 +146,6 @@ QGCTool::QGCTool(QGCApplication* app) , _app(app) , _toolbox(NULL) { - } void QGCTool::setToolbox(QGCToolbox* toolbox) -- GitLab From a42937f815b603aabe3d8c7f63456ef339d24526 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 29 Nov 2016 14:16:00 -0800 Subject: [PATCH 070/398] MainToolbarIndicator now real control --- src/QmlControls/QGroundControl.Controls.qmldir | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index a04b37ba7..5334f916d 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -9,6 +9,7 @@ FactSliderPanel 1.0 FactSliderPanel.qml IndicatorButton 1.0 IndicatorButton.qml JoystickThumbPad 1.0 JoystickThumbPad.qml MainToolBar 1.0 MainToolBar.qml +MainToolBarIndicators 1.0 MainToolBarIndicators.qml MissionCommandDialog 1.0 MissionCommandDialog.qml MissionItemEditor 1.0 MissionItemEditor.qml MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml -- GitLab From 7602c276079a7b879480ede85c673872185e68fa Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 29 Nov 2016 14:16:36 -0800 Subject: [PATCH 071/398] Multi-vehicle fixes * Vehicle selection must be global control * Disconnect controls must be associated with Vehicle --- src/ui/toolbar/MainToolBar.qml | 233 +++++----- src/ui/toolbar/MainToolBarIndicators.qml | 554 +++++++++++------------ 2 files changed, 375 insertions(+), 412 deletions(-) diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 2d5670039..574b32320 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -7,17 +7,9 @@ * ****************************************************************************/ - -/** - * @file - * @brief QGC Main Tool Bar - * @author Gus Grubba - */ - -import QtQuick 2.5 -import QtQuick.Layouts 1.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 +import QtQuick 2.5 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 1.2 import QGroundControl 1.0 import QGroundControl.Controls 1.0 @@ -316,123 +308,136 @@ Rectangle { visible: qgcPal.globalTheme == QGCPalette.Light } - //--------------------------------------------- - // Toolbar Row - Row { - id: viewRow - height: mainWindow.tbCellHeight - spacing: mainWindow.tbSpacing - anchors.left: parent.left + RowLayout { anchors.bottomMargin: 1 - anchors.top: parent.top - anchors.bottom: parent.bottom - - ExclusiveGroup { id: mainActionGroup } - - QGCToolBarButton { - id: settingsButton - width: mainWindow.tbButtonWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - exclusiveGroup: mainActionGroup - source: "/res/QGCLogoWhite" - logo: true - onClicked: toolBar.showSettingsView() - visible: !QGroundControl.corePlugin.options.combineSettingsAndSetup - } + anchors.rightMargin: ScreenTools.defaultFontPixelWidth / 2 + anchors.fill: parent + spacing: mainWindow.tbSpacing * 2 + + //--------------------------------------------- + // Toolbar Row + Row { + id: viewRow + height: mainWindow.tbCellHeight + spacing: mainWindow.tbSpacing + anchors.top: parent.top + anchors.bottom: parent.bottom + + ExclusiveGroup { id: mainActionGroup } + + QGCToolBarButton { + id: settingsButton + width: mainWindow.tbButtonWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + exclusiveGroup: mainActionGroup + source: "/res/QGCLogoWhite" + logo: true + onClicked: toolBar.showSettingsView() + visible: !QGroundControl.corePlugin.options.combineSettingsAndSetup + } - QGCToolBarButton { - id: setupButton - width: mainWindow.tbButtonWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - exclusiveGroup: mainActionGroup - source: "/qmlimages/Gears.svg" - onClicked: toolBar.showSetupView() - } + QGCToolBarButton { + id: setupButton + width: mainWindow.tbButtonWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + exclusiveGroup: mainActionGroup + source: "/qmlimages/Gears.svg" + onClicked: toolBar.showSetupView() + } - QGCToolBarButton { - id: planButton - width: mainWindow.tbButtonWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - exclusiveGroup: mainActionGroup - source: "/qmlimages/Plan.svg" - onClicked: toolBar.showPlanView() - } + QGCToolBarButton { + id: planButton + width: mainWindow.tbButtonWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + exclusiveGroup: mainActionGroup + source: "/qmlimages/Plan.svg" + onClicked: toolBar.showPlanView() + } - QGCToolBarButton { - id: flyButton - width: mainWindow.tbButtonWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - exclusiveGroup: mainActionGroup - source: "/qmlimages/PaperPlane.svg" - onClicked: toolBar.showFlyView() - } + QGCToolBarButton { + id: flyButton + width: mainWindow.tbButtonWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + exclusiveGroup: mainActionGroup + source: "/qmlimages/PaperPlane.svg" + onClicked: toolBar.showFlyView() + } - QGCToolBarButton { - id: analyzeButton - width: mainWindow.tbButtonWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - exclusiveGroup: mainActionGroup - source: "/qmlimages/Analyze.svg" - visible: !ScreenTools.isMobile - onClicked: toolBar.showAnalyzeView() + QGCToolBarButton { + id: analyzeButton + width: mainWindow.tbButtonWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + exclusiveGroup: mainActionGroup + source: "/qmlimages/Analyze.svg" + visible: !ScreenTools.isMobile + onClicked: toolBar.showAnalyzeView() + } } - } - Item { - id: vehicleIndicators - height: mainWindow.tbCellHeight - anchors.leftMargin: mainWindow.tbSpacing * 2 - anchors.left: viewRow.right - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter + //------------------------------------------------------------------------- + //-- Vehicle Selector + QGCButton { + id: vehicleSelectorButton + width: ScreenTools.defaultFontPixelHeight * 8 + text: "Vehicle " + (activeVehicle ? activeVehicle.id : "None") + visible: QGroundControl.multiVehicleManager.vehicles.count > 1 + anchors.verticalCenter: parent.verticalCenter - property bool vehicleConnectionLost: activeVehicle ? activeVehicle.connectionLost : false + menu: vehicleMenu - Loader { - id: indicatorLoader - source: activeVehicle && !parent.vehicleConnectionLost ? "MainToolBarIndicators.qml" : "" - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - } + Menu { + id: vehicleMenu + } - QGCLabel { - id: connectionLost - text: qsTr("COMMUNICATION LOST") - font.pointSize: ScreenTools.largeFontPointSize - font.family: ScreenTools.demiboldFontFamily - color: colorRed - anchors.rightMargin: ScreenTools.defaultFontPixelWidth - anchors.right: disconnectButton.left - anchors.verticalCenter: parent.verticalCenter - visible: parent.vehicleConnectionLost - } + Component { + id: vehicleMenuItemComponent - QGCButton { - id: disconnectButton - anchors.rightMargin: mainWindow.tbSpacing * 2 - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Disconnect") - visible: parent.vehicleConnectionLost - primary: true - onClicked: activeVehicle.disconnectInactiveVehicle() - } + MenuItem { + checkable: true + onTriggered: QGroundControl.multiVehicleManager.activeVehicle = vehicle + + property int vehicleId: Number(text.split(" ")[1]) + property var vehicle: QGroundControl.multiVehicleManager.getVehicleById(vehicleId) + } + } + + property var vehicleMenuItems: [] - Image { - anchors.rightMargin: ScreenTools.defaultFontPixelWidth / 2 - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - visible: x > indicatorLoader.x + indicatorLoader.width && !disconnectButton.visible && source != "" - fillMode: Image.PreserveAspectFit - source: activeVehicle ? activeVehicle.brandImage : "" + function updateVehicleMenu() { + // Remove old menu items + for (var i = 0; i < vehicleMenuItems.length; i++) { + vehicleMenu.removeItem(vehicleMenuItems[i]) + } + vehicleMenuItems.length = 0 + + // Add new items + for (var i=0; i= 0) { - return activeVehicle.battery.voltage.valueString + activeVehicle.battery.voltage.units + if (_activeVehicle.battery.voltage.value >= 0) { + return _activeVehicle.battery.voltage.valueString + _activeVehicle.battery.voltage.units } return 'N/A'; } - //------------------------------------------------------------------------- function getBatteryPercentageText() { - if(activeVehicle) { - if(activeVehicle.battery.percentRemaining.value > 98.9) { + if(_activeVehicle) { + if(_activeVehicle.battery.percentRemaining.value > 98.9) { return "100%" } - if(activeVehicle.battery.percentRemaining.value > 0.1) { - return activeVehicle.battery.percentRemaining.valueString + activeVehicle.battery.percentRemaining.units + if(_activeVehicle.battery.percentRemaining.value > 0.1) { + return _activeVehicle.battery.percentRemaining.valueString + _activeVehicle.battery.percentRemaining.units } - if(activeVehicle.battery.voltage.value >= 0) { - return activeVehicle.battery.voltage.valueString + activeVehicle.battery.voltage.units + if(_activeVehicle.battery.voltage.value >= 0) { + return _activeVehicle.battery.voltage.valueString + _activeVehicle.battery.voltage.units } } return "N/A" } - //------------------------------------------------------------------------- - //-- Message Indicator - Item { - id: messages - width: mainWindow.tbCellHeight - height: mainWindow.tbCellHeight - visible: activeVehicle && activeVehicle.messageCount - anchors.verticalCenter: parent.verticalCenter - Item { - id: criticalMessage - anchors.fill: parent - visible: activeVehicle && activeVehicle.messageCount > 0 && isMessageImportant - Image { - source: "/qmlimages/Yield.svg" - height: mainWindow.tbCellHeight * 0.75 - sourceSize.height: height - fillMode: Image.PreserveAspectFit - cache: false - visible: isMessageImportant - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } - } - Item { - anchors.fill: parent - visible: !criticalMessage.visible - QGCColoredImage { - id: messageIcon - source: "/qmlimages/Megaphone.svg" - height: mainWindow.tbCellHeight * 0.5 - width: height - sourceSize.height: height - fillMode: Image.PreserveAspectFit - color: getMessageColor() - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } - } - MouseArea { - anchors.fill: parent - onClicked: { - mainWindow.showMessageArea() - } - } - } + Row { + id: indicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth * 1.5 + visible: !_communicationLost - //------------------------------------------------------------------------- - //-- GPS Indicator - Item { - id: satelitte - width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 - height: mainWindow.tbCellHeight - - QGCColoredImage { - id: gpsIcon - source: "/qmlimages/Gps.svg" - fillMode: Image.PreserveAspectFit - width: mainWindow.tbCellHeight * 0.65 - height: mainWindow.tbCellHeight * 0.5 - sourceSize.height: height - opacity: (activeVehicle && activeVehicle.gps.count.value >= 0) ? 1 : 0.5 - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter - } + QGCPalette { id: qgcPal } - Column { - id: gpsValuesColumn + //------------------------------------------------------------------------- + //-- Message Indicator + Item { + id: messages + width: mainWindow.tbCellHeight + height: mainWindow.tbCellHeight + visible: _activeVehicle && _activeVehicle.messageCount anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 - anchors.left: gpsIcon.right - - QGCLabel { - anchors.horizontalCenter: hdopValue.horizontalCenter - visible: activeVehicle && !isNaN(activeVehicle.gps.hdop.value) - color: qgcPal.buttonText - text: activeVehicle ? activeVehicle.gps.count.valueString : "" + Item { + id: criticalMessage + anchors.fill: parent + visible: _activeVehicle && _activeVehicle.messageCount > 0 && isMessageImportant + Image { + source: "/qmlimages/Yield.svg" + height: mainWindow.tbCellHeight * 0.75 + sourceSize.height: height + fillMode: Image.PreserveAspectFit + cache: false + visible: isMessageImportant + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } } - - QGCLabel { - id: hdopValue - visible: activeVehicle && !isNaN(activeVehicle.gps.hdop.value) - color: qgcPal.buttonText - text: activeVehicle ? activeVehicle.gps.hdop.value.toFixed(1) : "" + Item { + anchors.fill: parent + visible: !criticalMessage.visible + QGCColoredImage { + id: messageIcon + source: "/qmlimages/Megaphone.svg" + height: mainWindow.tbCellHeight * 0.5 + width: height + sourceSize.height: height + fillMode: Image.PreserveAspectFit + color: getMessageColor() + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } } - } // Column - - MouseArea { - anchors.fill: parent - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(gpsInfo, centerX) + MouseArea { + anchors.fill: parent + onClicked: { + mainWindow.showMessageArea() + } } } - } - //------------------------------------------------------------------------- - //-- RC RSSI - Item { - id: rcRssi - width: rssiRow.width * 1.1 - height: mainWindow.tbCellHeight - visible: activeVehicle ? activeVehicle.supportsRadio : true - Row { - id: rssiRow - height: parent.height - spacing: ScreenTools.defaultFontPixelWidth + //------------------------------------------------------------------------- + //-- GPS Indicator + Item { + id: satelitte + width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 + height: mainWindow.tbCellHeight + QGCColoredImage { + id: gpsIcon + source: "/qmlimages/Gps.svg" + fillMode: Image.PreserveAspectFit width: mainWindow.tbCellHeight * 0.65 - height: width + height: mainWindow.tbCellHeight * 0.5 sourceSize.height: height - source: "/qmlimages/RC.svg" - fillMode: Image.PreserveAspectFit - opacity: activeVehicle ? (((activeVehicle.rcRSSI < 0) || (activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 + opacity: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? 1 : 0.5 color: qgcPal.buttonText anchors.verticalCenter: parent.verticalCenter } - SignalStrength { - size: mainWindow.tbCellHeight * 0.5 - percent: activeVehicle ? ((activeVehicle.rcRSSI > 100) ? 0 : activeVehicle.rcRSSI) : 0 + + Column { + id: gpsValuesColumn anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 + anchors.left: gpsIcon.right + + QGCLabel { + anchors.horizontalCenter: hdopValue.horizontalCenter + visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) + color: qgcPal.buttonText + text: _activeVehicle ? _activeVehicle.gps.count.valueString : "" + } + + QGCLabel { + id: hdopValue + visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) + color: qgcPal.buttonText + text: _activeVehicle ? _activeVehicle.gps.hdop.value.toFixed(1) : "" + } + } // Column + + MouseArea { + anchors.fill: parent + onClicked: { + var centerX = mapToItem(toolBar, x, y).x + (width / 2) + mainWindow.showPopUp(gpsInfo, centerX) + } } } - MouseArea { - anchors.fill: parent - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(rcRSSIInfo, centerX) - } - } - } - //------------------------------------------------------------------------- - //-- Telemetry RSSI - Item { - id: telemRssi - width: telemIcon.width - height: mainWindow.tbCellHeight - visible: _controller.telemetryLRSSI < 0 - QGCColoredImage { - id: telemIcon - height: parent.height * 0.5 - sourceSize.height: height - width: height * 1.5 - source: "/qmlimages/TelemRSSI.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter - } - MouseArea { - anchors.fill: parent - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(telemRSSIInfo, centerX) + //------------------------------------------------------------------------- + //-- RC RSSI + Item { + id: rcRssi + width: rssiRow.width * 1.1 + height: mainWindow.tbCellHeight + visible: _activeVehicle ? _activeVehicle.supportsRadio : true + Row { + id: rssiRow + height: parent.height + spacing: ScreenTools.defaultFontPixelWidth + QGCColoredImage { + width: mainWindow.tbCellHeight * 0.65 + height: width + sourceSize.height: height + source: "/qmlimages/RC.svg" + fillMode: Image.PreserveAspectFit + opacity: _activeVehicle ? (((_activeVehicle.rcRSSI < 0) || (_activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 + color: qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + } + SignalStrength { + size: mainWindow.tbCellHeight * 0.5 + percent: _activeVehicle ? ((_activeVehicle.rcRSSI > 100) ? 0 : _activeVehicle.rcRSSI) : 0 + anchors.verticalCenter: parent.verticalCenter + } + } + MouseArea { + anchors.fill: parent + onClicked: { + var centerX = mapToItem(toolBar, x, y).x + (width / 2) + mainWindow.showPopUp(rcRSSIInfo, centerX) + } } } - } - //------------------------------------------------------------------------- - //-- Battery Indicator - Item { - id: batteryStatus - width: battRow.width * 1.1 - height: mainWindow.tbCellHeight - opacity: (activeVehicle && activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 - Row { - id: battRow - height: mainWindow.tbCellHeight - anchors.horizontalCenter: parent.horizontalCenter + //------------------------------------------------------------------------- + //-- Telemetry RSSI + Item { + id: telemRssi + width: telemIcon.width + height: mainWindow.tbCellHeight + visible: _controller.telemetryLRSSI < 0 QGCColoredImage { - height: mainWindow.tbCellHeight * 0.65 - width: height - sourceSize.width: width - source: "/qmlimages/Battery.svg" + id: telemIcon + height: parent.height * 0.5 + sourceSize.height: height + width: height * 1.5 + source: "/qmlimages/TelemRSSI.svg" fillMode: Image.PreserveAspectFit - color: qgcPal.text - anchors.verticalCenter: parent.verticalCenter - } - QGCLabel { - text: getBatteryPercentageText() - font.pointSize: ScreenTools.mediumFontPointSize - color: getBatteryColor() + color: qgcPal.buttonText anchors.verticalCenter: parent.verticalCenter } - } - MouseArea { - anchors.fill: parent - onClicked: { - if (activeVehicle) { + MouseArea { + anchors.fill: parent + onClicked: { var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(batteryInfo, centerX) + mainWindow.showPopUp(telemRSSIInfo, centerX) } } } - } - //------------------------------------------------------------------------- - //-- Vehicle Selector - QGCButton { - id: vehicleSelectorButton - width: ScreenTools.defaultFontPixelHeight * 8 - text: "Vehicle " + (activeVehicle ? activeVehicle.id : "None") - visible: QGroundControl.multiVehicleManager.vehicles.count > 1 - anchors.verticalCenter: parent.verticalCenter - - menu: vehicleMenu - - Menu { - id: vehicleMenu - } - - Component { - id: vehicleMenuItemComponent - - MenuItem { - checkable: true - onTriggered: QGroundControl.multiVehicleManager.activeVehicle = vehicle - - property int vehicleId: Number(text.split(" ")[1]) - property var vehicle: QGroundControl.multiVehicleManager.getVehicleById(vehicleId) + //------------------------------------------------------------------------- + //-- Battery Indicator + Item { + id: batteryStatus + width: battRow.width * 1.1 + height: mainWindow.tbCellHeight + opacity: (_activeVehicle && _activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 + Row { + id: battRow + height: mainWindow.tbCellHeight + anchors.horizontalCenter: parent.horizontalCenter + QGCColoredImage { + height: mainWindow.tbCellHeight * 0.65 + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.text + anchors.verticalCenter: parent.verticalCenter + } + QGCLabel { + text: getBatteryPercentageText() + font.pointSize: ScreenTools.mediumFontPointSize + color: getBatteryColor() + anchors.verticalCenter: parent.verticalCenter + } } - } - - property var vehicleMenuItems: [] - function updateVehicleMenu() { - // Remove old menu items - for (var i = 0; i < vehicleMenuItems.length; i++) { - vehicleMenu.removeItem(vehicleMenuItems[i]) - } - vehicleMenuItems.length = 0 - - // Add new items - for (var i=0; i indicatorRow.width && !_communicationLost + fillMode: Image.PreserveAspectFit + source: _activeVehicle ? _activeVehicle.brandImage : "" } -} + Row { + anchors.fill: parent + layoutDirection: Qt.RightToLeft + spacing: ScreenTools.defaultFontPixelWidth + visible: _communicationLost + + QGCButton { + id: disconnectButton + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Disconnect") + primary: true + onClicked: _activeVehicle.disconnectInactiveVehicle() + } + QGCLabel { + id: connectionLost + anchors.verticalCenter: parent.verticalCenter + text: qsTr("COMMUNICATION LOST") + font.pointSize: ScreenTools.largeFontPointSize + font.family: ScreenTools.demiboldFontFamily + color: colorRed + } + } // Row - Communication lost +} // Item -- GitLab From 63e27884e75d403dbb6bce9293408d957bb260c6 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Tue, 29 Nov 2016 17:18:33 -0800 Subject: [PATCH 072/398] Update PX4 airframe meta (#4266) --- .../PX4/AirframeFactMetaData.xml | 167 +++++++++--------- 1 file changed, 85 insertions(+), 82 deletions(-) diff --git a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml index ce0cb02a9..a5e7bd16f 100644 --- a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml +++ b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml @@ -14,10 +14,18 @@ - + Simon Wilks <simon@px4.io> Flying Wing - http://www.sparkletech.hk/ + + + Simon Wilks <simon@px4.io> + Flying Wing + + + Simon Wilks <simon@px4.io> + Flying Wing + https://pixhawk.org/platforms/planes/z-84_wing_wing feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -25,8 +33,8 @@ right aileron throttle - - Simon Wilks <simon@px4.io> + + Lorenz Meier <lorenz@px4.io> Flying Wing https://pixhawk.org/platforms/planes/z-84_wing_wing feed-through of RC AUX1 channel @@ -47,10 +55,6 @@ right aileron throttle - - Simon Wilks <simon@px4.io> - Flying Wing - Simon Wilks <simon@px4.io> Flying Wing @@ -62,14 +66,10 @@ right aileron throttle - + Simon Wilks <simon@px4.io> Flying Wing - - - Lorenz Meier <lorenz@px4.io> - Flying Wing - https://pixhawk.org/platforms/planes/z-84_wing_wing + http://www.sparkletech.hk/ feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -179,6 +179,10 @@ Anton Babushkin <anton@px4.io>, Simon Wilks <simon@px4.io> Quadrotor Wide + + Thomas Gubler <thomas@px4.io> + Quadrotor Wide + Anton Babushkin <anton@px4.io> Quadrotor Wide @@ -187,98 +191,97 @@ Simon Wilks <simon@px4.io> Quadrotor Wide - - Thomas Gubler <thomas@px4.io> - Quadrotor Wide - + + Michael Schaeuble + Quadrotor x + Lorenz Meier <lorenz@px4.io> Quadrotor x - - Lorenz Meier <lorenz@px4.io> + + Andreas Antener <andreas@uaventure.com> Quadrotor x - feed-through of RC AUX1 channel - feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - - Pavel Kirienko <pavel@px4.io> + + Blankered Quadrotor x - - Leon Mueller <thedevleon> + Quadrotor x - - Lorenz Meier <lorenz@px4.io> + + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Blankered + + Pavel Kirienko <pavel@px4.io> Quadrotor x - + + Leon Mueller <thedevleon> + Quadrotor x + + Lorenz Meier <lorenz@px4.io> Quadrotor x - feed-through of RC AUX1 channel - feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - - Mark Whitehorn <kd0aij@gmail.com> + + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - + James Goppert <james.goppert@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Lorenz Meier <lorenz@px4.io> - Quadrotor x - Thomas Gubler <thomas@px4.io> Quadrotor x - - Mark Whitehorn <kd0aij@gmail.com> + + James Goppert <james.goppert@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - James Goppert <james.goppert@gmail.com> + + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - + + Leon Mueller <thedevleon> Quadrotor x - - Michael Schaeuble + + Lorenz Meier <lorenz@px4.io> Quadrotor x + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel - - Leon Mueller <thedevleon> + + Lorenz Meier <lorenz@px4.io> Quadrotor x + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel - - Andreas Antener <andreas@uaventure.com> + Quadrotor x @@ -288,10 +291,6 @@ - - Anton Babushkin <anton@px4.io> - Simulation - Lorenz Meier <lorenz@px4.io> Simulation @@ -304,9 +303,13 @@ Anton Babushkin <anton@px4.io> Simulation + + Anton Babushkin <anton@px4.io> + Simulation + - + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel @@ -314,8 +317,8 @@ feed-through of RC AUX3 channel aileron elevator - rudder - throttle + throttle + rudder flaps @@ -332,9 +335,17 @@ wheel flaps - + Lorenz Meier <lorenz@px4.io> Standard Plane + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel + aileron + elevator + rudder + throttle + flaps Lorenz Meier <lorenz@px4.io> @@ -358,38 +369,30 @@ elevator throttle - + Lorenz Meier <lorenz@px4.io> Standard Plane - feed-through of RC AUX1 channel - feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - aileron - elevator - throttle - rudder - flaps - - Sander Smeets <sander@droneslab.com> + + Andreas Antener <andreas@uaventure.com> Standard VTOL Sander Smeets <sander@droneslab.com> Standard VTOL - + Simon Wilks <simon@uaventure.com> Standard VTOL - + Simon Wilks <simon@uaventure.com> Standard VTOL - - Andreas Antener <andreas@uaventure.com> + + Sander Smeets <sander@droneslab.com> Standard VTOL @@ -416,24 +419,24 @@ - + Roman Bapst <roman@px4.io> VTOL Quad Tailsitter - + Roman Bapst <roman@px4.io> VTOL Quad Tailsitter - - Samay Siga <samay_s@icloud.com> - VTOL Tiltrotor - Roman Bapst <roman@px4.io> VTOL Tiltrotor + + Samay Siga <samay_s@icloud.com> + VTOL Tiltrotor + -- GitLab From 9954bfc55bd4510f7623fdd128e8c706fec30be8 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 18:26:40 -0800 Subject: [PATCH 073/398] Adjust spacing to take less space --- src/MissionEditor/MissionItemStatus.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/MissionEditor/MissionItemStatus.qml b/src/MissionEditor/MissionItemStatus.qml index 9777157df..a12ed9840 100644 --- a/src/MissionEditor/MissionItemStatus.qml +++ b/src/MissionEditor/MissionItemStatus.qml @@ -10,6 +10,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.2 import QGroundControl.ScreenTools 1.0 import QGroundControl.Controls 1.0 @@ -89,14 +90,14 @@ Rectangle { anchors.margins: _margins spacing: _margins - Grid { - id: valueGrid - columns: 2 - columnSpacing: _margins + GridLayout { + id: valueGrid + columns: 2 + rowSpacing: 0 + columnSpacing: _margins anchors.verticalCenter: parent.verticalCenter - QGCLabel { text: qsTr("Selected waypoint") } - QGCLabel { text: qsTr(" ") } + QGCLabel { text: qsTr("Selected waypoint"); Layout.columnSpan: 2 } QGCLabel { text: qsTr("Distance:") } QGCLabel { text: _distanceText } -- GitLab From a0907e46586806d2f4fea9fdeb0a6241a96b3d1b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 18:55:19 -0800 Subject: [PATCH 074/398] Single instance app guarding mechanism --- qgroundcontrol.pro | 2 ++ src/RunGuard.cc | 87 ++++++++++++++++++++++++++++++++++++++++++++++ src/RunGuard.h | 38 ++++++++++++++++++++ src/main.cc | 20 +++++------ 4 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 src/RunGuard.cc create mode 100644 src/RunGuard.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index f05699945..85adc4fab 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -529,6 +529,7 @@ HEADERS += \ src/Joystick/JoystickSDL.h \ src/QGCFileDialog.h \ src/QGCMessageBox.h \ + src/RunGuard.h \ src/ViewWidgets/CustomCommandWidget.h \ src/ViewWidgets/CustomCommandWidgetController.h \ src/ViewWidgets/ViewWidgetController.h \ @@ -677,6 +678,7 @@ SOURCES += \ src/GPS/RTCM/RTCMMavlink.cc \ src/Joystick/JoystickSDL.cc \ src/QGCFileDialog.cc \ + src/RunGuard.cc \ src/ViewWidgets/CustomCommandWidget.cc \ src/ViewWidgets/CustomCommandWidgetController.cc \ src/ViewWidgets/ViewWidgetController.cc \ diff --git a/src/RunGuard.cc b/src/RunGuard.cc new file mode 100644 index 000000000..5e8a49253 --- /dev/null +++ b/src/RunGuard.cc @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * (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 "RunGuard.h" + +#include + +namespace +{ + +QString generateKeyHash( const QString& key, const QString& salt ) +{ + QByteArray data; + + data.append( key.toUtf8() ); + data.append( salt.toUtf8() ); + data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex(); + + return data; +} + +} + +RunGuard::RunGuard( const QString& key ) + : key( key ) + , memLockKey( generateKeyHash( key, "_memLockKey" ) ) + , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) ) + , sharedMem( sharedmemKey ) + , memLock( memLockKey, 1 ) +{ + memLock.acquire(); + { + QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/ + fix.attach(); + } + memLock.release(); +} + +RunGuard::~RunGuard() +{ + release(); +} + +bool RunGuard::isAnotherRunning() +{ + if ( sharedMem.isAttached() ) + return false; + + memLock.acquire(); + const bool isRunning = sharedMem.attach(); + if ( isRunning ) + sharedMem.detach(); + memLock.release(); + + return isRunning; +} + +bool RunGuard::tryToRun() +{ + if ( isAnotherRunning() ) // Extra check + return false; + + memLock.acquire(); + const bool result = sharedMem.create( sizeof( quint64 ) ); + memLock.release(); + if ( !result ) + { + release(); + return false; + } + + return true; +} + +void RunGuard::release() +{ + memLock.acquire(); + if ( sharedMem.isAttached() ) + sharedMem.detach(); + memLock.release(); +} diff --git a/src/RunGuard.h b/src/RunGuard.h new file mode 100644 index 000000000..0e82f5b62 --- /dev/null +++ b/src/RunGuard.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * (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 RunGuard_H +#define RunGuard_H + +#include +#include +#include + +class RunGuard +{ +public: + RunGuard( const QString& key ); + ~RunGuard(); + + bool isAnotherRunning(); + bool tryToRun(); + void release(); + +private: + const QString key; + const QString memLockKey; + const QString sharedmemKey; + + QSharedMemory sharedMem; + QSystemSemaphore memLock; + + Q_DISABLE_COPY( RunGuard ) +}; + +#endif diff --git a/src/main.cc b/src/main.cc index 80000cd33..4dec260fa 100644 --- a/src/main.cc +++ b/src/main.cc @@ -27,10 +27,9 @@ #include "QGCApplication.h" #include "AppMessages.h" -#define SINGLE_INSTANCE_PORT 14499 - #ifndef __mobile__ #include "QGCSerialPortInfo.h" + #include "RunGuard.h" #endif #ifdef UNITTEST_BUILD @@ -103,6 +102,13 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) int main(int argc, char *argv[]) { +#ifndef __mobile__ + RunGuard guard("QGroundControlRunGuardKey"); + if (!guard.tryToRun()) { + return 0; + } +#endif + #ifdef Q_OS_UNIX //Force writing to the console on UNIX/BSD devices if (!qEnvironmentVariableIsSet("QT_LOGGING_TO_CONSOLE")) @@ -112,16 +118,6 @@ int main(int argc, char *argv[]) // install the message handler AppMessages::installHandler(); -#ifndef __mobile__ - //-- Test for another instance already running. If that's the case, we simply exit. - QHostAddress host("127.0.0.1"); - QUdpSocket socket; - if(!socket.bind(host, SINGLE_INSTANCE_PORT, QAbstractSocket::DontShareAddress)) { - qWarning() << "Another instance already running. Exiting."; - exit(-1); - } -#endif - #ifdef Q_OS_MAC #ifndef __ios__ // Prevent Apple's app nap from screwing us over -- GitLab From d7234b8e7f509e6b8b8cbc7768ce3a29c4cf09cb Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 19:19:50 -0800 Subject: [PATCH 075/398] Use new QGCListView Shows horizontal/vertical scroll indicators --- qgroundcontrol.qrc | 1 + src/MissionEditor/MissionEditor.qml | 4 ++-- src/MissionEditor/MissionItemStatus.qml | 2 +- src/QmlControls/AppMessages.qml | 2 +- src/QmlControls/MissionCommandDialog.qml | 4 ++-- src/QmlControls/ParameterEditor.qml | 2 +- src/QmlControls/QGCFlickable.qml | 6 +++--- src/QmlControls/QGCListView.qml | 20 +++++++++++++++++++ src/QmlControls/QGCMobileFileDialog.qml | 2 +- .../QGroundControl.Controls.qmldir | 1 + src/ui/preferences/MavlinkSettings.qml | 2 +- 11 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 src/QmlControls/QGCListView.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index c1938e297..4801189b0 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -65,6 +65,7 @@ src/QmlControls/QGCFlickableHorizontalIndicator.qml src/QmlControls/QGCFlickableVerticalIndicator.qml src/QmlControls/QGCLabel.qml + src/QmlControls/QGCListView.qml src/QmlControls/QGCMobileFileDialog.qml src/QmlControls/QGCMovableItem.qml src/QmlControls/QGCPipable.qml diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 6b3faeaaf..3c82b8418 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -732,7 +732,7 @@ QGCView { onWheel: wheel.accepted = true } - ListView { + QGCListView { id: missionItemEditorListView anchors.left: parent.left anchors.right: parent.right @@ -766,7 +766,7 @@ QGCView { onMoveHomeToMapCenter: _visualItems.get(0).coordinate = editorMap.center } - } // ListView + } // QGCListView } // Item - Mission Item editor // GeoFence Editor diff --git a/src/MissionEditor/MissionItemStatus.qml b/src/MissionEditor/MissionItemStatus.qml index 9777157df..b90c796fb 100644 --- a/src/MissionEditor/MissionItemStatus.qml +++ b/src/MissionEditor/MissionItemStatus.qml @@ -111,7 +111,7 @@ Rectangle { QGCLabel { text: _azimuthText } } - ListView { + QGCListView { id: statusListView model: missionItems highlightMoveDuration: 250 diff --git a/src/QmlControls/AppMessages.qml b/src/QmlControls/AppMessages.qml index f851faaf9..de919c551 100644 --- a/src/QmlControls/AppMessages.qml +++ b/src/QmlControls/AppMessages.qml @@ -96,7 +96,7 @@ QGCView { } } - ListView { + QGCListView { Component.onCompleted: { loaded = true } diff --git a/src/QmlControls/MissionCommandDialog.qml b/src/QmlControls/MissionCommandDialog.qml index 48f5454ba..1a8503ecf 100644 --- a/src/QmlControls/MissionCommandDialog.qml +++ b/src/QmlControls/MissionCommandDialog.qml @@ -51,7 +51,7 @@ QGCViewDialog { onActivated: categorySelected(textAt(index)) } - ListView { + QGCListView { id: commandList anchors.margins: ScreenTools.defaultFontPixelHeight anchors.left: parent.left @@ -101,5 +101,5 @@ QGCViewDialog { } } } - } // ListView + } // QGCListView } // QGCViewDialog diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index b1e72301d..050aa21a5 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -180,7 +180,7 @@ QGCView { } /// Parameter list - ListView { + QGCListView { id: editorListView anchors.leftMargin: ScreenTools.defaultFontPixelWidth anchors.left: _searchFilter ? parent.left : groupScroll.right diff --git a/src/QmlControls/QGCFlickable.qml b/src/QmlControls/QGCFlickable.qml index 52b1b0c44..80a7e0346 100644 --- a/src/QmlControls/QGCFlickable.qml +++ b/src/QmlControls/QGCFlickable.qml @@ -1,8 +1,8 @@ -import QtQuick 2.5 +import QtQuick 2.5 -import QGroundControl.Palette 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +/// QGC version of Flickable control that shows horizontal/vertial scroll indicators Flickable { id: root boundsBehavior: Flickable.StopAtBounds diff --git a/src/QmlControls/QGCListView.qml b/src/QmlControls/QGCListView.qml new file mode 100644 index 000000000..3c5205a9e --- /dev/null +++ b/src/QmlControls/QGCListView.qml @@ -0,0 +1,20 @@ +import QtQuick 2.5 + +import QGroundControl.Palette 1.0 + +/// QGC version of ListVIew control that shows horizontal/vertial scroll indicators +ListView { + id: root + boundsBehavior: Flickable.StopAtBounds + + property color indicatorColor: qgcPal.text + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + Component.onCompleted: { + var indicatorComponent = Qt.createComponent("QGCFlickableVerticalIndicator.qml") + indicatorComponent.createObject(root) + indicatorComponent = Qt.createComponent("QGCFlickableHorizontalIndicator.qml") + indicatorComponent.createObject(root) + } +} diff --git a/src/QmlControls/QGCMobileFileDialog.qml b/src/QmlControls/QGCMobileFileDialog.qml index 91928a84f..99b48a41d 100644 --- a/src/QmlControls/QGCMobileFileDialog.qml +++ b/src/QmlControls/QGCMobileFileDialog.qml @@ -96,7 +96,7 @@ QGCViewDialog { anchors.margins: _margins anchors.fill: parent - ListView { + QGCListView { anchors.fill: parent spacing: _margins / 2 orientation: ListView.Vertical diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 5334f916d..8392f8abe 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -27,6 +27,7 @@ QGCColoredImage 1.0 QGCColoredImage.qml QGCComboBox 1.0 QGCComboBox.qml QGCFlickable 1.0 QGCFlickable.qml QGCLabel 1.0 QGCLabel.qml +QGCListView 1.0 QGCListView.qml QGCMobileFileDialog 1.0 QGCMobileFileDialog.qml QGCMovableItem 1.0 QGCMovableItem.qml QGCPipable 1.0 QGCPipable.qml diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 8cec2eb59..b33f1e718 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -340,7 +340,7 @@ Rectangle { color: qgcPal.window border.color: qgcPal.text border.width: 0.5 - ListView { + QGCListView { width: ScreenTools.defaultFontPixelWidth * 56 height: ScreenTools.defaultFontPixelHeight * 12 anchors.centerIn: parent -- GitLab From 8ad79198d8d3f84a06dee99e0e259cf8c67f4485 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 19:32:45 -0800 Subject: [PATCH 076/398] Turn off COMPASS_LEARN after cal --- src/AutoPilotPlugins/APM/APMSensorsComponentController.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index c88766304..988ee8863 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -152,6 +152,9 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll if (code == StopCalibrationSuccess) { _resetInternalState(); _progressBar->setProperty("value", 1); + if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))) { + _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))->setRawValue(0); + } } else { _progressBar->setProperty("value", 0); } -- GitLab From d2b2432fc87d2126d01f8b584909ce078475b5ae Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 30 Nov 2016 20:29:11 -0800 Subject: [PATCH 077/398] Level Horizon support --- .../APM/APMSensorsComponent.qml | 37 ++++++++++++++ .../APM/APMSensorsComponentController.cc | 48 +++++++++++++++++++ .../APM/APMSensorsComponentController.h | 8 +++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index bb87f6985..a0dcf688a 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -157,6 +157,7 @@ SetupPage { compassButton: compassButton accelButton: accelButton compassMotButton: motorInterferenceButton + levelButton: levelHorizonButton nextButton: nextButton cancelButton: cancelButton setOrientationsButton: setOrientationsButton @@ -453,6 +454,26 @@ SetupPage { } // QGCViewDialog } // Component - compassMotDialogComponent + Component { + id: levelHorizonDialogComponent + + QGCViewDialog { + id: levelHorizonDialog + + function accept() { + controller.levelHorizon() + levelHorizonDialog.hideDialog() + } + + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("To level the horizon you need to place the vehicle in its level flight position and press Ok.") + } + } // QGCViewDialog + } // Component - levelHorizonDialogComponent + Column { spacing: ScreenTools.defaultFontPixelHeight / 2 Layout.alignment: Qt.AlignLeft | Qt.AlignTop @@ -483,6 +504,22 @@ SetupPage { } } + QGCButton { + id: levelHorizonButton + width: parent.buttonWidth + text: _levelHorizonText + + readonly property string _levelHorizonText: qsTr("Level Horizon") + + onClicked: { + if (controller.accelSetupNeeded) { + showMessage(_levelHorizonText, qsTr("Accelerometer must be calibrated prior to Level Horizon."), StandardButton.Ok) + } else { + showDialog(levelHorizonDialogComponent, _levelHorizonText, qgcView.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Ok) + } + } + } + QGCButton { id: motorInterferenceButton width: parent.buttonWidth diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index c88766304..823fc0cb6 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -26,6 +26,7 @@ APMSensorsComponentController::APMSensorsComponentController(void) : _compassButton(NULL), _accelButton(NULL), _compassMotButton(NULL), + _levelButton(NULL), _nextButton(NULL), _cancelButton(NULL), _setOrientationsButton(NULL), @@ -33,6 +34,7 @@ APMSensorsComponentController::APMSensorsComponentController(void) : _magCalInProgress(false), _accelCalInProgress(false), _compassMotCalInProgress(false), + _levelInProgress(false), _orientationCalDownSideDone(false), _orientationCalUpsideDownSideDone(false), _orientationCalLeftSideDone(false), @@ -66,6 +68,8 @@ APMSensorsComponentController::APMSensorsComponentController(void) : _sensorsComponent = apmPlugin->sensorsComponent(); connect(_sensorsComponent, &VehicleComponent::setupCompleteChanged, this, &APMSensorsComponentController::setupNeededChanged); + + connect(qgcApp()->toolbox()->mavlinkProtocol(), &MAVLinkProtocol::messageReceived, this, &APMSensorsComponentController::_mavlinkMessageReceived); } /// Appends the specified text to the status log area in the ui @@ -90,6 +94,7 @@ void APMSensorsComponentController::_startLogCalibration(void) _compassButton->setEnabled(false); _accelButton->setEnabled(false); _compassMotButton->setEnabled(false); + _levelButton->setEnabled(false); _setOrientationsButton->setEnabled(false); if (_accelCalInProgress || _compassMotCalInProgress) { _nextButton->setEnabled(true); @@ -102,6 +107,7 @@ void APMSensorsComponentController::_startVisualCalibration(void) _compassButton->setEnabled(false); _accelButton->setEnabled(false); _compassMotButton->setEnabled(false); + _levelButton->setEnabled(false); _setOrientationsButton->setEnabled(false); _cancelButton->setEnabled(true); @@ -145,6 +151,7 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll _compassButton->setEnabled(true); _accelButton->setEnabled(true); _compassMotButton->setEnabled(true); + _levelButton->setEnabled(true); _setOrientationsButton->setEnabled(true); _nextButton->setEnabled(false); _cancelButton->setEnabled(false); @@ -168,6 +175,10 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll emit calibrationComplete(); break; + case StopCalibrationSuccessShowLog: + emit calibrationComplete(); + break; + case StopCalibrationCancelled: emit resetStatusTextArea(); _hideAllCalAreas(); @@ -183,6 +194,7 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll _magCalInProgress = false; _accelCalInProgress = false; _compassMotCalInProgress = false; + _levelInProgress = false; } void APMSensorsComponentController::calibrateCompass(void) @@ -210,6 +222,15 @@ void APMSensorsComponentController::calibrateMotorInterference(void) _uas->startCalibration(UASInterface::StartCalibrationCompassMot); } +void APMSensorsComponentController::levelHorizon(void) +{ + _levelInProgress = true; + _vehicle->setConnectionLostEnabled(false); + _startLogCalibration(); + _appendStatusLog(tr("Hold the vehicle in its level flight position.")); + _uas->startCalibration(UASInterface::StartCalibrationLevel); +} + void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, int severity, QString text) { Q_UNUSED(compId); @@ -495,3 +516,30 @@ bool APMSensorsComponentController::usingUDPLink(void) { return _vehicle->priorityLink()->getLinkConfiguration()->type() == LinkConfiguration::TypeUdp; } + +void APMSensorsComponentController::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) +{ + Q_UNUSED(link); + + if (message.sysid != _vehicle->id()) { + return; + } + + if (message.msgid == MAVLINK_MSG_ID_COMMAND_ACK && _levelInProgress) { + mavlink_command_ack_t commandAck; + mavlink_msg_command_ack_decode(&message, &commandAck); + + if (commandAck.command == MAV_CMD_PREFLIGHT_CALIBRATION) { + switch (commandAck.result) { + case MAV_RESULT_ACCEPTED: + _appendStatusLog(tr("Level horizon complete")); + _stopCalibration(StopCalibrationSuccessShowLog); + break; + default: + _appendStatusLog(tr("Level horizon failed")); + _stopCalibration(StopCalibrationFailed); + break; + } + } + } +} diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h index efd4d0338..ca7e7a350 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h @@ -35,6 +35,7 @@ public: Q_PROPERTY(QQuickItem* compassButton MEMBER _compassButton) Q_PROPERTY(QQuickItem* accelButton MEMBER _accelButton) Q_PROPERTY(QQuickItem* compassMotButton MEMBER _compassMotButton) + Q_PROPERTY(QQuickItem* levelButton MEMBER _levelButton) Q_PROPERTY(QQuickItem* nextButton MEMBER _nextButton) Q_PROPERTY(QQuickItem* cancelButton MEMBER _cancelButton) Q_PROPERTY(QQuickItem* setOrientationsButton MEMBER _setOrientationsButton) @@ -78,6 +79,7 @@ public: Q_INVOKABLE void calibrateCompass(void); Q_INVOKABLE void calibrateAccel(void); Q_INVOKABLE void calibrateMotorInterference(void); + Q_INVOKABLE void levelHorizon(void); Q_INVOKABLE void cancelCalibration(void); Q_INVOKABLE void nextClicked(void); Q_INVOKABLE bool usingUDPLink(void); @@ -99,7 +101,8 @@ signals: private slots: void _handleUASTextMessage(int uasId, int compId, int severity, QString text); - + void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); + private: void _startLogCalibration(void); void _startVisualCalibration(void); @@ -110,6 +113,7 @@ private: enum StopCalibrationCode { StopCalibrationSuccess, + StopCalibrationSuccessShowLog, StopCalibrationFailed, StopCalibrationCancelled }; @@ -125,6 +129,7 @@ private: QQuickItem* _compassButton; QQuickItem* _accelButton; QQuickItem* _compassMotButton; + QQuickItem* _levelButton; QQuickItem* _nextButton; QQuickItem* _cancelButton; QQuickItem* _setOrientationsButton; @@ -135,6 +140,7 @@ private: bool _magCalInProgress; bool _accelCalInProgress; bool _compassMotCalInProgress; + bool _levelInProgress; bool _orientationCalDownSideDone; bool _orientationCalUpsideDownSideDone; -- GitLab From 5902567dd831c812879d827c9fea0a97936194f4 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 1 Dec 2016 22:18:32 -0500 Subject: [PATCH 078/398] Handle delayed RTSP video connection. --- src/FlightDisplay/VideoManager.cc | 9 ++++ src/VideoStreaming/VideoReceiver.cc | 69 +++++++++++++++++++++++++++-- src/VideoStreaming/VideoReceiver.h | 24 ++++++++-- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index a68e880aa..d920acc4d 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -223,6 +223,15 @@ void VideoManager::_updateTimer() _videoRunning = false; _videoSurface->setLastFrame(0); emit videoRunningChanged(); + if(_videoReceiver) { + if(isGStreamer()) { + //-- Stop it + _videoReceiver->stop(); + QThread::msleep(100); + //-- And start over + _videoReceiver->start(); + } + } } } else diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 520aa48b2..594a028f0 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -16,15 +16,21 @@ #include "VideoReceiver.h" #include +#include VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) #if defined(QGC_GST_STREAMING) , _pipeline(NULL) , _videoSink(NULL) + , _socket(NULL) + , _serverPresent(false) #endif { - +#if defined(QGC_GST_STREAMING) + _timer.setSingleShot(true); + connect(&_timer, &QTimer::timeout, this, &VideoReceiver::_timeout); +#endif } VideoReceiver::~VideoReceiver() @@ -32,6 +38,9 @@ VideoReceiver::~VideoReceiver() #if defined(QGC_GST_STREAMING) stop(); setVideoSink(NULL); + if(_socket) { + delete _socket; + } #endif } @@ -66,6 +75,51 @@ static void newPadCB(GstElement * element, GstPad* pad, gpointer data) } #endif +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_connected() +{ + //-- Server showed up. Now we start the stream. + _timer.stop(); + delete _socket; + _socket = NULL; + _serverPresent = true; + start(); +} +#endif + +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_socketError(QAbstractSocket::SocketError socketError) +{ + Q_UNUSED(socketError); + delete _socket; + _socket = NULL; + //-- Try again in 5 seconds + _timer.start(5000); +} +#endif + +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_timeout() +{ + //-- If socket is live, we got no connection nor a socket error + if(_socket) { + delete _socket; + _socket = NULL; + } + //-- RTSP will try to connect to the server. If it cannot connect, + // it will simply give up and never try again. Instead, we keep + // attempting a connection on this timer. Once a connection is + // found to be working, only then we actually start the stream. + QUrl url(_uri); + _socket = new QTcpSocket; + connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); + connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); + //qDebug() << "Trying to connect to:" << url.host() << url.port(); + _socket->connectToHost(url.host(), url.port()); + _timer.start(5000); +} +#endif + void VideoReceiver::start() { #if defined(QGC_GST_STREAMING) @@ -78,8 +132,16 @@ void VideoReceiver::start() return; } + bool isUdp = _uri.contains("udp://"); + stop(); + //-- For RTSP, check to see if server is there first + if(!_serverPresent && !isUdp) { + _timer.start(100); + return; + } + bool running = false; GstElement* dataSource = NULL; @@ -88,8 +150,6 @@ void VideoReceiver::start() GstElement* parser = NULL; GstElement* decoder = NULL; - bool isUdp = _uri.contains("udp://"); - do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_pipeline_new()"; @@ -114,7 +174,7 @@ void VideoReceiver::start() } g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); } else { - g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, NULL); + g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, "udp-reconnect", 1, "timeout", 5000000, NULL); } if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) { @@ -208,6 +268,7 @@ void VideoReceiver::stop() gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); _pipeline = NULL; + _serverPresent = false; } #endif } diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 0faa58864..6069a99a1 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -18,6 +18,9 @@ #define VIDEORECEIVER_H #include +#include +#include + #if defined(QGC_GST_STREAMING) #include #endif @@ -33,10 +36,17 @@ public: void setVideoSink(GstElement* sink); #endif -public Q_SLOTS: - void start (); - void stop (); - void setUri (const QString& uri); +public slots: + void start (); + void stop (); + void setUri (const QString& uri); + +private slots: +#if defined(QGC_GST_STREAMING) + void _timeout (); + void _connected (); + void _socketError (QAbstractSocket::SocketError socketError); +#endif private: @@ -52,6 +62,12 @@ private: GstElement* _videoSink; #endif + //-- Wait for Video Server to show up before starting +#if defined(QGC_GST_STREAMING) + QTimer _timer; + QTcpSocket* _socket; + bool _serverPresent; +#endif }; #endif // VIDEORECEIVER_H -- GitLab From e3a1f0169a0bf4c436eed09bea96364d8c9b6173 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 2 Dec 2016 15:34:40 -0800 Subject: [PATCH 079/398] _requestListNoResponse fixed up and back on --- src/FactSystem/ParameterManagerTest.cc | 7 ++++--- src/FactSystem/ParameterManagerTest.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FactSystem/ParameterManagerTest.cc b/src/FactSystem/ParameterManagerTest.cc index ae362f278..9ed18f0cd 100644 --- a/src/FactSystem/ParameterManagerTest.cc +++ b/src/FactSystem/ParameterManagerTest.cc @@ -64,10 +64,12 @@ void ParameterManagerTest::_requestListMissingParamSuccess(void) _noFailureWorker(MockConfiguration::FailMissingParamOnInitialReqest); } -#if 0 // Test no response to param_request_list void ParameterManagerTest::_requestListNoResponse(void) { + // Will pop error about request failure + setExpectedMessageBox(QMessageBox::Ok); + Q_ASSERT(!_mockLink); _mockLink = MockLink::startPX4MockLink(false, MockConfiguration::FailParamNoReponseToRequestList); @@ -93,9 +95,8 @@ void ParameterManagerTest::_requestListNoResponse(void) QCOMPARE(spyParamsReady.wait(40000), false); // User should have been notified - checkMultipleExpectedMessageBox(5); + checkExpectedMessageBox(); } -#endif // MockLink will fail to send a param on initial request, it will also fail to send it on subsequent // param_read requests. diff --git a/src/FactSystem/ParameterManagerTest.h b/src/FactSystem/ParameterManagerTest.h index 7d732169f..374eebba5 100644 --- a/src/FactSystem/ParameterManagerTest.h +++ b/src/FactSystem/ParameterManagerTest.h @@ -22,8 +22,7 @@ class ParameterManagerTest : public UnitTest private slots: void _noFailure(void); - // FIXME: Hack to work around changed no reponse handling - //void _requestListNoResponse(void); + void _requestListNoResponse(void); void _requestListMissingParamSuccess(void); void _requestListMissingParamFail(void); -- GitLab From aa2771bdc362b7f683d93a8a7011e5949c51807a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 2 Dec 2016 15:34:59 -0800 Subject: [PATCH 080/398] Deal with ArduPilot 65536 index on PARAM_VALUE correctly --- src/FactSystem/ParameterManager.cc | 37 ++++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 1adfafe65..2c8bf1bbe 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -72,7 +72,7 @@ ParameterManager::ParameterManager(Vehicle* vehicle) _mavlink = qgcApp()->toolbox()->mavlinkProtocol(); _initialRequestTimeoutTimer.setSingleShot(true); - _initialRequestTimeoutTimer.setInterval(20000); + _initialRequestTimeoutTimer.setInterval(5000); connect(&_initialRequestTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_initialRequestTimeout); _waitingParamTimeoutTimer.setSingleShot(true); @@ -135,12 +135,8 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString return; } - if (parameterId >= parameterCount) { - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Discarding bogus update name:id:count" << parameterName << parameterId << parameterCount; - return; - } - _initialRequestTimeoutTimer.stop(); + _waitingParamTimeoutTimer.stop(); _dataMutex.lock(); @@ -179,14 +175,9 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString componentParamsComplete = true; } - if (_waitingReadParamIndexMap[componentId].contains(parameterId) || - _waitingReadParamNameMap[componentId].contains(parameterName) || - _waitingWriteParamNameMap[componentId].contains(parameterName)) { - // We were waiting for this parameter, restart wait timer. Otherwise it is a spurious parameter update which - // means we should not reset the wait timer. - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer (valid param received)"; - _waitingParamTimeoutTimer.start(); - } else { + if (!_waitingReadParamIndexMap[componentId].contains(parameterId) && + !_waitingReadParamNameMap[componentId].contains(parameterName) && + !_waitingWriteParamNameMap[componentId].contains(parameterName)) { qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Unrequested param update" << parameterName; } @@ -234,12 +225,17 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString int readWaitingParamCount = waitingReadParamIndexCount + waitingReadParamNameCount; int totalWaitingParamCount = readWaitingParamCount + waitingWriteParamNameCount; if (totalWaitingParamCount) { - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "totalWaitingParamCount:" << totalWaitingParamCount; - } else if (_defaultComponentId != MAV_COMP_ID_ALL || _defaultComponentIdParam.isEmpty()) { - // No more parameters to wait for, stop the timeout. Be careful to not stop timer if we don't have the default - // component yet. - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Stopping _waitingParamTimeoutTimer (all requests satisfied)"; - _waitingParamTimeoutTimer.stop(); + // More params to wait for, restart timer + _waitingParamTimeoutTimer.start(); + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer: totalWaitingParamCount:" << totalWaitingParamCount; + } else { + if (_defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { + // Still waiting for default component id, restart timer + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer (still waiting for default component)"; + _waitingParamTimeoutTimer.start(); + } else { + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; + } } // Update progress bar for waiting reads @@ -1064,6 +1060,7 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) void ParameterManager::_initialRequestTimeout(void) { if (!_disableAllRetries && ++_initialRequestRetryCount <= _maxInitialRequestListRetry) { + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Retyring initial parameter request list"; refreshAllParameters(); _initialRequestTimeoutTimer.start(); } else { -- GitLab From eb4de378ea308e2c2675fe485c1791c90486038c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 3 Dec 2016 15:33:43 -0800 Subject: [PATCH 081/398] Hide view before settings source This prevent the visibility change from trashing source setting --- src/ui/MainWindowInner.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index 1943f30e9..4224c88d6 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -66,12 +66,13 @@ Item { } //-- In settings view, the full height is available. Set to 0 so it is ignored. ScreenTools.availableHeight = 0 + hideAllViews() if (settingsViewLoader.source != _settingsViewSource) { settingsViewLoader.source = _settingsViewSource } - hideAllViews() settingsViewLoader.visible = true toolBar.checkSettingsButton() + console.log("showSettingsView") } function showSetupView() { @@ -80,10 +81,10 @@ Item { } //-- In setup view, the full height is available. Set to 0 so it is ignored. ScreenTools.availableHeight = 0 + hideAllViews() if (setupViewLoader.source != _setupViewSource) { setupViewLoader.source = _setupViewSource } - hideAllViews() setupViewLoader.visible = true toolBar.checkSetupButton() } @@ -284,13 +285,13 @@ Item { anchors.top: toolBar.bottom anchors.bottom: parent.bottom visible: false - +/* onVisibleChanged: { if (!visible) { // Free up the memory for this when not shown. No need to persist. source = "" } - } + }*/ } Loader { -- GitLab From 5ddddfda53a346d8058ccc25a44b796fbeb46348 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 3 Dec 2016 15:57:10 -0800 Subject: [PATCH 082/398] Correct use of specifiesCoordinate --- src/MissionManager/MissionController.cc | 46 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 1e19e2e89..03bf70e4e 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -1208,31 +1208,43 @@ double MissionController::_normalizeLon(double lon) /// Add the home position item to the front of the list void MissionController::_addPlannedHomePosition(QmlObjectListModel* visualItems, bool addToCenter) { + bool homePositionSet = false; + SimpleMissionItem* homeItem = new SimpleMissionItem(_activeVehicle, this); visualItems->insert(0, homeItem); if (visualItems->count() > 1 && addToCenter) { - VisualMissionItem* item = qobject_cast(visualItems->get(1)); - - double north = _normalizeLat(item->coordinate().latitude()); - double south = north; - double east = _normalizeLon(item->coordinate().longitude()); - double west = east; - - for (int i=2; icount(); i++) { - item = qobject_cast(visualItems->get(i)); + double north, south, east, west; + bool firstCoordSet = false; - double lat = _normalizeLat(item->coordinate().latitude()); - double lon = _normalizeLon(item->coordinate().longitude()); + for (int i=1; icount(); i++) { + VisualMissionItem* item = qobject_cast(visualItems->get(i)); + + if (item->specifiesCoordinate()) { + if (firstCoordSet) { + double lat = _normalizeLat(item->coordinate().latitude()); + double lon = _normalizeLon(item->coordinate().longitude()); + north = fmax(north, lat); + south = fmin(south, lat); + east = fmax(east, lon); + west = fmin(west, lon); + } else { + firstCoordSet = true; + north = _normalizeLat(item->coordinate().latitude()); + south = north; + east = _normalizeLon(item->coordinate().longitude()); + west = east; + } + } + } - north = fmax(north, lat); - south = fmin(south, lat); - east = fmax(east, lon); - west = fmin(west, lon); + if (firstCoordSet) { + homePositionSet = true; + homeItem->setCoordinate(QGeoCoordinate((south + ((north - south) / 2)) - 90.0, (west + ((east - west) / 2)) - 180.0, 0.0)); } + } - homeItem->setCoordinate(QGeoCoordinate((south + ((north - south) / 2)) - 90.0, (west + ((east - west) / 2)) - 180.0, 0.0)); - } else { + if (!homePositionSet) { homeItem->setCoordinate(qgcApp()->lastKnownHomePosition()); } } -- GitLab From f2f1a0d55b1d6535174e07526c8aee243ca48d78 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 3 Dec 2016 16:06:15 -0800 Subject: [PATCH 083/398] Fix z-order --- src/FlightDisplay/FlightDisplayViewMap.qml | 1 - src/MissionEditor/MissionEditor.qml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index 2d631ecec..69cc038a5 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -169,7 +169,6 @@ FlightMap { anchors.rightMargin: ScreenTools.defaultFontPixelHeight * (0.33) anchors.bottom: parent.bottom anchors.right: parent.right - z: QGroundControl.zOrderWidgets mapControl: flightMap visible: !ScreenTools.isTinyScreen } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 3c82b8418..fadd6595f 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -1054,7 +1054,6 @@ QGCView { anchors.margins: ScreenTools.defaultFontPixelHeight * (0.66) anchors.bottom: waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom anchors.left: parent.left - z: QGroundControl.zOrderWidgets mapControl: editorMap visible: !ScreenTools.isTinyScreen } -- GitLab From 6267f16b04949e92dfb6287c14d16cecb5e19410 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 3 Dec 2016 16:36:48 -0800 Subject: [PATCH 084/398] Default decimal places for PID params --- src/FirmwarePlugin/APM/APMParameterMetaData.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index 30032c2f2..929fe9e5e 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -568,6 +568,15 @@ void APMParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType) } } + // ArduPilot does not yet support decimal places meta data. So for P/I/D parameters we force to 6 places + if ((fact->name().endsWith(QStringLiteral("_P")) || + fact->name().endsWith(QStringLiteral("_I")) || + fact->name().endsWith(QStringLiteral("_D"))) && + (fact->type() == FactMetaData::valueTypeFloat || + fact->type() == FactMetaData::valueTypeDouble)) { + metaData->setDecimalPlaces(6); + } + fact->setMetaData(metaData); } -- GitLab From bee16a671b318d90d1995bfecc1ea14943eb6f03 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 3 Dec 2016 20:52:45 -0500 Subject: [PATCH 085/398] Switch Bluetooth connection from a generic Rfcomm protocol to a specific SerialPort service. --- src/comm/BluetoothLink.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc index 9ed804ed2..93bc9b8de 100644 --- a/src/comm/BluetoothLink.cc +++ b/src/comm/BluetoothLink.cc @@ -150,7 +150,7 @@ bool BluetoothLink::_hardwareConnect() _discoveryAgent->start(); #else _createSocket(); - _targetSocket->connectToService(QBluetoothAddress(_config->device().address), QBluetoothUuid(QBluetoothUuid::Rfcomm)); + _targetSocket->connectToService(QBluetoothAddress(_config->device().address), QBluetoothUuid(QBluetoothUuid::SerialPort)); #endif return true; } @@ -352,9 +352,17 @@ void BluetoothConfiguration::startScan() void BluetoothConfiguration::deviceDiscovered(QBluetoothDeviceInfo info) { - //print_device_info(info); if(!info.name().isEmpty() && info.isValid()) { +#if 0 + qDebug() << "Name: " << info.name(); + qDebug() << "Address: " << info.address().toString(); + qDebug() << "Service Classes:" << info.serviceClasses(); + QList uuids = info.serviceUuids(); + foreach (QBluetoothUuid uuid, uuids) { + qDebug() << "Service UUID: " << uuid.toString(); + } +#endif BluetoothData data; data.name = info.name(); #ifdef __ios__ -- GitLab From 1e0e17b55c1800dcbab81628330d5369489768eb Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 3 Dec 2016 18:11:13 -0800 Subject: [PATCH 086/398] Fix flag --- src/MissionManager/MissionManager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionManager/MissionManager.cc b/src/MissionManager/MissionManager.cc index 472dd453f..c41aed345 100644 --- a/src/MissionManager/MissionManager.cc +++ b/src/MissionManager/MissionManager.cc @@ -164,6 +164,7 @@ void MissionManager::requestMissionItems(void) } _retryCount = 0; + _readTransactionInProgress = true; emit inProgressChanged(true); _requestList(); } @@ -177,7 +178,6 @@ void MissionManager::_requestList(void) mavlink_mission_request_list_t request; _itemIndicesToRead.clear(); - _readTransactionInProgress = true; _clearMissionItems(); request.target_system = _vehicle->id(); -- GitLab From 2135a25d9ac49730833eb85caae42f42c35e2de7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 4 Dec 2016 11:52:28 -0800 Subject: [PATCH 087/398] Android build stop serial probe after first connect --- src/comm/LinkManager.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 34a0320fd..fb9056cbc 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -490,7 +490,16 @@ void LinkManager::_updateAutoConnectLinks(void) #ifndef NO_SERIAL_LINK QStringList currentPorts; - QList portList = QGCSerialPortInfo::availablePorts(); + QList portList; + +#ifdef __android__ + // Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial + // port is connected leaks file handles due to a bug somewhere in android serial code. In order to work around that + // bug after we connect the first serial port we stop probing for additional ports. + if (!_autoconnectConfigurations.count()) { + portList = QGCSerialPortInfo::availablePorts(); + } +#endif // Iterate Comm Ports foreach (QGCSerialPortInfo portInfo, portList) { @@ -601,6 +610,13 @@ void LinkManager::_updateAutoConnectLinks(void) } } +#ifndef __android__ + // Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial + // port is connected leaks file handles due to a bug somewhere in android serial code. In order to work around that + // bug after we connect the first serial port we stop probing for additional ports. The means we must rely on + // the port disconnecting itself when the radio is pulled to signal communication list as opposed to automatically + // closing the Link. + // Now we go through the current configuration list and make sure any dynamic config has gone away QList _confToDelete; for (int i=0; i<_autoconnectConfigurations.count(); i++) { @@ -633,6 +649,7 @@ void LinkManager::_updateAutoConnectLinks(void) } delete pDeleteConfig; } +#endif #endif // NO_SERIAL_LINK } -- GitLab From d84dd7e42da6aeb91b9b6068896e689ef921bd52 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 4 Dec 2016 16:17:14 -0800 Subject: [PATCH 088/398] New Vehicle::sendMavCommand - Queued MAV_CMD with retry --- src/AutoPilotPlugins/APM/APMCompassCal.cc | 5 +- .../Common/ESP8266ComponentController.cc | 18 +- .../Common/ESP8266ComponentController.h | 2 +- .../PX4/AirframeComponentController.cc | 2 +- .../APM/ArduCopterFirmwarePlugin.cc | 28 +-- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 177 +++++---------- src/Vehicle/MAVLinkLogManager.cc | 66 +----- src/Vehicle/MAVLinkLogManager.h | 5 +- src/Vehicle/Vehicle.cc | 214 +++++++++--------- src/Vehicle/Vehicle.h | 32 ++- .../CustomCommandWidgetController.cc | 18 +- .../CustomCommandWidgetController.h | 3 +- src/uas/UAS.cc | 27 --- src/uas/UAS.h | 3 - src/uas/UASInterface.h | 4 - 15 files changed, 244 insertions(+), 360 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMCompassCal.cc b/src/AutoPilotPlugins/APM/APMCompassCal.cc index 42939faee..88dd8a67b 100644 --- a/src/AutoPilotPlugins/APM/APMCompassCal.cc +++ b/src/AutoPilotPlugins/APM/APMCompassCal.cc @@ -145,7 +145,10 @@ CalWorkerThread::calibrate_return CalWorkerThread::calibrate(void) sensorId = 6.0f; } if (sensorId != 0.0f) { - _vehicle->doCommandLong(_vehicle->defaultComponentId(), MAV_CMD_PREFLIGHT_SET_SENSOR_OFFSETS, sensorId, -sphere_x[cur_mag], -sphere_y[cur_mag], -sphere_z[cur_mag]); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), + MAV_CMD_PREFLIGHT_SET_SENSOR_OFFSETS, + true, /* showErrors */ + sensorId, -sphere_x[cur_mag], -sphere_y[cur_mag], -sphere_z[cur_mag]); } } } diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc index 206b54bcd..c454a6120 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc @@ -38,7 +38,7 @@ ESP8266ComponentController::ESP8266ComponentController() _baudRates.append("460800"); _baudRates.append("921600"); connect(&_timer, &QTimer::timeout, this, &ESP8266ComponentController::_processTimeout); - connect(_vehicle, &Vehicle::commandLongAck, this, &ESP8266ComponentController::_commandAck); + connect(_vehicle, &Vehicle::mavCommandResult, this, &ESP8266ComponentController::_mavCommandResult); Fact* ssid = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSID4"); connect(ssid, &Fact::valueChanged, this, &ESP8266ComponentController::_ssidChanged); Fact* paswd = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_PASSWORD4"); @@ -381,21 +381,23 @@ ESP8266ComponentController::_processTimeout() //----------------------------------------------------------------------------- void -ESP8266ComponentController::_commandAck(uint8_t compID, uint16_t command, uint8_t result) +ESP8266ComponentController::_mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle) { - if(compID == MAV_COMP_ID_UDP_BRIDGE) { - if(result != MAV_RESULT_ACCEPTED) { + Q_UNUSED(vehicleId); + Q_UNUSED(noReponseFromVehicle); + + if (component == MAV_COMP_ID_UDP_BRIDGE) { + if (result != MAV_RESULT_ACCEPTED) { qWarning() << "ESP8266ComponentController command" << command << "rejected."; return; } - if((_waitType == WAIT_FOR_REBOOT && command == MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN) || - (_waitType == WAIT_FOR_RESTORE && command == MAV_CMD_PREFLIGHT_STORAGE)) - { + if ((_waitType == WAIT_FOR_REBOOT && command == MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN) || + (_waitType == WAIT_FOR_RESTORE && command == MAV_CMD_PREFLIGHT_STORAGE)) { _timer.stop(); _waitType = WAIT_FOR_NOTHING; emit busyChanged(); qCDebug(ESP8266ComponentControllerLog) << "_commandAck for" << command; - if(command == MAV_CMD_PREFLIGHT_STORAGE) { + if (command == MAV_CMD_PREFLIGHT_STORAGE) { _vehicle->parameterManager()->refreshAllParameters(MAV_COMP_ID_UDP_BRIDGE); } } diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h index d0b82db40..64c831fb1 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h @@ -83,7 +83,7 @@ signals: private slots: void _processTimeout (); - void _commandAck (uint8_t compID, uint16_t command, uint8_t result); + void _mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); void _ssidChanged (QVariant value); void _passwordChanged (QVariant value); void _baudChanged (QVariant value); diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index d656a998c..bbcb97b47 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -117,7 +117,7 @@ void AirframeComponentController::_waitParamWriteSignal(QVariant value) void AirframeComponentController::_rebootAfterStackUnwind(void) { - _uas->executeCommand(MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, 1, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true /* showError */, 1.0f); qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); for (unsigned i = 0; i < 2000; i++) { QGC::SLEEP::usleep(500); diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index d9bc3b4e8..dd9350fa6 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -125,29 +125,11 @@ void ArduCopterFirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitu return; } - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_NAV_TAKEOFF; - cmd.confirmation = 0; - cmd.param1 = 0.0f; - cmd.param2 = 0.0f; - cmd.param3 = 0.0f; - cmd.param4 = 0.0f; - cmd.param5 = 0.0f; - cmd.param6 = 0.0f; - cmd.param7 = vehicle->altitudeAMSL()->rawValue().toFloat() + altitudeRel; // AMSL meters - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_NAV_TAKEOFF, + true, // show error + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + vehicle->altitudeAMSL()->rawValue().toFloat() + altitudeRel); // AMSL meters } void ArduCopterFirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 9644e8a83..16d04d301 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -283,30 +283,16 @@ QObject* PX4FirmwarePlugin::loadParameterMetaData(const QString& metaDataFile) void PX4FirmwarePlugin::pauseVehicle(Vehicle* vehicle) { - // then tell it to loiter at the current position - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; - cmd.confirmation = 0; - cmd.param1 = -1.0f; - cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; - cmd.param3 = 0.0f; - cmd.param4 = NAN; - cmd.param5 = NAN; - cmd.param6 = NAN; - cmd.param7 = NAN; - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_DO_REPOSITION, + true, // show error if failed + -1.0f, + MAV_DO_REPOSITION_FLAGS_CHANGE_MODE, + 0.0f, + NAN, + NAN, + NAN, + NAN); } void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle) @@ -321,131 +307,82 @@ void PX4FirmwarePlugin::guidedModeLand(Vehicle* vehicle) void PX4FirmwarePlugin::guidedModeOrbit(Vehicle* vehicle, const QGeoCoordinate& centerCoord, double radius, double velocity, double altitude) { - //-- If not in "guided" mode, make it so. - if(!isGuidedMode(vehicle)) + if (!isGuidedMode(vehicle)) { setGuidedMode(vehicle, true); - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - mavlink_message_t msg; - mavlink_command_long_t cmd; - cmd.command = (uint16_t)MAV_CMD_SET_GUIDED_SUBMODE_CIRCLE; - cmd.confirmation = 0; - cmd.param1 = radius; - cmd.param2 = velocity; - cmd.param3 = altitude; - cmd.param4 = NAN; - cmd.param5 = centerCoord.isValid() ? centerCoord.latitude() : NAN; - cmd.param6 = centerCoord.isValid() ? centerCoord.longitude() : NAN; - cmd.param7 = centerCoord.isValid() ? centerCoord.altitude() : NAN; - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + } + + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_SET_GUIDED_SUBMODE_CIRCLE, + true, // show error if fails + radius, + velocity, + altitude, + NAN, + centerCoord.isValid() ? centerCoord.latitude() : NAN, + centerCoord.isValid() ? centerCoord.longitude() : NAN, + centerCoord.isValid() ? centerCoord.altitude() : NAN); } void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) { Q_UNUSED(altitudeRel); if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) { - qgcApp()->showMessage(QStringLiteral("Unable to takeoff, vehicle position not known.")); + qgcApp()->showMessage(tr("Unable to takeoff, vehicle position not known.")); return; } - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - - // Set destination altitude - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_NAV_TAKEOFF; - cmd.confirmation = 0; - cmd.param1 = -1.0f; - cmd.param2 = 0.0f; - cmd.param3 = 0.0f; - cmd.param4 = NAN; - cmd.param5 = NAN; - cmd.param6 = NAN; - cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble() + altitudeRel; - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_NAV_TAKEOFF, + true, // show error is fails + -1.0f, + 0.0f, + 0.0f, + NAN, + NAN, + NAN, + vehicle->altitudeAMSL()->rawValue().toDouble() + altitudeRel); } void PX4FirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord) { if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) { - qgcApp()->showMessage(QStringLiteral("Unable to go to location, vehicle position not known.")); + qgcApp()->showMessage(tr("Unable to go to location, vehicle position not known.")); return; } - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; - cmd.confirmation = 0; - cmd.param1 = -1.0f; - cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; - cmd.param3 = 0.0f; - cmd.param4 = NAN; - cmd.param5 = gotoCoord.latitude(); - cmd.param6 = gotoCoord.longitude(); - cmd.param7 = vehicle->altitudeAMSL()->rawValue().toDouble(); - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_DO_REPOSITION, + true, // show error is fails + -1.0f, + MAV_DO_REPOSITION_FLAGS_CHANGE_MODE, + 0.0f, + NAN, + gotoCoord.latitude(), + gotoCoord.longitude(), + vehicle->altitudeAMSL()->rawValue().toFloat()); } void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitudeRel) { if (!vehicle->homePositionAvailable()) { - qgcApp()->showMessage(QStringLiteral("Unable to change altitude, home position unknown.")); + qgcApp()->showMessage(tr("Unable to change altitude, home position unknown.")); return; } if (qIsNaN(vehicle->homePosition().altitude())) { - qgcApp()->showMessage(QStringLiteral("Unable to change altitude, home position altitude unknown.")); + qgcApp()->showMessage(tr("Unable to change altitude, home position altitude unknown.")); return; } - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_DO_REPOSITION; - cmd.confirmation = 0; - cmd.param1 = -1.0f; - cmd.param2 = MAV_DO_REPOSITION_FLAGS_CHANGE_MODE; - cmd.param3 = 0.0f; - cmd.param4 = NAN; - cmd.param5 = NAN; - cmd.param6 = NAN; - cmd.param7 = vehicle->homePosition().altitude() + altitudeRel; - cmd.target_system = vehicle->id(); - cmd.target_component = vehicle->defaultComponentId(); - - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - vehicle->sendMessageOnLink(vehicle->priorityLink(), msg); + vehicle->sendMavCommand(vehicle->defaultComponentId(), + MAV_CMD_DO_REPOSITION, + true, // show error is fails + -1.0f, + MAV_DO_REPOSITION_FLAGS_CHANGE_MODE, + 0.0f, + NAN, + NAN, + NAN, + vehicle->homePosition().altitude() + altitudeRel); } void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index 72e2d6e6e..5014150e0 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -19,8 +19,6 @@ #include #include -#define kTimeOutMilliseconds 1000 - QGC_LOGGING_CATEGORY(MAVLinkLogManagerLog, "MAVLinkLogManagerLog") static const char* kEmailAddressKey = "MAVLinkLogEmail"; @@ -301,7 +299,6 @@ MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) , _loggingDisabled(false) , _logProcessor(NULL) , _deleteAfterUpload(false) - , _loggingCmdTryCount(0) { //-- Get saved settings QSettings settings; @@ -347,7 +344,6 @@ MAVLinkLogManager::setToolbox(QGCToolbox* toolbox) qmlRegisterUncreatableType("QGroundControl.MAVLinkLogManager", 1, 0, "MAVLinkLogManager", "Reference only"); if(!_loggingDisabled) { connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MAVLinkLogManager::_activeVehicleChanged); - connect(&_ackTimer, &QTimer::timeout, this, &MAVLinkLogManager::_processCmdAck); } } @@ -540,8 +536,6 @@ MAVLinkLogManager::startLogging() if(_createNewLog()) { _vehicle->startMavlinkLog(); _logRunning = true; - _loggingCmdTryCount = 0; - _ackTimer.start(kTimeOutMilliseconds); emit logRunningChanged(); } } @@ -570,11 +564,6 @@ MAVLinkLogManager::stopLogging() delete _logProcessor; _logProcessor = NULL; _logRunning = false; - if(_vehicle) { - //-- Setup a timer to make sure vehicle received the command - _loggingCmdTryCount = 0; - _ackTimer.start(kTimeOutMilliseconds); - } emit logRunningChanged(); } } @@ -742,9 +731,9 @@ MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // For now, we only handle one log download at a time. // Disconnect the previous one (if any) if(_vehicle) { - disconnect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); - disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); - disconnect(_vehicle, &Vehicle::commandLongAck, this, &MAVLinkLogManager::_commandLongAck); + disconnect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); + disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); + disconnect(_vehicle, &Vehicle::mavCommandResult, this, &MAVLinkLogManager::_mavCommandResult); _vehicle = NULL; //-- Stop logging (if that's the case) stopLogging(); @@ -753,51 +742,17 @@ MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // Connect new system if(vehicle) { _vehicle = vehicle; - connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); - connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); - connect(_vehicle, &Vehicle::commandLongAck, this, &MAVLinkLogManager::_commandLongAck); + connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); + connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); + connect(_vehicle, &Vehicle::mavCommandResult, this, &MAVLinkLogManager::_mavCommandResult); emit canStartLogChanged(); } } -//----------------------------------------------------------------------------- -void -MAVLinkLogManager::_processCmdAck() -{ - if(_loggingCmdTryCount++ > 3) { - _ackTimer.stop(); - //-- Give up - if(_logRunning) { - qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command had no response."; - _discardLog(); - } else { - qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command had no response."; - } - } else { - if(_vehicle) { - if(_logRunning) { - _vehicle->startMavlinkLog(); - qCWarning(MAVLinkLogManagerLog) << "Start MAVLink log command sent again."; - } else { - _vehicle->stopMavlinkLog(); - qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command sent again."; - } - _ackTimer.start(kTimeOutMilliseconds); - } else { - //-- Vehicle went away on us - _ackTimer.stop(); - } - } -} - //----------------------------------------------------------------------------- void MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system*/, uint8_t /*target_component*/, uint16_t sequence, uint8_t first_message, QByteArray data, bool /*acked*/) { - //-- Disable timer if we got a message before an ACK for the start command - if(_logRunning) { - _ackTimer.stop(); - } if(_logProcessor && _logProcessor->valid()) { if(!_logProcessor->processStreamData(sequence, first_message, data)) { qCCritical(MAVLinkLogManagerLog) << "Error writing MAVLink log file:" << _logProcessor->fileName(); @@ -814,12 +769,15 @@ MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system //----------------------------------------------------------------------------- void -MAVLinkLogManager::_commandLongAck(uint8_t /*compID*/, uint16_t command, uint8_t result) +MAVLinkLogManager::_mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle) { + Q_UNUSED(vehicleId); + Q_UNUSED(component); + Q_UNUSED(noReponseFromVehicle) + if(command == MAV_CMD_LOGGING_START || command == MAV_CMD_LOGGING_STOP) { - _ackTimer.stop(); //-- Did it fail? - if(result) { + if(result != MAV_RESULT_ACCEPTED) { if(command == MAV_CMD_LOGGING_STOP) { //-- Not that it could happen but... qCWarning(MAVLinkLogManagerLog) << "Stop MAVLink log command failed."; diff --git a/src/Vehicle/MAVLinkLogManager.h b/src/Vehicle/MAVLinkLogManager.h index f0319502b..ae8604121 100644 --- a/src/Vehicle/MAVLinkLogManager.h +++ b/src/Vehicle/MAVLinkLogManager.h @@ -172,8 +172,7 @@ private slots: void _activeVehicleChanged (Vehicle* vehicle); void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked); void _armedChanged (bool armed); - void _commandLongAck (uint8_t compID, uint16_t command, uint8_t result); - void _processCmdAck (); + void _mavCommandResult (int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); private: bool _sendLog (const QString& logFile); @@ -200,8 +199,6 @@ private: bool _loggingDisabled; MAVLinkLogProcessor* _logProcessor; bool _deleteAfterUpload; - int _loggingCmdTryCount; - QTimer _ackTimer; }; #endif diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 90ad8003d..76ec74bd4 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -174,12 +174,18 @@ Vehicle::Vehicle(LinkInterface* link, _prearmErrorTimer.setInterval(_prearmErrorTimeoutMSecs); _prearmErrorTimer.setSingleShot(true); - // Connection Lost time - _connectionLostTimer.setInterval(Vehicle::_connectionLostTimeoutMSecs); + // Connection Lost timer + _connectionLostTimer.setInterval(_connectionLostTimeoutMSecs); _connectionLostTimer.setSingleShot(false); _connectionLostTimer.start(); connect(&_connectionLostTimer, &QTimer::timeout, this, &Vehicle::_connectionLostTimeout); + // Send MAV_CMD ack timer + _mavCommandAckTimer.setSingleShot(true); + _mavCommandAckTimer.setInterval(_mavCommandAckTimeoutMSecs); + _mavCommandAckTimer.setSingleShot(false); + connect(&_mavCommandAckTimer, &QTimer::timeout, this, &Vehicle::_sendMavCommandAgain); + _mav = uas(); // Listen for system messages @@ -212,23 +218,11 @@ Vehicle::Vehicle(LinkInterface* link, _rallyPointManager = _firmwarePlugin->newRallyPointManager(this); connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); - // Ask the vehicle for firmware version info. This must be MAV_COMP_ID_ALL since we don't know default component id yet. - - mavlink_message_t versionMsg; - mavlink_command_long_t versionCmd; - - versionCmd.command = MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES; - versionCmd.confirmation = 0; - versionCmd.param1 = 1; // Request firmware version - versionCmd.param2 = versionCmd.param3 = versionCmd.param4 = versionCmd.param5 = versionCmd.param6 = versionCmd.param7 = 0; - versionCmd.target_system = id(); - versionCmd.target_component = MAV_COMP_ID_ALL; - mavlink_msg_command_long_encode_chan(_mavlink->getSystemId(), - _mavlink->getComponentId(), - priorityLink()->mavlinkChannel(), - &versionMsg, - &versionCmd); - sendMessageMultiple(versionMsg); + // Ask the vehicle for firmware version info. + sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. + MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES, + false, // No error shown if fails + 1); // Request firmware version _firmwarePlugin->initializeVehicle(this); @@ -564,40 +558,41 @@ void Vehicle::_handleHilActuatorControls(mavlink_message_t &message) void Vehicle::_handleCommandAck(mavlink_message_t& message) { + bool showError = true; + mavlink_command_ack_t ack; mavlink_msg_command_ack_decode(&message, &ack); - emit commandLongAck(message.compid, ack.command, ack.result); + if (_mavCommandQueue.count() && ack.command == _mavCommandQueue[0].command) { + showError = _mavCommandQueue[0].showError; + _mavCommandQueue.removeFirst(); + } + + emit mavCommandResult(_id, message.compid, (MAV_CMD)ack.command, (MAV_RESULT)ack.result, false /* noResponsefromVehicle */); + + if (showError) { + QString commandName = qgcApp()->toolbox()->missionCommandTree()->friendlyName((MAV_CMD)ack.command); - // Disregard failures for these (handled above) - switch (ack.command) { - case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES: - case MAV_CMD_LOGGING_START: - case MAV_CMD_LOGGING_STOP: - return; + switch (ack.result) { + case MAV_RESULT_TEMPORARILY_REJECTED: + qgcApp()->showMessage(tr("%1 command temporarily rejected").arg(commandName)); + break; + case MAV_RESULT_DENIED: + qgcApp()->showMessage(tr("%1 command denied").arg(commandName)); + break; + case MAV_RESULT_UNSUPPORTED: + qgcApp()->showMessage(tr("%1 command not supported").arg(commandName)); + break; + case MAV_RESULT_FAILED: + qgcApp()->showMessage(tr("%1 command failed").arg(commandName)); + break; default: + // Do nothing break; + } } - QString commandName = qgcApp()->toolbox()->missionCommandTree()->friendlyName((MAV_CMD)ack.command); - - switch (ack.result) { - case MAV_RESULT_TEMPORARILY_REJECTED: - qgcApp()->showMessage(tr("%1 command temporarily rejected").arg(commandName)); - break; - case MAV_RESULT_DENIED: - qgcApp()->showMessage(tr("%1 command denied").arg(commandName)); - break; - case MAV_RESULT_UNSUPPORTED: - qgcApp()->showMessage(tr("%1 command not supported").arg(commandName)); - break; - case MAV_RESULT_FAILED: - qgcApp()->showMessage(tr("%1 command failed").arg(commandName)); - break; - default: - // Do nothing - break; - } + _sendNextQueuedMavCommand(); } void Vehicle::_handleExtendedSysState(mavlink_message_t& message) @@ -1303,29 +1298,10 @@ QGeoCoordinate Vehicle::homePosition(void) void Vehicle::setArmed(bool armed) { // We specifically use COMMAND_LONG:MAV_CMD_COMPONENT_ARM_DISARM since it is supported by more flight stacks. - - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_COMPONENT_ARM_DISARM; - cmd.confirmation = 0; - cmd.param1 = armed ? 1.0f : 0.0f; - cmd.param2 = 0.0f; - cmd.param3 = 0.0f; - cmd.param4 = 0.0f; - cmd.param5 = 0.0f; - cmd.param6 = 0.0f; - cmd.param7 = 0.0f; - cmd.target_system = id(); - cmd.target_component = defaultComponentId(); - - mavlink_msg_command_long_encode_chan(_mavlink->getSystemId(), - _mavlink->getComponentId(), - priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - sendMessageOnLink(priorityLink(), msg); + sendMavCommand(defaultComponentId(), + MAV_CMD_COMPONENT_ARM_DISARM, + true, // show error if fails + armed ? 1.0f : 0.0f); } bool Vehicle::flightModeSetAvailable(void) @@ -1812,27 +1788,11 @@ void Vehicle::setGuidedMode(bool guidedMode) void Vehicle::emergencyStop(void) { - mavlink_message_t msg; - mavlink_command_long_t cmd; - - cmd.command = (uint16_t)MAV_CMD_COMPONENT_ARM_DISARM; - cmd.confirmation = 0; - cmd.param1 = 0.0f; - cmd.param2 = 21196.0f; // Magic number for emergency stop - cmd.param3 = 0.0f; - cmd.param4 = 0.0f; - cmd.param5 = 0.0f; - cmd.param6 = 0.0f; - cmd.param7 = 0.0f; - cmd.target_system = id(); - cmd.target_component = defaultComponentId(); - mavlink_msg_command_long_encode_chan(_mavlink->getSystemId(), - _mavlink->getComponentId(), - priorityLink()->mavlinkChannel(), - &msg, - &cmd); - - sendMessageOnLink(priorityLink(), msg); + sendMavCommand(defaultComponentId(), + MAV_CMD_COMPONENT_ARM_DISARM, + true, // show error if fails + 0.0f, + 21196.0f); // Magic number for emergency stop } void Vehicle::setCurrentMissionSequence(int seq) @@ -1851,22 +1811,59 @@ void Vehicle::setCurrentMissionSequence(int seq) sendMessageOnLink(priorityLink(), msg); } -void Vehicle::doCommandLong(int component, MAV_CMD command, float param1, float param2, float param3, float param4, float param5, float param6, float param7) +void Vehicle::sendMavCommand(int component, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7) { + MavCommandQueueEntry_t entry; + + entry.component = component; + entry.command = command; + entry.showError = showError; + entry.rgParam[0] = param1; + entry.rgParam[1] = param2; + entry.rgParam[2] = param3; + entry.rgParam[3] = param4; + entry.rgParam[4] = param5; + entry.rgParam[5] = param6; + entry.rgParam[6] = param7; + + _mavCommandQueue.append(entry); + + if (_mavCommandQueue.count() == 1) { + _mavCommandRetryCount = 0; + _sendMavCommandAgain(); + } +} + +void Vehicle::_sendMavCommandAgain(void) +{ + MavCommandQueueEntry_t& queuedCommand = _mavCommandQueue[0]; + + if (_mavCommandRetryCount++ > _mavCommandMaxRetryCount) { + emit mavCommandResult(_id, queuedCommand.component, queuedCommand.command, MAV_RESULT_FAILED, true /* noResponsefromVehicle */); + if (queuedCommand.showError) { + qgcApp()->showMessage(tr("Vehicle did not respond to command: %1").arg(qgcApp()->toolbox()->missionCommandTree()->friendlyName(queuedCommand.command))); + } + _mavCommandQueue.removeFirst(); + _sendNextQueuedMavCommand(); + return; + } + + _mavCommandAckTimer.start(); + mavlink_message_t msg; mavlink_command_long_t cmd; - cmd.command = command; + cmd.command = queuedCommand.command; cmd.confirmation = 0; - cmd.param1 = param1; - cmd.param2 = param2; - cmd.param3 = param3; - cmd.param4 = param4; - cmd.param5 = param5; - cmd.param6 = param6; - cmd.param7 = param7; - cmd.target_system = id(); - cmd.target_component = component; + cmd.param1 = queuedCommand.rgParam[0]; + cmd.param2 = queuedCommand.rgParam[1]; + cmd.param3 = queuedCommand.rgParam[2]; + cmd.param4 = queuedCommand.rgParam[3]; + cmd.param5 = queuedCommand.rgParam[4]; + cmd.param6 = queuedCommand.rgParam[5]; + cmd.param7 = queuedCommand.rgParam[6]; + cmd.target_system = _id; + cmd.target_component = queuedCommand.component; mavlink_msg_command_long_encode_chan(_mavlink->getSystemId(), _mavlink->getComponentId(), priorityLink()->mavlinkChannel(), @@ -1876,6 +1873,15 @@ void Vehicle::doCommandLong(int component, MAV_CMD command, float param1, float sendMessageOnLink(priorityLink(), msg); } +void Vehicle::_sendNextQueuedMavCommand(void) +{ + if (_mavCommandQueue.count()) { + _mavCommandRetryCount = 0; + _sendMavCommandAgain(); + } +} + + void Vehicle::setPrearmError(const QString& prearmError) { _prearmError = prearmError; @@ -1921,7 +1927,7 @@ QString Vehicle::firmwareVersionTypeString(void) const void Vehicle::rebootVehicle() { - doCommandLong(defaultComponentId(), MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + sendMavCommand(defaultComponentId(), MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true, 1.0f); } int Vehicle::defaultComponentId(void) @@ -1941,7 +1947,7 @@ void Vehicle::setSoloFirmware(bool soloFirmware) // Temporarily removed, waiting for new command implementation void Vehicle::motorTest(int motor, int percent, int timeoutSecs) { - doCommandLong(defaultComponentId(), MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); + doCommandLongUnverified(defaultComponentId(), MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); } #endif @@ -2044,12 +2050,12 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject* parent) void Vehicle::startMavlinkLog() { - doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_START); + sendMavCommand(defaultComponentId(), MAV_CMD_LOGGING_START, false /* showError */); } void Vehicle::stopMavlinkLog() { - doCommandLong(defaultComponentId(), MAV_CMD_LOGGING_STOP); + sendMavCommand(defaultComponentId(), MAV_CMD_LOGGING_STOP, false /* showError */); } void Vehicle::_ackMavlinkLogData(uint16_t sequence) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 1ee3d62f6..2501a7acd 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -553,7 +553,13 @@ public: static const int cMaxRcChannels = 18; bool containsLink(LinkInterface* link) { return _links.contains(link); } - void doCommandLong(int component, MAV_CMD command, float param1 = 0.0f, float param2 = 0.0f, float param3 = 0.0f, float param4 = 0.0f, float param5 = 0.0f, float param6 = 0.0f, float param7 = 0.0f); + + /// Sends the specified MAV_CMD to the vehicle. If no Ack is received command will be retried. If a sendMavCommand is already in progress + /// the command will be queued and sent when the previous command completes. + /// @param component Component to send to + /// @param command MAV_CMD to send + /// @param showError true: Display error to user if command failed, false: no error shown + void sendMavCommand(int component, MAV_CMD command, bool showError, float param1 = 0.0f, float param2 = 0.0f, float param3 = 0.0f, float param4 = 0.0f, float param5 = 0.0f, float param6 = 0.0f, float param7 = 0.0f); int firmwareMajorVersion(void) const { return _firmwareMajorVersion; } int firmwareMinorVersion(void) const { return _firmwareMinorVersion; } @@ -609,7 +615,6 @@ signals: void flyingChanged(bool flying); void guidedModeChanged(bool guidedMode); void prearmErrorChanged(const QString& prearmError); - void commandLongAck(uint8_t compID, uint16_t command, uint8_t result); void soloFirmwareChanged(bool soloFirmware); void unhealthySensorsChanged(void); @@ -653,6 +658,14 @@ signals: // Mavlink Log Download void mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked); + /// Signalled in response to usage of sendMavCommand + /// @param vehicleId Vehicle which command was sent to + /// @param component Component which command was sent to + /// @param command MAV_CMD Command which was sent + /// @param result MAV_RESULT returned in ack + /// @param noResponseFromVehicle true: vehicle did not respond to command, false: vehicle responsed, MAV_RESULT in result + void mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); + private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); void _linkInactiveOrDeleted(LinkInterface* link); @@ -682,6 +695,7 @@ private slots: void _prearmErrorTimeout(void); void _newMissionItemsAvailable(void); void _newGeoFenceAvailable(void); + void _sendMavCommandAgain(void); private: bool _containsLink(LinkInterface* link); @@ -713,6 +727,7 @@ private: void _handleMavlinkLoggingData(mavlink_message_t& message); void _handleMavlinkLoggingDataAcked(mavlink_message_t& message); void _ackMavlinkLogData(uint16_t sequence); + void _sendNextQueuedMavCommand(void); private: int _id; ///< Mavlink system id @@ -765,6 +780,19 @@ private: uint32_t _onboardControlSensorsHealth; uint32_t _onboardControlSensorsUnhealthy; + typedef struct { + int component; + MAV_CMD command; + float rgParam[7]; + bool showError; + } MavCommandQueueEntry_t; + + QList _mavCommandQueue; + QTimer _mavCommandAckTimer; + int _mavCommandRetryCount; + static const int _mavCommandMaxRetryCount = 3; + static const int _mavCommandAckTimeoutMSecs = 1000; + QString _prearmError; QTimer _prearmErrorTimer; static const int _prearmErrorTimeoutMSecs = 35 * 1000; ///< Take away prearm error after 35 seconds diff --git a/src/ViewWidgets/CustomCommandWidgetController.cc b/src/ViewWidgets/CustomCommandWidgetController.cc index cfefb1ae9..b8791a5dc 100644 --- a/src/ViewWidgets/CustomCommandWidgetController.cc +++ b/src/ViewWidgets/CustomCommandWidgetController.cc @@ -21,10 +21,10 @@ const char* CustomCommandWidgetController::_settingsKey = "CustomCommand.QmlFile"; CustomCommandWidgetController::CustomCommandWidgetController(void) : - _uas(NULL) + _vehicle(NULL) { if(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()) { - _uas = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->uas(); + _vehicle = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle(); } QSettings settings; _customQmlFile = settings.value(_settingsKey).toString(); @@ -33,15 +33,21 @@ CustomCommandWidgetController::CustomCommandWidgetController(void) : void CustomCommandWidgetController::sendCommand(int commandId, QVariant componentId, QVariant confirm, QVariant param1, QVariant param2, QVariant param3, QVariant param4, QVariant param5, QVariant param6, QVariant param7) { - if(_uas) { - _uas->executeCommand((MAV_CMD)commandId, confirm.toInt(), param1.toFloat(), param2.toFloat(), param3.toFloat(), param4.toFloat(), param5.toFloat(), param6.toFloat(), param7.toFloat(), componentId.toInt()); + Q_UNUSED(confirm); + + if(_vehicle) { + _vehicle->sendMavCommand(componentId.toInt(), + (MAV_CMD)commandId, + true, // show error if fails + param1.toFloat(), param2.toFloat(), param3.toFloat(), param4.toFloat(), param5.toFloat(), param6.toFloat(), param7.toFloat()); } } void CustomCommandWidgetController::_activeVehicleChanged(Vehicle* activeVehicle) { - if(activeVehicle) - _uas = activeVehicle->uas(); + if (activeVehicle) { + _vehicle = activeVehicle; + } } void CustomCommandWidgetController::selectQmlFile(void) diff --git a/src/ViewWidgets/CustomCommandWidgetController.h b/src/ViewWidgets/CustomCommandWidgetController.h index 81f018e80..514e7a692 100644 --- a/src/ViewWidgets/CustomCommandWidgetController.h +++ b/src/ViewWidgets/CustomCommandWidgetController.h @@ -13,7 +13,6 @@ #include -#include "UASInterface.h" #include "AutoPilotPlugin.h" #include "FactPanelController.h" @@ -37,7 +36,7 @@ private slots: void _activeVehicleChanged (Vehicle* activeVehicle); private: - UASInterface* _uas; + Vehicle* _vehicle; QString _customQmlFile; static const char* _settingsKey; }; diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index e313e87aa..6d1a40f1a 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -1233,33 +1233,6 @@ void UAS::processParamValueMsg(mavlink_message_t& msg, const QString& paramName, emit parameterUpdate(uasId, compId, paramName, rawValue.param_count, rawValue.param_index, rawValue.param_type, paramValue); } -void UAS::executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component) -{ - if (!_vehicle) { - return; - } - - mavlink_message_t msg; - mavlink_command_long_t cmd; - cmd.command = (uint16_t)command; - cmd.confirmation = confirmation; - cmd.param1 = param1; - cmd.param2 = param2; - cmd.param3 = param3; - cmd.param4 = param4; - cmd.param5 = param5; - cmd.param6 = param6; - cmd.param7 = param7; - cmd.target_system = uasId; - cmd.target_component = component; - mavlink_msg_command_long_encode_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - &cmd); - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); -} - /** * Set the manual control commands. * This can only be done if the system has manual inputs enabled and is armed. diff --git a/src/uas/UAS.h b/src/uas/UAS.h index 1e93a0670..ad9b94646 100644 --- a/src/uas/UAS.h +++ b/src/uas/UAS.h @@ -472,9 +472,6 @@ public: void requestImage(); public slots: - /** @brief Executes a command with 7 params */ - void executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component); - /** @brief Order the robot to pair its receiver **/ void pairRX(int rxType, int rxSubType); diff --git a/src/uas/UASInterface.h b/src/uas/UASInterface.h index 10ad42fe8..8ae866f9b 100644 --- a/src/uas/UASInterface.h +++ b/src/uas/UASInterface.h @@ -141,10 +141,6 @@ public: virtual void stopBusConfig(void) = 0; public slots: - - /** @brief Executes a command **/ - virtual void executeCommand(MAV_CMD command, int confirmation, float param1, float param2, float param3, float param4, float param5, float param6, float param7, int component) = 0; - /** @brief Order the robot to pair its receiver **/ virtual void pairRX(int rxType, int rxSubType) = 0; -- GitLab From 159e12cd4e34f045034bfed9121816195ef29c7e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:08:11 -0800 Subject: [PATCH 089/398] Update to new mavCommandResult signature --- src/AutoPilotPlugins/Common/ESP8266ComponentController.cc | 2 +- src/AutoPilotPlugins/Common/ESP8266ComponentController.h | 2 +- src/Vehicle/MAVLinkLogManager.cc | 2 +- src/Vehicle/MAVLinkLogManager.h | 2 +- src/Vehicle/Vehicle.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc index c454a6120..4ba9430c7 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc @@ -381,7 +381,7 @@ ESP8266ComponentController::_processTimeout() //----------------------------------------------------------------------------- void -ESP8266ComponentController::_mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle) +ESP8266ComponentController::_mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle) { Q_UNUSED(vehicleId); Q_UNUSED(noReponseFromVehicle); diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h index 64c831fb1..afba9939c 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h @@ -83,7 +83,7 @@ signals: private slots: void _processTimeout (); - void _mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); + void _mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle); void _ssidChanged (QVariant value); void _passwordChanged (QVariant value); void _baudChanged (QVariant value); diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index 5014150e0..567b7d935 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -769,7 +769,7 @@ MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system //----------------------------------------------------------------------------- void -MAVLinkLogManager::_mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle) +MAVLinkLogManager::_mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle) { Q_UNUSED(vehicleId); Q_UNUSED(component); diff --git a/src/Vehicle/MAVLinkLogManager.h b/src/Vehicle/MAVLinkLogManager.h index ae8604121..dd1051542 100644 --- a/src/Vehicle/MAVLinkLogManager.h +++ b/src/Vehicle/MAVLinkLogManager.h @@ -172,7 +172,7 @@ private slots: void _activeVehicleChanged (Vehicle* vehicle); void _mavlinkLogData (Vehicle* vehicle, uint8_t target_system, uint8_t target_component, uint16_t sequence, uint8_t first_message, QByteArray data, bool acked); void _armedChanged (bool armed); - void _mavCommandResult (int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); + void _mavCommandResult (int vehicleId, int component, int command, int result, bool noReponseFromVehicle); private: bool _sendLog (const QString& logFile); diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 2501a7acd..fd19666a2 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -664,7 +664,7 @@ signals: /// @param command MAV_CMD Command which was sent /// @param result MAV_RESULT returned in ack /// @param noResponseFromVehicle true: vehicle did not respond to command, false: vehicle responsed, MAV_RESULT in result - void mavCommandResult(int vehicleId, int component, MAV_CMD command, MAV_RESULT result, bool noReponseFromVehicle); + void mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle); private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); -- GitLab From b2835e7d537c344f635b7c26496e28e1c7890acb Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:08:25 -0800 Subject: [PATCH 090/398] Fix timer stop bug --- src/Vehicle/Vehicle.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 76ec74bd4..79b61d2ad 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -564,11 +564,12 @@ void Vehicle::_handleCommandAck(mavlink_message_t& message) mavlink_msg_command_ack_decode(&message, &ack); if (_mavCommandQueue.count() && ack.command == _mavCommandQueue[0].command) { + _mavCommandAckTimer.stop(); showError = _mavCommandQueue[0].showError; _mavCommandQueue.removeFirst(); } - emit mavCommandResult(_id, message.compid, (MAV_CMD)ack.command, (MAV_RESULT)ack.result, false /* noResponsefromVehicle */); + emit mavCommandResult(_id, message.compid, ack.command, ack.result, false /* noResponsefromVehicle */); if (showError) { QString commandName = qgcApp()->toolbox()->missionCommandTree()->friendlyName((MAV_CMD)ack.command); -- GitLab From a3b4f2fee900aa9ee709f8c23e9d8bc8cfa753a7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:08:39 -0800 Subject: [PATCH 091/398] Vehicle::sendMavCommand unit test --- qgroundcontrol.pro | 2 + src/Vehicle/SendMavCommandTest.cc | 151 ++++++++++++++++++++++++++++++ src/Vehicle/SendMavCommandTest.h | 31 ++++++ src/comm/MockLink.cc | 34 +++++++ src/qgcunittest/UnitTestList.cc | 2 + 5 files changed, 220 insertions(+) create mode 100644 src/Vehicle/SendMavCommandTest.cc create mode 100644 src/Vehicle/SendMavCommandTest.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 85adc4fab..c0cd800d1 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -381,6 +381,7 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/qgcunittest/TCPLinkTest.h \ src/qgcunittest/TCPLoopBackServer.h \ src/qgcunittest/UnitTest.h \ + src/Vehicle/SendMavCommandTest.h \ SOURCES += \ src/AnalyzeView/LogDownloadTest.cc \ @@ -409,6 +410,7 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/qgcunittest/TCPLoopBackServer.cc \ src/qgcunittest/UnitTest.cc \ src/qgcunittest/UnitTestList.cc \ + src/Vehicle/SendMavCommandTest.cc \ } } } } } } # Main QGC Headers and Source files diff --git a/src/Vehicle/SendMavCommandTest.cc b/src/Vehicle/SendMavCommandTest.cc new file mode 100644 index 000000000..850bcc980 --- /dev/null +++ b/src/Vehicle/SendMavCommandTest.cc @@ -0,0 +1,151 @@ +/**************************************************************************** + * + * (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 "SendMavCommandTest.h" +#include "MultiVehicleManager.h" +#include "QGCApplication.h" + +void SendMavCommandTest::_noFailure(void) +{ + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_1, true /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_1); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_ACCEPTED); + QCOMPARE(arguments.at(4).toBool(), false); +} + +void SendMavCommandTest::_failureShowError(void) +{ + // Will pop error about request failure + setExpectedMessageBox(QMessageBox::Ok); + + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_2, true /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_2); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_FAILED); + QCOMPARE(arguments.at(4).toBool(), false); + + // User should have been notified + checkExpectedMessageBox(); +} + +void SendMavCommandTest::_failureNoShowError(void) +{ + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_2, false /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_2); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_FAILED); + QCOMPARE(arguments.at(4).toBool(), false); +} + +void SendMavCommandTest::_noFailureAfterRetry(void) +{ + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_3, true /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_3); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_ACCEPTED); + QCOMPARE(arguments.at(4).toBool(), false); +} + +void SendMavCommandTest::_failureAfterRetry(void) +{ + // Will pop error about request failure + setExpectedMessageBox(QMessageBox::Ok); + + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_4, true /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_4); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_FAILED); + QCOMPARE(arguments.at(4).toBool(), false); + + // User should have been notified + checkExpectedMessageBox(); +} + +void SendMavCommandTest::_failureAfterNoReponse(void) +{ + // Will pop error about request failure + setExpectedMessageBox(QMessageBox::Ok); + + _connectMockLink(); + + MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + Vehicle* vehicle = vehicleMgr->activeVehicle(); + QVERIFY(vehicle); + + vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_5, true /* showError */); + + QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); + QCOMPARE(spyResult.wait(10000), true); + QList arguments = spyResult.takeFirst(); + QCOMPARE(arguments.count(), 5); + QCOMPARE(arguments.at(0).toInt(), vehicle->id()); + QCOMPARE(arguments.at(2).toInt(), (int)MAV_CMD_USER_5); + QCOMPARE(arguments.at(3).toInt(), (int)MAV_RESULT_FAILED); + QCOMPARE(arguments.at(4).toBool(), true); + + // User should have been notified + checkExpectedMessageBox(); +} diff --git a/src/Vehicle/SendMavCommandTest.h b/src/Vehicle/SendMavCommandTest.h new file mode 100644 index 000000000..28624573b --- /dev/null +++ b/src/Vehicle/SendMavCommandTest.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * + * (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 SendMavCommandTest_H +#define SendMavCommandTest_H + +#include "UnitTest.h" + +class SendMavCommandTest : public UnitTest +{ + Q_OBJECT + +private slots: + void _noFailure(void); + void _failureShowError(void); + void _failureNoShowError(void); + void _noFailureAfterRetry(void); + void _failureAfterRetry(void); + void _failureAfterNoReponse(void); + +private: +}; + +#endif diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index 4509928d7..411287c4d 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -807,6 +807,40 @@ void MockLink::_handleCommandLong(const mavlink_message_t& msg) commandResult = MAV_RESULT_ACCEPTED; _respondWithAutopilotVersion(); break; + case MAV_CMD_USER_1: + // Test command which always returns MAV_RESULT_ACCEPTED + commandResult = MAV_RESULT_ACCEPTED; + break; + case MAV_CMD_USER_2: + // Test command which always returns MAV_RESULT_FAILED + commandResult = MAV_RESULT_FAILED; + break; + case MAV_CMD_USER_3: + // Test command which returns MAV_RESULT_ACCEPTED on second attempt + static bool firstCmdUser3 = true; + if (firstCmdUser3) { + firstCmdUser3 = false; + return; + } else { + firstCmdUser3 = true; + commandResult = MAV_RESULT_ACCEPTED; + } + break; + case MAV_CMD_USER_4: + // Test command which returns MAV_RESULT_FAILED on second attempt + static bool firstCmdUser4 = true; + if (firstCmdUser4) { + firstCmdUser4 = false; + return; + } else { + firstCmdUser4 = true; + commandResult = MAV_RESULT_FAILED; + } + break; + case MAV_CMD_USER_5: + // No response + return; + break; } mavlink_message_t commandAck; diff --git a/src/qgcunittest/UnitTestList.cc b/src/qgcunittest/UnitTestList.cc index d0c70f8a7..924ae4024 100644 --- a/src/qgcunittest/UnitTestList.cc +++ b/src/qgcunittest/UnitTestList.cc @@ -31,6 +31,7 @@ #include "ParameterManagerTest.h" #include "MissionCommandTreeTest.h" #include "LogDownloadTest.h" +#include "SendMavCommandTest.h" UT_REGISTER_TEST(FactSystemTestGeneric) UT_REGISTER_TEST(FactSystemTestPX4) @@ -50,6 +51,7 @@ UT_REGISTER_TEST(TCPLinkTest) UT_REGISTER_TEST(ParameterManagerTest) UT_REGISTER_TEST(MissionCommandTreeTest) UT_REGISTER_TEST(LogDownloadTest) +UT_REGISTER_TEST(SendMavCommandTest) // List of unit test which are currently disabled. // If disabling a new test, include reason in comment. -- GitLab From eebbd613565c80ee3995a6de3d1c411f6450425f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:08:47 -0800 Subject: [PATCH 092/398] Fix typo --- src/FactSystem/ParameterManager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 2c8bf1bbe..2cec90baa 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -1066,7 +1066,7 @@ void ParameterManager::_initialRequestTimeout(void) } else { if (!_vehicle->genericFirmware()) { // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user - QString errorMsg = tr("Vehicle %1 did not respond to request for parameters" + QString errorMsg = tr("Vehicle %1 did not respond to request for parameters. " "This will cause QGroundControl to be unable to display its full user interface.").arg(_vehicle->id()); qCDebug(ParameterManagerLog) << errorMsg; qgcApp()->showMessage(errorMsg); -- GitLab From 15d2036756ffa5fe21109fdafe906b77b549fbb2 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:40:46 -0800 Subject: [PATCH 093/398] Update fence visibility flags --- src/FirmwarePlugin/APM/APMGeoFenceManager.cc | 111 +++++++------------ src/FirmwarePlugin/APM/APMGeoFenceManager.h | 36 +++--- src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc | 15 +-- src/FirmwarePlugin/PX4/PX4GeoFenceManager.h | 12 +- src/FlightDisplay/FlightDisplayViewMap.qml | 8 +- src/MissionEditor/MissionEditor.qml | 14 +-- src/MissionManager/GeoFenceController.cc | 59 ++++------ src/MissionManager/GeoFenceController.h | 63 ++++++----- src/MissionManager/GeoFenceManager.h | 29 ++--- 9 files changed, 143 insertions(+), 204 deletions(-) diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc index a2562dd27..f65fb476c 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc @@ -21,14 +21,15 @@ const char* APMGeoFenceManager::_fenceEnableParam = "FENCE_ENABLE"; APMGeoFenceManager::APMGeoFenceManager(Vehicle* vehicle) : GeoFenceManager(vehicle) , _fenceSupported(false) - , _breachReturnSupported(vehicle->fixedWing()) - , _circleSupported(false) - , _polygonSupported(false) + , _breachReturnEnabled(vehicle->fixedWing()) + , _circleEnabled(false) + , _polygonEnabled(false) , _firstParamLoadComplete(false) - , _circleRadiusFact(NULL) , _readTransactionInProgress(false) , _writeTransactionInProgress(false) , _fenceTypeFact(NULL) + , _fenceEnableFact(NULL) + , _circleRadiusFact(NULL) { connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &APMGeoFenceManager::_mavlinkMessageReceived); connect(_vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &APMGeoFenceManager::_parametersReady); @@ -54,15 +55,15 @@ void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const return; } - if (!_geoFenceSupported()) { + if (!_fenceSupported) { return; } // Validate - int validatedPolygonCount = polygon.count(); - if (polygonSupported()) { - if (polygon.count() < 3) { - validatedPolygonCount = 0; + int validatedPolygonCount = 0; + if (polygonEnabled()) { + if (polygon.count() >= 3) { + validatedPolygonCount = polygon.count(); } if (polygon.count() > std::numeric_limits::max()) { _sendError(TooManyPoints, QStringLiteral("Geo-Fence polygon has too many points: %1.").arg(_polygon.count())); @@ -71,7 +72,11 @@ void APMGeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, const } _breachReturnPoint = breachReturn; - _polygon = polygon; + if (validatedPolygonCount) { + _polygon = polygon; + } else { + _polygon.clear(); + } // Total point count, +1 polygon close in last index, +1 for breach in index 0 _cWriteFencePoints = validatedPolygonCount ? validatedPolygonCount + 1 + 1 : 0; @@ -94,7 +99,7 @@ void APMGeoFenceManager::loadFromVehicle(void) _breachReturnPoint = QGeoCoordinate(); _polygon.clear(); - if (!_geoFenceSupported()) { + if (!_fenceSupported) { return; } @@ -217,57 +222,27 @@ bool APMGeoFenceManager::inProgress(void) const return _readTransactionInProgress || _writeTransactionInProgress; } -bool APMGeoFenceManager::_geoFenceSupported(void) +void APMGeoFenceManager::_updateEnabledFlags(void) { - // FIXME: MockLink doesn't support geo fence yet - if (qgcApp()->runningUnitTests()) { - return false; - } - - if (!_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceTotalParam) || - !_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceActionParam)) { - return false; + bool fenceEnabled; + if (_fenceEnableFact) { + fenceEnabled = _fenceEnableFact->rawValue().toBool(); } else { - return true; - } -} - -bool APMGeoFenceManager::fenceEnabled(void) const -{ - if (qgcApp()->runningUnitTests()) { - return false; - } - - if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceEnableParam)) { - bool fenceEnabled = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam)->rawValue().toBool(); - qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE available" << fenceEnabled; - return fenceEnabled; + fenceEnabled = true; } - qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE not available"; - return true; -} - -void APMGeoFenceManager::_fenceEnabledRawValueChanged(QVariant value) -{ - qCDebug(GeoFenceManagerLog) << "FENCE_ENABLE changed" << value.toBool(); - emit fenceEnabledChanged(!qgcApp()->runningUnitTests() && value.toBool()); -} - -void APMGeoFenceManager::_updateSupportedFlags(void) -{ - bool newCircleSupported = _fenceSupported && _vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 2); - if (newCircleSupported != _circleSupported) { - _circleSupported = newCircleSupported; - emit circleSupportedChanged(newCircleSupported); + bool newCircleEnabled = _fenceSupported && fenceEnabled && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 2); + if (newCircleEnabled != _circleEnabled) { + _circleEnabled = newCircleEnabled; + emit circleEnabledChanged(newCircleEnabled); } - bool newPolygonSupported = _fenceSupported && + bool newPolygonEnabled = _fenceSupported && fenceEnabled && ((_vehicle->multiRotor() && _fenceTypeFact && (_fenceTypeFact->rawValue().toInt() & 4)) || _vehicle->fixedWing()); - if (newPolygonSupported != _polygonSupported) { - _polygonSupported = newPolygonSupported; - emit polygonSupportedChanged(newPolygonSupported); + if (newPolygonEnabled != _polygonEnabled) { + _polygonEnabled = newPolygonEnabled; + emit polygonEnabledChanged(newPolygonEnabled); } } @@ -276,20 +251,21 @@ void APMGeoFenceManager::_parametersReady(void) if (!_firstParamLoadComplete) { _firstParamLoadComplete = true; - _fenceSupported = _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("FENCE_ACTION")); + _fenceSupported = _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("FENCE_ACTION")) && + !qgcApp()->runningUnitTests(); if (_fenceSupported) { QStringList paramNames; QStringList paramLabels; if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceEnableParam)) { - connect(_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam), &Fact::rawValueChanged, this, &APMGeoFenceManager::_fenceEnabledRawValueChanged); + _fenceEnableFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _fenceEnableParam); + connect(_fenceEnableFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateEnabledFlags); } if (_vehicle->multiRotor()) { _fenceTypeFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_TYPE")); - - connect(_fenceTypeFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateSupportedFlags); + connect(_fenceTypeFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_updateEnabledFlags); _circleRadiusFact = _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FENCE_RADIUS")); connect(_circleRadiusFact, &Fact::rawValueChanged, this, &APMGeoFenceManager::_circleRadiusRawValueChanged); @@ -319,18 +295,17 @@ void APMGeoFenceManager::_parametersReady(void) emit paramsChanged(_params); emit paramLabelsChanged(_paramLabels); - emit fenceSupportedChanged(_fenceSupported); - _updateSupportedFlags(); + _updateEnabledFlags(); } - qCDebug(GeoFenceManagerLog) << "fenceSupported:circleSupported:polygonSupported:breachReturnSupported" << - _fenceSupported << circleSupported() << polygonSupported() << _breachReturnSupported; + qCDebug(GeoFenceManagerLog) << "fenceSupported:circleEnabled:polygonEnabled:breachReturnEnabled" << + _fenceSupported << _circleEnabled << _polygonEnabled << _breachReturnEnabled; } } float APMGeoFenceManager::circleRadius(void) const { - if (_circleRadiusFact) { + if (_circleEnabled) { return _circleRadiusFact->rawValue().toFloat(); } else { return 0.0; @@ -342,16 +317,6 @@ void APMGeoFenceManager::_circleRadiusRawValueChanged(QVariant value) emit circleRadiusChanged(value.toFloat()); } -bool APMGeoFenceManager::circleSupported(void) const -{ - return _circleSupported; -} - -bool APMGeoFenceManager::polygonSupported(void) const -{ - return _polygonSupported; -} - QString APMGeoFenceManager::editorQml(void) const { return _vehicle->multiRotor() ? diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.h b/src/FirmwarePlugin/APM/APMGeoFenceManager.h index 8002de668..0540c3e68 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.h +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.h @@ -23,43 +23,37 @@ public: ~APMGeoFenceManager(); // Overrides from GeoFenceManager - bool inProgress (void) const final; - void loadFromVehicle (void) final; - void sendToVehicle (const QGeoCoordinate& breachReturn, const QList& polygon) final; - bool fenceSupported (void) const final { return _fenceSupported; } - bool fenceEnabled (void) const final; - bool circleSupported (void) const final; - bool polygonSupported (void) const final; - bool breachReturnSupported (void) const final { return _breachReturnSupported; } - float circleRadius (void) const final; - QVariantList params (void) const final { return _params; } - QStringList paramLabels (void) const final { return _paramLabels; } - QString editorQml (void) const final; + bool inProgress (void) const final; + void loadFromVehicle (void) final; + void sendToVehicle (const QGeoCoordinate& breachReturn, const QList& polygon) final; + bool circleEnabled (void) const final { return _circleEnabled; } + bool polygonEnabled (void) const final { return _polygonEnabled; } + bool breachReturnEnabled (void) const final { return _breachReturnEnabled; } + float circleRadius (void) const final; + QVariantList params (void) const final { return _params; } + QStringList paramLabels (void) const final { return _paramLabels; } + QString editorQml (void) const final; private slots: void _mavlinkMessageReceived(const mavlink_message_t& message); - void _updateSupportedFlags(void); + void _updateEnabledFlags(void); void _circleRadiusRawValueChanged(QVariant value); void _parametersReady(void); - void _fenceEnabledRawValueChanged(QVariant value); private: void _requestFencePoint(uint8_t pointIndex); void _sendFencePoint(uint8_t pointIndex); - bool _geoFenceSupported(void); private: bool _fenceSupported; - bool _breachReturnSupported; - bool _circleSupported; - bool _polygonSupported; + bool _breachReturnEnabled; + bool _circleEnabled; + bool _polygonEnabled; bool _firstParamLoadComplete; QVariantList _params; QStringList _paramLabels; - Fact* _circleRadiusFact; - bool _readTransactionInProgress; bool _writeTransactionInProgress; @@ -68,6 +62,8 @@ private: uint8_t _currentFencePoint; Fact* _fenceTypeFact; + Fact* _fenceEnableFact; + Fact* _circleRadiusFact; static const char* _fenceTotalParam; static const char* _fenceActionParam; diff --git a/src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc b/src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc index c7b2251ea..063b428ba 100644 --- a/src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc +++ b/src/FirmwarePlugin/PX4/PX4GeoFenceManager.cc @@ -15,6 +15,7 @@ PX4GeoFenceManager::PX4GeoFenceManager(Vehicle* vehicle) : GeoFenceManager(vehicle) , _firstParamLoadComplete(false) + , _circleEnabled(false) , _circleRadiusFact(NULL) { connect(_vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &PX4GeoFenceManager::_parametersReady); @@ -57,10 +58,11 @@ void PX4GeoFenceManager::_parametersReady(void) emit paramsChanged(_params); emit paramLabelsChanged(_paramLabels); - emit circleSupportedChanged(circleSupported()); + _circleEnabled = true; + emit circleEnabledChanged(true); qCDebug(GeoFenceManagerLog) << "fenceSupported:circleSupported:polygonSupported:breachReturnSupported" << - fenceSupported() << circleSupported() << polygonSupported() << breachReturnSupported(); + _circleEnabled << polygonEnabled() << breachReturnEnabled(); } } @@ -76,14 +78,5 @@ float PX4GeoFenceManager::circleRadius(void) const void PX4GeoFenceManager::_circleRadiusRawValueChanged(QVariant value) { emit circleRadiusChanged(value.toFloat()); - emit circleSupportedChanged(circleSupported()); } -bool PX4GeoFenceManager::circleSupported(void) const -{ - if (_circleRadiusFact) { - return _circleRadiusFact->rawValue().toFloat() >= 0.0; - } - - return false; -} diff --git a/src/FirmwarePlugin/PX4/PX4GeoFenceManager.h b/src/FirmwarePlugin/PX4/PX4GeoFenceManager.h index fbeed7774..b29cf1038 100644 --- a/src/FirmwarePlugin/PX4/PX4GeoFenceManager.h +++ b/src/FirmwarePlugin/PX4/PX4GeoFenceManager.h @@ -23,12 +23,11 @@ public: ~PX4GeoFenceManager(); // Overrides from GeoFenceManager - bool fenceSupported (void) const final { return true; } - bool circleSupported (void) const final; - float circleRadius (void) const final; - QVariantList params (void) const final { return _params; } - QStringList paramLabels (void) const final { return _paramLabels; } - QString editorQml (void) const final { return QStringLiteral("qrc:/FirmwarePlugin/PX4/PX4GeoFenceEditor.qml"); } + bool circleEnabled (void) const final { return _circleEnabled; } + float circleRadius (void) const final; + QVariantList params (void) const final { return _params; } + QStringList paramLabels (void) const final { return _paramLabels; } + QString editorQml (void) const final { return QStringLiteral("qrc:/FirmwarePlugin/PX4/PX4GeoFenceEditor.qml"); } private slots: void _circleRadiusRawValueChanged(QVariant value); @@ -39,6 +38,7 @@ private: QVariantList _params; QStringList _paramLabels; + bool _circleEnabled; Fact* _circleRadiusFact; }; diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index 69cc038a5..cce975b99 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -111,7 +111,7 @@ FlightMap { border.color: "#80FF0000" border.width: 3 path: geoFenceController.polygon.path - visible: geoFenceController.fenceEnabled && geoFenceController.polygonSupported + visible: geoFenceController.polygonEnabled } // GeoFence circle @@ -119,16 +119,16 @@ FlightMap { border.color: "#80FF0000" border.width: 3 center: missionController.plannedHomePosition - radius: (geoFenceController.fenceEnabled && geoFenceController.circleSupported) ? geoFenceController.circleRadius : 0 + radius: geoFenceController.circleRadius z: QGroundControl.zOrderMapItems - visible: geoFenceController.fenceEnabled && geoFenceController.circleSupported + visible: geoFenceController.circleEnabled } // GeoFence breach return point MapQuickItem { anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) coordinate: geoFenceController.breachReturnPoint - visible: geoFenceController.fenceEnabled && geoFenceController.breachReturnSupported + visible: geoFenceController.breachReturnEnabled sourceItem: MissionItemIndexLabel { label: "F" } z: QGroundControl.zOrderMapItems } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index fadd6595f..c7b8bfd14 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -145,14 +145,14 @@ QGCView { } function addFenceItemCoordsForFit(coordList) { - if (geoFenceController.circleSupported) { + if (geoFenceController.circleEnabled) { var azimuthList = [ 0, 180, 90, 270 ] for (var i=0; i 2) { + if (geoFenceController.polygonEnabled && geoFenceController.polygon.count() > 2) { for (var i=0; igeoFenceManager(); - connect(geoFenceManager, &GeoFenceManager::fenceSupportedChanged, this, &GeoFenceController::fenceSupportedChanged); - connect(geoFenceManager, &GeoFenceManager::fenceEnabledChanged, this, &GeoFenceController::fenceEnabledChanged); - connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::_setDirty); - connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::_setDirty); - connect(geoFenceManager, &GeoFenceManager::circleSupportedChanged, this, &GeoFenceController::circleSupportedChanged); - connect(geoFenceManager, &GeoFenceManager::polygonSupportedChanged, this, &GeoFenceController::polygonSupportedChanged); - connect(geoFenceManager, &GeoFenceManager::breachReturnSupportedChanged, this, &GeoFenceController::breachReturnSupportedChanged); - connect(geoFenceManager, &GeoFenceManager::circleRadiusChanged, this, &GeoFenceController::circleRadiusChanged); - connect(geoFenceManager, &GeoFenceManager::paramsChanged, this, &GeoFenceController::paramsChanged); - connect(geoFenceManager, &GeoFenceManager::paramLabelsChanged, this, &GeoFenceController::paramLabelsChanged); - connect(geoFenceManager, &GeoFenceManager::loadComplete, this, &GeoFenceController::_loadComplete); - connect(geoFenceManager, &GeoFenceManager::inProgressChanged, this, &GeoFenceController::syncInProgressChanged); + connect(geoFenceManager, &GeoFenceManager::polygonEnabledChanged, this, &GeoFenceController::_setDirty); + connect(geoFenceManager, &GeoFenceManager::circleEnabledChanged, this, &GeoFenceController::circleEnabledChanged); + connect(geoFenceManager, &GeoFenceManager::polygonEnabledChanged, this, &GeoFenceController::polygonEnabledChanged); + connect(geoFenceManager, &GeoFenceManager::breachReturnEnabledChanged, this, &GeoFenceController::breachReturnEnabledChanged); + connect(geoFenceManager, &GeoFenceManager::circleRadiusChanged, this, &GeoFenceController::circleRadiusChanged); + connect(geoFenceManager, &GeoFenceManager::paramsChanged, this, &GeoFenceController::paramsChanged); + connect(geoFenceManager, &GeoFenceManager::paramLabelsChanged, this, &GeoFenceController::paramLabelsChanged); + connect(geoFenceManager, &GeoFenceManager::loadComplete, this, &GeoFenceController::_loadComplete); + connect(geoFenceManager, &GeoFenceManager::inProgressChanged, this, &GeoFenceController::syncInProgressChanged); if (!geoFenceManager->inProgress()) { _loadComplete(geoFenceManager->breachReturnPoint(), geoFenceManager->polygon()); @@ -136,7 +131,7 @@ bool GeoFenceController::_loadJsonFile(QJsonDocument& jsonDoc, QString& errorStr return false; } - if (breachReturnSupported()) { + if (breachReturnEnabled()) { if (json.contains(_jsonBreachReturnKey) && !JsonHelper::loadGeoCoordinate(json[_jsonBreachReturnKey], false /* altitudeRequired */, _breachReturnPoint, errorString)) { return false; @@ -145,7 +140,7 @@ bool GeoFenceController::_loadJsonFile(QJsonDocument& jsonDoc, QString& errorStr _breachReturnPoint = QGeoCoordinate(); } - if (polygonSupported()) { + if (polygonEnabled()) { if (!_polygon.loadFromJson(json, false /* reauired */, errorString)) { return false; } @@ -289,13 +284,13 @@ void GeoFenceController::saveToFile(const QString& filename) paramMgr->saveToJson(paramMgr->defaultComponentId(), paramNames, fenceFileObject); } - if (breachReturnSupported()) { + if (breachReturnEnabled()) { QJsonValue jsonBreachReturn; JsonHelper::saveGeoCoordinate(_breachReturnPoint, false /* writeAltitude */, jsonBreachReturn); fenceFileObject[_jsonBreachReturnKey] = jsonBreachReturn; } - if (polygonSupported()) { + if (polygonEnabled()) { _polygon.saveToJson(fenceFileObject); } @@ -373,29 +368,19 @@ void GeoFenceController::_polygonDirtyChanged(bool dirty) } } -bool GeoFenceController::fenceSupported(void) const +bool GeoFenceController::circleEnabled(void) const { - return _activeVehicle->geoFenceManager()->fenceSupported(); + return _activeVehicle->geoFenceManager()->circleEnabled(); } -bool GeoFenceController::fenceEnabled(void) const +bool GeoFenceController::polygonEnabled(void) const { - return _activeVehicle->geoFenceManager()->fenceEnabled(); + return _activeVehicle->geoFenceManager()->polygonEnabled(); } -bool GeoFenceController::circleSupported(void) const +bool GeoFenceController::breachReturnEnabled(void) const { - return _activeVehicle->geoFenceManager()->circleSupported(); -} - -bool GeoFenceController::polygonSupported(void) const -{ - return _activeVehicle->geoFenceManager()->polygonSupported(); -} - -bool GeoFenceController::breachReturnSupported(void) const -{ - return _activeVehicle->geoFenceManager()->breachReturnSupported(); + return _activeVehicle->geoFenceManager()->breachReturnEnabled(); } void GeoFenceController::_setDirty(void) diff --git a/src/MissionManager/GeoFenceController.h b/src/MissionManager/GeoFenceController.h index ae5b8c6dc..34c87bdad 100644 --- a/src/MissionManager/GeoFenceController.h +++ b/src/MissionManager/GeoFenceController.h @@ -29,17 +29,26 @@ public: GeoFenceController(QObject* parent = NULL); ~GeoFenceController(); + Q_PROPERTY(bool circleEnabled READ circleEnabled NOTIFY circleEnabledChanged) + Q_PROPERTY(float circleRadius READ circleRadius NOTIFY circleRadiusChanged) + + Q_PROPERTY(bool polygonEnabled READ polygonEnabled NOTIFY polygonEnabledChanged) + Q_PROPERTY(QGCMapPolygon* polygon READ polygon CONSTANT) + + Q_PROPERTY(bool breachReturnEnabled READ breachReturnEnabled NOTIFY breachReturnEnabledChanged) + Q_PROPERTY(QGeoCoordinate breachReturnPoint READ breachReturnPoint WRITE setBreachReturnPoint NOTIFY breachReturnPointChanged) + + Q_PROPERTY(QVariantList params READ params NOTIFY paramsChanged) + Q_PROPERTY(QStringList paramLabels READ paramLabels NOTIFY paramLabelsChanged) + Q_PROPERTY(QString editorQml READ editorQml NOTIFY editorQmlChanged) + +#if 0 Q_PROPERTY(bool fenceSupported READ fenceSupported NOTIFY fenceSupportedChanged) Q_PROPERTY(bool fenceEnabled READ fenceEnabled NOTIFY fenceEnabledChanged) Q_PROPERTY(bool circleSupported READ circleSupported NOTIFY circleSupportedChanged) Q_PROPERTY(bool polygonSupported READ polygonSupported NOTIFY polygonSupportedChanged) Q_PROPERTY(bool breachReturnSupported READ breachReturnSupported NOTIFY breachReturnSupportedChanged) - Q_PROPERTY(float circleRadius READ circleRadius NOTIFY circleRadiusChanged) - Q_PROPERTY(QGCMapPolygon* polygon READ polygon CONSTANT) - Q_PROPERTY(QGeoCoordinate breachReturnPoint READ breachReturnPoint WRITE setBreachReturnPoint NOTIFY breachReturnPointChanged) - Q_PROPERTY(QVariantList params READ params NOTIFY paramsChanged) - Q_PROPERTY(QStringList paramLabels READ paramLabels NOTIFY paramLabelsChanged) - Q_PROPERTY(QString editorQml READ editorQml NOTIFY editorQmlChanged) +#endif void start (bool editMode) final; void loadFromVehicle (void) final; @@ -55,33 +64,29 @@ public: QString fileExtension(void) const final; - bool fenceSupported (void) const; - bool fenceEnabled (void) const; - bool circleSupported (void) const; - bool polygonSupported (void) const; - bool breachReturnSupported (void) const; - float circleRadius (void) const; - QGCMapPolygon* polygon (void) { return &_polygon; } - QGeoCoordinate breachReturnPoint (void) const { return _breachReturnPoint; } - QVariantList params (void) const; - QStringList paramLabels (void) const; - QString editorQml (void) const; + bool circleEnabled (void) const; + bool polygonEnabled (void) const; + bool breachReturnEnabled (void) const; + float circleRadius (void) const; + QGCMapPolygon* polygon (void) { return &_polygon; } + QGeoCoordinate breachReturnPoint (void) const { return _breachReturnPoint; } + QVariantList params (void) const; + QStringList paramLabels (void) const; + QString editorQml (void) const; void setBreachReturnPoint(const QGeoCoordinate& breachReturnPoint); signals: - void fenceSupportedChanged (bool fenceSupported); - void fenceEnabledChanged (bool fenceEnabled); - void circleSupportedChanged (bool circleSupported); - void polygonSupportedChanged (bool polygonSupported); - void breachReturnSupportedChanged (bool breachReturnSupported); - void circleRadiusChanged (float circleRadius); - void polygonPathChanged (const QVariantList& polygonPath); - void breachReturnPointChanged (QGeoCoordinate breachReturnPoint); - void paramsChanged (QVariantList params); - void paramLabelsChanged (QStringList paramLabels); - void editorQmlChanged (QString editorQml); - void loadComplete (void); + void circleEnabledChanged (bool circleEnabled); + void polygonEnabledChanged (bool polygonEnabled); + void breachReturnEnabledChanged (bool breachReturnEnabled); + void circleRadiusChanged (float circleRadius); + void polygonPathChanged (const QVariantList& polygonPath); + void breachReturnPointChanged (QGeoCoordinate breachReturnPoint); + void paramsChanged (QVariantList params); + void paramLabelsChanged (QStringList paramLabels); + void editorQmlChanged (QString editorQml); + void loadComplete (void); private slots: void _polygonDirtyChanged(bool dirty); diff --git a/src/MissionManager/GeoFenceManager.h b/src/MissionManager/GeoFenceManager.h index eb2e8f6b6..c88de86dd 100644 --- a/src/MissionManager/GeoFenceManager.h +++ b/src/MissionManager/GeoFenceManager.h @@ -38,12 +38,9 @@ public: /// Send the current settings to the vehicle virtual void sendToVehicle(const QGeoCoordinate& breachReturn, const QList& polygon); - // Support flags - virtual bool fenceSupported (void) const { return false; } - virtual bool fenceEnabled (void) const { return false; } - virtual bool circleSupported (void) const { return false; } - virtual bool polygonSupported (void) const { return false; } - virtual bool breachReturnSupported (void) const { return false; } + virtual bool circleEnabled (void) const { return false; } + virtual bool polygonEnabled (void) const { return false; } + virtual bool breachReturnEnabled (void) const { return false; } virtual float circleRadius (void) const { return 0.0; } QList polygon (void) const { return _polygon; } @@ -63,17 +60,15 @@ public: } ErrorCode_t; signals: - void loadComplete (const QGeoCoordinate& breachReturn, const QList& polygon); - void fenceSupportedChanged (bool fenceSupported); - void fenceEnabledChanged (bool fenceEnabled); - void circleSupportedChanged (bool circleSupported); - void polygonSupportedChanged (bool polygonSupported); - void breachReturnSupportedChanged (bool fenceSupported); - void circleRadiusChanged (float circleRadius); - void inProgressChanged (bool inProgress); - void error (int errorCode, const QString& errorMsg); - void paramsChanged (QVariantList params); - void paramLabelsChanged (QStringList paramLabels); + void loadComplete (const QGeoCoordinate& breachReturn, const QList& polygon); + void circleEnabledChanged (bool circleEnabled); + void polygonEnabledChanged (bool polygonEnabled); + void breachReturnEnabledChanged (bool fenceEnabled); + void circleRadiusChanged (float circleRadius); + void inProgressChanged (bool inProgress); + void error (int errorCode, const QString& errorMsg); + void paramsChanged (QVariantList params); + void paramLabelsChanged (QStringList paramLabels); protected: void _sendError(ErrorCode_t errorCode, const QString& errorMsg); -- GitLab From ee061d436b004bba073aa20c1a131b9f292007b2 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 6 Dec 2016 19:50:33 -0800 Subject: [PATCH 094/398] Change Vehicle.Battery.current to Amps units --- src/Vehicle/BatteryFact.json | 2 +- src/Vehicle/Vehicle.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/BatteryFact.json b/src/Vehicle/BatteryFact.json index b915bc4b2..6a5eecb36 100644 --- a/src/Vehicle/BatteryFact.json +++ b/src/Vehicle/BatteryFact.json @@ -25,7 +25,7 @@ "shortDescription": "Current", "type": "int32", "decimalPlaces": 0, - "units": "mA" + "units": "A" }, { "name": "temperature", diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 90ad8003d..be65e9303 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -661,7 +661,8 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) if (sysStatus.current_battery == -1) { _batteryFactGroup.current()->setRawValue(VehicleBatteryFactGroup::_currentUnavailable); } else { - _batteryFactGroup.current()->setRawValue((double)sysStatus.current_battery * 10); + // Current is in Amps, current_battery is 10 * milliamperes (1 = 10 milliampere) + _batteryFactGroup.current()->setRawValue((int)(sysStatus.current_battery / 100)); } if (sysStatus.voltage_battery == UINT16_MAX) { _batteryFactGroup.voltage()->setRawValue(VehicleBatteryFactGroup::_voltageUnavailable); -- GitLab From 9f1c81e6dfbc4dac47caf8971f2476960b2491d5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 7 Dec 2016 10:07:32 -0800 Subject: [PATCH 095/398] Add missing FlightDisplayViewVideo --- src/FlightDisplay/qmldir | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FlightDisplay/qmldir b/src/FlightDisplay/qmldir index 68ea784d7..b1bf782af 100644 --- a/src/FlightDisplay/qmldir +++ b/src/FlightDisplay/qmldir @@ -1,5 +1,6 @@ Module QGroundControl.FlightDisplay -FlightDisplayView 1.0 FlightDisplayView.qml -FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml +FlightDisplayView 1.0 FlightDisplayView.qml +FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml +FlightDisplayViewVideo 1.0 FlightDisplayViewVideo.qml -- GitLab From fa0d4719efa6ff830ea6b9489eb56581ac78b394 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 7 Dec 2016 12:06:02 -0800 Subject: [PATCH 096/398] Correct usage of Vehicle::sendMavCommand --- .../Common/ESP8266ComponentController.cc | 57 +----------- .../Common/ESP8266ComponentController.h | 2 - src/FactSystem/ParameterManager.cc | 35 +++----- src/Vehicle/Vehicle.cc | 4 + src/Vehicle/Vehicle.h | 2 +- src/uas/UAS.cc | 86 ++++++------------- 6 files changed, 42 insertions(+), 144 deletions(-) diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc index 4ba9430c7..1e7c937d1 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.cc @@ -37,7 +37,6 @@ ESP8266ComponentController::ESP8266ComponentController() _baudRates.append("230400"); _baudRates.append("460800"); _baudRates.append("921600"); - connect(&_timer, &QTimer::timeout, this, &ESP8266ComponentController::_processTimeout); connect(_vehicle, &Vehicle::mavCommandResult, this, &ESP8266ComponentController::_mavCommandResult); Fact* ssid = getParameterFact(MAV_COMP_ID_UDP_BRIDGE, "WIFI_SSID4"); connect(ssid, &Fact::valueChanged, this, &ESP8266ComponentController::_ssidChanged); @@ -316,67 +315,16 @@ ESP8266ComponentController::restoreDefaults() void ESP8266ComponentController::_reboot() { - mavlink_message_t msg; - - mavlink_msg_command_long_pack_chan( - qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), - qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - _vehicle->id(), - MAV_COMP_ID_UDP_BRIDGE, - MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, - 1.0f, // Confirmation - 0.0f, // Param1 - 1.0f, // Param2 - 0.0f,0.0f,0.0f,0.0f,0.0f); + _vehicle->sendMavCommand(MAV_COMP_ID_UDP_BRIDGE, MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true /* showError */, 0.0f, 1.0f); qCDebug(ESP8266ComponentControllerLog) << "_reboot()"; - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); - _timer.start(1000); } //----------------------------------------------------------------------------- void ESP8266ComponentController::_restoreDefaults() { - mavlink_message_t msg; - mavlink_msg_command_long_pack_chan( - qgcApp()->toolbox()->mavlinkProtocol()->getSystemId(), - qgcApp()->toolbox()->mavlinkProtocol()->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - _vehicle->id(), - MAV_COMP_ID_UDP_BRIDGE, - MAV_CMD_PREFLIGHT_STORAGE, - 1.0f, // Confirmation - 2.0f, // Param1 - 0.0f,0.0f,0.0f,0.0f,0.0f,0.0f); + _vehicle->sendMavCommand(MAV_COMP_ID_UDP_BRIDGE, MAV_CMD_PREFLIGHT_STORAGE, true /* showError */, 2.0f); qCDebug(ESP8266ComponentControllerLog) << "_restoreDefaults()"; - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); - _timer.start(1000); -} - -//----------------------------------------------------------------------------- -void -ESP8266ComponentController::_processTimeout() -{ - if(!--_retries) { - qCDebug(ESP8266ComponentControllerLog) << "_processTimeout Giving Up"; - _timer.stop(); - _waitType = WAIT_FOR_NOTHING; - emit busyChanged(); - } else { - switch(_waitType) { - case WAIT_FOR_REBOOT: - qCDebug(ESP8266ComponentControllerLog) << "_processTimeout for Reboot"; - _reboot(); - break; - case WAIT_FOR_RESTORE: - qCDebug(ESP8266ComponentControllerLog) << "_processTimeout for Restore Defaults"; - _restoreDefaults(); - break; - } - } } //----------------------------------------------------------------------------- @@ -393,7 +341,6 @@ ESP8266ComponentController::_mavCommandResult(int vehicleId, int component, int } if ((_waitType == WAIT_FOR_REBOOT && command == MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN) || (_waitType == WAIT_FOR_RESTORE && command == MAV_CMD_PREFLIGHT_STORAGE)) { - _timer.stop(); _waitType = WAIT_FOR_NOTHING; emit busyChanged(); qCDebug(ESP8266ComponentControllerLog) << "_commandAck for" << command; diff --git a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h index afba9939c..1093eb267 100644 --- a/src/AutoPilotPlugins/Common/ESP8266ComponentController.h +++ b/src/AutoPilotPlugins/Common/ESP8266ComponentController.h @@ -82,7 +82,6 @@ signals: void busyChanged (); private slots: - void _processTimeout (); void _mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle); void _ssidChanged (QVariant value); void _passwordChanged (QVariant value); @@ -94,7 +93,6 @@ private: void _restoreDefaults (); private: - QTimer _timer; QStringList _channels; QStringList _baudRates; QString _ipAddress; diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 2cec90baa..925621b65 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -816,15 +816,11 @@ void ParameterManager::_saveToEEPROM(void) if (_saveRequired) { _saveRequired = false; if (_vehicle->firmwarePlugin()->isCapable(_vehicle, FirmwarePlugin::MavCmdPreflightStorageCapability)) { - mavlink_message_t msg; - mavlink_msg_command_long_pack_chan(_mavlink->getSystemId(), - _mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - _vehicle->id(), - 0, - MAV_CMD_PREFLIGHT_STORAGE, 1, 1, -1, -1, -1, 0, 0, 0); - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(MAV_COMP_ID_ALL, + MAV_CMD_PREFLIGHT_STORAGE, + true, // showError + 1, // Write parameters to EEPROM + -1); // Don't do anything with mission storage qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM"; } else { qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM skipped due to FirmwarePlugin::isCapable"; @@ -1442,22 +1438,11 @@ bool ParameterManager::loadFromJson(const QJsonObject& json, bool required, QStr void ParameterManager::resetAllParametersToDefaults(void) { - mavlink_message_t msg; - MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol(); - - mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - _vehicle->id(), // Target systeem - _vehicle->defaultComponentId(), // Target component - MAV_CMD_PREFLIGHT_STORAGE, - 0, // Confirmation - 2, // 2 = Reset params to default - -1, // -1 = No change to mission storage - 0, // 0 = Ignore - 0, 0, 0, 0); // Unused - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(MAV_COMP_ID_ALL, + MAV_CMD_PREFLIGHT_STORAGE, + true, // showError + 2, // Reset params to default + -1); // Don't do anything with mission storage } QString ParameterManager::_logVehiclePrefix(int componentId) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 82afb8489..2c7cb24d6 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1850,6 +1850,10 @@ void Vehicle::_sendMavCommandAgain(void) return; } + if (_mavCommandRetryCount > 1) { + qDebug() << "Vehicle::_sendMavCommandAgain retrying command:_mavCommandRetryCount" << queuedCommand.command << _mavCommandRetryCount; + } + _mavCommandAckTimer.start(); mavlink_message_t msg; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index fd19666a2..1f8eb3cb3 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -791,7 +791,7 @@ private: QTimer _mavCommandAckTimer; int _mavCommandRetryCount; static const int _mavCommandMaxRetryCount = 3; - static const int _mavCommandAckTimeoutMSecs = 1000; + static const int _mavCommandAckTimeoutMSecs = 3000; QString _prearmError; QTimer _prearmErrorTimer; diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 6d1a40f1a..b7ada3e58 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -797,6 +797,8 @@ void UAS::startCalibration(UASInterface::StartCalibrationType calType) break; } + // We can't use sendMavCommand here since we have no idea how long it will be before the command returns a result. This in turn + // causes the retry logic to break down. mavlink_message_t msg; mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), mavlink->getComponentId(), @@ -822,23 +824,16 @@ void UAS::stopCalibration(void) return; } - mavlink_message_t msg; - mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - uasId, - _vehicle->defaultComponentId(), // target component - MAV_CMD_PREFLIGHT_CALIBRATION, // command id - 0, // 0=first transmission of command - 0, // gyro cal - 0, // mag cal - 0, // ground pressure - 0, // radio cal - 0, // accel cal - 0, // airspeed cal - 0); // unused - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_PREFLIGHT_CALIBRATION, // command id + true, // showError + 0, // gyro cal + 0, // mag cal + 0, // ground pressure + 0, // radio cal + 0, // accel cal + 0, // airspeed cal + 0); // unused } void UAS::startBusConfig(UASInterface::StartBusConfigType calType) @@ -858,23 +853,10 @@ void UAS::startBusConfig(UASInterface::StartBusConfigType calType) break; } - mavlink_message_t msg; - mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - uasId, - _vehicle->defaultComponentId(), // target component - MAV_CMD_PREFLIGHT_UAVCAN, // command id - 0, // 0=first transmission of command - actuatorCal, // actuators - 0, - 0, - 0, - 0, - 0, - 0); - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_PREFLIGHT_UAVCAN, // command id + true, // showError + actuatorCal); // actuators } void UAS::stopBusConfig(void) @@ -883,23 +865,10 @@ void UAS::stopBusConfig(void) return; } - mavlink_message_t msg; - mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - uasId, - _vehicle->defaultComponentId(), // target component - MAV_CMD_PREFLIGHT_UAVCAN, // command id - 0, // 0=first transmission of command - 0, - 0, - 0, - 0, - 0, - 0, - 0); - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_PREFLIGHT_UAVCAN, // command id + true, // showError + 0); // cancel } /** @@ -1478,16 +1447,11 @@ void UAS::pairRX(int rxType, int rxSubType) return; } - mavlink_message_t msg; - - mavlink_msg_command_long_pack_chan(mavlink->getSystemId(), - mavlink->getComponentId(), - _vehicle->priorityLink()->mavlinkChannel(), - &msg, - uasId, - _vehicle->defaultComponentId(), - MAV_CMD_START_RX_PAIR, 0, rxType, rxSubType, 0, 0, 0, 0, 0); - _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_START_RX_PAIR, // command id + true, // showError + rxType, + rxSubType); } /** -- GitLab From 506ed73b3ffd93316241b18222dced67ac64be81 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 7 Dec 2016 12:06:16 -0800 Subject: [PATCH 097/398] Fix non android serial port code --- src/comm/LinkManager.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index fb9056cbc..530b9b05c 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -499,6 +499,8 @@ void LinkManager::_updateAutoConnectLinks(void) if (!_autoconnectConfigurations.count()) { portList = QGCSerialPortInfo::availablePorts(); } +#else + portList = QGCSerialPortInfo::availablePorts(); #endif // Iterate Comm Ports -- GitLab From 9fc8a127f13b8a1da826264548fc6ffffb057d6a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 7 Dec 2016 12:33:57 -0800 Subject: [PATCH 098/398] Remove global property usage * Make MainToolbarIndicators reusable by other views * Change to font based ratio sizing --- src/QmlControls/QGCToolBarButton.qml | 1 + src/ui/MainWindowInner.qml | 27 +- src/ui/toolbar/MainToolBar.qml | 279 ++------------ src/ui/toolbar/MainToolBarIndicators.qml | 446 +++++++++++++++++------ 4 files changed, 375 insertions(+), 378 deletions(-) diff --git a/src/QmlControls/QGCToolBarButton.qml b/src/QmlControls/QGCToolBarButton.qml index 106267f33..f7d6c8ac9 100644 --- a/src/QmlControls/QGCToolBarButton.qml +++ b/src/QmlControls/QGCToolBarButton.qml @@ -17,6 +17,7 @@ import QGroundControl.ScreenTools 1.0 Item { id: _root + width: height state: "HelpShown" clip: true diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index 4224c88d6..9f54ee812 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -29,11 +29,6 @@ Item { QGCPalette { id: qgcPal; colorGroupEnabled: true } - property real tbHeight: ScreenTools.isMobile ? (ScreenTools.isTinyScreen ? (mainWindow.width * 0.0666) : (mainWindow.width * 0.05)) : ScreenTools.defaultFontPixelHeight * 3 - property int tbCellHeight: tbHeight * 0.75 - property real tbSpacing: ScreenTools.isMobile ? width * 0.00824 : 9.54 - property real tbButtonWidth: tbCellHeight * 1.35 - property real menuButtonWidth: (tbButtonWidth * 2) + (tbSpacing * 4) + 1 property var gcsPosition: QtPositioning.coordinate() // Starts as invalid coordinate property var currentPopUp: null property real currentCenterX: 0 @@ -261,21 +256,19 @@ Item { MainToolBar { id: toolBar - height: tbHeight + height: ScreenTools.defaultFontPixelHeight * 3 anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - mainWindow: mainWindow isBackgroundDark: flightView.isBackgroundDark z: QGroundControl.zOrderTopMost - onShowSettingsView: mainWindow.showSettingsView() - onShowSetupView: mainWindow.showSetupView() - onShowPlanView: mainWindow.showPlanView() - onShowFlyView: mainWindow.showFlyView() - onShowAnalyzeView: mainWindow.showAnalyzeView() - Component.onCompleted: { - ScreenTools.availableHeight = parent.height - toolBar.height - } + + Component.onCompleted: ScreenTools.availableHeight = parent.height - toolBar.height + onShowSettingsView: mainWindow.showSettingsView() + onShowSetupView: mainWindow.showSetupView() + onShowPlanView: mainWindow.showPlanView() + onShowFlyView: mainWindow.showFlyView() + onShowAnalyzeView: mainWindow.showAnalyzeView() } Loader { @@ -365,7 +358,7 @@ Item { border.width: 2 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - anchors.topMargin: tbHeight + ScreenTools.defaultFontPixelHeight + anchors.topMargin: toolBar.height + ScreenTools.defaultFontPixelHeight MouseArea { // This MouseArea prevents the Map below it from getting Mouse events. Without this // things like mousewheel will scroll the Flickable and then scroll the map as well. @@ -443,7 +436,7 @@ Item { radius: ScreenTools.defaultFontPixelHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top - anchors.topMargin: tbHeight + ScreenTools.defaultFontPixelHeight / 2 + anchors.topMargin: toolBar.height + ScreenTools.defaultFontPixelHeight / 2 border.color: "#808080" border.width: 2 diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 574b32320..5054eb8e7 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -24,18 +24,17 @@ Rectangle { QGCPalette { id: qgcPal; colorGroupEnabled: true } - property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property var mainWindow: null - property bool isMessageImportant: activeVehicle ? !activeVehicle.messageTypeNormal && !activeVehicle.messageTypeNone : false + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool isMessageImportant: _activeVehicle ? !_activeVehicle.messageTypeNormal && !_activeVehicle.messageTypeNone : false property bool isBackgroundDark: true property bool opaqueBackground: false - readonly property var colorGreen: "#05f068" - readonly property var colorOrange: "#f0ab06" - readonly property var colorRed: "#fc4638" - readonly property var colorGrey: "#7f7f7f" - readonly property var colorBlue: "#636efe" - readonly property var colorWhite: "#ffffff" + readonly property color colorGreen: "#05f068" + readonly property color colorOrange: "#f0ab06" + readonly property color colorRed: "#fc4638" + readonly property color colorGrey: "#7f7f7f" + readonly property color colorBlue: "#636efe" + readonly property color colorWhite: "#ffffff" signal showSettingsView signal showSetupView @@ -65,240 +64,12 @@ Rectangle { analyzeButton.checked = true } - function getBatteryColor() { - if(activeVehicle) { - if(activeVehicle.battery.percentRemaining.value > 75) { - return qgcPal.text - } - if(activeVehicle.battery.percentRemaining.value > 50) { - return colorOrange - } - if(activeVehicle.battery.percentRemaining.value > 0.1) { - return colorRed - } - } - return colorGrey - } - - function getRSSIColor(value) { - if(value >= 0) - return colorGrey; - if(value > -60) - return colorGreen; - if(value > -90) - return colorOrange; - return colorRed; - } - Component.onCompleted: { //-- TODO: Get this from the actual state flyButton.checked = true } - //--------------------------------------------- - // GPS Info - Component { - id: gpsInfo - - Rectangle { - width: gpsCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: gpsCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - - Column { - id: gpsCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(gpsGrid.width, gpsLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: gpsLabel - text: (activeVehicle && activeVehicle.gps.count.value >= 0) ? qsTr("GPS Status") : qsTr("GPS Data Unavailable") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: gpsGrid - visible: (activeVehicle && activeVehicle.gps.count.value >= 0) - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - columns: 2 - - QGCLabel { text: qsTr("GPS Count:") } - QGCLabel { text: activeVehicle ? activeVehicle.gps.count.valueString : qsTr("N/A", "No data to display") } - QGCLabel { text: qsTr("GPS Lock:") } - QGCLabel { text: activeVehicle ? activeVehicle.gps.lock.enumStringValue : qsTr("N/A", "No data to display") } - QGCLabel { text: qsTr("HDOP:") } - QGCLabel { text: activeVehicle ? activeVehicle.gps.hdop.valueString : qsTr("--.--", "No data to display") } - QGCLabel { text: qsTr("VDOP:") } - QGCLabel { text: activeVehicle ? activeVehicle.gps.vdop.valueString : qsTr("--.--", "No data to display") } - QGCLabel { text: qsTr("Course Over Ground:") } - QGCLabel { text: activeVehicle ? activeVehicle.gps.courseOverGround.valueString : qsTr("--.--", "No data to display") } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // Battery Info - Component { - id: batteryInfo - - Rectangle { - width: battCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: battCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - - Column { - id: battCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(battGrid.width, battLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: battLabel - text: qsTr("Battery Status") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: battGrid - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("Voltage:") } - QGCLabel { text: (activeVehicle && activeVehicle.battery.voltage.value != -1) ? (activeVehicle.battery.voltage.valueString + " " + activeVehicle.battery.voltage.units) : "N/A" } - QGCLabel { text: qsTr("Accumulated Consumption:") } - QGCLabel { text: (activeVehicle && activeVehicle.battery.mahConsumed.value != -1) ? (activeVehicle.battery.mahConsumed.valueString + " " + activeVehicle.battery.mahConsumed.units) : "N/A" } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // RC RSSI Info - Component { - id: rcRSSIInfo - - Rectangle { - width: rcrssiCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: rcrssiCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - - Column { - id: rcrssiCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(rcrssiGrid.width, rssiLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: rssiLabel - text: activeVehicle ? (activeVehicle.rcRSSI != 255 ? qsTr("RC RSSI Status") : qsTr("RC RSSI Data Unavailable")) : qsTr("N/A", "No data available") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: rcrssiGrid - visible: activeVehicle && activeVehicle.rcRSSI != 255 - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("RSSI:") } - QGCLabel { text: activeVehicle ? (activeVehicle.rcRSSI + "%") : 0 } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // Telemetry RSSI Info - Component { - id: telemRSSIInfo - - Rectangle { - width: telemCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: telemCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - - Column { - id: telemCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(telemGrid.width, telemLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: telemLabel - text: qsTr("Telemetry RSSI Status") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: telemGrid - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("Local RSSI:") } - QGCLabel { text: _controller.telemetryLRSSI + " dBm" } - QGCLabel { text: qsTr("Remote RSSI:") } - QGCLabel { text: _controller.telemetryRRSSI + " dBm" } - QGCLabel { text: qsTr("RX Errors:") } - QGCLabel { text: _controller.telemetryRXErrors } - QGCLabel { text: qsTr("Errors Fixed:") } - QGCLabel { text: _controller.telemetryFixed } - QGCLabel { text: qsTr("TX Buffer:") } - QGCLabel { text: _controller.telemetryTXBuffer } - QGCLabel { text: qsTr("Local Noise:") } - QGCLabel { text: _controller.telemetryLNoise } - QGCLabel { text: qsTr("Remote Noise:") } - QGCLabel { text: _controller.telemetryRNoise } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - + /// Bottom single pixel divider Rectangle { anchors.left: parent.left anchors.right: parent.right @@ -312,22 +83,20 @@ Rectangle { anchors.bottomMargin: 1 anchors.rightMargin: ScreenTools.defaultFontPixelWidth / 2 anchors.fill: parent - spacing: mainWindow.tbSpacing * 2 + spacing: ScreenTools.defaultFontPixelWidth * 2 //--------------------------------------------- // Toolbar Row Row { id: viewRow - height: mainWindow.tbCellHeight - spacing: mainWindow.tbSpacing anchors.top: parent.top anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth / 2 ExclusiveGroup { id: mainActionGroup } QGCToolBarButton { id: settingsButton - width: mainWindow.tbButtonWidth anchors.top: parent.top anchors.bottom: parent.bottom exclusiveGroup: mainActionGroup @@ -339,7 +108,6 @@ Rectangle { QGCToolBarButton { id: setupButton - width: mainWindow.tbButtonWidth anchors.top: parent.top anchors.bottom: parent.bottom exclusiveGroup: mainActionGroup @@ -349,7 +117,6 @@ Rectangle { QGCToolBarButton { id: planButton - width: mainWindow.tbButtonWidth anchors.top: parent.top anchors.bottom: parent.bottom exclusiveGroup: mainActionGroup @@ -359,7 +126,6 @@ Rectangle { QGCToolBarButton { id: flyButton - width: mainWindow.tbButtonWidth anchors.top: parent.top anchors.bottom: parent.bottom exclusiveGroup: mainActionGroup @@ -369,7 +135,6 @@ Rectangle { QGCToolBarButton { id: analyzeButton - width: mainWindow.tbButtonWidth anchors.top: parent.top anchors.bottom: parent.bottom exclusiveGroup: mainActionGroup @@ -377,6 +142,15 @@ Rectangle { visible: !ScreenTools.isMobile onClicked: toolBar.showAnalyzeView() } + + Rectangle { + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + anchors.top: parent.top + anchors.bottom: parent.bottom + width: 1 + color: qgcPal.text + visible: _activeVehicle + } } //------------------------------------------------------------------------- @@ -384,7 +158,7 @@ Rectangle { QGCButton { id: vehicleSelectorButton width: ScreenTools.defaultFontPixelHeight * 8 - text: "Vehicle " + (activeVehicle ? activeVehicle.id : "None") + text: "Vehicle " + (_activeVehicle ? _activeVehicle.id : "None") visible: QGroundControl.multiVehicleManager.vehicles.count > 1 anchors.verticalCenter: parent.verticalCenter @@ -433,10 +207,11 @@ Rectangle { } MainToolBarIndicators { - height: mainWindow.tbCellHeight - Layout.fillWidth: true - anchors.verticalCenter: parent.verticalCenter - visible: activeVehicle + anchors.margins: ScreenTools.defaultFontPixelHeight * 0.66 + anchors.top: parent.top + anchors.bottom: parent.bottom + Layout.fillWidth: true + visible: _activeVehicle } } @@ -445,7 +220,7 @@ Rectangle { id: progressBar anchors.bottom: parent.bottom height: toolBar.height * 0.05 - width: activeVehicle ? activeVehicle.parameterManager.loadProgress * parent.width : 0 + width: _activeVehicle ? _activeVehicle.parameterManager.loadProgress * parent.width : 0 color: colorGreen } diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index 7a815a303..73c339325 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -8,8 +8,9 @@ ****************************************************************************/ -import QtQuick 2.5 -import QtQuick.Controls 1.2 +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 import QGroundControl 1.0 import QGroundControl.Controls 1.0 @@ -21,6 +22,33 @@ Item { property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property bool _communicationLost: _activeVehicle ? _activeVehicle.connectionLost : false + QGCPalette { id: qgcPal } + + function getBatteryColor() { + if(_activeVehicle) { + if(_activeVehicle.battery.percentRemaining.value > 75) { + return qgcPal.text + } + if(_activeVehicle.battery.percentRemaining.value > 50) { + return colorOrange + } + if(_activeVehicle.battery.percentRemaining.value > 0.1) { + return colorRed + } + } + return colorGrey + } + + function getRSSIColor(value) { + if(value >= 0) + return colorGrey; + if(value > -60) + return colorGreen; + if(value > -90) + return colorOrange; + return colorRed; + } + function getMessageColor() { if (_activeVehicle) { if (_activeVehicle.messageTypeNone) @@ -61,78 +89,273 @@ Item { return "N/A" } - Row { - id: indicatorRow - anchors.top: parent.top - anchors.bottom: parent.bottom - spacing: ScreenTools.defaultFontPixelWidth * 1.5 - visible: !_communicationLost + //--------------------------------------------- + // GPS Info + Component { + id: gpsInfo + + Rectangle { + width: gpsCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: gpsCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: gpsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(gpsGrid.width, gpsLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: gpsLabel + text: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? qsTr("GPS Status") : qsTr("GPS Data Unavailable") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: gpsGrid + visible: (_activeVehicle && _activeVehicle.gps.count.value >= 0) + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + + QGCLabel { text: qsTr("GPS Count:") } + QGCLabel { text: _activeVehicle ? _activeVehicle.gps.count.valueString : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("GPS Lock:") } + QGCLabel { text: _activeVehicle ? _activeVehicle.gps.lock.enumStringValue : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("HDOP:") } + QGCLabel { text: _activeVehicle ? _activeVehicle.gps.hdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("VDOP:") } + QGCLabel { text: _activeVehicle ? _activeVehicle.gps.vdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("Course Over Ground:") } + QGCLabel { text: _activeVehicle ? _activeVehicle.gps.courseOverGround.valueString : qsTr("--.--", "No data to display") } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + //--------------------------------------------- + // Battery Info + Component { + id: batteryInfo + + Rectangle { + width: battCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: battCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: battCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(battGrid.width, battLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: battLabel + text: qsTr("Battery Status") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: battGrid + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("Voltage:") } + QGCLabel { text: (_activeVehicle && _activeVehicle.battery.voltage.value != -1) ? (_activeVehicle.battery.voltage.valueString + " " + _activeVehicle.battery.voltage.units) : "N/A" } + QGCLabel { text: qsTr("Accumulated Consumption:") } + QGCLabel { text: (_activeVehicle && _activeVehicle.battery.mahConsumed.value != -1) ? (_activeVehicle.battery.mahConsumed.valueString + " " + _activeVehicle.battery.mahConsumed.units) : "N/A" } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + //--------------------------------------------- + // RC RSSI Info + Component { + id: rcRSSIInfo + + Rectangle { + width: rcrssiCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: rcrssiCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: rcrssiCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(rcrssiGrid.width, rssiLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: rssiLabel + text: _activeVehicle ? (_activeVehicle.rcRSSI != 255 ? qsTr("RC RSSI Status") : qsTr("RC RSSI Data Unavailable")) : qsTr("N/A", "No data available") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: rcrssiGrid + visible: _activeVehicle && _activeVehicle.rcRSSI != 255 + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("RSSI:") } + QGCLabel { text: _activeVehicle ? (_activeVehicle.rcRSSI + "%") : 0 } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + //--------------------------------------------- + // Telemetry RSSI Info + Component { + id: telemRSSIInfo + + Rectangle { + width: telemCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: telemCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: telemCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(telemGrid.width, telemLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: telemLabel + text: qsTr("Telemetry RSSI Status") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: telemGrid + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("Local RSSI:") } + QGCLabel { text: _controller.telemetryLRSSI + " dBm" } + QGCLabel { text: qsTr("Remote RSSI:") } + QGCLabel { text: _controller.telemetryRRSSI + " dBm" } + QGCLabel { text: qsTr("RX Errors:") } + QGCLabel { text: _controller.telemetryRXErrors } + QGCLabel { text: qsTr("Errors Fixed:") } + QGCLabel { text: _controller.telemetryFixed } + QGCLabel { text: qsTr("TX Buffer:") } + QGCLabel { text: _controller.telemetryTXBuffer } + QGCLabel { text: qsTr("Local Noise:") } + QGCLabel { text: _controller.telemetryLNoise } + QGCLabel { text: qsTr("Remote Noise:") } + QGCLabel { text: _controller.telemetryRNoise } + } + } - QGCPalette { id: qgcPal } + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + Row { + id: indicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth * 1.5 + visible: !_communicationLost //------------------------------------------------------------------------- //-- Message Indicator Item { - id: messages - width: mainWindow.tbCellHeight - height: mainWindow.tbCellHeight - visible: _activeVehicle && _activeVehicle.messageCount - anchors.verticalCenter: parent.verticalCenter - Item { - id: criticalMessage + id: messages + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + visible: _activeVehicle && _activeVehicle.messageCount + + Image { + id: criticalMessageIcon anchors.fill: parent + source: "/qmlimages/Yield.svg" + sourceSize.height: height + fillMode: Image.PreserveAspectFit + cache: false visible: _activeVehicle && _activeVehicle.messageCount > 0 && isMessageImportant - Image { - source: "/qmlimages/Yield.svg" - height: mainWindow.tbCellHeight * 0.75 - sourceSize.height: height - fillMode: Image.PreserveAspectFit - cache: false - visible: isMessageImportant - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } } - Item { + + QGCColoredImage { anchors.fill: parent - visible: !criticalMessage.visible - QGCColoredImage { - id: messageIcon - source: "/qmlimages/Megaphone.svg" - height: mainWindow.tbCellHeight * 0.5 - width: height - sourceSize.height: height - fillMode: Image.PreserveAspectFit - color: getMessageColor() - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - } + source: "/qmlimages/Megaphone.svg" + sourceSize.height: height + fillMode: Image.PreserveAspectFit + color: getMessageColor() + visible: !criticalMessageIcon.visible } + MouseArea { - anchors.fill: parent - onClicked: { - mainWindow.showMessageArea() - } + anchors.fill: parent + onClicked: mainWindow.showMessageArea() } } //------------------------------------------------------------------------- //-- GPS Indicator Item { - id: satelitte - width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 - height: mainWindow.tbCellHeight + id: satelitte + width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 + anchors.top: parent.top + anchors.bottom: parent.bottom QGCColoredImage { - id: gpsIcon - source: "/qmlimages/Gps.svg" - fillMode: Image.PreserveAspectFit - width: mainWindow.tbCellHeight * 0.65 - height: mainWindow.tbCellHeight * 0.5 - sourceSize.height: height - opacity: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? 1 : 0.5 - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter + id: gpsIcon + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + source: "/qmlimages/Gps.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + opacity: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? 1 : 0.5 + color: qgcPal.buttonText } Column { @@ -142,10 +365,10 @@ Item { anchors.left: gpsIcon.right QGCLabel { - anchors.horizontalCenter: hdopValue.horizontalCenter - visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) - color: qgcPal.buttonText - text: _activeVehicle ? _activeVehicle.gps.count.valueString : "" + anchors.horizontalCenter: hdopValue.horizontalCenter + visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) + color: qgcPal.buttonText + text: _activeVehicle ? _activeVehicle.gps.count.valueString : "" } QGCLabel { @@ -168,32 +391,39 @@ Item { //------------------------------------------------------------------------- //-- RC RSSI Item { - id: rcRssi - width: rssiRow.width * 1.1 - height: mainWindow.tbCellHeight - visible: _activeVehicle ? _activeVehicle.supportsRadio : true + id: rcRssi + width: rssiRow.width * 1.1 + anchors.top: parent.top + anchors.bottom: parent.bottom + visible: _activeVehicle ? _activeVehicle.supportsRadio : true + Row { - id: rssiRow - height: parent.height - spacing: ScreenTools.defaultFontPixelWidth + id: rssiRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth + QGCColoredImage { - width: mainWindow.tbCellHeight * 0.65 - height: width - sourceSize.height: height - source: "/qmlimages/RC.svg" - fillMode: Image.PreserveAspectFit - opacity: _activeVehicle ? (((_activeVehicle.rcRSSI < 0) || (_activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/qmlimages/RC.svg" + fillMode: Image.PreserveAspectFit + opacity: _activeVehicle ? (((_activeVehicle.rcRSSI < 0) || (_activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 + color: qgcPal.buttonText } + SignalStrength { - size: mainWindow.tbCellHeight * 0.5 - percent: _activeVehicle ? ((_activeVehicle.rcRSSI > 100) ? 0 : _activeVehicle.rcRSSI) : 0 anchors.verticalCenter: parent.verticalCenter + size: parent.height * 0.5 + percent: _activeVehicle ? ((_activeVehicle.rcRSSI > 100) ? 0 : _activeVehicle.rcRSSI) : 0 } } + MouseArea { anchors.fill: parent + onClicked: { var centerX = mapToItem(toolBar, x, y).x + (width / 2) mainWindow.showPopUp(rcRSSIInfo, centerX) @@ -203,23 +433,18 @@ Item { //------------------------------------------------------------------------- //-- Telemetry RSSI - Item { - id: telemRssi - width: telemIcon.width - height: mainWindow.tbCellHeight - visible: _controller.telemetryLRSSI < 0 - QGCColoredImage { - id: telemIcon - height: parent.height * 0.5 - sourceSize.height: height - width: height * 1.5 - source: "/qmlimages/TelemRSSI.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter - } + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/qmlimages/TelemRSSI.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.buttonText + visible: _controller.telemetryLRSSI < 0 + MouseArea { - anchors.fill: parent + anchors.fill: parent + onClicked: { var centerX = mapToItem(toolBar, x, y).x + (width / 2) mainWindow.showPopUp(telemRSSIInfo, centerX) @@ -230,27 +455,30 @@ Item { //------------------------------------------------------------------------- //-- Battery Indicator Item { - id: batteryStatus - width: battRow.width * 1.1 - height: mainWindow.tbCellHeight - opacity: (_activeVehicle && _activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 + anchors.top: parent.top + anchors.bottom: parent.bottom + width: batteryIndicatorRow.width + Row { - id: battRow - height: mainWindow.tbCellHeight - anchors.horizontalCenter: parent.horizontalCenter + id: batteryIndicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + opacity: (_activeVehicle && _activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 + QGCColoredImage { - height: mainWindow.tbCellHeight * 0.65 - width: height - sourceSize.width: width - source: "/qmlimages/Battery.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.text - anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.text } + QGCLabel { - text: getBatteryPercentageText() - font.pointSize: ScreenTools.mediumFontPointSize - color: getBatteryColor() + text: getBatteryPercentageText() + font.pointSize: ScreenTools.mediumFontPointSize + color: getBatteryColor() anchors.verticalCenter: parent.verticalCenter } } -- GitLab From fe5234694c66378115b82f4605b27a0850324d25 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 7 Dec 2016 16:33:54 -0500 Subject: [PATCH 099/398] Adding a crash guard for when a command resend timeout occurs with an empty command queue. --- src/Vehicle/Vehicle.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 82afb8489..6f7bf3519 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1838,6 +1838,12 @@ void Vehicle::sendMavCommand(int component, MAV_CMD command, bool showError, flo void Vehicle::_sendMavCommandAgain(void) { + if(!_mavCommandQueue.size()) { + qWarning() << "Command resend with no commands in queue"; + _mavCommandAckTimer.stop(); + return; + } + MavCommandQueueEntry_t& queuedCommand = _mavCommandQueue[0]; if (_mavCommandRetryCount++ > _mavCommandMaxRetryCount) { -- GitLab From 12e5dd7145134ec1b254275cecb7dd96722714b4 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 12:24:23 -0800 Subject: [PATCH 100/398] Correct handling of GPS_RAW_INT, GLOBAL_POSITION_INT, VFR_HUD * Moved from UAS * Remove unused code --- src/Vehicle/Vehicle.cc | 192 ++++++++++++++++++----------------------- src/Vehicle/Vehicle.h | 24 ++---- 2 files changed, 89 insertions(+), 127 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 6c0740bc7..df7b8afa3 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -85,11 +85,6 @@ Vehicle::Vehicle(LinkInterface* link, , _currentWarningCount(0) , _currentNormalCount(0) , _currentMessageType(MessageNone) - , _navigationAltitudeError(0.0f) - , _navigationSpeedError(0.0f) - , _navigationCrosstrackError(0.0f) - , _navigationTargetBearing(0.0f) - , _refreshTimer(new QTimer(this)) , _updateCount(0) , _rcRSSI(255) , _rcRSSIstore(255) @@ -99,6 +94,9 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) , _onboardControlSensorsUnhealthy(0) + , _useGpsRawIntForPosition(true) + , _useGpsRawIntForAltitude(true) + , _useAltitudeForAltitude(false) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -150,11 +148,6 @@ Vehicle::Vehicle(LinkInterface* link, connect(this, &Vehicle::armedChanged, this, &Vehicle::_announceArmedChanged); _uas = new UAS(_mavlink, this, _firmwarePluginManager); - setLatitude(_uas->getLatitude()); - setLongitude(_uas->getLongitude()); - - connect(_uas, &UAS::latitudeChanged, this, &Vehicle::setLatitude); - connect(_uas, &UAS::longitudeChanged, this, &Vehicle::setLongitude); connect(_uas, &UAS::imageReady, this, &Vehicle::_imageReady); connect(this, &Vehicle::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged); @@ -164,11 +157,6 @@ Vehicle::Vehicle(LinkInterface* link, // connect this vehicle to the follow me handle manager connect(this, &Vehicle::flightModeChanged,qgcApp()->toolbox()->followMe(), &FollowMe::followMeHandleManager); - // Refresh timer - connect(_refreshTimer, &QTimer::timeout, this, &Vehicle::_checkUpdate); - _refreshTimer->setInterval(UPDATE_TIMER); - _refreshTimer->start(UPDATE_TIMER); - // PreArm Error self-destruct timer connect(&_prearmErrorTimer, &QTimer::timeout, this, &Vehicle::_prearmErrorTimeout); _prearmErrorTimer.setInterval(_prearmErrorTimeoutMSecs); @@ -196,11 +184,6 @@ Vehicle::Vehicle(LinkInterface* link, connect(_mav, SIGNAL(attitudeChanged (UASInterface*,int,double,double,double,quint64)), this, SLOT(_updateAttitude(UASInterface*,int,double, double, double, quint64))); connect(_mav, SIGNAL(statusChanged (UASInterface*,QString,QString)), this, SLOT(_updateState(UASInterface*, QString,QString))); - connect(_mav, &UASInterface::speedChanged, this, &Vehicle::_updateSpeed); - connect(_mav, &UASInterface::altitudeChanged, this, &Vehicle::_updateAltitude); - connect(_mav, &UASInterface::navigationControllerErrorsChanged,this, &Vehicle::_updateNavigationControllerErrors); - connect(_mav, &UASInterface::NavigationControllerDataChanged, this, &Vehicle::_updateNavigationControllerData); - _loadSettings(); _missionManager = new MissionManager(this); @@ -284,11 +267,6 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _currentWarningCount(0) , _currentNormalCount(0) , _currentMessageType(MessageNone) - , _navigationAltitudeError(0.0f) - , _navigationSpeedError(0.0f) - , _navigationCrosstrackError(0.0f) - , _navigationTargetBearing(0.0f) - , _refreshTimer(new QTimer(this)) , _updateCount(0) , _rcRSSI(255) , _rcRSSIstore(255) @@ -298,6 +276,9 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) , _onboardControlSensorsUnhealthy(0) + , _useGpsRawIntForPosition(true) + , _useGpsRawIntForAltitude(true) + , _useAltitudeForAltitude(false) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -496,6 +477,18 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes case MAVLINK_MSG_ID_LOGGING_DATA_ACKED: _handleMavlinkLoggingDataAcked(message); break; + case MAVLINK_MSG_ID_GPS_RAW_INT: + _handleGpsRawInt(message); + break; + case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: + _handleGlobalPositionInt(message); + break; + case MAVLINK_MSG_ID_ALTITUDE: + _handleAltitude(message); + break; + case MAVLINK_MSG_ID_VFR_HUD: + _handleVfrHud(message); + break; // Following are ArduPilot dialect messages @@ -509,6 +502,70 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes _uas->receiveMessage(message); } +void Vehicle::_handleVfrHud(mavlink_message_t& message) +{ + mavlink_vfr_hud_t vfrHud; + mavlink_msg_vfr_hud_decode(&message, &vfrHud); + + _airSpeedFact.setRawValue(qIsNaN(vfrHud.airspeed) ? 0 : vfrHud.airspeed); + _groundSpeedFact.setRawValue(qIsNaN(vfrHud.groundspeed) ? 0 : vfrHud.groundspeed); + _climbRateFact.setRawValue(qIsNaN(vfrHud.climb) ? 0 : vfrHud.climb); +} + +void Vehicle::_handleGpsRawInt(mavlink_message_t& message) +{ + mavlink_gps_raw_int_t gpsRawInt; + mavlink_msg_gps_raw_int_decode(&message, &gpsRawInt); + + if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) { + if (_useGpsRawIntForPosition) { + setLatitude(gpsRawInt.lat / (double)1E7); + setLongitude(gpsRawInt.lon / (double)1E7); + } + if (_useGpsRawIntForAltitude) { + _altitudeRelativeFact.setRawValue(gpsRawInt.alt / 1000.0); + } + } + + _gpsFactGroup.count()->setRawValue(gpsRawInt.satellites_visible == 255 ? 0 : gpsRawInt.satellites_visible); + _gpsFactGroup.hdop()->setRawValue(gpsRawInt.eph == UINT16_MAX ? 1e10f : gpsRawInt.eph / 100.0); + _gpsFactGroup.vdop()->setRawValue(gpsRawInt.epv == UINT16_MAX ? 1e10f : gpsRawInt.epv / 100.0); + _gpsFactGroup.courseOverGround()->setRawValue(gpsRawInt.cog == UINT16_MAX ? 0.0 : gpsRawInt.cog / 100.0); + _gpsFactGroup.lock()->setRawValue(gpsRawInt.fix_type); + + if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) { + _setCoordinateValid(true); + } +} + +void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message) +{ + mavlink_global_position_int_t globalPositionInt; + mavlink_msg_global_position_int_decode(&message, &globalPositionInt); + + _useGpsRawIntForPosition = false; + _useGpsRawIntForAltitude = false; + + setLatitude(globalPositionInt.lat / (double)1E7); + setLongitude(globalPositionInt.lon / (double)1E7); + if (!_useAltitudeForAltitude) { + _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0); + _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0); + } +} + +void Vehicle::_handleAltitude(mavlink_message_t& message) +{ + mavlink_altitude_t altitude; + mavlink_msg_altitude_decode(&message, &altitude); + + _useAltitudeForAltitude = true; + _useGpsRawIntForAltitude = false; + _altitudeRelativeFact.setRawValue(altitude.altitude_relative / 1000.0); + _altitudeAMSLFact.setRawValue(altitude.altitude_amsl / 1000.0); + +} + void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& message) { mavlink_autopilot_version_t autopilotVersion; @@ -986,31 +1043,6 @@ void Vehicle::_updateAttitude(UASInterface* uas, int, double roll, double pitch, _updateAttitude(uas, roll, pitch, yaw, timestamp); } -void Vehicle::_updateSpeed(UASInterface*, double groundSpeed, double airSpeed, quint64) -{ - _groundSpeedFact.setRawValue(groundSpeed); - _airSpeedFact.setRawValue(airSpeed); -} - -void Vehicle::_updateAltitude(UASInterface*, double altitudeAMSL, double altitudeRelative, double climbRate, quint64) -{ - _altitudeAMSLFact.setRawValue(altitudeAMSL); - _altitudeRelativeFact.setRawValue(altitudeRelative); - _climbRateFact.setRawValue(climbRate); -} - -void Vehicle::_updateNavigationControllerErrors(UASInterface*, double altitudeError, double speedError, double xtrackError) { - _navigationAltitudeError = altitudeError; - _navigationSpeedError = speedError; - _navigationCrosstrackError = xtrackError; -} - -void Vehicle::_updateNavigationControllerData(UASInterface *uas, float, float, float, float targetBearing, float) { - if (_mav == uas) { - _navigationTargetBearing = targetBearing; - } -} - int Vehicle::motorCount(void) { switch (_vehicleType) { @@ -1046,19 +1078,6 @@ bool Vehicle::xConfigMotors(void) * Internal */ -void Vehicle::_checkUpdate() -{ - // Update current location - if(_mav) { - if(latitude() != _mav->getLatitude()) { - setLatitude(_mav->getLatitude()); - } - if(longitude() != _mav->getLongitude()) { - setLongitude(_mav->getLongitude()); - } - } -} - QString Vehicle::getMavIconColor() { // TODO: Not using because not only the colors are ghastly, it doesn't respect dark/light palette @@ -2115,53 +2134,6 @@ void Vehicle::setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData) void VehicleGPSFactGroup::setVehicle(Vehicle* vehicle) { _vehicle = vehicle; - - if (!vehicle) { - // Disconnected Vehicle - return; - } - - connect(_vehicle->uas(), &UASInterface::localizationChanged, this, &VehicleGPSFactGroup::_setSatLoc); - - UAS* pUas = dynamic_cast(_vehicle->uas()); - connect(pUas, &UAS::satelliteCountChanged, this, &VehicleGPSFactGroup::_setSatelliteCount); - connect(pUas, &UAS::satRawHDOPChanged, this, &VehicleGPSFactGroup::_setSatRawHDOP); - connect(pUas, &UAS::satRawVDOPChanged, this, &VehicleGPSFactGroup::_setSatRawVDOP); - connect(pUas, &UAS::satRawCOGChanged, this, &VehicleGPSFactGroup::_setSatRawCOG); -} - -void VehicleGPSFactGroup::_setSatelliteCount(double val, QString) -{ - // I'm assuming that a negative value or over 99 means there is no GPS - if(val < 0.0) val = -1.0; - if(val > 99.0) val = -1.0; - - _countFact.setRawValue(val); -} - -void VehicleGPSFactGroup::_setSatRawHDOP(double val) -{ - _hdopFact.setRawValue(val); -} - -void VehicleGPSFactGroup::_setSatRawVDOP(double val) -{ - _vdopFact.setRawValue(val); -} - -void VehicleGPSFactGroup::_setSatRawCOG(double val) -{ - _courseOverGroundFact.setRawValue(val); -} - -void VehicleGPSFactGroup::_setSatLoc(UASInterface*, int fix) -{ - _lockFact.setRawValue(fix); - - // fix 0: lost, 1: at least one satellite, but no GPS fix, 2: 2D lock, 3: 3D lock - if (fix > 2) { - _vehicle->_setCoordinateValid(true); - } } const char* VehicleBatteryFactGroup::_voltageFactName = "voltage"; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 1f8eb3cb3..2d6bdcd4e 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -137,13 +137,6 @@ public: static const char* _countFactName; static const char* _lockFactName; -private slots: - void _setSatelliteCount(double val, QString); - void _setSatRawHDOP(double val); - void _setSatRawVDOP(double val); - void _setSatRawCOG(double val); - void _setSatLoc(UASInterface*, int fix); - private: Vehicle* _vehicle; Fact _hdopFact; @@ -683,11 +676,6 @@ private slots: void _updateAttitude (UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp); /** @brief Attitude from one specific component / redundant autopilot */ void _updateAttitude (UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp); - void _updateSpeed (UASInterface* uas, double _groundSpeed, double _airSpeed, quint64 timestamp); - void _updateAltitude (UASInterface* uas, double _altitudeAMSL, double _altitudeRelative, double _climbRate, quint64 timestamp); - void _updateNavigationControllerErrors (UASInterface* uas, double altitudeError, double speedError, double xtrackError); - void _updateNavigationControllerData (UASInterface *uas, float navRoll, float navPitch, float navBearing, float targetBearing, float targetDistance); - void _checkUpdate (); void _updateState (UASInterface* system, QString name, QString description); /** @brief A new camera image has arrived */ void _imageReady (UASInterface* uas); @@ -716,6 +704,10 @@ private: void _handleCommandAck(mavlink_message_t& message); void _handleAutopilotVersion(LinkInterface* link, mavlink_message_t& message); void _handleHilActuatorControls(mavlink_message_t& message); + void _handleGpsRawInt(mavlink_message_t& message); + void _handleGlobalPositionInt(mavlink_message_t& message); + void _handleAltitude(mavlink_message_t& message); + void _handleVfrHud(mavlink_message_t& message); void _missionManagerError(int errorCode, const QString& errorMsg); void _geoFenceManagerError(int errorCode, const QString& errorMsg); void _rallyPointManagerError(int errorCode, const QString& errorMsg); @@ -763,11 +755,6 @@ private: int _currentNormalCount; MessageType_t _currentMessageType; QString _latestError; - float _navigationAltitudeError; - float _navigationSpeedError; - float _navigationCrosstrackError; - float _navigationTargetBearing; - QTimer* _refreshTimer; QString _currentState; int _updateCount; QString _formatedMessage; @@ -779,6 +766,9 @@ private: uint32_t _onboardControlSensorsEnabled; uint32_t _onboardControlSensorsHealth; uint32_t _onboardControlSensorsUnhealthy; + bool _useGpsRawIntForPosition; + bool _useGpsRawIntForAltitude; + bool _useAltitudeForAltitude; typedef struct { int component; -- GitLab From 8c548e56c47352ec84f5087ab6ffc64d0e1782ce Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 12:24:38 -0800 Subject: [PATCH 101/398] Satellite count should be int --- src/Vehicle/GPSFact.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Vehicle/GPSFact.json b/src/Vehicle/GPSFact.json index 938e3dd37..db76da950 100644 --- a/src/Vehicle/GPSFact.json +++ b/src/Vehicle/GPSFact.json @@ -29,7 +29,6 @@ { "name": "count", "shortDescription": "Sat Count", - "type": "double", - "decimalPlaces": 0 + "type": "uint32" } ] -- GitLab From 099fe046c4d4dd7ac398adc3c52af7da2f4bb840 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 12:24:54 -0800 Subject: [PATCH 102/398] Remove unused code * Some code moved to Vehicle --- src/uas/UAS.cc | 241 +---------------------------------------- src/uas/UAS.h | 232 +-------------------------------------- src/uas/UASInterface.h | 56 ---------- 3 files changed, 3 insertions(+), 526 deletions(-) diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index b7ada3e58..a7c74fab3 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -7,14 +7,7 @@ * ****************************************************************************/ - -/** - * @file - * @brief Represents one unmanned aerial vehicle - * - * @author Lorenz Meier - * - */ +// THIS CLASS IS DEPRECATED. ALL NEW FUNCTIONALITY SHOULD GO INTO Vehicle class #include #include @@ -47,13 +40,7 @@ QGC_LOGGING_CATEGORY(UASLog, "UASLog") -/** -* Gets the settings from the previous UAS (name, airframe, autopilot, battery specs) -* by calling readSettings. This means the new UAS will have the same settings -* as the previous one created unless one calls deleteSettings in the code after -* creating the UAS. -*/ - +// THIS CLASS IS DEPRECATED. ALL NEW FUNCTIONALITY SHOULD GO INTO Vehicle class UAS::UAS(MAVLinkProtocol* protocol, Vehicle* vehicle, FirmwarePluginManager * firmwarePluginManager) : UASInterface(), lipoFull(4.2f), lipoEmpty(3.5f), @@ -77,30 +64,6 @@ UAS::UAS(MAVLinkProtocol* protocol, Vehicle* vehicle, FirmwarePluginManager * fi manualYawAngle(0), manualThrust(0), - isGlobalPositionKnown(false), - - latitude(0.0), - longitude(0.0), - altitudeAMSL(0.0), - altitudeAMSLFT(0.0), - altitudeRelative(0.0), - - satRawHDOP(1e10f), - satRawVDOP(1e10f), - satRawCOG(0.0), - - globalEstimatorActive(false), - - latitude_gps(0.0), - longitude_gps(0.0), - altitude_gps(0.0), - - speedX(0.0), - speedY(0.0), - speedZ(0.0), - - airSpeed(std::numeric_limits::quiet_NaN()), - groundSpeed(std::numeric_limits::quiet_NaN()), #ifndef __mobile__ fileManager(this, vehicle), #endif @@ -283,14 +246,6 @@ void UAS::receiveMessage(mavlink_message_t message) emit valueChanged(uasId, name.arg("custom_mode"), "bits", state.custom_mode, time); emit valueChanged(uasId, name.arg("system_status"), "-", state.system_status, time); - if ((state.system_status != this->status) && state.system_status != MAV_STATE_UNINIT) - { - this->status = state.system_status; - getStatusForCode((int)state.system_status, uasState, stateDescription); - emit statusChanged(this, uasState, stateDescription); - emit statusChanged(this->status); - } - // We got the mode receivedMode = true; } @@ -318,25 +273,7 @@ void UAS::receiveMessage(mavlink_message_t message) emit valueChanged(uasId, name.arg("errors_count4"), "-", state.errors_count4, time); // Process CPU load. - emit loadChanged(this,state.load/10.0f); emit valueChanged(uasId, name.arg("load"), "%", state.load/10.0f, time); - - // control_sensors_enabled: - // relevant bits: 11: attitude stabilization, 12: yaw position, 13: z/altitude control, 14: x/y position control - emit attitudeControlEnabled(state.onboard_control_sensors_enabled & (1 << 11)); - emit positionYawControlEnabled(state.onboard_control_sensors_enabled & (1 << 12)); - emit positionZControlEnabled(state.onboard_control_sensors_enabled & (1 << 13)); - emit positionXYControlEnabled(state.onboard_control_sensors_enabled & (1 << 14)); - - // Trigger drop rate updates as needed. Here we convert the incoming - // drop_rate_comm value from 1/100 of a percent in a uint16 to a true - // percentage as a float. We also cap the incoming value at 100% as defined - // by the MAVLink specifications. - if (state.drop_rate_comm > 10000) - { - state.drop_rate_comm = 10000; - } - emit dropRateChanged(this->getUASID(), state.drop_rate_comm/100.0f); emit valueChanged(uasId, name.arg("drop_rate_comm"), "%", state.drop_rate_comm/100.0f, time); } break; @@ -357,7 +294,6 @@ void UAS::receiveMessage(mavlink_message_t message) attitudeKnown = true; emit attitudeChanged(this, getRoll(), getPitch(), getYaw(), time); - emit attitudeRotationRatesChanged(uasId, attitude.rollspeed, attitude.pitchspeed, attitude.yawspeed, time); } } break; @@ -418,7 +354,6 @@ void UAS::receiveMessage(mavlink_message_t message) attitudeKnown = true; emit attitudeChanged(this, getRoll(), getPitch(), getYaw(), time); - emit attitudeRotationRatesChanged(uasId, attitude.rollspeed, attitude.pitchspeed, attitude.yawspeed, time); } } break; @@ -434,41 +369,12 @@ void UAS::receiveMessage(mavlink_message_t message) mavlink_vfr_hud_t hud; mavlink_msg_vfr_hud_decode(&message, &hud); quint64 time = getUnixTime(); - // Display updated values - emit thrustChanged(this, hud.throttle/100.0); if (!attitudeKnown) { setYaw(QGC::limitAngleToPMPId((((double)hud.heading)/180.0)*M_PI)); emit attitudeChanged(this, getRoll(), getPitch(), getYaw(), time); } - - setAltitudeAMSL(hud.alt); - setGroundSpeed(hud.groundspeed); - if (!qIsNaN(hud.airspeed)) - setAirSpeed(hud.airspeed); - speedZ = -hud.climb; - emit altitudeChanged(this, altitudeAMSL, altitudeRelative, -speedZ, time); - emit speedChanged(this, groundSpeed, airSpeed, time); - } - break; - case MAVLINK_MSG_ID_LOCAL_POSITION_NED: - //std::cerr << std::endl; - //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; - { - mavlink_local_position_ned_t pos; - mavlink_msg_local_position_ned_decode(&message, &pos); - quint64 time = getUnixTime(pos.time_boot_ms); - - if (!wrongComponent) - { - speedX = pos.vx; - speedY = pos.vy; - speedZ = pos.vz; - - // Emit - emit velocityChanged_NED(this, speedX, speedY, speedZ, time); - } } break; case MAVLINK_MSG_ID_GLOBAL_VISION_POSITION_ESTIMATE: @@ -479,114 +385,6 @@ void UAS::receiveMessage(mavlink_message_t message) emit attitudeChanged(this, message.compid, pos.roll, pos.pitch, pos.yaw, time); } break; - case MAVLINK_MSG_ID_GLOBAL_POSITION_INT: - //std::cerr << std::endl; - //std::cerr << "Decoded attitude message:" << " roll: " << std::dec << mavlink_msg_attitude_get_roll(message.payload) << " pitch: " << mavlink_msg_attitude_get_pitch(message.payload) << " yaw: " << mavlink_msg_attitude_get_yaw(message.payload) << std::endl; - { - mavlink_global_position_int_t pos; - mavlink_msg_global_position_int_decode(&message, &pos); - - quint64 time = getUnixTime(); - - setLatitude(pos.lat/(double)1E7); - setLongitude(pos.lon/(double)1E7); - setAltitudeRelative(pos.relative_alt/1000.0); - - globalEstimatorActive = true; - - speedX = pos.vx/100.0; - speedY = pos.vy/100.0; - speedZ = pos.vz/100.0; - - emit globalPositionChanged(this, getLatitude(), getLongitude(), getAltitudeAMSL(), time); - emit altitudeChanged(this, altitudeAMSL, altitudeRelative, -speedZ, time); - // We had some frame mess here, global and local axes were mixed. - emit velocityChanged_NED(this, speedX, speedY, speedZ, time); - - setGroundSpeed(qSqrt(speedX*speedX+speedY*speedY)); - emit speedChanged(this, groundSpeed, airSpeed, time); - - isGlobalPositionKnown = true; - } - break; - case MAVLINK_MSG_ID_GPS_RAW_INT: - { - mavlink_gps_raw_int_t pos; - mavlink_msg_gps_raw_int_decode(&message, &pos); - - quint64 time = getUnixTime(pos.time_usec); - - // TODO: track localization state not only for gps but also for other loc. sources - int loc_type = pos.fix_type; - if (loc_type == 1) - { - loc_type = 0; - } - setSatelliteCount(pos.satellites_visible); - - if (pos.fix_type > 2) - { - isGlobalPositionKnown = true; - - latitude_gps = pos.lat/(double)1E7; - longitude_gps = pos.lon/(double)1E7; - altitude_gps = pos.alt/1000.0; - - // If no GLOBAL_POSITION_INT messages ever received, use these raw GPS values instead. - if (!globalEstimatorActive) { - setLatitude(latitude_gps); - setLongitude(longitude_gps); - emit globalPositionChanged(this, getLatitude(), getLongitude(), getAltitudeAMSL(), time); - emit altitudeChanged(this, altitudeAMSL, altitudeRelative, -speedZ, time); - - float vel = pos.vel/100.0f; - // Smaller than threshold and not NaN - if ((vel < 1000000) && !qIsNaN(vel) && !qIsInf(vel)) { - setGroundSpeed(vel); - emit speedChanged(this, groundSpeed, airSpeed, time); - } else { - emit textMessageReceived(uasId, message.compid, MAV_SEVERITY_NOTICE, QString("GCS ERROR: RECEIVED INVALID SPEED OF %1 m/s").arg(vel)); - } - } - } - - double dtmp; - //-- Raw GPS data - dtmp = pos.eph == 0xFFFF ? 1e10f : pos.eph / 100.0; - if(dtmp != satRawHDOP) - { - satRawHDOP = dtmp; - emit satRawHDOPChanged(satRawHDOP); - } - dtmp = pos.epv == 0xFFFF ? 1e10f : pos.epv / 100.0; - if(dtmp != satRawVDOP) - { - satRawVDOP = dtmp; - emit satRawVDOPChanged(satRawVDOP); - } - dtmp = pos.cog == 0xFFFF ? 0.0 : pos.cog / 100.0; - if(dtmp != satRawCOG) - { - satRawCOG = dtmp; - emit satRawCOGChanged(satRawCOG); - } - - // Emit this signal after the above signals. This way a trigger on gps lock signal which then asks for vehicle position - // gets a good position. - emit localizationChanged(this, loc_type); - } - break; - case MAVLINK_MSG_ID_GPS_STATUS: - { - mavlink_gps_status_t pos; - mavlink_msg_gps_status_decode(&message, &pos); - for(int i = 0; i < (int)pos.satellites_visible; i++) - { - emit gpsSatelliteStatusChanged(uasId, (unsigned char)pos.satellite_prn[i], (unsigned char)pos.satellite_elevation[i], (unsigned char)pos.satellite_azimuth[i], (unsigned char)pos.satellite_snr[i], static_cast(pos.satellite_used[i])); - } - setSatelliteCount(pos.satellites_visible); - } - break; case MAVLINK_MSG_ID_PARAM_VALUE: { @@ -610,7 +408,6 @@ void UAS::receiveMessage(mavlink_message_t message) float roll, pitch, yaw; mavlink_quaternion_to_euler(out.q, &roll, &pitch, &yaw); quint64 time = getUnixTimeFromMs(out.time_boot_ms); - emit attitudeThrustSetPointChanged(this, roll, pitch, yaw, out.thrust, time); // For plotting emit roll sp, pitch sp and yaw sp values emit valueChanged(uasId, "roll sp", "rad", roll, time); @@ -619,25 +416,6 @@ void UAS::receiveMessage(mavlink_message_t message) } break; - case MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED: - { - if (multiComponentSourceDetected && wrongComponent) - { - break; - } - mavlink_position_target_local_ned_t p; - mavlink_msg_position_target_local_ned_decode(&message, &p); - quint64 time = getUnixTimeFromMs(p.time_boot_ms); - emit positionSetPointsChanged(uasId, p.x, p.y, p.z, 0/* XXX remove yaw and move it to attitude */, time); - } - break; - case MAVLINK_MSG_ID_SET_POSITION_TARGET_LOCAL_NED: - { - mavlink_set_position_target_local_ned_t p; - mavlink_msg_set_position_target_local_ned_decode(&message, &p); - emit userPositionSetPointsChanged(uasId, p.x, p.y, p.z, 0/* XXX remove yaw and move it to attitude */); - } - break; case MAVLINK_MSG_ID_STATUSTEXT: { QByteArray b; @@ -718,17 +496,6 @@ void UAS::receiveMessage(mavlink_message_t message) } break; - case MAVLINK_MSG_ID_NAV_CONTROLLER_OUTPUT: - { - mavlink_nav_controller_output_t p; - mavlink_msg_nav_controller_output_decode(&message,&p); - setDistToWaypoint(p.wp_dist); - setBearingToWaypoint(p.nav_bearing); - emit navigationControllerErrorsChanged(this, p.alt_error, p.aspd_error, p.xtrack_error); - emit NavigationControllerDataChanged(this, p.nav_roll, p.nav_pitch, p.nav_bearing, p.target_bearing, p.wp_dist); - } - break; - case MAVLINK_MSG_ID_LOG_ENTRY: { mavlink_log_entry_t log; @@ -1388,8 +1155,6 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t } _vehicle->sendMessageOnLink(_vehicle->priorityLink(), message); - // Emit an update in control values to other UI elements, like the HSI display - emit attitudeThrustSetPointChanged(this, roll, pitch, yaw, thrust, QGC::groundTimeMilliseconds()); } } @@ -1428,8 +1193,6 @@ void UAS::setManual6DOFControlCommands(double x, double y, double z, double roll MAV_FRAME_LOCAL_NED, position_mask, x, y, z, 0, 0, 0, 0, 0, 0, yaw, yawrate); _vehicle->sendMessageOnLink(_vehicle->priorityLink(), message); qDebug() << __FILE__ << __LINE__ << ": SENT 6DOF CONTROL MESSAGES: x" << x << " y: " << y << " z: " << z << " roll: " << roll << " pitch: " << pitch << " yaw: " << yaw; - - //emit attitudeThrustSetPointChanged(this, roll, pitch, yaw, thrust, QGC::groundTimeMilliseconds()); } else { diff --git a/src/uas/UAS.h b/src/uas/UAS.h index ad9b94646..4dad4d5fd 100644 --- a/src/uas/UAS.h +++ b/src/uas/UAS.h @@ -65,197 +65,13 @@ public: /** @brief The time interval the robot is switched on */ quint64 getUptime() const; - Q_PROPERTY(double latitude READ getLatitude WRITE setLatitude NOTIFY latitudeChanged) - Q_PROPERTY(double longitude READ getLongitude WRITE setLongitude NOTIFY longitudeChanged) - Q_PROPERTY(double satelliteCount READ getSatelliteCount WRITE setSatelliteCount NOTIFY satelliteCountChanged) - Q_PROPERTY(bool isGlobalPositionKnown READ globalPositionKnown) Q_PROPERTY(double roll READ getRoll WRITE setRoll NOTIFY rollChanged) Q_PROPERTY(double pitch READ getPitch WRITE setPitch NOTIFY pitchChanged) Q_PROPERTY(double yaw READ getYaw WRITE setYaw NOTIFY yawChanged) - Q_PROPERTY(double distToWaypoint READ getDistToWaypoint WRITE setDistToWaypoint NOTIFY distToWaypointChanged) - Q_PROPERTY(double airSpeed READ getGroundSpeed WRITE setGroundSpeed NOTIFY airSpeedChanged) - Q_PROPERTY(double groundSpeed READ getGroundSpeed WRITE setGroundSpeed NOTIFY groundSpeedChanged) - Q_PROPERTY(double bearingToWaypoint READ getBearingToWaypoint WRITE setBearingToWaypoint NOTIFY bearingToWaypointChanged) - Q_PROPERTY(double altitudeAMSL READ getAltitudeAMSL WRITE setAltitudeAMSL NOTIFY altitudeAMSLChanged) - Q_PROPERTY(double altitudeAMSLFT READ getAltitudeAMSLFT NOTIFY altitudeAMSLFTChanged) - Q_PROPERTY(double altitudeRelative READ getAltitudeRelative WRITE setAltitudeRelative NOTIFY altitudeRelativeChanged) - Q_PROPERTY(double satRawHDOP READ getSatRawHDOP NOTIFY satRawHDOPChanged) - Q_PROPERTY(double satRawVDOP READ getSatRawVDOP NOTIFY satRawVDOPChanged) - Q_PROPERTY(double satRawCOG READ getSatRawCOG NOTIFY satRawCOGChanged) /// Vehicle is about to go away void shutdownVehicle(void); - void setGroundSpeed(double val) - { - groundSpeed = val; - emit groundSpeedChanged(val,"groundSpeed"); - emit valueChanged(this->uasId,"groundSpeed","m/s",QVariant(val),getUnixTime()); - } - double getGroundSpeed() const - { - return groundSpeed; - } - - void setAirSpeed(double val) - { - airSpeed = val; - emit airSpeedChanged(val,"airSpeed"); - emit valueChanged(this->uasId,"airSpeed","m/s",QVariant(val),getUnixTime()); - } - - double getAirSpeed() const - { - return airSpeed; - } - - void setLocalX(double val) - { - localX = val; - emit localXChanged(val,"localX"); - emit valueChanged(this->uasId,"localX","m",QVariant(val),getUnixTime()); - } - - double getLocalX() const - { - return localX; - } - - void setLocalY(double val) - { - localY = val; - emit localYChanged(val,"localY"); - emit valueChanged(this->uasId,"localY","m",QVariant(val),getUnixTime()); - } - double getLocalY() const - { - return localY; - } - - void setLocalZ(double val) - { - localZ = val; - emit localZChanged(val,"localZ"); - emit valueChanged(this->uasId,"localZ","m",QVariant(val),getUnixTime()); - } - double getLocalZ() const - { - return localZ; - } - - void setLatitude(double val) - { - latitude = val; - emit latitudeChanged(val,"latitude"); - emit valueChanged(this->uasId,"latitude","deg",QVariant(val),getUnixTime()); - } - - double getLatitude() const - { - return latitude; - } - - void setLongitude(double val) - { - longitude = val; - emit longitudeChanged(val,"longitude"); - emit valueChanged(this->uasId,"longitude","deg",QVariant(val),getUnixTime()); - } - - double getLongitude() const - { - return longitude; - } - - void setAltitudeAMSL(double val) - { - altitudeAMSL = val; - emit altitudeAMSLChanged(val, "altitudeAMSL"); - emit valueChanged(this->uasId,"altitudeAMSL","m",QVariant(altitudeAMSL),getUnixTime()); - altitudeAMSLFT = 3.28084 * altitudeAMSL; - emit altitudeAMSLFTChanged(val, "altitudeAMSLFT"); - emit valueChanged(this->uasId,"altitudeAMSLFT","m",QVariant(altitudeAMSLFT),getUnixTime()); - } - - double getAltitudeAMSL() const - { - return altitudeAMSL; - } - - double getAltitudeAMSLFT() const - { - return altitudeAMSLFT; - } - - void setAltitudeRelative(double val) - { - altitudeRelative = val; - emit altitudeRelativeChanged(val, "altitudeRelative"); - emit valueChanged(this->uasId,"altitudeRelative","m",QVariant(val),getUnixTime()); - } - - double getAltitudeRelative() const - { - return altitudeRelative; - } - - double getSatRawHDOP() const - { - return satRawHDOP; - } - - double getSatRawVDOP() const - { - return satRawVDOP; - } - - double getSatRawCOG() const - { - return satRawCOG; - } - - void setSatelliteCount(double val) - { - satelliteCount = val; - emit satelliteCountChanged(val,"satelliteCount"); - emit valueChanged(this->uasId,"satelliteCount","",QVariant(val),getUnixTime()); - } - - double getSatelliteCount() const - { - return satelliteCount; - } - - virtual bool globalPositionKnown() const - { - return isGlobalPositionKnown; - } - - void setDistToWaypoint(double val) - { - distToWaypoint = val; - emit distToWaypointChanged(val,"distToWaypoint"); - emit valueChanged(this->uasId,"distToWaypoint","m",QVariant(val),getUnixTime()); - } - - double getDistToWaypoint() const - { - return distToWaypoint; - } - - void setBearingToWaypoint(double val) - { - bearingToWaypoint = val; - emit bearingToWaypointChanged(val,"bearingToWaypoint"); - emit valueChanged(this->uasId,"bearingToWaypoint","deg",QVariant(val),getUnixTime()); - } - - double getBearingToWaypoint() const - { - return bearingToWaypoint; - } - - void setRoll(double val) { roll = val; @@ -377,34 +193,6 @@ protected: //COMMENTS FOR TEST UNIT /// POSITION bool isGlobalPositionKnown; ///< If the global position has been received for this MAV - double localX; - double localY; - double localZ; - - double latitude; ///< Global latitude as estimated by position estimator - double longitude; ///< Global longitude as estimated by position estimator - double altitudeAMSL; ///< Global altitude as estimated by position estimator, AMSL - double altitudeAMSLFT; ///< Global altitude as estimated by position estimator, AMSL - double altitudeRelative; ///< Altitude above home as estimated by position estimator - - double satRawHDOP; - double satRawVDOP; - double satRawCOG; - - double satelliteCount; ///< Number of satellites visible to raw GPS - bool globalEstimatorActive; ///< Global position estimator present, do not fall back to GPS raw for position - double latitude_gps; ///< Global latitude as estimated by raw GPS - double longitude_gps; ///< Global longitude as estimated by raw GPS - double altitude_gps; ///< Global altitude as estimated by raw GPS - double speedX; ///< True speed in X axis - double speedY; ///< True speed in Y axis - double speedZ; ///< True speed in Z axis - - /// WAYPOINT NAVIGATION - double distToWaypoint; ///< Distance to next waypoint - double airSpeed; ///< Airspeed - double groundSpeed; ///< Groundspeed - double bearingToWaypoint; ///< Bearing to next waypoint #ifndef __mobile__ FileManager fileManager; #endif @@ -545,34 +333,16 @@ public slots: /** @brief Send command to disable all bindings/maps between RC and parameters */ void unsetRCToParameterMap(); signals: - void loadChanged(UASInterface* uas, double load); void imageStarted(quint64 timestamp); /** @brief A new camera image has arrived */ void imageReady(UASInterface* uas); /** @brief HIL controls have changed */ void hilControlsChanged(quint64 time, float rollAilerons, float pitchElevator, float yawRudder, float throttle, quint8 systemMode, quint8 navMode); - void localXChanged(double val,QString name); - void localYChanged(double val,QString name); - void localZChanged(double val,QString name); - void longitudeChanged(double val,QString name); - void latitudeChanged(double val,QString name); - void altitudeAMSLChanged(double val,QString name); - void altitudeAMSLFTChanged(double val,QString name); - void altitudeRelativeChanged(double val,QString name); - - void satRawHDOPChanged (double value); - void satRawVDOPChanged (double value); - void satRawCOGChanged (double value); - void rollChanged(double val,QString name); void pitchChanged(double val,QString name); void yawChanged(double val,QString name); - void satelliteCountChanged(double val,QString name); - void distToWaypointChanged(double val,QString name); - void groundSpeedChanged(double val, QString name); - void airSpeedChanged(double val, QString name); - void bearingToWaypointChanged(double val,QString name); + protected: /** @brief Get the UNIX timestamp in milliseconds, enter microseconds */ quint64 getUnixTime(quint64 time=0); diff --git a/src/uas/UASInterface.h b/src/uas/UASInterface.h index 8ae866f9b..8ce2460b9 100644 --- a/src/uas/UASInterface.h +++ b/src/uas/UASInterface.h @@ -50,12 +50,6 @@ public: /** @brief The time interval the robot is switched on **/ virtual quint64 getUptime() const = 0; - virtual double getLatitude() const = 0; - virtual double getLongitude() const = 0; - virtual double getAltitudeAMSL() const = 0; - virtual double getAltitudeRelative() const = 0; - virtual bool globalPositionKnown() const = 0; - virtual double getRoll() const = 0; virtual double getPitch() const = 0; virtual double getYaw() const = 0; @@ -172,16 +166,6 @@ protected: QColor color; signals: - /** @brief The robot state has changed */ - void statusChanged(int stateFlag); - /** @brief The robot state has changed - * - * @param uas this robot - * @param status short description of status, e.g. "connected" - * @param description longer textual description. Should be however limited to a short text, e.g. 200 chars. - */ - void statusChanged(UASInterface* uas, QString status, QString description); - /** @brief A text message from the system has been received */ void textMessageReceived(int uasid, int componentid, int severity, QString text); @@ -200,13 +184,6 @@ signals: */ void errCountChanged(int uasid, QString component, QString device, int count); - /** - * @brief Drop rate of communication link updated - * - * @param systemId id of the air system - * @param receiveDrop drop rate of packets this MAV receives (sent from GCS or other MAVs) - */ - void dropRateChanged(int systemId, float receiveDrop); /** @brief The robot is connected **/ void connected(); /** @brief The robot is disconnected **/ @@ -238,39 +215,12 @@ signals: */ void batteryChanged(UASInterface* uas, double voltage, double current, double percent, int seconds); void statusChanged(UASInterface* uas, QString status); - void thrustChanged(UASInterface*, double thrust); void attitudeChanged(UASInterface*, double roll, double pitch, double yaw, quint64 usec); void attitudeChanged(UASInterface*, int component, double roll, double pitch, double yaw, quint64 usec); - void attitudeRotationRatesChanged(int uas, double rollrate, double pitchrate, double yawrate, quint64 usec); - void attitudeThrustSetPointChanged(UASInterface*, float rollDesired, float pitchDesired, float yawDesired, float thrustDesired, quint64 usec); - /** @brief The MAV set a new setpoint in the local (not body) NED X, Y, Z frame */ - void positionSetPointsChanged(int uasid, float xDesired, float yDesired, float zDesired, float yawDesired, quint64 usec); - /** @brief A user (or an autonomous mission or obstacle avoidance planner) requested to set a new setpoint */ - void userPositionSetPointsChanged(int uasid, float xDesired, float yDesired, float zDesired, float yawDesired); - void globalPositionChanged(UASInterface*, double lat, double lon, double altAMSL, quint64 usec); - void altitudeChanged(UASInterface*, double altitudeAMSL, double altitudeRelative, double climbRate, quint64 usec); - /** @brief Update the status of one satellite used for localization */ - void gpsSatelliteStatusChanged(int uasid, int satid, float azimuth, float direction, float snr, bool used); - - // The horizontal speed (a scalar) - void speedChanged(UASInterface* uas, double groundSpeed, double airSpeed, quint64 usec); - // Consider adding a MAV_FRAME parameter to this; could help specifying what the 3 scalars are. - void velocityChanged_NED(UASInterface*, double vx, double vy, double vz, quint64 usec); - - void navigationControllerErrorsChanged(UASInterface*, double altitudeError, double speedError, double xtrackError); - void NavigationControllerDataChanged(UASInterface *uas, float navRoll, float navPitch, float navBearing, float targetBearing, float targetDist); void imageStarted(int imgid, int width, int height, int depth, int channels); void imageDataReceived(int imgid, const unsigned char* imageData, int length, int startIndex); - /** @brief Attitude control enabled/disabled */ - void attitudeControlEnabled(bool enabled); - /** @brief Position 2D control enabled/disabled */ - void positionXYControlEnabled(bool enabled); - /** @brief Altitude control enabled/disabled */ - void positionZControlEnabled(bool enabled); - /** @brief Heading control enabled/disabled */ - void positionYawControlEnabled(bool enabled); /** @brief Optical flow status changed */ void opticalFlowStatusChanged(bool supported, bool enabled, bool ok); /** @brief Vision based localization status changed */ @@ -288,12 +238,6 @@ signals: /** @brief Differential pressure / airspeed status changed */ void airspeedStatusChanged(bool supported, bool enabled, bool ok); - /** - * @brief Localization quality changed - * @param fix 0: lost, 1: 2D local position hold, 2: 2D localization, 3: 3D localization - */ - void localizationChanged(UASInterface* uas, int fix); - // ERROR AND STATUS SIGNALS /** @brief Name of system changed */ void nameChanged(QString newName); -- GitLab From 004bda505bd54f34708fddf8e793d6a643f89bc5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 12:25:10 -0800 Subject: [PATCH 103/398] Correct guided mode AMSL versus Relative handling --- src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc | 7 +------ src/FlightDisplay/FlightDisplayViewWidgets.qml | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index dd9350fa6..dee9bc789 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -120,16 +120,11 @@ void ArduCopterFirmwarePlugin::guidedModeLand(Vehicle* vehicle) void ArduCopterFirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) { - if (qIsNaN(vehicle->altitudeAMSL()->rawValue().toDouble())) { - qgcApp()->showMessage(QStringLiteral("Unable to takeoff, vehicle position not known.")); - return; - } - vehicle->sendMavCommand(vehicle->defaultComponentId(), MAV_CMD_NAV_TAKEOFF, true, // show error 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - vehicle->altitudeAMSL()->rawValue().toFloat() + altitudeRel); // AMSL meters + altitudeRel); } void ArduCopterFirmwarePlugin::guidedModeGotoLocation(Vehicle* vehicle, const QGeoCoordinate& gotoCoord) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 094a3fa25..7492de0d0 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -437,7 +437,7 @@ Item { break; case confirmChangeAlt: altitudeSlider.visible = true - altitudeSlider.setInitialValueAppSettingsDistanceUnits(_activeVehicle.altitudeAMSL.value) + altitudeSlider.setInitialValueAppSettingsDistanceUnits(_activeVehicle.altitudeRelative.value) guidedModeConfirm.confirmText = qsTr("change altitude") break; case confirmGoTo: -- GitLab From 47f18d80c047c47a3b76e95ae2f111bc8022b5b7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 15:10:09 -0800 Subject: [PATCH 104/398] Incorrect setSingleShow --- src/Vehicle/Vehicle.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 6c0740bc7..d06a10603 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -183,7 +183,6 @@ Vehicle::Vehicle(LinkInterface* link, // Send MAV_CMD ack timer _mavCommandAckTimer.setSingleShot(true); _mavCommandAckTimer.setInterval(_mavCommandAckTimeoutMSecs); - _mavCommandAckTimer.setSingleShot(false); connect(&_mavCommandAckTimer, &QTimer::timeout, this, &Vehicle::_sendMavCommandAgain); _mav = uas(); -- GitLab From a0c1f8c08da64bb7cbee76358f6a9035ce7febc6 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Thu, 8 Dec 2016 18:26:53 -0500 Subject: [PATCH 105/398] Fix most (all?) QML font rendering glitches on Windows --- src/QmlControls/ScreenTools.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index a39be4d57..3f546a30e 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -87,8 +87,8 @@ Item { function _setBasePointSize(pointSize) { _textMeasure.font.pointSize = pointSize defaultFontPointSize = pointSize - defaultFontPixelHeight = _textMeasure.fontHeight - defaultFontPixelWidth = _textMeasure.fontWidth + defaultFontPixelHeight = Math.round(_textMeasure.fontHeight/2.0)*2 + defaultFontPixelWidth = Math.round(_textMeasure.fontWidth/2.0)*2 smallFontPointSize = defaultFontPointSize * _screenTools.smallFontPointRatio mediumFontPointSize = defaultFontPointSize * _screenTools.mediumFontPointRatio largeFontPointSize = defaultFontPointSize * _screenTools.largeFontPointRatio -- GitLab From 43ad0f563424e98b7c1e85c4054c35232d50e956 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 8 Dec 2016 17:51:44 -0800 Subject: [PATCH 106/398] Change battery.current to float with 2 decimal places --- src/Vehicle/BatteryFact.json | 4 ++-- src/Vehicle/Vehicle.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Vehicle/BatteryFact.json b/src/Vehicle/BatteryFact.json index 6a5eecb36..f6ca0c9b0 100644 --- a/src/Vehicle/BatteryFact.json +++ b/src/Vehicle/BatteryFact.json @@ -23,8 +23,8 @@ { "name": "current", "shortDescription": "Current", - "type": "int32", - "decimalPlaces": 0, + "type": "float", + "decimalPlaces": 2, "units": "A" }, { diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 844ce29d7..3ffb4672f 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -714,7 +714,7 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) _batteryFactGroup.current()->setRawValue(VehicleBatteryFactGroup::_currentUnavailable); } else { // Current is in Amps, current_battery is 10 * milliamperes (1 = 10 milliampere) - _batteryFactGroup.current()->setRawValue((int)(sysStatus.current_battery / 100)); + _batteryFactGroup.current()->setRawValue((float)sysStatus.current_battery / 100.0f); } if (sysStatus.voltage_battery == UINT16_MAX) { _batteryFactGroup.voltage()->setRawValue(VehicleBatteryFactGroup::_voltageUnavailable); -- GitLab From 8f1aac18db92a6f75c36709fd26b173e4b5e7489 Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Fri, 9 Dec 2016 16:30:16 -0800 Subject: [PATCH 107/398] Check for null pointer to avoid crash. --- src/uas/UAS.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index a7c74fab3..55a2c2ee7 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -979,6 +979,10 @@ void UAS::setExternalControlSetpoint(float roll, float pitch, float yaw, float t return; } + if (!_vehicle->priorityLink()) { + return; + } + // Store the previous manual commands static float manualRollAngle = 0.0; static float manualPitchAngle = 0.0; -- GitLab From 51e1fe0d7f92fe300480ee60c15660181cc47cd8 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 13 Dec 2016 08:02:42 -0500 Subject: [PATCH 108/398] =?UTF-8?q?Updated=20MAVLink=20logging=20to=20the?= =?UTF-8?q?=20new=20=E2=80=9Cflightreport"=20form=20upload.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Vehicle/MAVLinkLogManager.cc | 120 +++++++++++++++++-- src/Vehicle/MAVLinkLogManager.h | 25 ++++ src/ui/preferences/MavlinkSettings.qml | 158 ++++++++++++++++++++++--- 3 files changed, 276 insertions(+), 27 deletions(-) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index 567b7d935..11effa34c 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -21,16 +21,23 @@ QGC_LOGGING_CATEGORY(MAVLinkLogManagerLog, "MAVLinkLogManagerLog") -static const char* kEmailAddressKey = "MAVLinkLogEmail"; -static const char* kDescriptionsKey = "MAVLinkLogDescription"; +static const char* kMAVLinkLogGroup = "MAVLinkLogGroup"; +static const char* kEmailAddressKey = "Email"; +static const char* kDescriptionsKey = "Description"; static const char* kDefaultDescr = "QGroundControl Session"; -static const char* kPx4URLKey = "MAVLinkLogURL"; +static const char* kPx4URLKey = "LogURL"; static const char* kDefaultPx4URL = "http://logs.px4.io/upload"; -static const char* kEnableAutoUploadKey = "EnableAutoUploadKey"; -static const char* kEnableAutoStartKey = "EnableAutoStartKey"; -static const char* kEnableDeletetKey = "EnableDeleteKey"; +static const char* kEnableAutoUploadKey = "EnableAutoUpload"; +static const char* kEnableAutoStartKey = "EnableAutoStart"; +static const char* kEnableDeletetKey = "EnableDelete"; static const char* kUlogExtension = ".ulg"; static const char* kSidecarExtension = ".uploaded"; +static const char* kVideoURLKey = "VideoURL"; +static const char* kWindSpeedKey = "WindSpeed"; +static const char* kRateKey = "RateKey"; +static const char* kPublicLogKey = "PublicLog"; +static const char* kFeedback = "feedback"; +static const char* kVideoURL = "videoUrl"; //----------------------------------------------------------------------------- MAVLinkLogFiles::MAVLinkLogFiles(MAVLinkLogManager* manager, const QString& filePath, bool newFile) @@ -299,15 +306,22 @@ MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) , _loggingDisabled(false) , _logProcessor(NULL) , _deleteAfterUpload(false) + , _windSpeed(-1) + , _publicLog(false) { //-- Get saved settings QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); setEmailAddress(settings.value(kEmailAddressKey, QString()).toString()); setDescription(settings.value(kDescriptionsKey, QString(kDefaultDescr)).toString()); setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString()); + setVideoURL(settings.value(kVideoURLKey, QString()).toString()); setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool()); setEnableAutoStart(settings.value(kEnableAutoStartKey, true).toBool()); setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool()); + setWindSpeed(settings.value(kWindSpeedKey, -1).toInt()); + setRating(settings.value(kRateKey, "notset").toString()); + setPublicLog(settings.value(kPublicLogKey, true).toBool()); //-- Logging location _logPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); _logPath += "/MAVLinkLogs"; @@ -353,6 +367,7 @@ MAVLinkLogManager::setEmailAddress(QString email) { _emailAddress = email; QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEmailAddressKey, email); emit emailAddressChanged(); } @@ -363,6 +378,7 @@ MAVLinkLogManager::setDescription(QString description) { _description = description; QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kDescriptionsKey, description); emit descriptionChanged(); } @@ -376,16 +392,37 @@ MAVLinkLogManager::setUploadURL(QString url) _uploadURL = kDefaultPx4URL; } QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kPx4URLKey, _uploadURL); emit uploadURLChanged(); } +//----------------------------------------------------------------------------- +void +MAVLinkLogManager::setFeedback(QString fb) +{ + _feedback = fb; + emit feedbackChanged(); +} + +//----------------------------------------------------------------------------- +void +MAVLinkLogManager::setVideoURL(QString url) +{ + _videoURL = url; + QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); + settings.setValue(kVideoURLKey, url); + emit videoURLChanged(); +} + //----------------------------------------------------------------------------- void MAVLinkLogManager::setEnableAutoUpload(bool enable) { _enableAutoUpload = enable; QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableAutoUploadKey, enable); emit enableAutoUploadChanged(); } @@ -396,6 +433,7 @@ MAVLinkLogManager::setEnableAutoStart(bool enable) { _enableAutoStart = enable; QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableAutoStartKey, enable); emit enableAutoStartChanged(); } @@ -406,10 +444,44 @@ MAVLinkLogManager::setDeleteAfterUpload(bool enable) { _deleteAfterUpload = enable; QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); settings.setValue(kEnableDeletetKey, enable); emit deleteAfterUploadChanged(); } +//----------------------------------------------------------------------------- +void +MAVLinkLogManager::setWindSpeed(int speed) +{ + _windSpeed = speed; + QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); + settings.setValue(kWindSpeedKey, speed); + emit windSpeedChanged(); +} + +//----------------------------------------------------------------------------- +void +MAVLinkLogManager::setRating(QString rate) +{ + _rating = rate; + QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); + settings.setValue(kRateKey, rate); + emit ratingChanged(); +} + +//----------------------------------------------------------------------------- +void +MAVLinkLogManager::setPublicLog(bool pub) +{ + _publicLog = pub; + QSettings settings; + settings.beginGroup(kMAVLinkLogGroup); + settings.setValue(kPublicLogKey, pub); + emit publicLogChanged(); +} + //----------------------------------------------------------------------------- bool MAVLinkLogManager::uploading() @@ -619,17 +691,41 @@ MAVLinkLogManager::_sendLog(const QString& logFile) 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); + QHttpPart sourcePart = create_form_part("source", "QGroundControl"); + QHttpPart versionPart = create_form_part("version", _app->applicationVersion()); + QHttpPart typePart = create_form_part("type", "flightreport"); + QHttpPart windPart = create_form_part("windSpeed", QString::number(_windSpeed)); + QHttpPart ratingPart = create_form_part("rating", _rating); + QHttpPart publicPart = create_form_part("public", _publicLog ? "true" : "false"); //-- Assemble request and POST it multiPart->append(emailPart); multiPart->append(descriptionPart); multiPart->append(sourcePart); multiPart->append(versionPart); + multiPart->append(typePart); + multiPart->append(windPart); + multiPart->append(ratingPart); + multiPart->append(publicPart); + //-- Optional + QHttpPart feedbackPart; + if(_feedback.isEmpty()) { + feedbackPart = create_form_part(kFeedback, "None Given"); + } else { + feedbackPart = create_form_part(kFeedback, _feedback); + } + multiPart->append(feedbackPart); + QHttpPart videoPart; + if(_videoURL.isEmpty()) { + videoPart = create_form_part(kVideoURL, "None"); + } else { + videoPart = create_form_part(kVideoURL, _videoURL); + } + multiPart->append(videoPart); + //-- Actual Log File + 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); multiPart->append(logPart); file->setParent(multiPart); QNetworkRequest request(_uploadURL); diff --git a/src/Vehicle/MAVLinkLogManager.h b/src/Vehicle/MAVLinkLogManager.h index dd1051542..a64b8998f 100644 --- a/src/Vehicle/MAVLinkLogManager.h +++ b/src/Vehicle/MAVLinkLogManager.h @@ -112,13 +112,18 @@ public: 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(QString feedback READ feedback WRITE setFeedback NOTIFY feedbackChanged) + Q_PROPERTY(QString videoURL READ videoURL WRITE setVideoURL NOTIFY videoURLChanged) Q_PROPERTY(bool enableAutoUpload READ enableAutoUpload WRITE setEnableAutoUpload NOTIFY enableAutoUploadChanged) Q_PROPERTY(bool enableAutoStart READ enableAutoStart WRITE setEnableAutoStart NOTIFY enableAutoStartChanged) Q_PROPERTY(bool deleteAfterUpload READ deleteAfterUpload WRITE setDeleteAfterUpload NOTIFY deleteAfterUploadChanged) + Q_PROPERTY(bool publicLog READ publicLog WRITE setPublicLog NOTIFY publicLogChanged) Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged) Q_PROPERTY(bool logRunning READ logRunning NOTIFY logRunningChanged) Q_PROPERTY(bool canStartLog READ canStartLog NOTIFY canStartLogChanged) Q_PROPERTY(QmlObjectListModel* logFiles READ logFiles NOTIFY logFilesChanged) + Q_PROPERTY(int windSpeed READ windSpeed WRITE setWindSpeed NOTIFY windSpeedChanged) + Q_PROPERTY(QString rating READ rating WRITE setRating NOTIFY ratingChanged) Q_INVOKABLE void uploadLog (); Q_INVOKABLE void deleteLog (); @@ -129,21 +134,31 @@ public: QString emailAddress () { return _emailAddress; } QString description () { return _description; } QString uploadURL () { return _uploadURL; } + QString feedback () { return _feedback; } + QString videoURL () { return _videoURL; } bool enableAutoUpload () { return _enableAutoUpload; } bool enableAutoStart () { return _enableAutoStart; } bool uploading (); bool logRunning () { return _logRunning; } bool canStartLog () { return _vehicle != NULL; } bool deleteAfterUpload () { return _deleteAfterUpload; } + bool publicLog () { return _publicLog; } + int windSpeed () { return _windSpeed; } + QString rating () { return _rating; } QmlObjectListModel* logFiles () { return &_logFiles; } void setEmailAddress (QString email); void setDescription (QString description); void setUploadURL (QString url); + void setFeedback (QString feedback); + void setVideoURL (QString url); void setEnableAutoUpload (bool enable); void setEnableAutoStart (bool enable); void setDeleteAfterUpload(bool enable); + void setWindSpeed (int speed); + void setRating (QString rate); + void setPublicLog (bool publicLog); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); @@ -152,6 +167,7 @@ signals: void emailAddressChanged (); void descriptionChanged (); void uploadURLChanged (); + void feedbackChanged (); void enableAutoUploadChanged (); void enableAutoStartChanged (); void logFilesChanged (); @@ -164,6 +180,10 @@ signals: void logRunningChanged (); void canStartLogChanged (); void deleteAfterUploadChanged (); + void windSpeedChanged (); + void ratingChanged (); + void videoURLChanged (); + void publicLogChanged (); private slots: void _uploadFinished (); @@ -188,7 +208,9 @@ private: QString _description; QString _emailAddress; QString _uploadURL; + QString _feedback; QString _logPath; + QString _videoURL; bool _enableAutoUpload; bool _enableAutoStart; QNetworkAccessManager* _nam; @@ -199,6 +221,9 @@ private: bool _loggingDisabled; MAVLinkLogProcessor* _logProcessor; bool _deleteAfterUpload; + int _windSpeed; + QString _rating; + bool _publicLog; }; #endif diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index b33f1e718..4de149c26 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -52,6 +52,22 @@ Rectangle { } } + function saveItems() + { + QGroundControl.mavlinkSystemID = parseInt(sysidField.text) + QGroundControl.mavlinkLogManager.videoURL = videoUrlField.text + QGroundControl.mavlinkLogManager.feedback = feedbackTextArea.text + QGroundControl.mavlinkLogManager.emailAddress = emailField.text + QGroundControl.mavlinkLogManager.description = descField.text + QGroundControl.mavlinkLogManager.uploadURL = urlField.text + QGroundControl.mavlinkLogManager.emailAddress = emailField.text + if(autoUploadCheck.checked && QGroundControl.mavlinkLogManager.emailAddress === "") { + autoUploadCheck.checked = false + } else { + QGroundControl.mavlinkLogManager.enableAutoUpload = autoUploadCheck.checked + } + } + MessageDialog { id: emptyEmailDialog visible: false @@ -111,7 +127,7 @@ Rectangle { inputMethodHints: Qt.ImhFormattedNumbersOnly anchors.verticalCenter: parent.verticalCenter onEditingFinished: { - QGroundControl.mavlinkSystemID = parseInt(sysidField.text) + saveItems(); } } } @@ -234,11 +250,7 @@ Rectangle { inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhEmailCharactersOnly anchors.verticalCenter: parent.verticalCenter onEditingFinished: { - QGroundControl.mavlinkLogManager.emailAddress = emailField.text - if(emailField.text === "") { - autoUploadCheck.checked = false - QGroundControl.mavlinkLogManager.enableAutoUpload = false - } + saveItems(); } } } @@ -257,7 +269,7 @@ Rectangle { width: _valueWidth anchors.verticalCenter: parent.verticalCenter onEditingFinished: { - QGroundControl.mavlinkLogManager.description = descField.text + saveItems(); } } } @@ -277,24 +289,140 @@ Rectangle { inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly anchors.verticalCenter: parent.verticalCenter onEditingFinished: { - QGroundControl.mavlinkLogManager.uploadURL = urlField.text + saveItems(); + } + } + } + //----------------------------------------------------------------- + //-- Video URL + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: videoUrlField.baseline + text: qsTr("Video URL:") + } + QGCTextField { + id: videoUrlField + text: QGroundControl.mavlinkLogManager.videoURL + width: _valueWidth + inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhUrlCharactersOnly + anchors.verticalCenter: parent.verticalCenter + } + } + //----------------------------------------------------------------- + //-- Wind Speed + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: windCombo.baseline + text: qsTr("Wind Speed:") + } + QGCComboBox { + id: windCombo + width: _valueWidth + model: ListModel { + id: windItems + ListElement { text: "Please Select"; value: -1 } + ListElement { text: "Calm"; value: 0 } + ListElement { text: "Breeze"; value: 5 } + ListElement { text: "Gale"; value: 8 } + ListElement { text: "Storm"; value: 10 } + } + onActivated: { + saveItems(); + QGroundControl.mavlinkLogManager.windSpeed = windItems.get(index).value + console.log('Set Wind: ' + windItems.get(index).value) + } + Component.onCompleted: { + for(var i = 0; i < windItems.count; i++) { + if(windItems.get(i).value === QGroundControl.mavlinkLogManager.windSpeed) { + windCombo.currentIndex = i; + console.log('Wind: ' + windItems.get(i).value) + break; + } + } + } + } + } + //----------------------------------------------------------------- + //-- Flight Rating + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + anchors.baseline: ratingCombo.baseline + text: qsTr("Flight Rating:") + } + QGCComboBox { + id: ratingCombo + width: _valueWidth + model: ListModel { + id: ratingItems + ListElement { text: "Please Select"; value: "notset"} + ListElement { text: "Crashed (Pilot Error)"; value: "crash_pilot" } + ListElement { text: "Crashed (Software or Hardware issue)"; value: "crash_sw_hw" } + ListElement { text: "Unsatisfactory"; value: "unsatisfactory" } + ListElement { text: "Good"; value: "good" } + ListElement { text: "Great"; value: "great" } + } + onActivated: { + saveItems(); + QGroundControl.mavlinkLogManager.rating = ratingItems.get(index).value + console.log('Set Rating: ' + ratingItems.get(index).value) + } + Component.onCompleted: { + for(var i = 0; i < ratingItems.count; i++) { + if(ratingItems.get(i).value === QGroundControl.mavlinkLogManager.rating) { + ratingCombo.currentIndex = i; + console.log('Rating: ' + ratingItems.get(i).value) + break; + } + } } } } //----------------------------------------------------------------- + //-- Feedback + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + width: _labelWidth + text: qsTr("Additional Feedback:") + } + TextArea { + id: feedbackTextArea + width: _valueWidth + height: ScreenTools.defaultFontPixelHeight * 4 + frameVisible: false + font.pointSize: ScreenTools.defaultFontPointSize + text: QGroundControl.mavlinkLogManager.feedback + style: TextAreaStyle { + textColor: qgcPal.windowShade + backgroundColor: qgcPal.text + } + } + } + //----------------------------------------------------------------- + //-- Public Log + QGCCheckBox { + text: qsTr("Make this log publicly available") + checked: QGroundControl.mavlinkLogManager.publicLog + onClicked: { + QGroundControl.mavlinkLogManager.publicLog = checked + } + } + //----------------------------------------------------------------- //-- Automatic Upload QGCCheckBox { id: autoUploadCheck text: qsTr("Enable automatic log uploads") checked: QGroundControl.mavlinkLogManager.enableAutoUpload onClicked: { - QGroundControl.mavlinkLogManager.emailAddress = emailField.text - if(checked && QGroundControl.mavlinkLogManager.emailAddress === "") { - checked = false + saveItems(); + if(checked && QGroundControl.mavlinkLogManager.emailAddress === "") emptyEmailDialog.open() - } else { - QGroundControl.mavlinkLogManager.enableAutoUpload = checked - } } } //----------------------------------------------------------------- @@ -443,7 +571,7 @@ Rectangle { enabled: _selectedCount > 0 && !QGroundControl.mavlinkLogManager.uploading && !QGroundControl.mavlinkLogManager.logRunning && !_uploadedSelected visible: !QGroundControl.mavlinkLogManager.uploading onClicked: { - QGroundControl.mavlinkLogManager.emailAddress = emailField.text + saveItems(); if(QGroundControl.mavlinkLogManager.emailAddress === "") emptyEmailDialog.open() else -- GitLab From 5db16aec234af48dc193dd37768b18379b3fa8a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beat=20K=C3=BCng?= Date: Wed, 14 Dec 2016 18:30:46 +0100 Subject: [PATCH 109/398] MAVLinkLogManager: disable automatic start of log streaming It's not desired to have automatic log streaming for several reasons: - There's already SD logging (which is the case for the majority of the users) - The link does not provide the necessary bandwidth (eg. wireless telemetry) --- src/Vehicle/MAVLinkLogManager.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index 11effa34c..94e9b3467 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -298,7 +298,7 @@ MAVLinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) : QGCTool(app) , _enableAutoUpload(true) - , _enableAutoStart(true) + , _enableAutoStart(false) , _nam(NULL) , _currentLogfile(NULL) , _vehicle(NULL) @@ -317,7 +317,7 @@ MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) setUploadURL(settings.value(kPx4URLKey, QString(kDefaultPx4URL)).toString()); setVideoURL(settings.value(kVideoURLKey, QString()).toString()); setEnableAutoUpload(settings.value(kEnableAutoUploadKey, true).toBool()); - setEnableAutoStart(settings.value(kEnableAutoStartKey, true).toBool()); + setEnableAutoStart(settings.value(kEnableAutoStartKey, false).toBool()); setDeleteAfterUpload(settings.value(kEnableDeletetKey, false).toBool()); setWindSpeed(settings.value(kWindSpeedKey, -1).toInt()); setRating(settings.value(kRateKey, "notset").toString()); -- GitLab From 0cc2ce2853f876187ec961f9570f3d032ba5d697 Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Fri, 16 Dec 2016 10:04:44 -0800 Subject: [PATCH 110/398] Change airplane compass icon to generic arrow icon. --- qgcresources.qrc | 1 + src/FlightMap/Images/compassInstrumentArrow.svg | 9 +++++++++ src/FlightMap/Widgets/QGCCompassWidget.qml | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/FlightMap/Images/compassInstrumentArrow.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 51a35405d..96751a013 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -97,6 +97,7 @@ src/FlightMap/Images/buttonHome.svg src/FlightMap/Images/buttonMore.svg src/FlightMap/Images/compassInstrumentAirplane.svg + src/FlightMap/Images/compassInstrumentArrow.svg src/FlightMap/Images/compassInstrumentDial.svg src/FlightMap/Images/crossHair.svg src/FlightMap/Images/PiP.svg diff --git a/src/FlightMap/Images/compassInstrumentArrow.svg b/src/FlightMap/Images/compassInstrumentArrow.svg new file mode 100644 index 000000000..708155571 --- /dev/null +++ b/src/FlightMap/Images/compassInstrumentArrow.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/FlightMap/Widgets/QGCCompassWidget.qml b/src/FlightMap/Widgets/QGCCompassWidget.qml index a16b2782e..2a3aff1e5 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -48,7 +48,7 @@ Item { Image { id: pointer - source: "/qmlimages/compassInstrumentAirplane.svg" + source: "/qmlimages/compassInstrumentArrow.svg" mipmap: true width: size * 0.75 sourceSize.width: width -- GitLab From 3262f6fc0a6d47cc055f745de912832cce28f63b Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 21 Dec 2016 13:35:41 -0500 Subject: [PATCH 111/398] Charting Tweaks --- src/ui/Linechart.ui | 21 +++-- src/ui/linechart/ChartPlot.cc | 110 +++++++++++-------------- src/ui/linechart/ChartPlot.h | 17 ++-- src/ui/linechart/IncrementalPlot.cc | 32 ++++---- src/ui/linechart/LinechartPlot.cc | 32 ++++---- src/ui/linechart/LinechartWidget.cc | 119 ++++++++++++---------------- src/ui/linechart/LinechartWidget.h | 1 - 7 files changed, 153 insertions(+), 179 deletions(-) diff --git a/src/ui/Linechart.ui b/src/ui/Linechart.ui index a46a50691..d56d7c2cf 100644 --- a/src/ui/Linechart.ui +++ b/src/ui/Linechart.ui @@ -29,7 +29,16 @@ - + + 6 + + + 6 + + + 6 + + 6 @@ -103,8 +112,8 @@ 0 0 - 884 - 491 + 879 + 462 @@ -113,7 +122,7 @@ - 2 + 4 @@ -139,7 +148,7 @@ - 0 + 6 @@ -172,7 +181,7 @@ - 0 + 6 QLayout::SetMinimumSize diff --git a/src/ui/linechart/ChartPlot.cc b/src/ui/linechart/ChartPlot.cc index 61f1de095..736120dcb 100644 --- a/src/ui/linechart/ChartPlot.cc +++ b/src/ui/linechart/ChartPlot.cc @@ -2,80 +2,72 @@ #include "QGCApplication.h" const QColor ChartPlot::baseColors[numColors] = { - QColor(242,255,128), - QColor(70,80,242), - QColor(232,33,47), - QColor(116,251,110), - QColor(81,183,244), - QColor(234,38,107), - QColor(92,247,217), - QColor(151,59,239), - QColor(231,72,28), - QColor(236,48,221), - QColor(75,133,243), - QColor(203,254,121), - QColor(104,64,240), - QColor(200,54,238), - QColor(104,250,138), - QColor(235,43,165), - QColor(98,248,176), - QColor(161,252,116), - QColor(87,231,246), - QColor(230,126,23) + QColor(242, 255, 128), + QColor(70, 80, 242), + QColor(232, 33, 47), + QColor(116, 251, 110), + QColor(81, 183, 244), + QColor(234, 38, 107), + QColor(92, 247, 217), + QColor(151, 59, 239), + QColor(231, 72, 28), + QColor(236, 48, 221), + QColor(75, 133, 243), + QColor(203, 254, 121), + QColor(104, 64, 240), + QColor(200, 54, 238), + QColor(104, 250, 138), + QColor(235, 43, 165), + QColor(98, 248, 176), + QColor(161, 252, 116), + QColor(87, 231, 246), + QColor(230, 126, 23) }; -ChartPlot::ChartPlot(QWidget *parent): +ChartPlot::ChartPlot(QWidget* parent): QwtPlot(parent), - nextColorIndex(0), - symbolWidth(2.0f), - curveWidth(2.0f), - gridWidth(0.8f) + _nextColorIndex(0), + _symbolWidth(2.0f), + _curveWidth(2.0f), + _gridWidth(0.8f) { // Initialize the list of curves. - curves = QMap(); - + _curves = QMap(); // Set the grid. The colorscheme was already set in generateColorScheme(). - grid = new QwtPlotGrid; - grid->enableXMin(true); - grid->attach(this); - - colors = QList(); - + _grid = new QwtPlotGrid; + _grid->enableXMin(true); + _grid->attach(this); + _colors = QList(); ///> Color map for plots, includes 20 colors ///> Map will start from beginning when the first 20 colors are exceeded - for (int i = 0; i < numColors; ++i) - { - colors.append(baseColors[i]); + for(int i = 0; i < numColors; ++i) { + _colors.append(baseColors[i]); } - // Now that all objects have been initialized, color everything. styleChanged(qgcApp()->styleIsDark()); } ChartPlot::~ChartPlot() { - } QColor ChartPlot::getNextColor() { - if(nextColorIndex >= colors.count()) - { - nextColorIndex = 0; + if(_nextColorIndex >= _colors.count()) { + _nextColorIndex = 0; } - return colors[nextColorIndex++]; + return _colors[_nextColorIndex++]; } -QColor ChartPlot::getColorForCurve(const QString &id) +QColor ChartPlot::getColorForCurve(const QString& id) { - return curves.value(id)->pen().color(); + return _curves.value(id)->pen().color(); } void ChartPlot::shuffleColors() { - foreach (QwtPlotCurve* curve, curves) - { - if (curve->isVisible()) { + foreach(QwtPlotCurve* curve, _curves) { + if(curve->isVisible()) { QPen pen(curve->pen()); pen.setColor(getNextColor()); curve->setPen(pen); @@ -86,32 +78,24 @@ void ChartPlot::shuffleColors() void ChartPlot::styleChanged(bool styleIsDark) { // Generate a new color list for curves and recolor them. - for (int i = 0; i < numColors; ++i) - { - colors[i] = styleIsDark ? baseColors[i].lighter(150) : baseColors[i].darker(150); + for(int i = 0; i < numColors; ++i) { + _colors[i] = styleIsDark ? baseColors[i].lighter(150) : baseColors[i].darker(150); } shuffleColors(); - // Configure the rest of the UI colors based on the current theme. - if (styleIsDark) - { + if(styleIsDark) { // Set canvas background setCanvasBackground(QColor(0, 0, 0)); - // Configure the plot grid. - grid->setMinorPen(QPen(QColor(0xAA, 0xAA, 0xAA), gridWidth, Qt::DotLine)); - grid->setMajorPen(QPen(QColor(0xDD, 0xDD, 0xDD), gridWidth, Qt::DotLine)); - } - else - { + _grid->setMinorPen(QPen(QColor(64, 64, 64), _gridWidth, Qt::SolidLine)); + _grid->setMajorPen(QPen(QColor(96, 96, 96), _gridWidth, Qt::SolidLine)); + } else { // Set canvas background setCanvasBackground(QColor(0xFF, 0xFF, 0xFF)); - // Configure the plot grid. - grid->setMinorPen(QPen(QColor(0x55, 0x55, 0x55), gridWidth, Qt::DotLine)); - grid->setMajorPen(QPen(QColor(0x22, 0x22, 0x22), gridWidth, Qt::DotLine)); + _grid->setMinorPen(QPen(QColor(192, 192, 192), _gridWidth, Qt::SolidLine)); + _grid->setMajorPen(QPen(QColor(128, 128, 128), _gridWidth, Qt::SolidLine)); } - // And finally refresh the widget to make sure all color changes are redrawn. replot(); } diff --git a/src/ui/linechart/ChartPlot.h b/src/ui/linechart/ChartPlot.h index acbb85a46..1a6a5b52a 100644 --- a/src/ui/linechart/ChartPlot.h +++ b/src/ui/linechart/ChartPlot.h @@ -31,14 +31,15 @@ public slots: protected: const static int numColors = 20; const static QColor baseColors[numColors]; - QList colors; ///< Colormap for curves - int nextColorIndex; ///< Next index in color map - QMap curves; ///< Plot curves - QwtPlotGrid* grid; ///< Plot grid - - float symbolWidth; ///< Width of curve symbols in pixels - float curveWidth; ///< Width of curve lines in pixels - float gridWidth; ///< Width of gridlines in pixels + + QList _colors; ///< Colormap for curves + int _nextColorIndex; ///< Next index in color map + QMap _curves; ///< Plot curves + QwtPlotGrid* _grid; ///< Plot grid + + float _symbolWidth; ///< Width of curve symbols in pixels + float _curveWidth; ///< Width of curve lines in pixels + float _gridWidth; ///< Width of gridlines in pixels }; #endif // CHARTPLOT_H diff --git a/src/ui/linechart/IncrementalPlot.cc b/src/ui/linechart/IncrementalPlot.cc index ff594e27c..11f336e6a 100644 --- a/src/ui/linechart/IncrementalPlot.cc +++ b/src/ui/linechart/IncrementalPlot.cc @@ -143,7 +143,7 @@ void IncrementalPlot::showLegend(bool show) void IncrementalPlot::setStyleText(const QString &style) { styleText = style.toLower(); - foreach (QwtPlotCurve* curve, curves) { + foreach (QwtPlotCurve* curve, _curves) { updateStyle(curve); } replot(); @@ -161,24 +161,24 @@ void IncrementalPlot::updateStyle(QwtPlotCurve *curve) // Update the symbol style QwtSymbol *newSymbol = NULL; if (styleText.contains("circles")) { - newSymbol = new QwtSymbol(QwtSymbol::Ellipse, Qt::NoBrush, QPen(oldColor, symbolWidth), QSize(6, 6)); + newSymbol = new QwtSymbol(QwtSymbol::Ellipse, Qt::NoBrush, QPen(oldColor, _symbolWidth), QSize(6, 6)); } else if (styleText.contains("crosses")) { - newSymbol = new QwtSymbol(QwtSymbol::XCross, Qt::NoBrush, QPen(oldColor, symbolWidth), QSize(5, 5)); + newSymbol = new QwtSymbol(QwtSymbol::XCross, Qt::NoBrush, QPen(oldColor, _symbolWidth), QSize(5, 5)); } else if (styleText.contains("rect")) { - newSymbol = new QwtSymbol(QwtSymbol::Rect, Qt::NoBrush, QPen(oldColor, symbolWidth), QSize(6, 6)); + newSymbol = new QwtSymbol(QwtSymbol::Rect, Qt::NoBrush, QPen(oldColor, _symbolWidth), QSize(6, 6)); } // Else-case already handled by NULL value, which indicates no symbol curve->setSymbol(newSymbol); // Update the line style if (styleText.contains("dotted")) { - curve->setPen(QPen(oldColor, curveWidth, Qt::DotLine)); + curve->setPen(QPen(oldColor, _curveWidth, Qt::DotLine)); } else if (styleText.contains("dashed")) { - curve->setPen(QPen(oldColor, curveWidth, Qt::DashLine)); + curve->setPen(QPen(oldColor, _curveWidth, Qt::DashLine)); } else if (styleText.contains("line") || styleText.contains("solid")) { - curve->setPen(QPen(oldColor, curveWidth, Qt::SolidLine)); + curve->setPen(QPen(oldColor, _curveWidth, Qt::SolidLine)); } else { - curve->setPen(QPen(oldColor, curveWidth, Qt::NoPen)); + curve->setPen(QPen(oldColor, _curveWidth, Qt::NoPen)); } curve->setStyle(QwtPlotCurve::Lines); } @@ -260,22 +260,22 @@ void IncrementalPlot::appendData(const QString &key, double *x, double *y, int s } // If this is a new curve, create it. - if (!curves.contains(key)) { + if (!_curves.contains(key)) { curve = new QwtPlotCurve(key); - curves.insert(key, curve); + _curves.insert(key, curve); curve->setStyle(QwtPlotCurve::NoCurve); curve->setPaintAttribute(QwtPlotCurve::FilterPoints); // Set the color. Only the pen needs to be set const QColor &c = getNextColor(); - curve->setPen(c, symbolWidth); + curve->setPen(c, _symbolWidth); qDebug() << "Creating curve" << key << "with color" << c; updateStyle(curve); curve->attach(this); } else { - curve = curves.value(key); + curve = _curves.value(key); } data->append(x, y, size); @@ -359,21 +359,21 @@ int IncrementalPlot::data(const QString &key, double* r_x, double* r_y, int maxS */ void IncrementalPlot::showGrid(bool show) { - grid->setVisible(show); + _grid->setVisible(show); replot(); } bool IncrementalPlot::gridEnabled() const { - return grid->isVisible(); + return _grid->isVisible(); } void IncrementalPlot::removeData() { - foreach (QwtPlotCurve* curve, curves) { + foreach (QwtPlotCurve* curve, _curves) { delete curve; } - curves.clear(); + _curves.clear(); foreach (CurveData* data, d_data) { delete data; diff --git a/src/ui/linechart/LinechartPlot.cc b/src/ui/linechart/LinechartPlot.cc index dc6b84f96..61d14b870 100644 --- a/src/ui/linechart/LinechartPlot.cc +++ b/src/ui/linechart/LinechartPlot.cc @@ -190,7 +190,7 @@ void LinechartPlot::removeTimedOutCurves() { // Remove this curve // Delete curves - QwtPlotCurve* curve = curves.take(key); + QwtPlotCurve* curve = _curves.take(key); // Delete the object delete curve; // Set the pointer null @@ -276,7 +276,7 @@ void LinechartPlot::appendData(QString dataname, quint64 ms, double value) valueInterval = maxValue - minValue; // Assign dataset to curve - QwtPlotCurve* curve = curves.value(dataname); + QwtPlotCurve* curve = _curves.value(dataname); curve->setRawSamples(dataset->getPlotX(), dataset->getPlotY(), dataset->getPlotCount()); // qDebug() << "mintime" << minTime << "maxtime" << maxTime << "last max time" << "window position" << getWindowPosition(); @@ -321,7 +321,7 @@ void LinechartPlot::addCurve(QString id) // Create new curve and set style QwtPlotCurve* curve = new QwtPlotCurve(id); // Add curve to list - curves.insert(id, curve); + _curves.insert(id, curve); curve->setStyle(QwtPlotCurve::Lines); curve->setPaintAttribute(QwtPlotCurve::FilterPoints, true); @@ -414,15 +414,15 @@ void LinechartPlot::setScaling(int scaling) **/ void LinechartPlot::setVisibleById(QString id, bool visible) { - if(curves.contains(id)) { - curves.value(id)->setVisible(visible); + if(_curves.contains(id)) { + _curves.value(id)->setVisible(visible); if(visible) { - curves.value(id)->attach(this); + _curves.value(id)->attach(this); } else { - curves.value(id)->detach(); + _curves.value(id)->detach(); } } } @@ -467,9 +467,9 @@ void LinechartPlot::showCurve(QString id) **/ void LinechartPlot::setCurveColor(QString id, QColor color) { - QwtPlotCurve* curve = curves.value(id); + QwtPlotCurve* curve = _curves.value(id); // Change the color of the curve. - curve->setPen(QPen(QBrush(color), curveWidth)); + curve->setPen(QPen(QBrush(color), _curveWidth)); //qDebug() << "Setting curve" << id << "to" << color; @@ -477,7 +477,7 @@ void LinechartPlot::setCurveColor(QString id, QColor color) const QwtSymbol *oldSymbol = curve->symbol(); QwtSymbol *newSymbol = NULL; if (oldSymbol) { - newSymbol = new QwtSymbol(oldSymbol->style(), QBrush(color), QPen(color, symbolWidth), QSize(symbolWidth, symbolWidth)); + newSymbol = new QwtSymbol(oldSymbol->style(), QBrush(color), QPen(color, _symbolWidth), QSize(_symbolWidth, _symbolWidth)); } curve->setSymbol(newSymbol); } @@ -490,7 +490,7 @@ void LinechartPlot::setCurveColor(QString id, QColor color) **/ bool LinechartPlot::isVisible(QString id) { - return curves.value(id)->isVisible(); + return _curves.value(id)->isVisible(); } /** @@ -499,9 +499,9 @@ bool LinechartPlot::isVisible(QString id) bool LinechartPlot::anyCurveVisible() { bool visible = false; - foreach (const QString &key, curves.keys()) + foreach (const QString &key, _curves.keys()) { - if (curves.value(key)->isVisible()) + if (_curves.value(key)->isVisible()) { visible = true; } @@ -530,7 +530,7 @@ void LinechartPlot::setAutoScroll(bool active) **/ QList LinechartPlot::getCurves() { - return curves.values(); + return _curves.values(); } /** @@ -702,10 +702,10 @@ void LinechartPlot::removeAllData() datalock.lock(); // Delete curves QMap::iterator i; - for(i = curves.begin(); i != curves.end(); ++i) + for(i = _curves.begin(); i != _curves.end(); ++i) { // Remove from curve list - QwtPlotCurve* curve = curves.take(i.key()); + QwtPlotCurve* curve = _curves.take(i.key()); // Delete the object delete curve; // Set the pointer null diff --git a/src/ui/linechart/LinechartWidget.cc b/src/ui/linechart/LinechartWidget.cc index ba6ca0414..339e1f84f 100644 --- a/src/ui/linechart/LinechartWidget.cc +++ b/src/ui/linechart/LinechartWidget.cc @@ -52,7 +52,6 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent curveMeans(new QMap()), curveMedians(new QMap()), curveVariances(new QMap()), - curveMenu(new QMenu(this)), logFile(new QFile()), logindex(1), logging(false), @@ -63,34 +62,28 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent { // Add elements defined in Qt Designer ui.setupUi(this); - this->setMinimumSize(200, 150); + this->setMinimumSize(600, 400); // Add and customize curve list elements (left side) curvesWidget = new QWidget(ui.curveListWidget); ui.curveListWidget->setWidget(curvesWidget); curvesWidgetLayout = new QGridLayout(curvesWidget); - curvesWidgetLayout->setMargin(2); - curvesWidgetLayout->setSpacing(4); - //curvesWidgetLayout->setSizeConstraint(QSizePolicy::Expanding); + curvesWidgetLayout->setMargin(6); + curvesWidgetLayout->setSpacing(6); curvesWidgetLayout->setAlignment(Qt::AlignTop); + curvesWidgetLayout->setColumnMinimumWidth(0, 10); curvesWidgetLayout->setColumnStretch(0, 0); - curvesWidgetLayout->setColumnStretch(1, 0); + curvesWidgetLayout->setColumnStretch(1, 10); curvesWidgetLayout->setColumnStretch(2, 80); curvesWidgetLayout->setColumnStretch(3, 50); curvesWidgetLayout->setColumnStretch(4, 50); curvesWidgetLayout->setColumnStretch(5, 50); -// horizontalLayout->setColumnStretch(median, 50); curvesWidgetLayout->setColumnStretch(6, 50); curvesWidget->setLayout(curvesWidgetLayout); // Create curve list headings - QLabel* label; - QLabel* value; - QLabel* mean; - QLabel* variance; - connect(ui.recolorButton, &QPushButton::clicked, this, &LinechartWidget::recolor); connect(ui.shortNameCheckBox, &QCheckBox::clicked, this, &LinechartWidget::setShortNames); connect(ui.plotFilterLineEdit, &QLineEdit::textChanged, this, &LinechartWidget::filterCurves); @@ -100,31 +93,24 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent int labelRow = curvesWidgetLayout->rowCount(); - selectAllCheckBox = new QCheckBox("", this); + selectAllCheckBox = new QCheckBox(this); connect(selectAllCheckBox, &QCheckBox::clicked, this, &LinechartWidget::selectAllCurves); - curvesWidgetLayout->addWidget(selectAllCheckBox, labelRow, 0, 1, 2); + curvesWidgetLayout->addWidget(selectAllCheckBox, labelRow, 0); - label = new QLabel(this); - label->setText("Name"); - curvesWidgetLayout->addWidget(label, labelRow, 2); + QWidget* colorIcon = new QWidget(this); + colorIcon->setMinimumSize(QSize(5, 14)); + colorIcon->setMaximumSize(QSize(5, 14)); + curvesWidgetLayout->addWidget(colorIcon, labelRow, 1); - // Value - value = new QLabel(this); - value->setText("Val"); - curvesWidgetLayout->addWidget(value, labelRow, 3); + curvesWidgetLayout->addWidget(new QLabel(tr("Name")), labelRow, 2); + curvesWidgetLayout->addWidget(new QLabel(tr("Val")), labelRow, 3, Qt::AlignRight); - // Unit - //curvesWidgetLayout->addWidget(new QLabel(tr("Unit")), labelRow, 4); + QLabel* pUnit = new QLabel(tr("Unit")); + curvesWidgetLayout->addWidget(pUnit, labelRow, 4); - // Mean - mean = new QLabel(this); - mean->setText("Mean"); - curvesWidgetLayout->addWidget(mean, labelRow, 5); + curvesWidgetLayout->addWidget(new QLabel(tr("Mean")), labelRow, 5, Qt::AlignRight); + curvesWidgetLayout->addWidget(new QLabel(tr("Variance")), labelRow, 6, Qt::AlignRight); - // Variance - variance = new QLabel(this); - variance->setText("Variance"); - curvesWidgetLayout->addWidget(variance, labelRow, 6); // Create the layout createLayout(); @@ -134,9 +120,11 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent updateTimer->setInterval(updateInterval); connect(updateTimer, &QTimer::timeout, this, &LinechartWidget::refresh); - connect(ui.uasSelectionBox, static_cast(&QComboBox::currentIndexChanged), - this, &LinechartWidget::selectActiveSystem); + connect(ui.uasSelectionBox, static_cast(&QComboBox::currentIndexChanged), this, &LinechartWidget::selectActiveSystem); + readSettings(); + pUnit->setVisible(ui.showUnitsCheckBox->isChecked()); + connect(ui.showUnitsCheckBox, &QCheckBox::clicked, pUnit, &QLabel::setVisible); } LinechartWidget::~LinechartWidget() @@ -320,24 +308,25 @@ void LinechartWidget::appendData(int uasId, const QString& curve, const QString& if(!ok || type == QMetaType::QByteArray || type == QMetaType::QString) return; bool isDouble = type == QMetaType::Float || type == QMetaType::Double; + QString curveID = curve + unit; if ((selectedMAV == -1 && isVisible()) || (selectedMAV == uasId && isVisible())) { // Order matters here, first append to plot, then update curve list - activePlot->appendData(curve+unit, usec, value); + activePlot->appendData(curveID, usec, value); // Store data - QLabel* label = curveLabels->value(curve+unit, NULL); + QLabel* label = curveLabels->value(curveID, NULL); // Make sure the curve will be created if it does not yet exist if(!label) { if(!isDouble) - intData.insert(curve+unit, 0); + intData.insert(curveID, 0); addCurve(curve, unit); } // Add int data if(!isDouble) - intData.insert(curve+unit, variant.toInt()); + intData.insert(curveID, variant.toInt()); } if (lastTimestamp == 0 && usec != 0) @@ -360,7 +349,7 @@ void LinechartWidget::appendData(int uasId, const QString& curve, const QString& // Log data if (logging) { - if (activePlot->isVisible(curve+unit)) + if (activePlot->isVisible(curveID)) { if (usec == 0) usec = QGC::groundTimeMilliseconds(); if (logStartTime == 0) logStartTime = usec; @@ -514,67 +503,59 @@ void LinechartWidget::createActions() void LinechartWidget::addCurve(const QString& curve, const QString& unit) { LinechartPlot* plot = activePlot; -// QHBoxLayout *horizontalLayout; - QCheckBox *checkBox; - QLabel* label; - QLabel* value; - QLabel* unitLabel; - QLabel* mean; - QLabel* variance; - - curveNames.insert(curve+unit, curve); - + QString curveID = curve + unit; + curveNames.insert(curveID, curve); int labelRow = curvesWidgetLayout->rowCount(); // Checkbox - checkBox = new QCheckBox(this); + QCheckBox* checkBox = new QCheckBox(this); checkBox->setCheckable(true); - checkBox->setObjectName(curve+unit); + checkBox->setObjectName(curveID); checkBox->setToolTip(tr("Enable the curve in the graph window")); checkBox->setWhatsThis(tr("Enable the curve in the graph window")); - checkBoxes.insert(curve+unit, checkBox); + checkBoxes.insert(curveID, checkBox); curvesWidgetLayout->addWidget(checkBox, labelRow, 0); // Icon QWidget* colorIcon = new QWidget(this); - colorIcons.insert(curve+unit, colorIcon); + colorIcons.insert(curveID, colorIcon); colorIcon->setMinimumSize(QSize(5, 14)); - colorIcon->setMaximumSize(4, 14); + colorIcon->setMaximumSize(QSize(5, 14)); curvesWidgetLayout->addWidget(colorIcon, labelRow, 1); // Label - label = new QLabel(this); - label->setText(getCurveName(curve+unit, ui.shortNameCheckBox->isChecked())); - curveNameLabels.insert(curve+unit, label); + QLabel* label = new QLabel(this); + label->setText(getCurveName(curveID, ui.shortNameCheckBox->isChecked())); + curveNameLabels.insert(curveID, label); curvesWidgetLayout->addWidget(label, labelRow, 2); // Value - value = new QLabel(this); + QLabel* value = new QLabel(this); value->setNum(0.00); value->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); value->setToolTip(tr("Current value of %1 in %2 units").arg(curve, unit)); value->setWhatsThis(tr("Current value of %1 in %2 units").arg(curve, unit)); - curveLabels->insert(curve+unit, value); - curvesWidgetLayout->addWidget(value, labelRow, 3); + curveLabels->insert(curveID, value); + curvesWidgetLayout->addWidget(value, labelRow, 3, Qt::AlignRight); // Unit - unitLabel = new QLabel(this); + QLabel* unitLabel = new QLabel(this); unitLabel->setText(unit); unitLabel->setToolTip(tr("Unit of ") + curve); unitLabel->setWhatsThis(tr("Unit of ") + curve); - curveUnits.insert(curve+unit, unitLabel); + curveUnits.insert(curveID, unitLabel); curvesWidgetLayout->addWidget(unitLabel, labelRow, 4); unitLabel->setVisible(ui.showUnitsCheckBox->isChecked()); connect(ui.showUnitsCheckBox, &QCheckBox::clicked, unitLabel, &QLabel::setVisible); // Mean - mean = new QLabel(this); + QLabel* mean = new QLabel(this); mean->setNum(0.00); mean->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); mean->setToolTip(tr("Arithmetic mean of %1 in %2 units").arg(curve, unit)); mean->setWhatsThis(tr("Arithmetic mean of %1 in %2 units").arg(curve, unit)); - curveMeans->insert(curve+unit, mean); - curvesWidgetLayout->addWidget(mean, labelRow, 5); + curveMeans->insert(curveID, mean); + curvesWidgetLayout->addWidget(mean, labelRow, 5, Qt::AlignRight); // // Median // median = new QLabel(form); @@ -583,13 +564,13 @@ void LinechartWidget::addCurve(const QString& curve, const QString& unit) // horizontalLayout->addWidget(median); // Variance - variance = new QLabel(this); + QLabel* variance = new QLabel(this); variance->setNum(0.00); variance->setStyleSheet(QString("QLabel {font-family:\"Courier\"; font-weight: bold;}")); variance->setToolTip(tr("Variance of %1 in (%2)^2 units").arg(curve, unit)); variance->setWhatsThis(tr("Variance of %1 in (%2)^2 units").arg(curve, unit)); - curveVariances->insert(curve+unit, variance); - curvesWidgetLayout->addWidget(variance, labelRow, 6); + curveVariances->insert(curveID, variance); + curvesWidgetLayout->addWidget(variance, labelRow, 6, Qt::AlignRight); /* Color picker QColor color = QColorDialog::getColor(Qt::green, this); @@ -612,7 +593,7 @@ void LinechartWidget::addCurve(const QString& curve, const QString& unit) // Set UI components to initial state checkBox->setChecked(false); - plot->setVisibleById(curve+unit, false); + plot->setVisibleById(curveID, false); } /** @@ -684,7 +665,7 @@ void LinechartWidget::filterCurve(const QString &key, bool match) (*curveLabels)[key]->setVisible(match); (*curveMeans)[key]->setVisible(match); (*curveVariances)[key]->setVisible(match); - curveUnits[key]->setVisible(match); + curveUnits[key]->setVisible(match && ui.showUnitsCheckBox->isChecked()); checkBoxes[key]->setVisible(match); } } diff --git a/src/ui/linechart/LinechartWidget.h b/src/ui/linechart/LinechartWidget.h index cac806a14..372acfa43 100644 --- a/src/ui/linechart/LinechartWidget.h +++ b/src/ui/linechart/LinechartWidget.h @@ -138,7 +138,6 @@ protected: QAction* addNewCurve; ///< Add curve candidate to the active curves - QMenu* curveMenu; QComboBox *timeScaleCmb; QToolButton* scalingLogButton; -- GitLab From d2a7447853e31723f579e53a7c815b38c8b26018 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 22 Dec 2016 09:04:14 -0500 Subject: [PATCH 112/398] Handling proxies when fetching tiles. --- src/QtLocationPlugin/QGeoMapReplyQGC.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp index 2e1ec5bdc..066b12356 100644 --- a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp @@ -152,11 +152,16 @@ QGeoTiledMapReplyQGC::cacheError(QGCMapTask::TaskType type, QString /*errorStrin qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; } //-- Tile not in cache. Get it off the Internet. + QNetworkProxy proxy = _networkManager->proxy(); + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::DefaultProxy); + _networkManager->setProxy(tProxy); _reply = _networkManager->get(_request); _reply->setParent(0); connect(_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed())); + _networkManager->setProxy(proxy); } //----------------------------------------------------------------------------- -- GitLab From aff2247ff7c5f8887a2ec6c799bdf47e056f1d7b Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Thu, 22 Dec 2016 08:54:45 -0800 Subject: [PATCH 113/398] Thicken compass arrow icon for better visibility. --- src/FlightMap/Images/compassInstrumentArrow.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightMap/Images/compassInstrumentArrow.svg b/src/FlightMap/Images/compassInstrumentArrow.svg index 708155571..433119885 100644 --- a/src/FlightMap/Images/compassInstrumentArrow.svg +++ b/src/FlightMap/Images/compassInstrumentArrow.svg @@ -3,7 +3,7 @@ -- GitLab From 54c158c8b54b90f59fd4cbaa835a633df18317ce Mon Sep 17 00:00:00 2001 From: Rustom Jehangir Date: Thu, 22 Dec 2016 08:56:09 -0800 Subject: [PATCH 114/398] Update map vehicle icon to match new compass arrow icon. --- qgcresources.qrc | 2 ++ src/FlightMap/Images/vehicleArrowOpaque.svg | 9 +++++++++ src/FlightMap/Images/vehicleArrowOutline.svg | 9 +++++++++ src/FlightMap/MapItems/VehicleMapItem.qml | 2 +- 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/FlightMap/Images/vehicleArrowOpaque.svg create mode 100644 src/FlightMap/Images/vehicleArrowOutline.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 96751a013..c8597ebdb 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -109,6 +109,8 @@ src/FlightMap/Images/scale_endLight.png src/FlightMap/Images/airplaneOutline.svg src/FlightMap/Images/airplaneOpaque.svg + src/FlightMap/Images/vehicleArrowOutline.svg + src/FlightMap/Images/vehicleArrowOpaque.svg src/FlightMap/Images/ZoomPlus.svg src/FlightMap/Images/ZoomMinus.svg src/FlightMap/Images/ArrowHead.svg diff --git a/src/FlightMap/Images/vehicleArrowOpaque.svg b/src/FlightMap/Images/vehicleArrowOpaque.svg new file mode 100644 index 000000000..3f71ec00d --- /dev/null +++ b/src/FlightMap/Images/vehicleArrowOpaque.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/FlightMap/Images/vehicleArrowOutline.svg b/src/FlightMap/Images/vehicleArrowOutline.svg new file mode 100644 index 000000000..991d6185e --- /dev/null +++ b/src/FlightMap/Images/vehicleArrowOutline.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/FlightMap/MapItems/VehicleMapItem.qml b/src/FlightMap/MapItems/VehicleMapItem.qml index 4bc9a3a49..8940e563d 100644 --- a/src/FlightMap/MapItems/VehicleMapItem.qml +++ b/src/FlightMap/MapItems/VehicleMapItem.qml @@ -30,7 +30,7 @@ MapQuickItem { sourceItem: Image { id: vehicleIcon - source: isSatellite ? "/qmlimages/airplaneOpaque.svg" : "/qmlimages/airplaneOutline.svg" + source: isSatellite ? "/qmlimages/vehicleArrowOpaque.svg" : "/qmlimages/vehicleArrowOutline.svg" mipmap: true width: size sourceSize.width: size -- GitLab From 1ead374cf01da6876863f0d22de5a92058279895 Mon Sep 17 00:00:00 2001 From: yaoling <165577564@qq.com> Date: Fri, 23 Dec 2016 16:43:21 +0800 Subject: [PATCH 115/398] the message handleAltitude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit altitudeAMSL and altitudeRelative do not need divide 1000,the unit is meter and float format。 can you use _handleGlobalPositionInt message for altitudeAMSL and altitudeRelative? because _handleGlobalPositionInt is default 5Hz and _handleAltitude is 1Hz. --- src/Vehicle/Vehicle.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 3ffb4672f..8936de609 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -560,8 +560,8 @@ void Vehicle::_handleAltitude(mavlink_message_t& message) _useAltitudeForAltitude = true; _useGpsRawIntForAltitude = false; - _altitudeRelativeFact.setRawValue(altitude.altitude_relative / 1000.0); - _altitudeAMSLFact.setRawValue(altitude.altitude_amsl / 1000.0); + _altitudeRelativeFact.setRawValue(altitude.altitude_relative); + _altitudeAMSLFact.setRawValue(altitude.altitude_amsl); } -- GitLab From 57eb94e06222e98b815610555a5dadc3d3fb525a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 23 Dec 2016 06:17:08 -0800 Subject: [PATCH 116/398] Turn off command retry logic for PX4 Stack --- src/Vehicle/Vehicle.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 3ffb4672f..a91eb23ed 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1875,6 +1875,14 @@ void Vehicle::_sendMavCommandAgain(void) } if (_mavCommandRetryCount > 1) { + if (queuedCommand.command == MAV_CMD_START_RX_PAIR) { + // Implementation of this command in all firmwares is incorrect. It does not send Ack so we can't retry. + return; + } + if (px4Firmware()) { + // PX4 stack is inconsistent with repect to sending back an Ack from a COMMAND_LONG, hence we can't support retry logic for it. + return; + } qDebug() << "Vehicle::_sendMavCommandAgain retrying command:_mavCommandRetryCount" << queuedCommand.command << _mavCommandRetryCount; } -- GitLab From 5d2503d52ca6438dbfef0d904166cea8d66ddd10 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 23 Dec 2016 06:55:22 -0800 Subject: [PATCH 117/398] Hide dynamic links --- src/ui/preferences/LinkSettings.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ui/preferences/LinkSettings.qml b/src/ui/preferences/LinkSettings.qml index 202d5edcd..447982474 100644 --- a/src/ui/preferences/LinkSettings.qml +++ b/src/ui/preferences/LinkSettings.qml @@ -61,12 +61,13 @@ Rectangle { spacing: ScreenTools.defaultFontPixelHeight / 2 Repeater { model: QGroundControl.linkManager.linkConfigurations - delegate: - QGCButton { - text: object.name - width: _linkRoot.width * 0.5 - exclusiveGroup: linkGroup - anchors.horizontalCenter: settingsColumn.horizontalCenter + delegate: QGCButton { + anchors.horizontalCenter: settingsColumn.horizontalCenter + width: _linkRoot.width * 0.5 + text: object.name + exclusiveGroup: linkGroup + visible: !object.dynamic + onClicked: { checked = true _currentSelection = object -- GitLab From 4f5346de2a7b9d1c6c31823493615889868c389c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 23 Dec 2016 06:55:31 -0800 Subject: [PATCH 118/398] Naming change --- src/ui/preferences/GeneralSettings.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 1d81a84a1..d1975d189 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -437,7 +437,7 @@ QGCView { visible: QGroundControl.corePlugin.options.enableAutoConnectOptions QGCLabel { id: autoConnectLabel - text: qsTr("Autoconnect to the following devices:") + text: qsTr("AutoConnect to the following devices:") font.family: ScreenTools.demiboldFontFamily } } -- GitLab From 0ac444d9b53cc5fa1799de58d02ac6176af0715b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 23 Dec 2016 06:56:26 -0800 Subject: [PATCH 119/398] Naming changes No need to add dynamic MockLink comm links --- src/comm/LinkManager.cc | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 530b9b05c..8a400a955 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -47,7 +47,7 @@ const char* LinkManager::_autoconnect3DRRadioKey = "Autoconnect3DRRadio"; const char* LinkManager::_autoconnectPX4FlowKey = "AutoconnectPX4Flow"; const char* LinkManager::_autoconnectRTKGPSKey = "AutoconnectRTKGPS"; const char* LinkManager::_autoconnectLibrePilotKey = "AutoconnectLibrePilot"; -const char* LinkManager::_defaultUPDLinkName = "Default UDP Link"; +const char* LinkManager::_defaultUPDLinkName = "UDP Link (AutoConnect)"; const int LinkManager::_autoconnectUpdateTimerMSecs = 1000; #ifdef Q_OS_WIN @@ -353,9 +353,6 @@ void LinkManager::saveLinkConfigurationList() void LinkManager::loadLinkConfigurationList() { bool linksChanged = false; -#ifdef QT_DEBUG - bool mockPresent = false; -#endif QSettings settings; // Is the group even there? if(settings.contains(LinkConfiguration::settingsRoot() + "/count")) { @@ -397,7 +394,6 @@ void LinkManager::loadLinkConfigurationList() #ifdef QT_DEBUG case LinkConfiguration::TypeMock: pLink = (LinkConfiguration*)new MockConfiguration(name); - mockPresent = true; break; #endif default: @@ -425,16 +421,6 @@ void LinkManager::loadLinkConfigurationList() } } } - // Debug buids always add MockLink automatically (if one is not already there) -#ifdef QT_DEBUG - if(!mockPresent) - { - MockConfiguration* pMock = new MockConfiguration("Mock Link PX4"); - pMock->setDynamic(true); - _linkConfigurations.append(pMock); - linksChanged = true; - } -#endif if(linksChanged) { emit linkConfigurationsChanged(); @@ -544,47 +530,47 @@ void LinkManager::_updateAutoConnectLinks(void) case QGCSerialPortInfo::BoardTypePX4FMUV2: case QGCSerialPortInfo::BoardTypePX4FMUV4: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("Pixhawk on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypeAeroCore: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("AeroCore on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypeMINDPXFMUV2: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(QString("MindPX on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("MindPX on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypeTAPV1: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(QString("TAP on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("TAP on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypeASCV1: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(QString("ASC on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("ASC on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypePX4Flow: if (_autoconnectPX4Flow) { - pSerialConfig = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("PX4Flow on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); } break; case QGCSerialPortInfo::BoardTypeSikRadio: if (_autoconnect3DRRadio) { - pSerialConfig = new SerialConfiguration(QString("SiK Radio on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("SiK Radio on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); } break; case QGCSerialPortInfo::BoardTypeLibrePilot: if (_autoconnectLibrePilot) { - pSerialConfig = new SerialConfiguration(QString("LibrePilot on %1").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("LibrePilot on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); } break; #ifndef __mobile__ -- GitLab From 2f3d27bd5531350733ca106dfe19133ed03d26c1 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 24 Dec 2016 05:59:38 -0800 Subject: [PATCH 120/398] Change priority of messages from which altitude comes from Also changed GPS FactGroup values to use NaN appropriately to show unavailable values. --- src/Vehicle/Vehicle.cc | 45 ++++++++++++++++++++---------------------- src/Vehicle/Vehicle.h | 5 ++--- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 8936de609..a632ceb9c 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -94,9 +94,8 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) , _onboardControlSensorsUnhealthy(0) - , _useGpsRawIntForPosition(true) - , _useGpsRawIntForAltitude(true) - , _useAltitudeForAltitude(false) + , _gpsRawIntMessageAvailable(false) + , _globalPositionIntMessageAvailable(false) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -275,9 +274,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) , _onboardControlSensorsUnhealthy(0) - , _useGpsRawIntForPosition(true) - , _useGpsRawIntForAltitude(true) - , _useAltitudeForAltitude(false) + , _gpsRawIntMessageAvailable(false) + , _globalPositionIntMessageAvailable(false) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -516,20 +514,20 @@ void Vehicle::_handleGpsRawInt(mavlink_message_t& message) mavlink_gps_raw_int_t gpsRawInt; mavlink_msg_gps_raw_int_decode(&message, &gpsRawInt); + _gpsRawIntMessageAvailable = true; + if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) { - if (_useGpsRawIntForPosition) { + if (!_globalPositionIntMessageAvailable) { setLatitude(gpsRawInt.lat / (double)1E7); setLongitude(gpsRawInt.lon / (double)1E7); - } - if (_useGpsRawIntForAltitude) { - _altitudeRelativeFact.setRawValue(gpsRawInt.alt / 1000.0); + _altitudeAMSLFact.setRawValue(gpsRawInt.alt / 1000.0); } } _gpsFactGroup.count()->setRawValue(gpsRawInt.satellites_visible == 255 ? 0 : gpsRawInt.satellites_visible); - _gpsFactGroup.hdop()->setRawValue(gpsRawInt.eph == UINT16_MAX ? 1e10f : gpsRawInt.eph / 100.0); - _gpsFactGroup.vdop()->setRawValue(gpsRawInt.epv == UINT16_MAX ? 1e10f : gpsRawInt.epv / 100.0); - _gpsFactGroup.courseOverGround()->setRawValue(gpsRawInt.cog == UINT16_MAX ? 0.0 : gpsRawInt.cog / 100.0); + _gpsFactGroup.hdop()->setRawValue(gpsRawInt.eph == UINT16_MAX ? std::numeric_limits::quiet_NaN() : gpsRawInt.eph / 100.0); + _gpsFactGroup.vdop()->setRawValue(gpsRawInt.epv == UINT16_MAX ? std::numeric_limits::quiet_NaN() : gpsRawInt.epv / 100.0); + _gpsFactGroup.courseOverGround()->setRawValue(gpsRawInt.cog == UINT16_MAX ? std::numeric_limits::quiet_NaN() : gpsRawInt.cog / 100.0); _gpsFactGroup.lock()->setRawValue(gpsRawInt.fix_type); if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) { @@ -542,15 +540,12 @@ void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message) mavlink_global_position_int_t globalPositionInt; mavlink_msg_global_position_int_decode(&message, &globalPositionInt); - _useGpsRawIntForPosition = false; - _useGpsRawIntForAltitude = false; + _globalPositionIntMessageAvailable = true; setLatitude(globalPositionInt.lat / (double)1E7); setLongitude(globalPositionInt.lon / (double)1E7); - if (!_useAltitudeForAltitude) { - _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0); - _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0); - } + _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0); + _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0); } void Vehicle::_handleAltitude(mavlink_message_t& message) @@ -558,11 +553,13 @@ void Vehicle::_handleAltitude(mavlink_message_t& message) mavlink_altitude_t altitude; mavlink_msg_altitude_decode(&message, &altitude); - _useAltitudeForAltitude = true; - _useGpsRawIntForAltitude = false; - _altitudeRelativeFact.setRawValue(altitude.altitude_relative); - _altitudeAMSLFact.setRawValue(altitude.altitude_amsl); - + // If data from GPS is available it takes precedence over ALTITUDE message + if (!_globalPositionIntMessageAvailable) { + _altitudeRelativeFact.setRawValue(altitude.altitude_relative); + if (!_gpsRawIntMessageAvailable) { + _altitudeAMSLFact.setRawValue(altitude.altitude_amsl); + } + } } void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& message) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 2d6bdcd4e..df0d0036c 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -766,9 +766,8 @@ private: uint32_t _onboardControlSensorsEnabled; uint32_t _onboardControlSensorsHealth; uint32_t _onboardControlSensorsUnhealthy; - bool _useGpsRawIntForPosition; - bool _useGpsRawIntForAltitude; - bool _useAltitudeForAltitude; + bool _gpsRawIntMessageAvailable; + bool _globalPositionIntMessageAvailable; typedef struct { int component; -- GitLab From 5040e3d933395bbce61208cb07cceaafcd07e9f3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 26 Dec 2016 13:27:04 -0800 Subject: [PATCH 121/398] Use ArduPilot for testing PX4 doesn't retry commands by default --- src/Vehicle/SendMavCommandTest.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Vehicle/SendMavCommandTest.cc b/src/Vehicle/SendMavCommandTest.cc index 850bcc980..de4a8c7ad 100644 --- a/src/Vehicle/SendMavCommandTest.cc +++ b/src/Vehicle/SendMavCommandTest.cc @@ -14,7 +14,7 @@ void SendMavCommandTest::_noFailure(void) { - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -37,7 +37,7 @@ void SendMavCommandTest::_failureShowError(void) // Will pop error about request failure setExpectedMessageBox(QMessageBox::Ok); - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -60,7 +60,7 @@ void SendMavCommandTest::_failureShowError(void) void SendMavCommandTest::_failureNoShowError(void) { - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -80,7 +80,7 @@ void SendMavCommandTest::_failureNoShowError(void) void SendMavCommandTest::_noFailureAfterRetry(void) { - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -103,7 +103,7 @@ void SendMavCommandTest::_failureAfterRetry(void) // Will pop error about request failure setExpectedMessageBox(QMessageBox::Ok); - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -129,7 +129,7 @@ void SendMavCommandTest::_failureAfterNoReponse(void) // Will pop error about request failure setExpectedMessageBox(QMessageBox::Ok); - _connectMockLink(); + _connectMockLink(MAV_AUTOPILOT_ARDUPILOTMEGA); MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); Vehicle* vehicle = vehicleMgr->activeVehicle(); @@ -138,7 +138,7 @@ void SendMavCommandTest::_failureAfterNoReponse(void) vehicle->sendMavCommand(MAV_COMP_ID_ALL, MAV_CMD_USER_5, true /* showError */); QSignalSpy spyResult(vehicle, SIGNAL(mavCommandResult(int, int, int, int, bool))); - QCOMPARE(spyResult.wait(10000), true); + QCOMPARE(spyResult.wait(20000), true); QList arguments = spyResult.takeFirst(); QCOMPARE(arguments.count(), 5); QCOMPARE(arguments.at(0).toInt(), vehicle->id()); -- GitLab From 76677481f6c3d0ef2b497d91e5998b362d1fe022 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 26 Dec 2016 13:27:29 -0800 Subject: [PATCH 122/398] Reference counting for LinkInterface, LinkConfiguration Prevent shutdown ordering crashes --- qgroundcontrol.pro | 1 + src/QmlControls/QGroundControlQmlGlobal.cc | 4 +- src/Vehicle/MultiVehicleManager.cc | 6 +- src/Vehicle/Vehicle.cc | 36 +++- src/Vehicle/Vehicle.h | 8 +- src/comm/LinkConfiguration.h | 5 +- src/comm/LinkInterface.cc | 157 ++++++++++++++++ src/comm/LinkInterface.h | 122 ++----------- src/comm/LinkManager.cc | 201 ++++++++++++++------- src/comm/LinkManager.h | 51 +++--- src/comm/LogReplayLink.cc | 13 +- src/comm/LogReplayLink.h | 6 +- src/comm/MAVLinkProtocol.cc | 2 +- src/comm/MockLink.cc | 24 ++- src/comm/MockLink.h | 6 +- src/comm/SerialLink.cc | 69 ++++--- src/comm/SerialLink.h | 5 +- src/comm/TCPLink.cc | 16 +- src/comm/TCPLink.h | 5 +- src/comm/UDPLink.cc | 39 ++-- src/comm/UDPLink.h | 20 +- src/qgcunittest/LinkManagerTest.cc | 14 +- src/qgcunittest/TCPLinkTest.cc | 22 +-- src/qgcunittest/TCPLinkTest.h | 10 +- src/ui/QGCMAVLinkLogPlayer.cc | 5 +- 25 files changed, 487 insertions(+), 360 deletions(-) create mode 100644 src/comm/LinkInterface.cc diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index c0cd800d1..ce9cd426e 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -641,6 +641,7 @@ SOURCES += \ src/VehicleSetup/JoystickConfigController.cc \ src/audio/QGCAudioWorker.cpp \ src/comm/LinkConfiguration.cc \ + src/comm/LinkInterface.cc \ src/comm/LinkManager.cc \ src/comm/MAVLinkProtocol.cc \ src/comm/QGCMAVLink.cc \ diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 2a8168db3..27966dd36 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -155,8 +155,8 @@ void QGroundControlQmlGlobal::stopAllMockLinks(void) #ifdef QT_DEBUG LinkManager* linkManager = qgcApp()->toolbox()->linkManager(); - for (int i=0; ilinks()->count(); i++) { - LinkInterface* link = linkManager->links()->value(i); + for (int i=0; ilinks().count(); i++) { + LinkInterface* link = linkManager->links()[i]; MockLink* mockLink = qobject_cast(link); if (mockLink) { diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 5baa4fab6..eae33e12b 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -306,9 +306,9 @@ void MultiVehicleManager::setGcsHeartbeatEnabled(bool gcsHeartBeatEnabled) void MultiVehicleManager::_sendGCSHeartbeat(void) { // Send a heartbeat out on each link - QmlObjectListModel* links = _toolbox->linkManager()->links(); - for (int i=0; icount(); i++) { - LinkInterface* link = links->value(i); + LinkManager* linkMgr = _toolbox->linkManager(); + for (int i=0; ilinks().count(); i++) { + LinkInterface* link = linkMgr->links()[i]; if (link->isConnected()) { mavlink_message_t message; mavlink_msg_heartbeat_pack_chan(_mavlinkProtocol->getSystemId(), diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 52a7183f9..4b8387c03 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -926,8 +926,9 @@ bool Vehicle::_containsLink(LinkInterface* link) void Vehicle::_addLink(LinkInterface* link) { if (!_containsLink(link)) { - _links += link; qCDebug(VehicleLog) << "_addLink:" << QString("%1").arg((ulong)link, 0, 16); + _links += link; + _updatePriorityLink(); connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkInactive, this, &Vehicle::_linkInactiveOrDeleted); connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDeleted, this, &Vehicle::_linkInactiveOrDeleted); } @@ -938,6 +939,7 @@ void Vehicle::_linkInactiveOrDeleted(LinkInterface* link) qCDebug(VehicleLog) << "_linkInactiveOrDeleted linkCount" << _links.count(); _links.removeOne(link); + _updatePriorityLink(); if (_links.count() == 0 && !_allLinksInactiveSent) { qCDebug(VehicleLog) << "All links inactive"; @@ -983,26 +985,42 @@ void Vehicle::_sendMessageOnLink(LinkInterface* link, mavlink_message_t message) emit messagesSentChanged(); } -/// @return Direct usb connection link to board if one, NULL if none -LinkInterface* Vehicle::priorityLink(void) +void Vehicle::_updatePriorityLink(void) { #ifndef NO_SERIAL_LINK - foreach (LinkInterface* link, _links) { + LinkInterface* newPriorityLink = NULL; + + // Note that this routine specificallty does not clear _priorityLink when there are no links remaining. + // By doing this we hold a reference on the last link as the Vehicle shuts down. Thus preventing shutdown + // ordering NULL pointer crashes where priorityLink() is still called during shutdown sequence. + for (int i=0; i<_links.count(); i++) { + LinkInterface* link = _links[i]; if (link->isConnected()) { SerialLink* pSerialLink = qobject_cast(link); if (pSerialLink) { - LinkConfiguration* pLinkConfig = pSerialLink->getLinkConfiguration(); - if (pLinkConfig) { - SerialConfiguration* pSerialConfig = qobject_cast(pLinkConfig); + LinkConfiguration* config = pSerialLink->getLinkConfiguration(); + if (config) { + SerialConfiguration* pSerialConfig = qobject_cast(config); if (pSerialConfig && pSerialConfig->usbDirect()) { - return link; + if (_priorityLink.data() != link) { + newPriorityLink = link; + break; + } + return; } } } } } + + if (!newPriorityLink && !_priorityLink.data() && _links.count()) { + newPriorityLink = _links[0]; + } + + if (newPriorityLink) { + _priorityLink = qgcApp()->toolbox()->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink); + } #endif - return _links.count() ? _links[0] : NULL; } void Vehicle::setLatitude(double latitude) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 2d6bdcd4e..aadbf51b9 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -416,8 +416,9 @@ public: MAV_TYPE vehicleType(void) const { return _vehicleType; } Q_INVOKABLE QString vehicleTypeName(void) const; - /// Returns the highest quality link available to the Vehicle - LinkInterface* priorityLink(void); + /// Returns the highest quality link available to the Vehicle. If you need to hold a refernce to this link use + /// LinkManager::sharedLinkInterfaceForGet to get QSharedPointer for link. + LinkInterface* priorityLink(void) { return _priorityLink.data(); } /// Sends a message to the specified link /// @return true: message sent, false: Link no longer connected @@ -720,6 +721,7 @@ private: void _handleMavlinkLoggingDataAcked(mavlink_message_t& message); void _ackMavlinkLogData(uint16_t sequence); void _sendNextQueuedMavCommand(void); + void _updatePriorityLink(void); private: int _id; ///< Mavlink system id @@ -851,6 +853,8 @@ private: static const int _lowBatteryAnnounceRepeatMSecs; // Amount of time in between each low battery announcement QElapsedTimer _lowBatteryAnnounceTimer; + SharedLinkInterfacePointer _priorityLink; // We always keep a reference to the priority link to manage shutdown ordering + // FactGroup facts Fact _rollFact; diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 59fa8f36f..af6b79b4b 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -7,7 +7,6 @@ * ****************************************************************************/ - #ifndef LINKCONFIGURATION_H #define LINKCONFIGURATION_H @@ -37,7 +36,7 @@ public: // Property accessors - const QString name(void) { return _name; } + QString name(void) const { return _name; } LinkInterface* link(void) { return _link; } void setName(const QString name); @@ -190,4 +189,6 @@ private: bool _autoConnect; ///< This connection is started automatically at boot }; +typedef QSharedPointer SharedLinkConfigurationPointer; + #endif // LINKCONFIGURATION_H diff --git a/src/comm/LinkInterface.cc b/src/comm/LinkInterface.cc new file mode 100644 index 000000000..a65d3fb31 --- /dev/null +++ b/src/comm/LinkInterface.cc @@ -0,0 +1,157 @@ +/**************************************************************************** + * + * (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 "LinkInterface.h" +#include "QGCApplication.h" + +/// mavlink channel to use for this link, as used by mavlink_parse_char. The mavlink channel is only +/// set into the link when it is added to LinkManager +uint8_t LinkInterface::mavlinkChannel(void) const +{ + if (!_mavlinkChannelSet) { + qWarning() << "Call to LinkInterface::mavlinkChannel with _mavlinkChannelSet == false"; + } + return _mavlinkChannel; +} +// Links are only created by LinkManager so constructor is not public +LinkInterface::LinkInterface(SharedLinkConfigurationPointer& config) + : QThread(0) + , _config(config) + , _mavlinkChannelSet(false) + , _active(false) + , _enableRateCollection(false) +{ + _config->setLink(this); + + // Initialize everything for the data rate calculation buffers. + _inDataIndex = 0; + _outDataIndex = 0; + + // Initialize our data rate buffers. + memset(_inDataWriteAmounts, 0, sizeof(_inDataWriteAmounts)); + memset(_inDataWriteTimes, 0, sizeof(_inDataWriteTimes)); + memset(_outDataWriteAmounts,0, sizeof(_outDataWriteAmounts)); + memset(_outDataWriteTimes, 0, sizeof(_outDataWriteTimes)); + + QObject::connect(this, &LinkInterface::_invokeWriteBytes, this, &LinkInterface::_writeBytes); + qRegisterMetaType("LinkInterface*"); +} + +/// This function logs the send times and amounts of datas for input. Data is used for calculating +/// the transmission rate. +/// @param byteCount Number of bytes received +/// @param time Time in ms send occurred +void LinkInterface::_logInputDataRate(quint64 byteCount, qint64 time) { + if(_enableRateCollection) + _logDataRateToBuffer(_inDataWriteAmounts, _inDataWriteTimes, &_inDataIndex, byteCount, time); +} + +/// This function logs the send times and amounts of datas for output. Data is used for calculating +/// the transmission rate. +/// @param byteCount Number of bytes sent +/// @param time Time in ms receive occurred +void LinkInterface::_logOutputDataRate(quint64 byteCount, qint64 time) { + if(_enableRateCollection) + _logDataRateToBuffer(_outDataWriteAmounts, _outDataWriteTimes, &_outDataIndex, byteCount, time); +} + +/** + * @brief logDataRateToBuffer Stores transmission times/amounts for statistics + * + * This function logs the send times and amounts of datas to the given circular buffers. + * This data is used for calculating the transmission rate. + * + * @param bytesBuffer[out] The buffer to write the bytes value into. + * @param timeBuffer[out] The buffer to write the time value into + * @param writeIndex[out] The write index used for this buffer. + * @param bytes The amount of bytes transmit. + * @param time The time (in ms) this transmission occurred. + */ +void LinkInterface::_logDataRateToBuffer(quint64 *bytesBuffer, qint64 *timeBuffer, int *writeIndex, quint64 bytes, qint64 time) +{ + QMutexLocker dataRateLocker(&_dataRateMutex); + + int i = *writeIndex; + + // Now write into the buffer, if there's no room, we just overwrite the first data point. + bytesBuffer[i] = bytes; + timeBuffer[i] = time; + + // Increment and wrap the write index + ++i; + if (i == _dataRateBufferSize) + { + i = 0; + } + *writeIndex = i; +} + +/** + * @brief getCurrentDataRate Get the current data rate given a data rate buffer. + * + * This function attempts to use the times and number of bytes transmit into a current data rate + * estimation. Since it needs to use timestamps to get the timeperiods over when the data was sent, + * this is effectively a global data rate over the last _dataRateBufferSize - 1 data points. Also note + * that data points older than NOW - dataRateCurrentTimespan are ignored. + * + * @param index The first valid sample in the data rate buffer. Refers to the oldest time sample. + * @param dataWriteTimes The time, in ms since epoch, that each data sample took place. + * @param dataWriteAmounts The amount of data (in bits) that was transferred. + * @return The bits per second of data transferrence of the interface over the last [-statsCurrentTimespan, 0] timespan. + */ +qint64 LinkInterface::_getCurrentDataRate(int index, const qint64 dataWriteTimes[], const quint64 dataWriteAmounts[]) const +{ + const qint64 now = QDateTime::currentMSecsSinceEpoch(); + + // Limit the time we calculate to the recent past + const qint64 cutoff = now - _dataRateCurrentTimespan; + + // Grab the mutex for working with the stats variables + QMutexLocker dataRateLocker(&_dataRateMutex); + + // Now iterate through the buffer of all received data packets adding up all values + // within now and our cutof. + qint64 totalBytes = 0; + qint64 totalTime = 0; + qint64 lastTime = 0; + int size = _dataRateBufferSize; + while (size-- > 0) + { + // If this data is within our cutoff time, include it in our calculations. + // This also accounts for when the buffer is empty and filled with 0-times. + if (dataWriteTimes[index] > cutoff && lastTime > 0) { + // Track the total time, using the previous time as our timeperiod. + totalTime += dataWriteTimes[index] - lastTime; + totalBytes += dataWriteAmounts[index]; + } + + // Track the last time sample for doing timespan calculations + lastTime = dataWriteTimes[index]; + + // Increment and wrap the index if necessary. + if (++index == _dataRateBufferSize) + { + index = 0; + } + } + + // Return the final calculated value in bits / s, converted from bytes/ms. + qint64 dataRate = (totalTime != 0)?(qint64)((float)totalBytes * 8.0f / ((float)totalTime / 1000.0f)):0; + + // Finally return our calculated data rate. + return dataRate; +} + +/// Sets the mavlink channel to use for this link +void LinkInterface::_setMavlinkChannel(uint8_t channel) +{ + Q_ASSERT(!_mavlinkChannelSet); + _mavlinkChannelSet = true; + _mavlinkChannel = channel; +} diff --git a/src/comm/LinkInterface.h b/src/comm/LinkInterface.h index ecb894740..63ce68257 100644 --- a/src/comm/LinkInterface.h +++ b/src/comm/LinkInterface.h @@ -19,9 +19,9 @@ #include #include "QGCMAVLink.h" +#include "LinkConfiguration.h" class LinkManager; -class LinkConfiguration; /** * The link interface defines the interface for all links used to communicate @@ -35,18 +35,16 @@ class LinkInterface : public QThread // Only LinkManager is allowed to create/delete or _connect/_disconnect a link friend class LinkManager; -public: +public: + ~LinkInterface() { _config->setLink(NULL); } + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) // Property accessors - bool active(void) { return _active; } - void setActive(bool active) { _active = active; emit activeChanged(active); } + bool active(void) { return _active; } + void setActive(bool active) { _active = active; emit activeChanged(active); } - /** - * @brief Get link configuration - * @return A pointer to the instance of LinkConfiguration - **/ - virtual LinkConfiguration* getLinkConfiguration() = 0; + LinkConfiguration* getLinkConfiguration(void) { return _config.data(); } /* Connection management */ @@ -116,13 +114,7 @@ public: /// mavlink channel to use for this link, as used by mavlink_parse_char. The mavlink channel is only /// set into the link when it is added to LinkManager - uint8_t mavlinkChannel(void) const - { - if (!_mavlinkChannelSet) { - qWarning() << "Call to LinkInterface::mavlinkChannel with _mavlinkChannelSet == false"; - } - return _mavlinkChannel; - } + uint8_t mavlinkChannel(void) const; // These are left unimplemented in order to cause linker errors which indicate incorrect usage of // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. @@ -192,43 +184,21 @@ signals: protected: // Links are only created by LinkManager so constructor is not public - LinkInterface() : - QThread(0) - , _mavlinkChannelSet(false) - , _active(false) - , _enableRateCollection(false) - { - // Initialize everything for the data rate calculation buffers. - _inDataIndex = 0; - _outDataIndex = 0; - - // Initialize our data rate buffers. - memset(_inDataWriteAmounts, 0, sizeof(_inDataWriteAmounts)); - memset(_inDataWriteTimes, 0, sizeof(_inDataWriteTimes)); - memset(_outDataWriteAmounts,0, sizeof(_outDataWriteAmounts)); - memset(_outDataWriteTimes, 0, sizeof(_outDataWriteTimes)); - - QObject::connect(this, &LinkInterface::_invokeWriteBytes, this, &LinkInterface::_writeBytes); - qRegisterMetaType("LinkInterface*"); - } + LinkInterface(SharedLinkConfigurationPointer& config); /// This function logs the send times and amounts of datas for input. Data is used for calculating /// the transmission rate. /// @param byteCount Number of bytes received /// @param time Time in ms send occurred - void _logInputDataRate(quint64 byteCount, qint64 time) { - if(_enableRateCollection) - _logDataRateToBuffer(_inDataWriteAmounts, _inDataWriteTimes, &_inDataIndex, byteCount, time); - } + void _logInputDataRate(quint64 byteCount, qint64 time); /// This function logs the send times and amounts of datas for output. Data is used for calculating /// the transmission rate. /// @param byteCount Number of bytes sent /// @param time Time in ms receive occurred - void _logOutputDataRate(quint64 byteCount, qint64 time) { - if(_enableRateCollection) - _logDataRateToBuffer(_outDataWriteAmounts, _outDataWriteTimes, &_outDataIndex, byteCount, time); - } + void _logOutputDataRate(quint64 byteCount, qint64 time); + + SharedLinkConfigurationPointer _config; private: /** @@ -243,24 +213,7 @@ private: * @param bytes The amount of bytes transmit. * @param time The time (in ms) this transmission occurred. */ - void _logDataRateToBuffer(quint64 *bytesBuffer, qint64 *timeBuffer, int *writeIndex, quint64 bytes, qint64 time) - { - QMutexLocker dataRateLocker(&_dataRateMutex); - - int i = *writeIndex; - - // Now write into the buffer, if there's no room, we just overwrite the first data point. - bytesBuffer[i] = bytes; - timeBuffer[i] = time; - - // Increment and wrap the write index - ++i; - if (i == _dataRateBufferSize) - { - i = 0; - } - *writeIndex = i; - } + void _logDataRateToBuffer(quint64 *bytesBuffer, qint64 *timeBuffer, int *writeIndex, quint64 bytes, qint64 time); /** * @brief getCurrentDataRate Get the current data rate given a data rate buffer. @@ -275,48 +228,7 @@ private: * @param dataWriteAmounts The amount of data (in bits) that was transferred. * @return The bits per second of data transferrence of the interface over the last [-statsCurrentTimespan, 0] timespan. */ - qint64 _getCurrentDataRate(int index, const qint64 dataWriteTimes[], const quint64 dataWriteAmounts[]) const - { - const qint64 now = QDateTime::currentMSecsSinceEpoch(); - - // Limit the time we calculate to the recent past - const qint64 cutoff = now - _dataRateCurrentTimespan; - - // Grab the mutex for working with the stats variables - QMutexLocker dataRateLocker(&_dataRateMutex); - - // Now iterate through the buffer of all received data packets adding up all values - // within now and our cutof. - qint64 totalBytes = 0; - qint64 totalTime = 0; - qint64 lastTime = 0; - int size = _dataRateBufferSize; - while (size-- > 0) - { - // If this data is within our cutoff time, include it in our calculations. - // This also accounts for when the buffer is empty and filled with 0-times. - if (dataWriteTimes[index] > cutoff && lastTime > 0) { - // Track the total time, using the previous time as our timeperiod. - totalTime += dataWriteTimes[index] - lastTime; - totalBytes += dataWriteAmounts[index]; - } - - // Track the last time sample for doing timespan calculations - lastTime = dataWriteTimes[index]; - - // Increment and wrap the index if necessary. - if (++index == _dataRateBufferSize) - { - index = 0; - } - } - - // Return the final calculated value in bits / s, converted from bytes/ms. - qint64 dataRate = (totalTime != 0)?(qint64)((float)totalBytes * 8.0f / ((float)totalTime / 1000.0f)):0; - - // Finally return our calculated data rate. - return dataRate; - } + qint64 _getCurrentDataRate(int index, const qint64 dataWriteTimes[], const quint64 dataWriteAmounts[]) const; /** * @brief Connect this interface logically @@ -328,7 +240,7 @@ private: virtual void _disconnect(void) = 0; /// Sets the mavlink channel to use for this link - void _setMavlinkChannel(uint8_t channel) { Q_ASSERT(!_mavlinkChannelSet); _mavlinkChannelSet = true; _mavlinkChannel = channel; } + void _setMavlinkChannel(uint8_t channel); bool _mavlinkChannelSet; ///< true: _mavlinkChannel has been set uint8_t _mavlinkChannel; ///< mavlink channel to use for this link, as used by mavlink_parse_char @@ -355,6 +267,6 @@ private: bool _enableRateCollection; }; -typedef QSharedPointer SharedLinkInterface; +typedef QSharedPointer SharedLinkInterfacePointer; #endif // _LINKINTERFACE_H_ diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 8a400a955..c4482009d 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -7,15 +7,6 @@ * ****************************************************************************/ - -/** - * @file - * @brief Brief Description - * - * @author Lorenz Meier - * - */ - #include #include #include @@ -108,17 +99,21 @@ void LinkManager::setToolbox(QGCToolbox *toolbox) } -LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) +LinkInterface* LinkManager::createConnectedLink(SharedLinkConfigurationPointer& config) { - Q_ASSERT(config); + if (!config) { + qWarning() << "LinkManager::createConnectedLink called with NULL config"; + return NULL; + } + LinkInterface* pLink = NULL; switch(config->type()) { #ifndef NO_SERIAL_LINK case LinkConfiguration::TypeSerial: { - SerialConfiguration* serialConfig = dynamic_cast(config); + SerialConfiguration* serialConfig = dynamic_cast(config.data()); if (serialConfig) { - pLink = new SerialLink(serialConfig); + pLink = new SerialLink(config); if (serialConfig->usbDirect()) { _activeLinkCheckList.append((SerialLink*)pLink); if (!_activeLinkCheckTimer.isActive()) { @@ -130,43 +125,45 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) break; #endif case LinkConfiguration::TypeUdp: - pLink = new UDPLink(dynamic_cast(config)); + pLink = new UDPLink(config); break; case LinkConfiguration::TypeTcp: - pLink = new TCPLink(dynamic_cast(config)); + pLink = new TCPLink(config); break; #ifdef QGC_ENABLE_BLUETOOTH case LinkConfiguration::TypeBluetooth: - pLink = new BluetoothLink(dynamic_cast(config)); + pLink = new BluetoothLink(config); break; #endif #ifndef __mobile__ case LinkConfiguration::TypeLogReplay: - pLink = new LogReplayLink(dynamic_cast(config)); + pLink = new LogReplayLink(config); break; #endif #ifdef QT_DEBUG case LinkConfiguration::TypeMock: - pLink = new MockLink(dynamic_cast(config)); + pLink = new MockLink(config); break; #endif case LinkConfiguration::TypeLast: default: break; } - if(pLink) { + + if (pLink) { _addLink(pLink); connectLink(pLink); } + return pLink; } LinkInterface* LinkManager::createConnectedLink(const QString& name) { Q_ASSERT(name.isEmpty() == false); - for(int i = 0; i < _linkConfigurations.count(); i++) { - LinkConfiguration* conf = _linkConfigurations.value(i); - if(conf && conf->name() == name) + for(int i = 0; i < _sharedConfigurations.count(); i++) { + SharedLinkConfigurationPointer& conf = _sharedConfigurations[i]; + if (conf->name() == name) return createConnectedLink(conf); } return NULL; @@ -183,7 +180,7 @@ void LinkManager::_addLink(LinkInterface* link) return; } - if (!_links.contains(link)) { + if (!containsLink(link)) { bool channelSet = false; // Find a mavlink channel to use for this link, Channel 0 is reserved for internal use. @@ -205,7 +202,7 @@ void LinkManager::_addLink(LinkInterface* link) qWarning() << "Ran out of mavlink channels"; } - _links.append(link); + _sharedLinks.append(SharedLinkInterfacePointer(link)); emit newLink(link); } @@ -225,8 +222,8 @@ void LinkManager::_addLink(LinkInterface* link) void LinkManager::disconnectAll(void) { // Walk list in reverse order to preserve indices during delete - for (int i=_links.count()-1; i>=0; i--) { - disconnectLink(_links.value(i)); + for (int i=_sharedLinks.count()-1; i>=0; i--) { + disconnectLink(_sharedLinks[i].data()); } } @@ -243,23 +240,22 @@ bool LinkManager::connectLink(LinkInterface* link) void LinkManager::disconnectLink(LinkInterface* link) { - if (!link || !_links.contains(link)) { + if (!link || !containsLink(link)) { return; } link->_disconnect(); + LinkConfiguration* config = link->getLinkConfiguration(); - if (config) { - if (_autoconnectConfigurations.contains(config)) { - config->setLink(NULL); + for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { + if (_sharedAutoconnectConfigurations[i].data() == config) { + qCDebug(LinkManagerLog) << "Removing disconnected autoconnect config" << config->name(); + _sharedAutoconnectConfigurations.removeAt(i); + break; } } + _deleteLink(link); - if (_autoconnectConfigurations.contains(config)) { - qCDebug(LinkManagerLog) << "Removing disconnected autoconnect config" << config->name(); - _autoconnectConfigurations.removeOne(config); - delete config; - } } void LinkManager::_deleteLink(LinkInterface* link) @@ -276,13 +272,29 @@ void LinkManager::_deleteLink(LinkInterface* link) // Free up the mavlink channel associated with this link _mavlinkChannelsUsedBitMask &= ~(1 << link->mavlinkChannel()); - _links.removeOne(link); - delete link; + for (int i=0; i<_sharedLinks.count(); i++) { + if (_sharedLinks[i].data() == link) { + _sharedLinks.removeAt(i); + break; + } + } // Emit removal of link emit linkDeleted(link); } +SharedLinkInterfacePointer LinkManager::sharedLinkInterfacePointerForLink(LinkInterface* link) +{ + for (int i=0; i<_sharedLinks.count(); i++) { + if (_sharedLinks[i].data() == link) { + return _sharedLinks[i]; + } + } + + qWarning() << "LinkManager::sharedLinkInterfaceForLink returning NULL"; + return SharedLinkInterfacePointer(NULL); +} + /// @brief If all new connections should be suspended a message is displayed to the user and true /// is returned; bool LinkManager::_connectionsSuspendedMsg(void) @@ -328,11 +340,10 @@ void LinkManager::saveLinkConfigurationList() QSettings settings; settings.remove(LinkConfiguration::settingsRoot()); int trueCount = 0; - for (int i = 0; i < _linkConfigurations.count(); i++) { - LinkConfiguration* linkConfig = _linkConfigurations.value(i); + for (int i = 0; i < _sharedConfigurations.count(); i++) { + SharedLinkConfigurationPointer linkConfig = _sharedConfigurations[i]; if (linkConfig) { - if(!linkConfig->isDynamic()) - { + if (!linkConfig->isDynamic()) { QString root = LinkConfiguration::settingsRoot(); root += QString("/Link%1").arg(trueCount++); settings.setValue(root + "/name", linkConfig->name()); @@ -404,7 +415,7 @@ void LinkManager::loadLinkConfigurationList() //-- Have the instance load its own values pLink->setAutoConnect(autoConnect); pLink->loadSettings(settings, root); - _linkConfigurations.append(pLink); + addConfiguration(pLink); linksChanged = true; } } else { @@ -434,12 +445,12 @@ SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const Q { QString searchPort = portName.trimmed(); - for (int i=0; i<_autoconnectConfigurations.count(); i++) { - SerialConfiguration* linkConfig = _autoconnectConfigurations.value(i); + for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { + SerialConfiguration* serialConfig = qobject_cast(_sharedAutoconnectConfigurations[i].data()); - if (linkConfig) { - if (linkConfig->portName() == searchPort) { - return linkConfig; + if (serialConfig) { + if (serialConfig->portName() == searchPort) { + return serialConfig; } } else { qWarning() << "Internal error"; @@ -457,8 +468,8 @@ void LinkManager::_updateAutoConnectLinks(void) // Re-add UDP if we need to bool foundUDP = false; - for (int i=0; i<_links.count(); i++) { - LinkConfiguration* linkConfig = _links.value(i)->getLinkConfiguration(); + for (int i=0; i<_sharedLinks.count(); i++) { + LinkConfiguration* linkConfig = _sharedLinks[i]->getLinkConfiguration(); if (linkConfig->type() == LinkConfiguration::TypeUdp && linkConfig->name() == _defaultUPDLinkName) { foundUDP = true; break; @@ -468,9 +479,9 @@ void LinkManager::_updateAutoConnectLinks(void) qCDebug(LinkManagerLog) << "New auto-connect UDP port added"; UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUPDLinkName); udpConfig->setLocalPort(QGC_UDP_LOCAL_PORT); - udpConfig->setDynamic(true); - _linkConfigurations.append(udpConfig); - createConnectedLink(udpConfig); + udpConfig->setDynamic(true); + SharedLinkConfigurationPointer config = addConfiguration(udpConfig); + createConnectedLink(config); emit linkConfigurationsChanged(); } @@ -591,8 +602,8 @@ void LinkManager::_updateAutoConnectLinks(void) pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardTypeSikRadio ? 57600 : 115200); pSerialConfig->setDynamic(true); pSerialConfig->setPortName(portInfo.systemLocation()); - _autoconnectConfigurations.append(pSerialConfig); - createConnectedLink(pSerialConfig); + _sharedAutoconnectConfigurations.append(SharedLinkConfigurationPointer(pSerialConfig)); + createConnectedLink(_sharedAutoconnectConfigurations.last()); } } } @@ -607,13 +618,13 @@ void LinkManager::_updateAutoConnectLinks(void) // Now we go through the current configuration list and make sure any dynamic config has gone away QList _confToDelete; - for (int i=0; i<_autoconnectConfigurations.count(); i++) { - SerialConfiguration* linkConfig = _autoconnectConfigurations.value(i); - if (linkConfig) { - if (!currentPorts.contains(linkConfig->portName())) { - if (linkConfig->link()) { - if (linkConfig->link()->isConnected()) { - if (linkConfig->link()->active()) { + for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { + SerialConfiguration* serialConfig = qobject_cast(_sharedAutoconnectConfigurations[i].data()); + if (serialConfig) { + if (!currentPorts.contains(serialConfig->portName())) { + if (serialConfig->link()) { + if (serialConfig->link()->isConnected()) { + if (serialConfig->link()->active()) { // We don't remove links which are still connected which have been active with a vehicle on them // even though at this point the cable may have been pulled. Instead we wait for the user to // Disconnect. Once the user disconnects, the link will be removed. @@ -621,7 +632,7 @@ void LinkManager::_updateAutoConnectLinks(void) } } } - _confToDelete.append(linkConfig); + _confToDelete.append(serialConfig); } } else { qWarning() << "Internal error"; @@ -631,7 +642,12 @@ void LinkManager::_updateAutoConnectLinks(void) // Now remove all configs that are gone foreach (LinkConfiguration* pDeleteConfig, _confToDelete) { qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name(); - _autoconnectConfigurations.removeOne(pDeleteConfig); + for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { + if (_sharedAutoconnectConfigurations[i].data() == pDeleteConfig) { + _sharedAutoconnectConfigurations.removeAt(i); + break; + } + } if (pDeleteConfig->link()) { disconnectLink(pDeleteConfig->link()); } @@ -789,7 +805,7 @@ bool LinkManager::endCreateConfiguration(LinkConfiguration* config) { Q_ASSERT(config != NULL); _fixUnnamed(config); - _linkConfigurations.append(config); + addConfiguration(config); saveLinkConfigurationList(); return true; } @@ -883,16 +899,19 @@ void LinkManager::removeConfiguration(LinkConfiguration* config) if(iface) { disconnectLink(iface); } - // Remove configuration - _linkConfigurations.removeOne(config); - delete config; - // Save list + + _removeConfiguration(config); saveLinkConfigurationList(); } bool LinkManager::isAutoconnectLink(LinkInterface* link) { - return _autoconnectConfigurations.contains(link->getLinkConfiguration()); + for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { + if (_sharedAutoconnectConfigurations[i].data() == link->getLinkConfiguration()) { + return true; + } + } + return false; } bool LinkManager::isBluetoothAvailable(void) @@ -908,7 +927,7 @@ void LinkManager::_activeLinkCheck(void) if (_activeLinkCheckList.count() != 0) { link = _activeLinkCheckList.takeFirst(); - if (_links.contains(link) && link->isConnected()) { + if (containsLink(link) && link->isConnected()) { // Make sure there is a vehicle on the link QmlObjectListModel* vehicles = _toolbox->multiVehicleManager()->vehicles(); for (int i=0; icount(); i++) { @@ -945,3 +964,45 @@ void LinkManager::_activeLinkCheck(void) } } #endif + +bool LinkManager::containsLink(LinkInterface* link) +{ + for (int i=0; i<_sharedLinks.count(); i++) { + if (_sharedLinks[i].data() == link) { + return true; + } + } + return false; +} + +SharedLinkConfigurationPointer LinkManager::addConfiguration(LinkConfiguration* config) +{ + _qmlConfigurations.append(config); + _sharedConfigurations.append(SharedLinkConfigurationPointer(config)); + return _sharedConfigurations.last(); +} + +void LinkManager::_removeConfiguration(LinkConfiguration* config) +{ + _qmlConfigurations.removeOne(config); + + for (int i=0; i<_sharedConfigurations.count(); i++) { + if (_sharedConfigurations[i].data() == config) { + _sharedConfigurations.removeAt(i); + return; + } + } + + qWarning() << "LinkManager::_removeConfiguration called with unknown config"; +} + +QList LinkManager::links(void) +{ + QList rawLinks; + + for (int i=0; i<_sharedLinks.count(); i++) { + rawLinks.append(_sharedLinks[i].data()); + } + + return rawLinks; +} diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 23cddcbdf..f75fc50f1 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -67,19 +67,12 @@ public: Q_PROPERTY(bool autoconnectPX4Flow READ autoconnectPX4Flow WRITE setAutoconnectPX4Flow NOTIFY autoconnectPX4FlowChanged) Q_PROPERTY(bool autoconnectRTKGPS READ autoconnectRTKGPS WRITE setAutoconnectRTKGPS NOTIFY autoconnectRTKGPSChanged) Q_PROPERTY(bool autoconnectLibrePilot READ autoconnectLibrePilot WRITE setAutoconnectLibrePilot NOTIFY autoconnectLibrePilotChanged) - Q_PROPERTY(bool isBluetoothAvailable READ isBluetoothAvailable CONSTANT) - - /// LinkInterface Accessor - Q_PROPERTY(QmlObjectListModel* links READ links CONSTANT) - /// LinkConfiguration Accessor - Q_PROPERTY(QmlObjectListModel* linkConfigurations READ linkConfigurations NOTIFY linkConfigurationsChanged) - /// List of comm type strings - Q_PROPERTY(QStringList linkTypeStrings READ linkTypeStrings CONSTANT) - /// List of supported baud rates for serial links - Q_PROPERTY(QStringList serialBaudRates READ serialBaudRates CONSTANT) - /// List of comm ports display names + Q_PROPERTY(bool isBluetoothAvailable READ isBluetoothAvailable CONSTANT) + + Q_PROPERTY(QmlObjectListModel* linkConfigurations READ _qmlLinkConfigurations NOTIFY linkConfigurationsChanged) + Q_PROPERTY(QStringList linkTypeStrings READ linkTypeStrings CONSTANT) + Q_PROPERTY(QStringList serialBaudRates READ serialBaudRates CONSTANT) Q_PROPERTY(QStringList serialPortStrings READ serialPortStrings NOTIFY commPortStringsChanged) - /// List of comm ports Q_PROPERTY(QStringList serialPorts READ serialPorts NOTIFY commPortsChanged) // Create/Edit Link Configuration @@ -100,12 +93,11 @@ public: bool autoconnectLibrePilot (void) { return _autoconnectLibrePilot; } bool isBluetoothAvailable (void); - QmlObjectListModel* links (void) { return &_links; } - QmlObjectListModel* linkConfigurations (void) { return &_linkConfigurations; } - QStringList linkTypeStrings (void) const; - QStringList serialBaudRates (void); - QStringList serialPortStrings (void); - QStringList serialPorts (void); + QList links (void); + QStringList linkTypeStrings (void) const; + QStringList serialBaudRates (void); + QStringList serialPortStrings (void); + QStringList serialPorts (void); void setAutoconnectUDP (bool autoconnect); void setAutoconnectPixhawk (bool autoconnect); @@ -132,7 +124,7 @@ public: /// Creates, connects (and adds) a link based on the given configuration instance. /// Link takes ownership of config. - Q_INVOKABLE LinkInterface* createConnectedLink(LinkConfiguration* config); + Q_INVOKABLE LinkInterface* createConnectedLink(SharedLinkConfigurationPointer& config); /// Creates, connects (and adds) a link based on the given configuration name. LinkInterface* createConnectedLink(const QString& name); @@ -165,6 +157,17 @@ public: // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); + /// @return This mavlink channel is never assigned to a vehicle. + uint8_t reservedMavlinkChannel(void) { return 0; } + + /// If you are going to hold a reference to a LinkInterface* in your object you must reference count it + /// by using this method to get access to the shared pointer. + SharedLinkInterfacePointer sharedLinkInterfacePointerForLink(LinkInterface* link); + + bool containsLink(LinkInterface* link); + + SharedLinkConfigurationPointer addConfiguration(LinkConfiguration* config); + signals: void autoconnectUDPChanged (bool autoconnect); void autoconnectPixhawkChanged (bool autoconnect); @@ -204,11 +207,13 @@ private slots: #endif private: + QmlObjectListModel* _qmlLinkConfigurations (void) { return &_qmlConfigurations; } bool _connectionsSuspendedMsg(void); void _updateAutoConnectLinks(void); void _updateSerialPorts(); void _fixUnnamed(LinkConfiguration* config); bool _setAutoconnectWorker(bool& currentAutoconnect, bool newAutoconnect, const char* autoconnectKey); + void _removeConfiguration(LinkConfiguration* config); #ifndef NO_SERIAL_LINK SerialConfiguration* _autoconnectConfigurationsContainsPort(const QString& portName); @@ -223,9 +228,11 @@ private: MAVLinkProtocol* _mavlinkProtocol; - QmlObjectListModel _links; - QmlObjectListModel _linkConfigurations; - QmlObjectListModel _autoconnectConfigurations; + + QList _sharedLinks; + QList _sharedConfigurations; + QList _sharedAutoconnectConfigurations; + QmlObjectListModel _qmlConfigurations; QMap _autoconnectWaitList; ///< key: QGCSerialPortInfo.systemLocation, value: wait count QStringList _commPortList; diff --git a/src/comm/LogReplayLink.cc b/src/comm/LogReplayLink.cc index 4a15dd841..d6b818467 100644 --- a/src/comm/LogReplayLink.cc +++ b/src/comm/LogReplayLink.cc @@ -64,12 +64,13 @@ QString LogReplayLinkConfiguration::logFilenameShort(void) return fi.fileName(); } -LogReplayLink::LogReplayLink(LogReplayLinkConfiguration* config) : - _connected(false), - _replayAccelerationFactor(1.0f) +LogReplayLink::LogReplayLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) + , _logReplayConfig(qobject_cast(config.data())) + , _connected(false) + , _replayAccelerationFactor(1.0f) { - Q_ASSERT(config); - _config = config; + Q_ASSERT(_logReplayConfig); _readTickTimer.moveToThread(this); @@ -184,7 +185,7 @@ quint64 LogReplayLink::_seekToNextMavlinkMessage(mavlink_message_t* nextMsg) bool LogReplayLink::_loadLogFile(void) { QString errorMsg; - QString logFilename = _config->logFilename(); + QString logFilename = _logReplayConfig->logFilename(); QFileInfo logFileInfo; int logDurationSecondsTotal; diff --git a/src/comm/LogReplayLink.h b/src/comm/LogReplayLink.h index 923dfb637..2b7875626 100644 --- a/src/comm/LogReplayLink.h +++ b/src/comm/LogReplayLink.h @@ -57,8 +57,6 @@ class LogReplayLink : public LinkInterface friend class LinkManager; public: - virtual LinkConfiguration* getLinkConfiguration() { return _config; } - /// @return true: log is currently playing, false: log playback is paused bool isPlaying(void) { return _readTickTimer.isActive(); } @@ -111,7 +109,7 @@ private slots: private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public - LogReplayLink(LogReplayLinkConfiguration* config); + LogReplayLink(SharedLinkConfigurationPointer& config); ~LogReplayLink(); void _replayError(const QString& errorMsg); @@ -129,7 +127,7 @@ private: // Virtuals from QThread virtual void run(void); - LogReplayLinkConfiguration* _config; + LogReplayLinkConfiguration* _logReplayConfig; bool _connected; QTimer _readTickTimer; ///< Timer which signals a read of next log record diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 49c1a1a38..4fee75482 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -159,7 +159,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) // Since receiveBytes signals cross threads we can end up with signals in the queue // that come through after the link is disconnected. For these we just drop the data // since the link is closed. - if (!_linkMgr->links()->contains(link)) { + if (!_linkMgr->containsLink(link)) { return; } diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index 411287c4d..f8dcb64c1 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -44,8 +44,9 @@ const char* MockConfiguration::_vehicleTypeKey = "VehicleType"; const char* MockConfiguration::_sendStatusTextKey = "SendStatusText"; const char* MockConfiguration::_failureModeKey = "FailureMode"; -MockLink::MockLink(MockConfiguration* config) - : _missionItemHandler(this, qgcApp()->toolbox()->mavlinkProtocol()) +MockLink::MockLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) + , _missionItemHandler(this, qgcApp()->toolbox()->mavlinkProtocol()) , _name("MockLink") , _connected(false) , _vehicleSystemId(_nextVehicleSystemId++) @@ -67,14 +68,11 @@ MockLink::MockLink(MockConfiguration* config) , _logDownloadCurrentOffset(0) , _logDownloadBytesRemaining(0) { - _config = config; - if (_config) { - _firmwareType = config->firmwareType(); - _vehicleType = config->vehicleType(); - _sendStatusText = config->sendStatusText(); - _failureMode = config->failureMode(); - _config->setLink(this); - } + MockConfiguration* mockConfig = qobject_cast(_config.data()); + _firmwareType = mockConfig->firmwareType(); + _vehicleType = mockConfig->vehicleType(); + _sendStatusText = mockConfig->sendStatusText(); + _failureMode = mockConfig->failureMode(); union px4_custom_mode px4_cm; @@ -1041,12 +1039,12 @@ void MockConfiguration::updateSettings() MockLink* MockLink::_startMockLink(MockConfiguration* mockConfig) { - LinkManager* linkManager = qgcApp()->toolbox()->linkManager(); + LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); mockConfig->setDynamic(true); - linkManager->linkConfigurations()->append(mockConfig); + SharedLinkConfigurationPointer config = linkMgr->addConfiguration(mockConfig); - return qobject_cast(linkManager->createConnectedLink(mockConfig)); + return qobject_cast(linkMgr->createConnectedLink(config)); } MockLink* MockLink::startPX4MockLink(bool sendStatusText, MockConfiguration::FailureMode_t failureMode) diff --git a/src/comm/MockLink.h b/src/comm/MockLink.h index cdb25373f..15e8a5eeb 100644 --- a/src/comm/MockLink.h +++ b/src/comm/MockLink.h @@ -90,8 +90,7 @@ class MockLink : public LinkInterface Q_OBJECT public: - // LinkConfiguration is optional for MockLink - MockLink(MockConfiguration* config = NULL); + MockLink(SharedLinkConfigurationPointer& config); ~MockLink(void); // MockLink methods @@ -126,8 +125,6 @@ public: bool connect(void); bool disconnect(void); - LinkConfiguration* getLinkConfiguration() { return _config; } - /// Sets a failure mode for unit testing /// @param failureMode Type of failure to simulate void setMissionItemFailureMode(MockLinkMissionItemHandler::FailureMode_t failureMode); @@ -216,7 +213,6 @@ private: uint32_t _mavCustomMode; uint8_t _mavState; - MockConfiguration* _config; MAV_AUTOPILOT _firmwareType; MAV_TYPE _vehicleType; diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index 3e345f682..9e600db18 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -30,19 +30,19 @@ QGC_LOGGING_CATEGORY(SerialLinkLog, "SerialLinkLog") static QStringList kSupportedBaudRates; -SerialLink::SerialLink(SerialConfiguration* config) +SerialLink::SerialLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) + , _port(NULL) + , _bytesRead(0) + , _stopp(false) + , _reqReset(false) + , _serialConfig(qobject_cast(config.data())) { - _bytesRead = 0; - _port = Q_NULLPTR; - _stopp = false; - _reqReset = false; - Q_ASSERT(config != NULL); - _config = config; - _config->setLink(this); - - qCDebug(SerialLinkLog) << "Create SerialLink " << config->portName() << config->baud() << config->flowControl() - << config->parity() << config->dataBits() << config->stopBits(); - qCDebug(SerialLinkLog) << "portName: " << config->portName(); + Q_ASSERT(_serialConfig); + + qCDebug(SerialLinkLog) << "Create SerialLink " << _serialConfig->portName() << _serialConfig->baud() << _serialConfig->flowControl() + << _serialConfig->parity() << _serialConfig->dataBits() << _serialConfig->stopBits(); + qCDebug(SerialLinkLog) << "portName: " << _serialConfig->portName(); } void SerialLink::requestReset() @@ -53,10 +53,10 @@ void SerialLink::requestReset() SerialLink::~SerialLink() { - // Disconnect link from configuration - _config->setLink(NULL); _disconnect(); - if(_port) delete _port; + if (_port) { + delete _port; + } _port = NULL; } @@ -70,7 +70,7 @@ bool SerialLink::_isBootloader() { qCDebug(SerialLinkLog) << "PortName : " << info.portName() << "Description : " << info.description(); qCDebug(SerialLinkLog) << "Manufacturer: " << info.manufacturer(); - if (info.portName().trimmed() == _config->portName() && + if (info.portName().trimmed() == _serialConfig->portName() && (info.description().toLower().contains("bootloader") || info.description().toLower().contains("px4 bl") || info.description().toLower().contains("px4 fmu v1.6"))) { @@ -159,7 +159,7 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& _port = NULL; } - qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _config->portName(); + qCDebug(SerialLinkLog) << "SerialLink: hardwareConnect to " << _serialConfig->portName(); // If we are in the Pixhawk bootloader code wait for it to timeout if (_isBootloader()) { @@ -181,7 +181,7 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& } } - _port = new QSerialPort(_config->portName()); + _port = new QSerialPort(_serialConfig->portName()); QObject::connect(_port, static_cast(&QSerialPort::error), this, &SerialLink::linkError); @@ -219,17 +219,17 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& _port->setDataTerminalReady(true); qCDebug(SerialLinkLog) << "Configuring port"; - _port->setBaudRate (_config->baud()); - _port->setDataBits (static_cast (_config->dataBits())); - _port->setFlowControl (static_cast (_config->flowControl())); - _port->setStopBits (static_cast (_config->stopBits())); - _port->setParity (static_cast (_config->parity())); + _port->setBaudRate (_serialConfig->baud()); + _port->setDataBits (static_cast (_serialConfig->dataBits())); + _port->setFlowControl (static_cast (_serialConfig->flowControl())); + _port->setStopBits (static_cast (_serialConfig->stopBits())); + _port->setParity (static_cast (_serialConfig->parity())); emit communicationUpdate(getName(), "Opened port!"); emit connected(); - qCDebug(SerialLinkLog) << "Connection SeriaLink: " << "with settings" << _config->portName() - << _config->baud() << _config->dataBits() << _config->parity() << _config->stopBits(); + qCDebug(SerialLinkLog) << "Connection SeriaLink: " << "with settings" << _serialConfig->portName() + << _serialConfig->baud() << _serialConfig->dataBits() << _serialConfig->parity() << _serialConfig->stopBits(); return true; // successful connection } @@ -281,7 +281,7 @@ bool SerialLink::isConnected() const QString SerialLink::getName() const { - return _config->portName(); + return _serialConfig->portName(); } /** @@ -294,7 +294,7 @@ qint64 SerialLink::getConnectionSpeed() const if (_port) { baudRate = _port->baudRate(); } else { - baudRate = _config->baud(); + baudRate = _serialConfig->baud(); } qint64 dataRate; switch (baudRate) @@ -334,11 +334,11 @@ qint64 SerialLink::getConnectionSpeed() const void SerialLink::_resetConfiguration() { if (_port) { - _port->setBaudRate (_config->baud()); - _port->setDataBits (static_cast (_config->dataBits())); - _port->setFlowControl (static_cast (_config->flowControl())); - _port->setStopBits (static_cast (_config->stopBits())); - _port->setParity (static_cast (_config->parity())); + _port->setBaudRate (_serialConfig->baud()); + _port->setDataBits (static_cast (_serialConfig->dataBits())); + _port->setFlowControl (static_cast (_serialConfig->flowControl())); + _port->setStopBits (static_cast (_serialConfig->stopBits())); + _port->setParity (static_cast (_serialConfig->parity())); } } @@ -349,11 +349,6 @@ void SerialLink::_emitLinkError(const QString& errorMsg) emit communicationError(tr("Link Error"), msg.arg(getName()).arg(errorMsg)); } -LinkConfiguration* SerialLink::getLinkConfiguration() -{ - return _config; -} - //-------------------------------------------------------------------------- //-- SerialConfiguration diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index 82b0edee7..be46a06e9 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -133,7 +133,6 @@ class SerialLink : public LinkInterface public: // LinkInterface - LinkConfiguration* getLinkConfiguration(); QString getName() const; void requestReset(); bool isConnected() const; @@ -168,7 +167,7 @@ private slots: private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public - SerialLink(SerialConfiguration* config); + SerialLink(SharedLinkConfigurationPointer& config); ~SerialLink(); // From LinkInterface @@ -186,7 +185,7 @@ private: volatile bool _reqReset; QMutex _stoppMutex; // Mutex for accessing _stopp QByteArray _transmitBuffer; // An internal buffer for receiving data from member functions and actually transmitting them via the serial port. - SerialConfiguration* _config; + SerialConfiguration* _serialConfig; signals: void aboutToCloseFlag(); diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index a57cb61d8..d540f6444 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -24,16 +24,14 @@ /// /// @author Don Gagne -TCPLink::TCPLink(TCPConfiguration *config) - : _config(config) +TCPLink::TCPLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) + , _tcpConfig(qobject_cast(config.data())) , _socket(NULL) , _socketIsConnected(false) { - Q_ASSERT(_config != NULL); - // We're doing it wrong - because the Qt folks got the API wrong: - // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ + Q_ASSERT(_tcpConfig); moveToThread(this); - //qDebug() << "TCP Created " << _config->name(); } TCPLink::~TCPLink() @@ -69,7 +67,7 @@ void TCPLink::_writeDebugBytes(const QByteArray data) ascii.append(219); } } - qDebug() << "Sent" << size << "bytes to" << _config->address().toString() << ":" << _config->port() << "data:"; + qDebug() << "Sent" << size << "bytes to" << _tcpConfig->address().toString() << ":" << _tcpConfig->port() << "data:"; qDebug() << bytes; qDebug() << "ASCII:" << ascii; } @@ -148,7 +146,7 @@ bool TCPLink::_hardwareConnect() _socket = new QTcpSocket(); QSignalSpy errorSpy(_socket, static_cast(&QTcpSocket::error)); - _socket->connectToHost(_config->address(), _config->port()); + _socket->connectToHost(_tcpConfig->address(), _tcpConfig->port()); QObject::connect(_socket, &QTcpSocket::readyRead, this, &TCPLink::readBytes); QObject::connect(_socket,static_cast(&QTcpSocket::error), @@ -189,7 +187,7 @@ bool TCPLink::isConnected() const QString TCPLink::getName() const { - return _config->name(); + return _tcpConfig->name(); } qint64 TCPLink::getConnectionSpeed() const diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h index c67606f58..9eb2827fe 100644 --- a/src/comm/TCPLink.h +++ b/src/comm/TCPLink.h @@ -121,7 +121,6 @@ class TCPLink : public LinkInterface public: QTcpSocket* getSocket(void) { return _socket; } - virtual LinkConfiguration* getLinkConfiguration() { return _config; } void signalBytesWritten(void); @@ -160,7 +159,7 @@ protected: private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public - TCPLink(TCPConfiguration* config); + TCPLink(SharedLinkConfigurationPointer& config); ~TCPLink(); // From LinkInterface @@ -174,7 +173,7 @@ private: void _writeDebugBytes(const QByteArray data); #endif - TCPConfiguration* _config; + TCPConfiguration* _tcpConfig; QTcpSocket* _socket; bool _socketIsConnected; diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index 627c6d282..51faba4ad 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -65,27 +65,22 @@ static QString get_ip_address(const QString& address) return QString(""); } -UDPLink::UDPLink(UDPConfiguration* config) - : _socket(NULL) - , _connectState(false) - #if defined(QGC_ZEROCONF_ENABLED) +UDPLink::UDPLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) +#if defined(QGC_ZEROCONF_ENABLED) , _dnssServiceRef(NULL) - #endif +#endif , _running(false) + , _socket(NULL) + , _udpConfig(qobject_cast(config.data())) + , _connectState(false) { - Q_ASSERT(config != NULL); - _config = config; - _config->setLink(this); - - // We're doing it wrong - because the Qt folks got the API wrong: - // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ + Q_ASSERT(_udpConfig); moveToThread(this); } UDPLink::~UDPLink() { - // Disconnect link from configuration - _config->setLink(NULL); _disconnect(); // Tell the thread to exit _running = false; @@ -121,17 +116,17 @@ void UDPLink::_restartConnection() QString UDPLink::getName() const { - return _config->name(); + return _udpConfig->name(); } void UDPLink::addHost(const QString& host) { - _config->addHost(host); + _udpConfig->addHost(host); } void UDPLink::removeHost(const QString& host) { - _config->removeHost(host); + _udpConfig->removeHost(host); } void UDPLink::_writeBytes(const QByteArray data) @@ -143,7 +138,7 @@ void UDPLink::_writeBytes(const QByteArray data) // Send to all connected systems QString host; int port; - if(_config->firstHost(host, port)) { + if(_udpConfig->firstHost(host, port)) { do { QHostAddress currentHost(host); if(_socket->writeDatagram(data, currentHost, (quint16)port) < 0) { @@ -162,10 +157,10 @@ void UDPLink::_writeBytes(const QByteArray data) // unit sent by UDP. _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); } - } while (_config->nextHost(host, port)); + } while (_udpConfig->nextHost(host, port)); //-- Remove hosts that are no longer there foreach (const QString& ghost, goneHosts) { - _config->removeHost(ghost); + _udpConfig->removeHost(ghost); } } } @@ -194,7 +189,7 @@ void UDPLink::readBytes() // added to the list and will start receiving datagrams from here. Even a port scanner // would trigger this. // Add host to broadcast list if not yet present, or update its port - _config->addHost(sender.toString(), (int)senderPort); + _udpConfig->addHost(sender.toString(), (int)senderPort); } //-- Send whatever is left if(databuffer.size()) { @@ -248,7 +243,7 @@ bool UDPLink::_hardwareConnect() QHostAddress host = QHostAddress::AnyIPv4; _socket = new QUdpSocket(); _socket->setProxy(QNetworkProxy::NoProxy); - _connectState = _socket->bind(host, _config->localPort(), QAbstractSocket::ReuseAddressHint | QUdpSocket::ShareAddress); + _connectState = _socket->bind(host, _udpConfig->localPort(), QAbstractSocket::ReuseAddressHint | QUdpSocket::ShareAddress); if (_connectState) { //-- Make sure we have a large enough IO buffers #ifdef __mobile__ @@ -258,7 +253,7 @@ bool UDPLink::_hardwareConnect() _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 256 * 1024); _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 512 * 1024); #endif - _registerZeroconf(_config->localPort(), kZeroconfRegistration); + _registerZeroconf(_udpConfig->localPort(), kZeroconfRegistration); QObject::connect(_socket, &QUdpSocket::readyRead, this, &UDPLink::readBytes); emit connected(); } else { diff --git a/src/comm/UDPLink.h b/src/comm/UDPLink.h index 1c1c977a9..df0c58e73 100644 --- a/src/comm/UDPLink.h +++ b/src/comm/UDPLink.h @@ -177,10 +177,7 @@ public: bool connect(void); bool disconnect(void); - LinkConfiguration* getLinkConfiguration() { return _config; } - public slots: - /*! @brief Add a new host to broadcast messages to */ void addHost (const QString& host); /*! @brief Remove a host from broadcasting messages to */ @@ -189,23 +186,11 @@ public slots: void readBytes(); private slots: - /*! - * @brief Write a number of bytes to the interface. - * - * @param data Pointer to the data byte array - * @param size The size of the bytes array - **/ void _writeBytes(const QByteArray data); -protected: - - QUdpSocket* _socket; - UDPConfiguration* _config; - bool _connectState; - private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public - UDPLink(UDPConfiguration* config); + UDPLink(SharedLinkConfigurationPointer& config); ~UDPLink(); // From LinkInterface @@ -223,6 +208,9 @@ private: #endif bool _running; + QUdpSocket* _socket; + UDPConfiguration* _udpConfig; + bool _connectState; }; #endif // UDPLINK_H diff --git a/src/qgcunittest/LinkManagerTest.cc b/src/qgcunittest/LinkManagerTest.cc index bc5af5bb3..77f4e8b3c 100644 --- a/src/qgcunittest/LinkManagerTest.cc +++ b/src/qgcunittest/LinkManagerTest.cc @@ -57,29 +57,29 @@ void LinkManagerTest::cleanup(void) void LinkManagerTest::_add_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->links()->count() == 0); + Q_ASSERT(_linkMgr->links().count() == 0); _connectMockLink(); - QCOMPARE(_linkMgr->links()->count(), 1); - QCOMPARE(_linkMgr->links()->value(0), _mockLink); + QCOMPARE(_linkMgr->links().count(), 1); + QCOMPARE(_linkMgr->links().at(0), _mockLink); } void LinkManagerTest::_delete_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->links()->count() == 0); + Q_ASSERT(_linkMgr->links().count() == 0); _connectMockLink(); _disconnectMockLink(); - QCOMPARE(_linkMgr->links()->count(), 0); + QCOMPARE(_linkMgr->links().count(), 0); } void LinkManagerTest::_addSignals_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->links()->count() == 0); + Q_ASSERT(_linkMgr->links().count() == 0); Q_ASSERT(_multiSpy->checkNoSignals() == true); _connectMockLink(); @@ -99,7 +99,7 @@ void LinkManagerTest::_addSignals_test(void) void LinkManagerTest::_deleteSignals_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->links()->count() == 0); + Q_ASSERT(_linkMgr->links().count() == 0); Q_ASSERT(_multiSpy->checkNoSignals() == true); _connectMockLink(); diff --git a/src/qgcunittest/TCPLinkTest.cc b/src/qgcunittest/TCPLinkTest.cc index f1a1835ce..996951eaf 100644 --- a/src/qgcunittest/TCPLinkTest.cc +++ b/src/qgcunittest/TCPLinkTest.cc @@ -17,8 +17,7 @@ #include "TCPLoopBackServer.h" TCPLinkTest::TCPLinkTest(void) - : _config(NULL) - , _link(NULL) + : _link(NULL) , _multiSpy(NULL) { @@ -31,13 +30,12 @@ void TCPLinkTest::init(void) Q_ASSERT(_link == nullptr); Q_ASSERT(_multiSpy == nullptr); - Q_ASSERT(_config == nullptr); - _config = new TCPConfiguration("MockTCP"); - _config->setAddress(QHostAddress::LocalHost); - _config->setPort(5760); - _link = new TCPLink(_config); - Q_ASSERT(_link != NULL); + TCPConfiguration* tcpConfig = new TCPConfiguration("MockTCP"); + tcpConfig->setAddress(QHostAddress::LocalHost); + tcpConfig->setPort(5760); + _sharedConfig = SharedLinkConfigurationPointer(tcpConfig); + _link = new TCPLink(_sharedConfig); _rgSignals[bytesReceivedSignalIndex] = SIGNAL(bytesReceived(LinkInterface*, QByteArray)); _rgSignals[connectedSignalIndex] = SIGNAL(connected(void)); @@ -55,7 +53,6 @@ void TCPLinkTest::cleanup(void) { Q_ASSERT(_multiSpy); Q_ASSERT(_link); - Q_ASSERT(_config); delete _multiSpy; _multiSpy = nullptr; @@ -63,15 +60,13 @@ void TCPLinkTest::cleanup(void) delete _link; _link = nullptr; - delete _config; - _config = nullptr; + _sharedConfig.clear(); UnitTest::cleanup(); } void TCPLinkTest::_connectFail_test(void) { - Q_ASSERT(_config); Q_ASSERT(_link); Q_ASSERT(_multiSpy); Q_ASSERT(_multiSpy->checkNoSignals() == true); @@ -110,7 +105,8 @@ void TCPLinkTest::_connectSucceed_test(void) Q_ASSERT(_multiSpy->checkNoSignals() == true); // Start the server side - TCPLoopBackServer* server = new TCPLoopBackServer(_config->address(), _config->port()); + TCPConfiguration* tcpConfig = qobject_cast(_sharedConfig.data()); + TCPLoopBackServer* server = new TCPLoopBackServer(tcpConfig->address(), tcpConfig->port()); Q_CHECK_PTR(server); // Connect to the server diff --git a/src/qgcunittest/TCPLinkTest.h b/src/qgcunittest/TCPLinkTest.h index 196da6ef0..e34d993da 100644 --- a/src/qgcunittest/TCPLinkTest.h +++ b/src/qgcunittest/TCPLinkTest.h @@ -60,11 +60,11 @@ private: //deleteLinkSignalMask = 1 << deleteLinkSignalIndex, }; - TCPConfiguration* _config; - TCPLink* _link; - MultiSignalSpy* _multiSpy; - static const size_t _cSignals = maxSignalIndex; - const char* _rgSignals[_cSignals]; + SharedLinkConfigurationPointer _sharedConfig; + TCPLink* _link; + MultiSignalSpy* _multiSpy; + static const size_t _cSignals = maxSignalIndex; + const char* _rgSignals[_cSignals]; }; #endif diff --git a/src/ui/QGCMAVLinkLogPlayer.cc b/src/ui/QGCMAVLinkLogPlayer.cc index 22267f376..55f26ec58 100644 --- a/src/ui/QGCMAVLinkLogPlayer.cc +++ b/src/ui/QGCMAVLinkLogPlayer.cc @@ -85,7 +85,10 @@ void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void) linkConfig->setLogFilename(logFilename); linkConfig->setName(linkConfig->logFilenameShort()); _ui->logFileNameLabel->setText(linkConfig->logFilenameShort()); - _replayLink = (LogReplayLink*)qgcApp()->toolbox()->linkManager()->createConnectedLink(linkConfig); + + LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); + SharedLinkConfigurationPointer sharedConfig = linkMgr->addConfiguration(linkConfig); + _replayLink = (LogReplayLink*)qgcApp()->toolbox()->linkManager()->createConnectedLink(sharedConfig); connect(_replayLink, &LogReplayLink::logFileStats, this, &QGCMAVLinkLogPlayer::_logFileStats); connect(_replayLink, &LogReplayLink::playbackStarted, this, &QGCMAVLinkLogPlayer::_playbackStarted); -- GitLab From d7f2c8f548ae4e713493dc1dc0164d722f508c55 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 26 Dec 2016 18:27:48 -0800 Subject: [PATCH 123/398] Fix compiler errors --- src/comm/MockLink.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index f8dcb64c1..6b946f30f 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -780,6 +780,9 @@ void MockLink::_handleFTP(const mavlink_message_t& msg) void MockLink::_handleCommandLong(const mavlink_message_t& msg) { + static bool firstCmdUser3 = true; + static bool firstCmdUser4 = true; + mavlink_command_long_t request; uint8_t commandResult = MAV_RESULT_UNSUPPORTED; @@ -815,7 +818,6 @@ void MockLink::_handleCommandLong(const mavlink_message_t& msg) break; case MAV_CMD_USER_3: // Test command which returns MAV_RESULT_ACCEPTED on second attempt - static bool firstCmdUser3 = true; if (firstCmdUser3) { firstCmdUser3 = false; return; @@ -826,7 +828,6 @@ void MockLink::_handleCommandLong(const mavlink_message_t& msg) break; case MAV_CMD_USER_4: // Test command which returns MAV_RESULT_FAILED on second attempt - static bool firstCmdUser4 = true; if (firstCmdUser4) { firstCmdUser4 = false; return; -- GitLab From 2374891c3e46a7dd7c01518976205349b004142c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 26 Dec 2016 18:28:23 -0800 Subject: [PATCH 124/398] Remove XBee support Even though it hasn't been supported forever, the files where still being compiled --- QGCExternalLibs.pri | 97 - libs/thirdParty/libxbee/LICENSE | 674 ----- libs/thirdParty/libxbee/README | 39 - libs/thirdParty/libxbee/api.c | 2433 ----------------- libs/thirdParty/libxbee/api.h | 254 -- .../libxbee/doc/man/man3/libxbee.3.html | 127 - .../libxbee/doc/man/man3/xbee_con.3.html | 26 - .../libxbee/doc/man/man3/xbee_end.3.html | 27 - .../libxbee/doc/man/man3/xbee_endcon.3.html | 4 - .../libxbee/doc/man/man3/xbee_flushcon.3.html | 4 - .../doc/man/man3/xbee_getanalog.3.html | 140 - .../doc/man/man3/xbee_getdigital.3.html | 134 - .../doc/man/man3/xbee_getpacket.3.html | 130 - .../doc/man/man3/xbee_hasanalog.3.html | 4 - .../doc/man/man3/xbee_hasdigital.3.html | 4 - .../libxbee/doc/man/man3/xbee_logit.3.html | 27 - .../libxbee/doc/man/man3/xbee_newcon.3.html | 201 -- .../doc/man/man3/xbee_nsenddata.3.html | 4 - .../libxbee/doc/man/man3/xbee_pkt.3.html | 107 - .../libxbee/doc/man/man3/xbee_senddata.3.html | 129 - .../libxbee/doc/man/man3/xbee_setup.3.html | 142 - .../libxbee/doc/man/man3/xbee_setupAPI.3.html | 4 - .../libxbee/doc/man/man3/xbee_setuplog.3.html | 4 - .../doc/man/man3/xbee_setuplogAPI.3.html | 4 - .../doc/man/man3/xbee_vsenddata.3.html | 4 - libs/thirdParty/libxbee/lib/libxbee.dll | Bin 151552 -> 0 bytes libs/thirdParty/libxbee/lib/libxbee.exp | Bin 7707 -> 0 bytes libs/thirdParty/libxbee/lib/libxbee.lib | Bin 12566 -> 0 bytes libs/thirdParty/libxbee/lib/libxbee.map | 897 ------ libs/thirdParty/libxbee/main.c | 229 -- libs/thirdParty/libxbee/makefile | 226 -- libs/thirdParty/libxbee/man/man3/libxbee.3 | 91 - libs/thirdParty/libxbee/man/man3/xbee_con.3 | 22 - libs/thirdParty/libxbee/man/man3/xbee_end.3 | 23 - .../thirdParty/libxbee/man/man3/xbee_endcon.3 | 1 - .../libxbee/man/man3/xbee_flushcon.3 | 1 - .../libxbee/man/man3/xbee_getanalog.3 | 96 - .../libxbee/man/man3/xbee_getdigital.3 | 91 - .../libxbee/man/man3/xbee_getpacket.3 | 88 - .../libxbee/man/man3/xbee_hasanalog.3 | 1 - .../libxbee/man/man3/xbee_hasdigital.3 | 1 - libs/thirdParty/libxbee/man/man3/xbee_logit.3 | 23 - .../thirdParty/libxbee/man/man3/xbee_newcon.3 | 152 - .../libxbee/man/man3/xbee_nsenddata.3 | 1 - libs/thirdParty/libxbee/man/man3/xbee_pkt.3 | 79 - .../libxbee/man/man3/xbee_purgecon.3 | 1 - .../libxbee/man/man3/xbee_senddata.3 | 86 - libs/thirdParty/libxbee/man/man3/xbee_setup.3 | 108 - .../libxbee/man/man3/xbee_setupAPI.3 | 1 - .../libxbee/man/man3/xbee_setuplog.3 | 1 - .../libxbee/man/man3/xbee_setuplogAPI.3 | 1 - .../libxbee/man/man3/xbee_vsenddata.3 | 1 - .../libxbee/notes/Notepad++ Style.xml | 36 - libs/thirdParty/libxbee/notes/v1-v2.txt | 29 - libs/thirdParty/libxbee/pdf/api.c.pdf | Bin 75665 -> 0 bytes libs/thirdParty/libxbee/pdf/api.h.pdf | Bin 11188 -> 0 bytes libs/thirdParty/libxbee/pdf/main.c.pdf | Bin 8923 -> 0 bytes libs/thirdParty/libxbee/pdf/xbee.h.pdf | Bin 11925 -> 0 bytes libs/thirdParty/libxbee/sample/README | 12 - libs/thirdParty/libxbee/sample/analog.c | 71 - libs/thirdParty/libxbee/sample/api.c | 42 - libs/thirdParty/libxbee/sample/atis.c | 81 - libs/thirdParty/libxbee/sample/atsetup.c | 157 -- libs/thirdParty/libxbee/sample/callback.c | 88 - libs/thirdParty/libxbee/sample/digital.c | 146 - libs/thirdParty/libxbee/sample/digitalout.c | 128 - libs/thirdParty/libxbee/sample/multi.c | 100 - libs/thirdParty/libxbee/sample/scan.c | 144 - libs/thirdParty/libxbee/sample/scan_adv.c | 589 ---- libs/thirdParty/libxbee/sample/simple.c | 68 - libs/thirdParty/libxbee/sample/talk_to_me.c | 82 - libs/thirdParty/libxbee/sample/vb6/README.txt | 8 - .../libxbee/sample/vb6/demo/Form1.frm | 64 - .../libxbee/sample/vb6/demo/demo.bas | 19 - .../libxbee/sample/vb6/demo/demo.vbp | 33 - .../thirdParty/libxbee/sample/vb6/libxbee.bas | 285 -- .../libxbee/sample/vb6/talk_to_me/Form1.frm | 1197 -------- .../sample/vb6/talk_to_me/talk_to_me.bas | 431 --- .../sample/vb6/talk_to_me/talk_to_me.vbp | 33 - libs/thirdParty/libxbee/sample/xbee2_rx.c | 60 - libs/thirdParty/libxbee/sample/xbee2_tx.c | 54 - libs/thirdParty/libxbee/umakefile | 91 - libs/thirdParty/libxbee/win32.README.txt | 31 - libs/thirdParty/libxbee/win32.makefile | 48 - libs/thirdParty/libxbee/xbee.h | 218 -- libs/thirdParty/libxbee/xsys/README | 1 - libs/thirdParty/libxbee/xsys/linux.c | 148 - libs/thirdParty/libxbee/xsys/linux.h | 57 - libs/thirdParty/libxbee/xsys/pdf/linux.c.pdf | Bin 8394 -> 0 bytes libs/thirdParty/libxbee/xsys/pdf/linux.h.pdf | Bin 5205 -> 0 bytes libs/thirdParty/libxbee/xsys/pdf/win32.c.pdf | Bin 14179 -> 0 bytes .../thirdParty/libxbee/xsys/pdf/win32.def.pdf | Bin 3538 -> 0 bytes .../libxbee/xsys/pdf/win32.dll.c.pdf | Bin 7893 -> 0 bytes libs/thirdParty/libxbee/xsys/pdf/win32.h.pdf | Bin 5959 -> 0 bytes libs/thirdParty/libxbee/xsys/pdf/win32.rc.pdf | Bin 4744 -> 0 bytes libs/thirdParty/libxbee/xsys/win32.c | 279 -- libs/thirdParty/libxbee/xsys/win32.def | 71 - libs/thirdParty/libxbee/xsys/win32.dll.c | 131 - libs/thirdParty/libxbee/xsys/win32.h | 73 - libs/thirdParty/libxbee/xsys/win32.rc | 47 - src/comm/HexSpinBox.cpp | 35 - src/comm/HexSpinBox.h | 25 - src/comm/LinkConfiguration.h | 6 - src/comm/XbeeLink.cpp | 239 -- src/comm/XbeeLink.h | 77 - src/comm/XbeeLinkInterface.h | 27 - src/ui/XbeeConfigurationWindow.cpp | 444 --- src/ui/XbeeConfigurationWindow.h | 62 - 108 files changed, 13011 deletions(-) delete mode 100644 libs/thirdParty/libxbee/LICENSE delete mode 100644 libs/thirdParty/libxbee/README delete mode 100644 libs/thirdParty/libxbee/api.c delete mode 100644 libs/thirdParty/libxbee/api.h delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/libxbee.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_con.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_end.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_endcon.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_flushcon.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_getanalog.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_getdigital.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_getpacket.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_hasanalog.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_hasdigital.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_logit.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_newcon.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_nsenddata.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_pkt.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_senddata.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_setup.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_setupAPI.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_setuplog.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_setuplogAPI.3.html delete mode 100644 libs/thirdParty/libxbee/doc/man/man3/xbee_vsenddata.3.html delete mode 100644 libs/thirdParty/libxbee/lib/libxbee.dll delete mode 100644 libs/thirdParty/libxbee/lib/libxbee.exp delete mode 100644 libs/thirdParty/libxbee/lib/libxbee.lib delete mode 100644 libs/thirdParty/libxbee/lib/libxbee.map delete mode 100644 libs/thirdParty/libxbee/main.c delete mode 100644 libs/thirdParty/libxbee/makefile delete mode 100644 libs/thirdParty/libxbee/man/man3/libxbee.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_con.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_end.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_endcon.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_flushcon.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_getanalog.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_getdigital.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_getpacket.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_hasanalog.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_hasdigital.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_logit.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_newcon.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_nsenddata.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_pkt.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_purgecon.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_senddata.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_setup.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_setupAPI.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_setuplog.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_setuplogAPI.3 delete mode 100644 libs/thirdParty/libxbee/man/man3/xbee_vsenddata.3 delete mode 100644 libs/thirdParty/libxbee/notes/Notepad++ Style.xml delete mode 100644 libs/thirdParty/libxbee/notes/v1-v2.txt delete mode 100644 libs/thirdParty/libxbee/pdf/api.c.pdf delete mode 100644 libs/thirdParty/libxbee/pdf/api.h.pdf delete mode 100644 libs/thirdParty/libxbee/pdf/main.c.pdf delete mode 100644 libs/thirdParty/libxbee/pdf/xbee.h.pdf delete mode 100644 libs/thirdParty/libxbee/sample/README delete mode 100644 libs/thirdParty/libxbee/sample/analog.c delete mode 100644 libs/thirdParty/libxbee/sample/api.c delete mode 100644 libs/thirdParty/libxbee/sample/atis.c delete mode 100644 libs/thirdParty/libxbee/sample/atsetup.c delete mode 100644 libs/thirdParty/libxbee/sample/callback.c delete mode 100644 libs/thirdParty/libxbee/sample/digital.c delete mode 100644 libs/thirdParty/libxbee/sample/digitalout.c delete mode 100644 libs/thirdParty/libxbee/sample/multi.c delete mode 100644 libs/thirdParty/libxbee/sample/scan.c delete mode 100644 libs/thirdParty/libxbee/sample/scan_adv.c delete mode 100644 libs/thirdParty/libxbee/sample/simple.c delete mode 100644 libs/thirdParty/libxbee/sample/talk_to_me.c delete mode 100644 libs/thirdParty/libxbee/sample/vb6/README.txt delete mode 100644 libs/thirdParty/libxbee/sample/vb6/demo/Form1.frm delete mode 100644 libs/thirdParty/libxbee/sample/vb6/demo/demo.bas delete mode 100644 libs/thirdParty/libxbee/sample/vb6/demo/demo.vbp delete mode 100644 libs/thirdParty/libxbee/sample/vb6/libxbee.bas delete mode 100644 libs/thirdParty/libxbee/sample/vb6/talk_to_me/Form1.frm delete mode 100644 libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.bas delete mode 100644 libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.vbp delete mode 100644 libs/thirdParty/libxbee/sample/xbee2_rx.c delete mode 100644 libs/thirdParty/libxbee/sample/xbee2_tx.c delete mode 100644 libs/thirdParty/libxbee/umakefile delete mode 100644 libs/thirdParty/libxbee/win32.README.txt delete mode 100644 libs/thirdParty/libxbee/win32.makefile delete mode 100644 libs/thirdParty/libxbee/xbee.h delete mode 100644 libs/thirdParty/libxbee/xsys/README delete mode 100644 libs/thirdParty/libxbee/xsys/linux.c delete mode 100644 libs/thirdParty/libxbee/xsys/linux.h delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/linux.c.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/linux.h.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/win32.c.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/win32.def.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/win32.dll.c.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/win32.h.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/pdf/win32.rc.pdf delete mode 100644 libs/thirdParty/libxbee/xsys/win32.c delete mode 100644 libs/thirdParty/libxbee/xsys/win32.def delete mode 100644 libs/thirdParty/libxbee/xsys/win32.dll.c delete mode 100644 libs/thirdParty/libxbee/xsys/win32.h delete mode 100644 libs/thirdParty/libxbee/xsys/win32.rc delete mode 100644 src/comm/HexSpinBox.cpp delete mode 100644 src/comm/HexSpinBox.h delete mode 100644 src/comm/XbeeLink.cpp delete mode 100644 src/comm/XbeeLink.h delete mode 100644 src/comm/XbeeLinkInterface.h delete mode 100644 src/ui/XbeeConfigurationWindow.cpp delete mode 100644 src/ui/XbeeConfigurationWindow.h diff --git a/QGCExternalLibs.pri b/QGCExternalLibs.pri index 0f31a6e15..52e5de7fb 100644 --- a/QGCExternalLibs.pri +++ b/QGCExternalLibs.pri @@ -65,103 +65,6 @@ DEPENDPATH += libs/qwt INCLUDEPATH += libs/qwt } -# -# [OPTIONAL] XBee wireless support. This is not necessary for basic serial/UART communications. -# It's only required for speaking directly to the Xbee using their proprietary API. -# Unsupported on Mac. -# Installation on Windows is unnecessary, as we just link to our included .dlls directly. -# Installing on Linux involves running `make;sudo make install` in `libs/thirdParty/libxbee` -# Uninstalling from Linux can be done with `sudo make uninstall`. -# -XBEE_DEPENDENT_HEADERS += \ - src/comm/XbeeLinkInterface.h \ - src/comm/XbeeLink.h \ - src/comm/HexSpinBox.h \ - src/ui/XbeeConfigurationWindow.h \ - src/comm/CallConv.h -XBEE_DEPENDENT_SOURCES += \ - src/comm/XbeeLink.cpp \ - src/comm/HexSpinBox.cpp \ - src/ui/XbeeConfigurationWindow.cpp -XBEE_DEFINES = QGC_XBEE_ENABLED - -contains(DEFINES, DISABLE_XBEE) { - message("Skipping support for native XBee API (manual override from command line)") - DEFINES -= DISABLE_XBEE -# Otherwise the user can still disable this feature in the user_config.pri file. -} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_XBEE) { - message("Skipping support for native XBee API (manual override from user_config.pri)") -} else:LinuxBuild { - linux-g++-64 { - message("Skipping support for XBee API (64-bit Linux builds not supported)") - } else:exists(/usr/include/xbee.h) { - message("Including support for XBee API") - - HEADERS += $$XBEE_DEPENDENT_HEADERS - SOURCES += $$XBEE_DEPENDENT_SOURCES - DEFINES += $$XBEE_DEFINES - LIBS += -L/usr/lib -lxbee - } else { - warning("Skipping support for XBee API (missing libraries, see README)") - } -} else:WindowsBuild { - message("Including support for XBee API") - HEADERS += $$XBEE_DEPENDENT_HEADERS - SOURCES += $$XBEE_DEPENDENT_SOURCES - DEFINES += $$XBEE_DEFINES - INCLUDEPATH += libs/thirdParty/libxbee - LIBS += -l$$BASEDIR/libs/thirdParty/libxbee/lib/libxbee -} else { - message("Skipping support for XBee API (unsupported platform)") -} - -# -# [OPTIONAL] Opal RT-LAB Library. Provides integration with Opal-RT's RT-LAB simulator. -# -contains(DEFINES, DISABLE_RTLAB) { - message("Skipping support for RT-LAB (manual override from command line)") - DEFINES -= DISABLE_RTLAB -# Otherwise the user can still disable this feature in the user_config.pri file. -} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_RTLAB) { - message("Skipping support for RT-LAB (manual override from user_config.pri)") -} else:WindowsBuild { - exists(src/lib/opalrt/OpalApi.h) : exists(C:/OPAL-RT/RT-LAB7.2.4/Common/bin) { - message("Including support for RT-LAB") - - DEFINES += QGC_RTLAB_ENABLED - - INCLUDEPATH += - src/lib/opalrt - libs/lib/opal/include \ - - FORMS += src/ui/OpalLinkSettings.ui - - HEADERS += \ - src/comm/OpalRT.h \ - src/comm/OpalLink.h \ - src/comm/Parameter.h \ - src/comm/QGCParamID.h \ - src/comm/ParameterList.h \ - src/ui/OpalLinkConfigurationWindow.h - - SOURCES += \ - src/comm/OpalRT.cc \ - src/comm/OpalLink.cc \ - src/comm/Parameter.cc \ - src/comm/QGCParamID.cc \ - src/comm/ParameterList.cc \ - src/ui/OpalLinkConfigurationWindow.cc - - LIBS += \ - -LC:/OPAL-RT/RT-LAB7.2.4/Common/bin \ - -lOpalApi - } else { - warning("Skipping support for RT-LAB (missing libraries, see README)") - } -} else { - message("Skipping support for RT-LAB (unsupported platform)") -} - # # [REQUIRED] SDL dependency. Provides joystick/gamepad support. # The SDL is packaged with QGC for the Mac and Windows. Linux support requires installing the SDL diff --git a/libs/thirdParty/libxbee/LICENSE b/libs/thirdParty/libxbee/LICENSE deleted file mode 100644 index 94a9ed024..000000000 --- a/libs/thirdParty/libxbee/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/libs/thirdParty/libxbee/README b/libs/thirdParty/libxbee/README deleted file mode 100644 index a5dbbfe0f..000000000 --- a/libs/thirdParty/libxbee/README +++ /dev/null @@ -1,39 +0,0 @@ -Welcome to libxbee! - -I have proveded sample code in the ./sample directory. Hopefully this will help -get you up and running with libxbee. If you would like samples showing a different -aspect of libxbee, then please do not hesitate to file an 'issue' on the project -site, and I will get to it ASAP: - http://code.google.com/p/libxbee/issues/list - - -Documentation is avaliable via the man page system once you have installed the -library, or as HTML in the 'doc' directory. - $ man libxbee - - -Please note that this project is still in development, so should not be used for -any purpose other than learning/playing/testing etc... Basically don't use it to -make money, and then hold me responsible if it breaks! - -Feel free to contact me directly with any queries: - attie@attie.co.uk - - -For those of you that are planning to use this on an embedded board, I have -included a small makefile (umakefile) that has only the information needed to -compile the library. I suggest you use this instead! - -=== Installation === -To install simply type: - $ make install - -For more information, or if you can't install it, please see the wiki: - http://code.google.com/p/libxbee/wiki/install_libxbee - - -=== Usage === -If you are compiling the object file directly into your executable instead -of making use of the shared library, you must include the following link -flags: - -lpthread -lrt diff --git a/libs/thirdParty/libxbee/api.c b/libs/thirdParty/libxbee/api.c deleted file mode 100644 index ef5923b56..000000000 --- a/libs/thirdParty/libxbee/api.c +++ /dev/null @@ -1,2433 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -const char *SVN_REV = "$Id: api.c 508 2011-06-12 23:22:34Z attie@attie.co.uk $"; -char svn_rev[128] = "\0"; - -#include "api.h" - -const char *xbee_svn_version(void) { - if (svn_rev[0] == '\0') { - char *t; - sprintf(svn_rev,"r%s",&SVN_REV[11]); - t = strrchr(svn_rev,' '); - if (t) { - t[0] = '\0'; - } - } - return svn_rev; -} - -const char *xbee_build_info(void) { - return "Built on " __DATE__ " @ " __TIME__ " for " HOST_OS; -} - -/* ################################################################# */ -/* ### Memory Handling ############################################# */ -/* ################################################################# */ - -/* malloc wrapper function */ -static void *Xmalloc2(xbee_hnd xbee, size_t size) { - void *t; - t = malloc(size); - if (!t) { - /* uhoh... thats pretty bad... */ - xbee_perror("libxbee:malloc()"); - exit(1); - } - return t; -} - -/* calloc wrapper function */ -static void *Xcalloc2(xbee_hnd xbee, size_t size) { - void *t; - t = calloc(1, size); - if (!t) { - /* uhoh... thats pretty bad... */ - xbee_perror("libxbee:calloc()"); - exit(1); - } - return t; -} - -/* realloc wrapper function */ -static void *Xrealloc2(xbee_hnd xbee, void *ptr, size_t size) { - void *t; - t = realloc(ptr,size); - if (!t) { - /* uhoh... thats pretty bad... */ - fprintf(stderr,"libxbee:realloc(): Returned NULL\n"); - exit(1); - } - return t; -} - -/* free wrapper function (uses the Xfree macro and sets the pointer to NULL after freeing it) */ -static void Xfree2(void **ptr) { - if (!*ptr) return; - free(*ptr); - *ptr = NULL; -} - -/* ################################################################# */ -/* ### Helper Functions ############################################ */ -/* ################################################################# */ - -/* ################################################################# - returns 1 if the packet has data for the digital input else 0 */ -int xbee_hasdigital(xbee_pkt *pkt, int sample, int input) { - int mask = 0x0001; - if (input < 0 || input > 7) return 0; - if (sample >= pkt->samples) return 0; - - mask <<= input; - return !!(pkt->IOdata[sample].IOmask & mask); -} - -/* ################################################################# - returns 1 if the digital input is high else 0 (or 0 if no digital data present) */ -int xbee_getdigital(xbee_pkt *pkt, int sample, int input) { - int mask = 0x0001; - if (!xbee_hasdigital(pkt,sample,input)) return 0; - - mask <<= input; - return !!(pkt->IOdata[sample].IOdigital & mask); -} - -/* ################################################################# - returns 1 if the packet has data for the analog input else 0 */ -int xbee_hasanalog(xbee_pkt *pkt, int sample, int input) { - int mask = 0x0200; - if (input < 0 || input > 5) return 0; - if (sample >= pkt->samples) return 0; - - mask <<= input; - return !!(pkt->IOdata[sample].IOmask & mask); -} - -/* ################################################################# - returns analog input as a voltage if vRef is non-zero, else raw value (or 0 if no analog data present) */ -double xbee_getanalog(xbee_pkt *pkt, int sample, int input, double Vref) { - if (!xbee_hasanalog(pkt,sample,input)) return 0; - - if (Vref) return (Vref / 1023) * pkt->IOdata[sample].IOanalog[input]; - return pkt->IOdata[sample].IOanalog[input]; -} - -/* ################################################################# */ -/* ### XBee Functions ############################################## */ -/* ################################################################# */ - -static void xbee_logf(xbee_hnd xbee, const char *logformat, const char *file, - const int line, const char *function, char *format, ...) { - char buf[128]; - va_list ap; - if (!xbee) return; - if (!xbee->log) return; - va_start(ap,format); - vsnprintf(buf,127,format,ap); - va_end(ap); - fprintf(xbee->log,logformat,file,line,function,buf); -} -void xbee_logitf(char *format, ...) { - char buf[128]; - va_list ap; - va_start(ap,format); - vsnprintf(buf,127,format,ap); - va_end(ap); - xbee_logit(buf); -} -void _xbee_logitf(xbee_hnd xbee, char *format, ...) { - char buf[128]; - va_list ap; - va_start(ap,format); - vsnprintf(buf,127,format,ap); - va_end(ap); - _xbee_logit(xbee, buf); -} -void xbee_logit(char *str) { - _xbee_logit(default_xbee, str); -} -void _xbee_logit(xbee_hnd xbee, char *str) { - if (!xbee) return; - if (!xbee->log) return; - xbee_mutex_lock(xbee->logmutex); - fprintf(xbee->log,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,str); - xbee_mutex_unlock(xbee->logmutex); -} - -/* ################################################################# - xbee_sendAT - INTERNAL - allows for an at command to be send, and the reply to be captured */ -static int xbee_sendAT(xbee_hnd xbee, char *command, char *retBuf, int retBuflen) { - return xbee_sendATdelay(xbee, 0, command, retBuf, retBuflen); -} -static int xbee_sendATdelay(xbee_hnd xbee, int guardTime, char *command, char *retBuf, int retBuflen) { - struct timeval to; - - int ret; - int bufi = 0; - - /* if there is a guardTime given, then use it and a bit more */ - if (guardTime) usleep(guardTime * 1200); - - /* get rid of any pre-command sludge... */ - memset(&to, 0, sizeof(to)); - ret = xbee_select(xbee,&to); - if (ret > 0) { - char t[128]; - while (xbee_read(xbee,t,127)); - } - - /* send the requested command */ - xbee_log("sendATdelay: Sending '%s'", command); - xbee_write(xbee,command, strlen(command)); - - /* if there is a guardTime, then use it */ - if (guardTime) { - usleep(guardTime * 900); - - /* get rid of any post-command sludge... */ - memset(&to, 0, sizeof(to)); - ret = xbee_select(xbee,&to); - if (ret > 0) { - char t[128]; - while (xbee_read(xbee,t,127)); - } - } - - /* retrieve the data */ - memset(retBuf, 0, retBuflen); - memset(&to, 0, sizeof(to)); - if (guardTime) { - /* select on the xbee fd... wait at most 0.2 the guardTime for the response */ - to.tv_usec = guardTime * 200; - } else { - /* or 250ms */ - to.tv_usec = 250000; - } - if ((ret = xbee_select(xbee,&to)) == -1) { - xbee_perror("libxbee:xbee_sendATdelay()"); - exit(1); - } - - if (!ret) { - /* timed out, and there is nothing to be read */ - xbee_log("sendATdelay: No Data to read - Timeout..."); - return 1; - } - - /* check for any dribble... */ - do { - /* if there is actually no space in the retBuf then break out */ - if (bufi >= retBuflen - 1) { - break; - } - - /* read as much data as is possible into retBuf */ - if ((ret = xbee_read(xbee,&retBuf[bufi], retBuflen - bufi - 1)) == 0) { - break; - } - - /* advance the 'end of string' pointer */ - bufi += ret; - - /* wait at most 150ms for any more data */ - memset(&to, 0, sizeof(to)); - to.tv_usec = 150000; - if ((ret = xbee_select(xbee,&to)) == -1) { - xbee_perror("libxbee:xbee_sendATdelay()"); - exit(1); - } - - /* loop while data was read */ - } while (ret); - - if (!bufi) { - xbee_log("sendATdelay: No response..."); - return 1; - } - - /* terminate the string */ - retBuf[bufi] = '\0'; - - xbee_log("sendATdelay: Recieved '%s'",retBuf); - return 0; -} - - -/* ################################################################# - xbee_start - sets up the correct API mode for the xbee - cmdSeq = CC - cmdTime = GT */ -static int xbee_startAPI(xbee_hnd xbee) { - char buf[256]; - - if (xbee->cmdSeq == 0 || xbee->cmdTime == 0) return 1; - - /* setup the command sequence string */ - memset(buf,xbee->cmdSeq,3); - buf[3] = '\0'; - - /* try the command sequence */ - if (xbee_sendATdelay(xbee, xbee->cmdTime, buf, buf, sizeof(buf))) { - /* if it failed... try just entering 'AT' which should return OK */ - if (xbee_sendAT(xbee, "AT\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1; - } else if (strncmp(&buf[strlen(buf)-3],"OK\r",3)) { - /* if data was returned, but it wasn't OK... then something went wrong! */ - return 1; - } - - /* get the current API mode */ - if (xbee_sendAT(xbee, "ATAP\r", buf, 3)) return 1; - buf[1] = '\0'; - xbee->oldAPI = atoi(buf); - - if (xbee->oldAPI != 2) { - /* if it wasnt set to mode 2 already, then set it to mode 2 */ - if (xbee_sendAT(xbee, "ATAP2\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1; - } - - /* quit from command mode, ready for some packets! :) */ - if (xbee_sendAT(xbee, "ATCN\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1; - - return 0; -} - -/* ################################################################# - xbee_end - resets the API mode to the saved value - you must have called xbee_setup[log]API */ -int xbee_end(void) { - return _xbee_end(default_xbee); -} -int _xbee_end(xbee_hnd xbee) { - int ret = 1; - xbee_con *con, *ncon; - xbee_pkt *pkt, *npkt; - xbee_hnd xbeet; - - ISREADYR(0); - xbee_log("Stopping libxbee instance..."); - - /* unlink the instance from list... */ - xbee_log("Unlinking instance from list..."); - xbee_mutex_lock(xbee_hnd_mutex); - if (xbee == default_xbee) { - default_xbee = default_xbee->next; - if (!default_xbee) { - xbee_mutex_destroy(xbee_hnd_mutex); - } - } else { - xbeet = default_xbee; - while (xbeet) { - if (xbeet->next == xbee) { - xbeet->next = xbee->next; - break; - } - xbeet = xbeet->next; - } - } - if (default_xbee) xbee_mutex_unlock(xbee_hnd_mutex); - - /* if the api mode was not 2 to begin with then put it back */ - if (xbee->oldAPI == 2) { - xbee_log("XBee was already in API mode 2, no need to reset"); - ret = 0; - } else { - int to = 5; - - con = _xbee_newcon(xbee,'I',xbee_localAT); - con->callback = NULL; - con->waitforACK = 1; - _xbee_senddata(xbee,con,"AP%c",xbee->oldAPI); - - pkt = NULL; - - while (!pkt && to--) { - pkt = _xbee_getpacketwait(xbee,con); - } - if (pkt) { - ret = pkt->status; - Xfree(pkt); - } - _xbee_endcon(xbee,con); - } - - /* xbee_* functions may no longer run... */ - xbee->xbee_ready = 0; - - /* nullify everything */ - - /* stop listening for data... either after timeout or next char read which ever is first */ - xbee->run = 0; - - xbee_thread_cancel(xbee->listent,0); - xbee_thread_join(xbee->listent); - - xbee_thread_cancel(xbee->threadt,0); - xbee_thread_join(xbee->threadt); - - /* free all connections */ - con = xbee->conlist; - xbee->conlist = NULL; - while (con) { - ncon = con->next; - Xfree(con); - con = ncon; - } - - /* free all packets */ - xbee->pktlast = NULL; - pkt = xbee->pktlist; - xbee->pktlist = NULL; - while (pkt) { - npkt = pkt->next; - Xfree(pkt); - pkt = npkt; - } - - /* destroy mutexes */ - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - - /* close the serial port */ - Xfree(xbee->path); - if (xbee->tty) xbee_close(xbee->tty); - - /* close log and tty */ - if (xbee->log) { - fflush(xbee->log); - xbee_close(xbee->log); - } - xbee_mutex_destroy(xbee->logmutex); - - Xfree(xbee); - - return ret; -} - -/* ################################################################# - xbee_setup - opens xbee serial port & creates xbee listen thread - the xbee must be configured for API mode 2 - THIS MUST BE CALLED BEFORE ANY OTHER XBEE FUNCTION */ -int xbee_setup(char *path, int baudrate) { - return xbee_setuplogAPI(path,baudrate,0,0,0); -} -xbee_hnd _xbee_setup(char *path, int baudrate) { - return _xbee_setuplogAPI(path,baudrate,0,0,0); -} -int xbee_setuplog(char *path, int baudrate, int logfd) { - return xbee_setuplogAPI(path,baudrate,logfd,0,0); -} -xbee_hnd _xbee_setuplog(char *path, int baudrate, int logfd) { - return _xbee_setuplogAPI(path,baudrate,logfd,0,0); -} -int xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime) { - return xbee_setuplogAPI(path,baudrate,0,cmdSeq,cmdTime); -} -xbee_hnd _xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime) { - return _xbee_setuplogAPI(path,baudrate,0,cmdSeq,cmdTime); -} -int xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime) { - if (default_xbee) return 0; - default_xbee = _xbee_setuplogAPI(path,baudrate,logfd,cmdSeq,cmdTime); - return (default_xbee?0:-1); -} -xbee_hnd _xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime) { - int ret; - xbee_hnd xbee = NULL; - - /* create a new instance */ - xbee = Xcalloc(sizeof(struct xbee_hnd)); - xbee->next = NULL; - - xbee_mutex_init(xbee->logmutex); -#ifdef DEBUG - if (!logfd) logfd = 2; -#endif - if (logfd) { - xbee->logfd = dup(logfd); - xbee->log = fdopen(xbee->logfd,"w"); - if (!xbee->log) { - /* errno == 9 is bad file descriptor (probrably not provided) */ - if (errno != 9) xbee_perror("xbee_setup(): Failed opening logfile"); - xbee->logfd = 0; - } else { -#ifdef __GNUC__ /* ---- */ - /* set to line buffer - ensure lines are written to file when complete */ - setvbuf(xbee->log,NULL,_IOLBF,BUFSIZ); -#else /* -------------- */ - /* Win32 is rubbish... so we have to completely disable buffering... */ - setvbuf(xbee->log,NULL,_IONBF,BUFSIZ); -#endif /* ------------- */ - } - } - - xbee_logS("---------------------------------------------------------------------"); - xbee_logI("libxbee Starting..."); - xbee_logI("SVN Info: %s",xbee_svn_version()); - xbee_logI("Build Info: %s",xbee_build_info()); - xbee_logE("---------------------------------------------------------------------"); - - /* setup the connection stuff */ - xbee->conlist = NULL; - - /* setup the packet stuff */ - xbee->pktlist = NULL; - xbee->pktlast = NULL; - xbee->pktcount = 0; - xbee->run = 1; - - /* setup the mutexes */ - if (xbee_mutex_init(xbee->conmutex)) { - xbee_perror("xbee_setup():xbee_mutex_init(conmutex)"); - if (xbee->log) xbee_close(xbee->log); - Xfree(xbee); - return NULL; - } - if (xbee_mutex_init(xbee->pktmutex)) { - xbee_perror("xbee_setup():xbee_mutex_init(pktmutex)"); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - Xfree(xbee); - return NULL; - } - if (xbee_mutex_init(xbee->sendmutex)) { - xbee_perror("xbee_setup():xbee_mutex_init(sendmutex)"); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - Xfree(xbee); - return NULL; - } - - /* take a copy of the XBee device path */ - if ((xbee->path = Xmalloc(sizeof(char) * (strlen(path) + 1))) == NULL) { - xbee_perror("xbee_setup():Xmalloc(path)"); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee); - return NULL; - } - strcpy(xbee->path,path); - if (xbee->log) xbee_log("Opening serial port '%s'...",xbee->path); - - /* call the relevant init function */ - if ((ret = init_serial(xbee,baudrate)) != 0) { - xbee_log("Something failed while opening the serial port..."); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); - Xfree(xbee); - return NULL; - } - - /* when xbee_end() is called, if this is not 2 then ATAP will be set to this value */ - xbee->oldAPI = 2; - xbee->cmdSeq = cmdSeq; - xbee->cmdTime = cmdTime; - if (xbee->cmdSeq && xbee->cmdTime) { - if (xbee_startAPI(xbee)) { - if (xbee->log) { - xbee_log("Couldn't communicate with XBee..."); - xbee_close(xbee->log); - } - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); -#ifdef __GNUC__ /* ---- */ - close(xbee->ttyfd); -#endif /* ------------- */ - xbee_close(xbee->tty); - Xfree(xbee); - return NULL; - } - } - - /* allow the listen thread to start */ - xbee->xbee_ready = -1; - - /* can start xbee_listen thread now */ - if (xbee_thread_create(xbee->listent, xbee_listen_wrapper, xbee)) { - xbee_perror("xbee_setup():xbee_thread_create(listent)"); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); -#ifdef __GNUC__ /* ---- */ - close(xbee->ttyfd); -#endif /* ------------- */ - xbee_close(xbee->tty); - Xfree(xbee); - return NULL; - } - - /* can start xbee_thread_watch thread thread now */ - if (xbee_thread_create(xbee->threadt, xbee_thread_watch, xbee)) { - xbee_perror("xbee_setup():xbee_thread_create(threadt)"); - if (xbee->log) xbee_close(xbee->log); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); -#ifdef __GNUC__ /* ---- */ - close(xbee->ttyfd); -#endif /* ------------- */ - xbee_close(xbee->tty); - Xfree(xbee); - return NULL; - } - - usleep(500); - while (xbee->xbee_ready != -2) { - usleep(500); - xbee_log("Waiting for xbee_listen() to be ready..."); - } - - /* allow other functions to be used! */ - xbee->xbee_ready = 1; - - xbee_log("Linking xbee instance..."); - if (!default_xbee) { - xbee_mutex_init(xbee_hnd_mutex); - xbee_mutex_lock(xbee_hnd_mutex); - default_xbee = xbee; - xbee_mutex_unlock(xbee_hnd_mutex); - } else { - xbee_hnd xbeet; - xbee_mutex_lock(xbee_hnd_mutex); - xbeet = default_xbee; - while (xbeet->next) { - xbeet = xbeet->next; - } - xbeet->next = xbee; - xbee_mutex_unlock(xbee_hnd_mutex); - } - - xbee_log("libxbee: Started!"); - - return xbee; -} - -/* ################################################################# - xbee_con - produces a connection to the specified device and frameID - if a connection had already been made, then this connection will be returned */ -xbee_con *xbee_newcon(unsigned char frameID, xbee_types type, ...) { - xbee_con *ret; - va_list ap; - - /* xbee_vnewcon() wants a va_list... */ - va_start(ap, type); - /* hand it over :) */ - ret = _xbee_vnewcon(default_xbee, frameID, type, ap); - va_end(ap); - return ret; -} -xbee_con *_xbee_newcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, ...) { - xbee_con *ret; - va_list ap; - - /* xbee_vnewcon() wants a va_list... */ - va_start(ap, type); - /* hand it over :) */ - ret = _xbee_vnewcon(xbee, frameID, type, ap); - va_end(ap); - return ret; -} -xbee_con *_xbee_vnewcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, va_list ap) { - xbee_con *con, *ocon; - unsigned char tAddr[8]; - int t; - int i; - - ISREADYR(NULL); - - if (!type || type == xbee_unknown) type = xbee_localAT; /* default to local AT */ - else if (type == xbee_remoteAT) type = xbee_64bitRemoteAT; /* if remote AT, default to 64bit */ - - /* if: 64 bit address expected (2 ints) */ - if ((type == xbee_64bitRemoteAT) || - (type == xbee_64bitData) || - (type == xbee_64bitIO) || - (type == xbee2_data)) { - t = va_arg(ap, int); - tAddr[0] = (t >> 24) & 0xFF; - tAddr[1] = (t >> 16) & 0xFF; - tAddr[2] = (t >> 8) & 0xFF; - tAddr[3] = (t ) & 0xFF; - t = va_arg(ap, int); - tAddr[4] = (t >> 24) & 0xFF; - tAddr[5] = (t >> 16) & 0xFF; - tAddr[6] = (t >> 8) & 0xFF; - tAddr[7] = (t ) & 0xFF; - - /* if: 16 bit address expected (1 int) */ - } else if ((type == xbee_16bitRemoteAT) || - (type == xbee_16bitData) || - (type == xbee_16bitIO)) { - t = va_arg(ap, int); - tAddr[0] = (t >> 8) & 0xFF; - tAddr[1] = (t ) & 0xFF; - tAddr[2] = 0; - tAddr[3] = 0; - tAddr[4] = 0; - tAddr[5] = 0; - tAddr[6] = 0; - tAddr[7] = 0; - - /* otherwise clear the address */ - } else { - memset(tAddr,0,8); - } - - /* lock the connection mutex */ - xbee_mutex_lock(xbee->conmutex); - - /* are there any connections? */ - if (xbee->conlist) { - con = xbee->conlist; - while (con) { - /* if: looking for a modemStatus, and the types match! */ - if ((type == xbee_modemStatus) && - (con->type == type)) { - xbee_mutex_unlock(xbee->conmutex); - return con; - - /* if: looking for a txStatus and frameIDs match! */ - } else if ((type == xbee_txStatus) && - (con->type == type) && - (frameID == con->frameID)) { - xbee_mutex_unlock(xbee->conmutex); - return con; - - /* if: looking for a localAT, and the frameIDs match! */ - } else if ((type == xbee_localAT) && - (con->type == type) && - (frameID == con->frameID)) { - xbee_mutex_unlock(xbee->conmutex); - return con; - - /* if: connection types match, the frameIDs match, and the addresses match! */ - } else if ((type == con->type) && - (frameID == con->frameID) && - (!memcmp(tAddr,con->tAddr,8))) { - xbee_mutex_unlock(xbee->conmutex); - return con; - } - - /* if there are more, move along, dont want to loose that last item! */ - if (con->next == NULL) break; - con = con->next; - } - - /* keep hold of the last connection... we will need to link it up later */ - ocon = con; - } - - /* unlock the connection mutex */ - xbee_mutex_unlock(xbee->conmutex); - - /* create a new connection and set its attributes */ - con = Xcalloc(sizeof(xbee_con)); - con->type = type; - /* is it a 64bit connection? */ - if ((type == xbee_64bitRemoteAT) || - (type == xbee_64bitData) || - (type == xbee_64bitIO) || - (type == xbee2_data)) { - con->tAddr64 = TRUE; - } - con->atQueue = 0; /* queue AT commands? */ - con->txDisableACK = 0; /* disable ACKs? */ - con->txBroadcastPAN = 0; /* broadcast? */ - con->frameID = frameID; - con->waitforACK = 0; - memcpy(con->tAddr,tAddr,8); /* copy in the remote address */ - xbee_mutex_init(con->callbackmutex); - xbee_mutex_init(con->callbackListmutex); - xbee_mutex_init(con->Txmutex); - xbee_sem_init(con->waitforACKsem); - - if (xbee->log) { - switch(type) { - case xbee_localAT: - xbee_log("New local AT connection!"); - break; - case xbee_16bitRemoteAT: - case xbee_64bitRemoteAT: - xbee_logc("New %d-bit remote AT connection! (to: ",(con->tAddr64?64:16)); - for (i=0;i<(con->tAddr64?8:2);i++) { - fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]); - } - fprintf(xbee->log,")"); - xbee_logcf(); - break; - case xbee_16bitData: - case xbee_64bitData: - xbee_logc("New %d-bit data connection! (to: ",(con->tAddr64?64:16)); - for (i=0;i<(con->tAddr64?8:2);i++) { - fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]); - } - fprintf(xbee->log,")"); - xbee_logcf(); - break; - case xbee_16bitIO: - case xbee_64bitIO: - xbee_logc("New %d-bit IO connection! (to: ",(con->tAddr64?64:16)); - for (i=0;i<(con->tAddr64?8:2);i++) { - fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]); - } - fprintf(xbee->log,")"); - xbee_logcf(); - break; - case xbee2_data: - xbee_logc("New Series 2 data connection! (to: "); - for (i=0;i<8;i++) { - fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]); - } - fprintf(xbee->log,")"); - xbee_logcf(); - break; - case xbee_txStatus: - xbee_log("New Tx status connection!"); - break; - case xbee_modemStatus: - xbee_log("New modem status connection!"); - break; - case xbee_unknown: - default: - xbee_log("New unknown connection!"); - } - } - - /* lock the connection mutex */ - xbee_mutex_lock(xbee->conmutex); - - /* make it the last in the list */ - con->next = NULL; - /* add it to the list */ - if (xbee->conlist) { - ocon->next = con; - } else { - xbee->conlist = con; - } - - /* unlock the mutex */ - xbee_mutex_unlock(xbee->conmutex); - return con; -} - -/* ################################################################# - xbee_conflush - removes any packets that have been collected for the specified - connection */ -void xbee_purgecon(xbee_con *con) { - _xbee_purgecon(default_xbee, con); -} -void _xbee_purgecon(xbee_hnd xbee, xbee_con *con) { - xbee_pkt *r, *p, *n; - - ISREADYP(); - - /* lock the packet mutex */ - xbee_mutex_lock(xbee->pktmutex); - - /* if: there are packets */ - if ((p = xbee->pktlist) != NULL) { - r = NULL; - /* get all packets for this connection */ - do { - /* does the packet match the connection? */ - if (xbee_matchpktcon(xbee,p,con)) { - /* if it was the first packet */ - if (!r) { - /* move the chain along */ - xbee->pktlist = p->next; - } else { - /* otherwise relink the list */ - r->next = p->next; - } - xbee->pktcount--; - - /* free this packet! */ - n = p->next; - Xfree(p); - /* move on */ - p = n; - } else { - /* move on */ - r = p; - p = p->next; - } - } while (p); - xbee->pktlast = r; - } - - /* unlock the packet mutex */ - xbee_mutex_unlock(xbee->pktmutex); -} - -/* ################################################################# - xbee_endcon - close the unwanted connection - free wrapper function (uses the Xfree macro and sets the pointer to NULL after freeing it) */ -void xbee_endcon2(xbee_con **con, int alreadyUnlinked) { - _xbee_endcon2(default_xbee, con, alreadyUnlinked); -} -void _xbee_endcon2(xbee_hnd xbee, xbee_con **con, int alreadyUnlinked) { - xbee_con *t, *u; - - ISREADYP(); - - /* lock the connection mutex */ - xbee_mutex_lock(xbee->conmutex); - - u = t = xbee->conlist; - while (t && t != *con) { - u = t; - t = t->next; - } - if (!t) { - /* this could be true if comming from the destroySelf signal... */ - if (!alreadyUnlinked) { - /* invalid connection given... */ - if (xbee->log) { - xbee_log("Attempted to close invalid connection..."); - } - /* unlock the connection mutex */ - xbee_mutex_unlock(xbee->conmutex); - return; - } - } else { - /* extract this connection from the list */ - if (t == xbee->conlist) { - xbee->conlist = t->next; - } else { - u->next = t->next; - } - } - - /* unlock the connection mutex */ - xbee_mutex_unlock(xbee->conmutex); - - /* check if a callback thread is running... */ - if (t->callback && xbee_mutex_trylock(t->callbackmutex)) { - /* if it is running... tell it to destroy the connection on completion */ - xbee_log("Attempted to close a connection with active callbacks... " - "Connection will be destroyed when callbacks have completeted..."); - t->destroySelf = 1; - return; - } - - /* remove all packets for this connection */ - _xbee_purgecon(xbee,t); - - /* destroy the callback mutex */ - xbee_mutex_destroy(t->callbackmutex); - xbee_mutex_destroy(t->callbackListmutex); - xbee_mutex_destroy(t->Txmutex); - xbee_sem_destroy(t->waitforACKsem); - - /* free the connection! */ - Xfree(*con); -} - -/* ################################################################# - xbee_senddata - send the specified data to the provided connection */ -int xbee_senddata(xbee_con *con, char *format, ...) { - int ret; - va_list ap; - - /* xbee_vsenddata() wants a va_list... */ - va_start(ap, format); - /* hand it over :) */ - ret = _xbee_vsenddata(default_xbee, con, format, ap); - va_end(ap); - return ret; -} -int _xbee_senddata(xbee_hnd xbee, xbee_con *con, char *format, ...) { - int ret; - va_list ap; - - /* xbee_vsenddata() wants a va_list... */ - va_start(ap, format); - /* hand it over :) */ - ret = _xbee_vsenddata(xbee, con, format, ap); - va_end(ap); - return ret; -} - -int xbee_vsenddata(xbee_con *con, char *format, va_list ap) { - return _xbee_vsenddata(default_xbee, con, format, ap); -} -int _xbee_vsenddata(xbee_hnd xbee, xbee_con *con, char *format, va_list ap) { - unsigned char data[128]; /* max payload is 100 bytes... plus a bit of fluff... */ - int length; - - /* make up the data and keep the length, its possible there are nulls in there */ - length = vsnprintf((char *)data, 128, format, ap); - - /* hand it over :) */ - return _xbee_nsenddata(xbee, con, (char *)data, length); -} - -/* returns: - 1 - if NAC was recieved - 0 - if packet was successfully sent (or just sent if waitforACK is off) - -1 - if there was an error building the packet - -2 - if the connection type was unknown */ -int xbee_nsenddata(xbee_con *con, char *data, int length) { - return _xbee_nsenddata(default_xbee, con, data, length); -} -int _xbee_nsenddata(xbee_hnd xbee, xbee_con *con, char *data, int length) { - t_data *pkt; - int i; - unsigned char buf[128]; /* max payload is 100 bytes... plus a bit for the headers etc... */ - - ISREADYR(-1); - - if (!con) return -1; - if (con->type == xbee_unknown) return -1; - if (length > 127) return -1; - - if (xbee->log) { - xbee_logS("--== TX Packet ============--"); - xbee_logIc("Connection Type: "); - switch (con->type) { - case xbee_unknown: fprintf(xbee->log,"Unknown"); break; - case xbee_localAT: fprintf(xbee->log,"Local AT"); break; - case xbee_remoteAT: fprintf(xbee->log,"Remote AT"); break; - case xbee_16bitRemoteAT: fprintf(xbee->log,"Remote AT (16-bit)"); break; - case xbee_64bitRemoteAT: fprintf(xbee->log,"Remote AT (64-bit)"); break; - case xbee_16bitData: fprintf(xbee->log,"Data (16-bit)"); break; - case xbee_64bitData: fprintf(xbee->log,"Data (64-bit)"); break; - case xbee_16bitIO: fprintf(xbee->log,"IO (16-bit)"); break; - case xbee_64bitIO: fprintf(xbee->log,"IO (64-bit)"); break; - case xbee2_data: fprintf(xbee->log,"Series 2 Data"); break; - case xbee2_txStatus: fprintf(xbee->log,"Series 2 Tx Status"); break; - case xbee_txStatus: fprintf(xbee->log,"Tx Status"); break; - case xbee_modemStatus: fprintf(xbee->log,"Modem Status"); break; - } - xbee_logIcf(); - switch (con->type) { - case xbee_localAT: case xbee_remoteAT: case xbee_txStatus: case xbee_modemStatus: - break; - default: - xbee_logIc("Destination: "); - for (i=0;i<(con->tAddr64?8:2);i++) { - fprintf(xbee->log,(i?":%02X":"%02X"),con->tAddr[i]); - } - xbee_logIcf(); - } - xbee_logI("Length: %d",length); - for (i=0;i 32) && (data[i] < 127)) { - fprintf(xbee->log,"'%c'",data[i]); - } else{ - fprintf(xbee->log," _"); - } - xbee_logIcf(); - } - xbee_logEf(); - } - - /* ########################################## */ - /* if: local AT */ - if (con->type == xbee_localAT) { - /* AT commands are 2 chars long (plus optional parameter) */ - if (length < 2) return -1; - if (length > 32) return -1; - - /* use the command? */ - buf[0] = ((!con->atQueue)?XBEE_LOCAL_ATREQ:XBEE_LOCAL_ATQUE); - buf[1] = con->frameID; - - /* copy in the data */ - for (i=0;itype == xbee_16bitRemoteAT) || - (con->type == xbee_64bitRemoteAT)) { - if (length < 2) return -1; /* at commands are 2 chars long (plus optional parameter) */ - if (length > 32) return -1; - buf[0] = XBEE_REMOTE_ATREQ; - buf[1] = con->frameID; - - /* copy in the relevant address */ - if (con->tAddr64) { - memcpy(&buf[2],con->tAddr,8); - buf[10] = 0xFF; - buf[11] = 0xFE; - } else { - memset(&buf[2],0,8); - memcpy(&buf[10],con->tAddr,2); - } - /* queue the command? */ - buf[12] = ((!con->atQueue)?0x02:0x00); - - /* copy in the data */ - for (i=0;itype == xbee_16bitData) || - (con->type == xbee_64bitData)) { - int offset; - if (length > 100) return -1; - - /* if: 16bit Data */ - if (con->type == xbee_16bitData) { - buf[0] = XBEE_16BIT_DATATX; - offset = 5; - /* copy in the address */ - memcpy(&buf[2],con->tAddr,2); - - /* if: 64bit Data */ - } else { /* 64bit Data */ - buf[0] = XBEE_64BIT_DATATX; - offset = 11; - /* copy in the address */ - memcpy(&buf[2],con->tAddr,8); - } - - /* copy frameID */ - buf[1] = con->frameID; - - /* disable ack? broadcast? */ - buf[offset-1] = ((con->txDisableACK)?0x01:0x00) | ((con->txBroadcastPAN)?0x04:0x00); - - /* copy in the data */ - for (i=0;itype == xbee_64bitIO) || - (con->type == xbee_16bitIO)) { - /* not currently implemented... is it even allowed? */ - if (xbee->log) { - xbee_log("******* TODO ********\n"); - } - - /* ########################################## */ - /* if: Series 2 Data */ - } else if (con->type == xbee2_data) { - if (length > 72) return -1; - - buf[0] = XBEE2_DATATX; - buf[1] = con->frameID; - - /* copy in the relevant address */ - memcpy(&buf[2],con->tAddr,8); - buf[10] = 0xFF; - buf[11] = 0xFE; - - /* Maximum Radius/hops */ - buf[12] = 0x00; - - /* Options */ - buf[13] = 0x00; - - /* copy in the data */ - for (i=0;ipktmutex); - - /* if: there are no packets */ - if ((p = xbee->pktlist) == NULL) { - xbee_mutex_unlock(xbee->pktmutex); - /*if (xbee->log) { - xbee_log("No packets avaliable..."); - }*/ - return NULL; - } - - l = NULL; - q = NULL; - /* get the first avaliable packet for this connection */ - do { - /* does the packet match the connection? */ - if (xbee_matchpktcon(xbee, p, con)) { - q = p; - break; - } - /* move on */ - l = p; - p = p->next; - } while (p); - - /* if: no packet was found */ - if (!q) { - xbee_mutex_unlock(xbee->pktmutex); - if (xbee->log) { - struct timeval tv; - xbee_logS("--== Get Packet ==========--"); - gettimeofday(&tv,NULL); - xbee_logE("Didn't get a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec); - } - return NULL; - } - - /* if it was the first packet */ - if (l) { - /* relink the list */ - l->next = p->next; - if (!l->next) xbee->pktlast = l; - } else { - /* move the chain along */ - xbee->pktlist = p->next; - if (!xbee->pktlist) { - xbee->pktlast = NULL; - } else if (!xbee->pktlist->next) { - xbee->pktlast = xbee->pktlist; - } - } - xbee->pktcount--; - - /* unlink this packet from the chain! */ - q->next = NULL; - - if (xbee->log) { - struct timeval tv; - xbee_logS("--== Get Packet ==========--"); - gettimeofday(&tv,NULL); - xbee_logI("Got a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec); - xbee_logE("Packets left: %d",xbee->pktcount); - } - - /* unlock the packet mutex */ - xbee_mutex_unlock(xbee->pktmutex); - - /* and return the packet (must be free'd by caller!) */ - return q; -} - -/* ################################################################# - xbee_matchpktcon - INTERNAL - checks if the packet matches the connection */ -static int xbee_matchpktcon(xbee_hnd xbee, xbee_pkt *pkt, xbee_con *con) { - /* if: the connection type matches the packet type OR - the connection is 16/64bit remote AT, and the packet is a remote AT response */ - if ((pkt->type == con->type) || /* -- */ - ((pkt->type == xbee_remoteAT) && /* -- */ - ((con->type == xbee_16bitRemoteAT) || - (con->type == xbee_64bitRemoteAT)))) { - - - /* if: is a modem status (there can only be 1 modem status connection) */ - if (pkt->type == xbee_modemStatus) return 1; - - /* if: the packet is a txStatus or localAT and the frameIDs match */ - if ((pkt->type == xbee_txStatus) || - (pkt->type == xbee_localAT)) { - if (pkt->frameID == con->frameID) { - return 1; - } - /* if: the packet was sent as a 16bit remoteAT, and the 16bit addresss match */ - } else if ((pkt->type == xbee_remoteAT) && - (con->type == xbee_16bitRemoteAT) && - !memcmp(pkt->Addr16,con->tAddr,2)) { - return 1; - /* if: the packet was sent as a 64bit remoteAT, and the 64bit addresss match */ - } else if ((pkt->type == xbee_remoteAT) && - (con->type == xbee_64bitRemoteAT) && - !memcmp(pkt->Addr64,con->tAddr,8)) { - return 1; - /* if: the packet is 64bit addressed, and the addresses match */ - } else if (pkt->sAddr64 && !memcmp(pkt->Addr64,con->tAddr,8)) { - return 1; - /* if: the packet is 16bit addressed, and the addresses match */ - } else if (!pkt->sAddr64 && !memcmp(pkt->Addr16,con->tAddr,2)) { - return 1; - } else if (con->type == pkt->type && - (con->type == xbee_16bitData || con->type == xbee_64bitData) && - (pkt->isBroadcastADR || pkt->isBroadcastPAN)) { - unsigned char t[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - if ((con->tAddr64 && !memcmp(con->tAddr,t,8)) || - (!con->tAddr64 && !memcmp(con->tAddr,t,2))) { - return 1; - } - } - } - return 0; -} - -/* ################################################################# - xbee_parse_io - INTERNAL - parses the data given into the packet io information */ -static int xbee_parse_io(xbee_hnd xbee, xbee_pkt *p, unsigned char *d, - int maskOffset, int sampleOffset, int sample) { - xbee_sample *s = &(p->IOdata[sample]); - - /* copy in the I/O data mask */ - s->IOmask = (((d[maskOffset]<<8) | d[maskOffset + 1]) & 0x7FFF); - - /* copy in the digital I/O data */ - s->IOdigital = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x01FF); - - /* advance over the digital data, if its there */ - sampleOffset += ((s->IOmask & 0x01FF)?2:0); - - /* copy in the analog I/O data */ - if (s->IOmask & 0x0200) { - s->IOanalog[0] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - if (s->IOmask & 0x0400) { - s->IOanalog[1] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - if (s->IOmask & 0x0800) { - s->IOanalog[2] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - if (s->IOmask & 0x1000) { - s->IOanalog[3] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - if (s->IOmask & 0x2000) { - s->IOanalog[4] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - if (s->IOmask & 0x4000) { - s->IOanalog[5] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF); - sampleOffset+=2; - } - - if (xbee->log) { - if (s->IOmask & 0x0001) - xbee_logI("Digital 0: %c",((s->IOdigital & 0x0001)?'1':'0')); - if (s->IOmask & 0x0002) - xbee_logI("Digital 1: %c",((s->IOdigital & 0x0002)?'1':'0')); - if (s->IOmask & 0x0004) - xbee_logI("Digital 2: %c",((s->IOdigital & 0x0004)?'1':'0')); - if (s->IOmask & 0x0008) - xbee_logI("Digital 3: %c",((s->IOdigital & 0x0008)?'1':'0')); - if (s->IOmask & 0x0010) - xbee_logI("Digital 4: %c",((s->IOdigital & 0x0010)?'1':'0')); - if (s->IOmask & 0x0020) - xbee_logI("Digital 5: %c",((s->IOdigital & 0x0020)?'1':'0')); - if (s->IOmask & 0x0040) - xbee_logI("Digital 6: %c",((s->IOdigital & 0x0040)?'1':'0')); - if (s->IOmask & 0x0080) - xbee_logI("Digital 7: %c",((s->IOdigital & 0x0080)?'1':'0')); - if (s->IOmask & 0x0100) - xbee_logI("Digital 8: %c",((s->IOdigital & 0x0100)?'1':'0')); - if (s->IOmask & 0x0200) - xbee_logI("Analog 0: %d (~%.2fv)",s->IOanalog[0],(3.3/1023)*s->IOanalog[0]); - if (s->IOmask & 0x0400) - xbee_logI("Analog 1: %d (~%.2fv)",s->IOanalog[1],(3.3/1023)*s->IOanalog[1]); - if (s->IOmask & 0x0800) - xbee_logI("Analog 2: %d (~%.2fv)",s->IOanalog[2],(3.3/1023)*s->IOanalog[2]); - if (s->IOmask & 0x1000) - xbee_logI("Analog 3: %d (~%.2fv)",s->IOanalog[3],(3.3/1023)*s->IOanalog[3]); - if (s->IOmask & 0x2000) - xbee_logI("Analog 4: %d (~%.2fv)",s->IOanalog[4],(3.3/1023)*s->IOanalog[4]); - if (s->IOmask & 0x4000) - xbee_logI("Analog 5: %d (~%.2fv)",s->IOanalog[5],(3.3/1023)*s->IOanalog[5]); - } - - return sampleOffset; -} - -/* ################################################################# - xbee_listen_stop - stops the listen thread after the current packet has been processed */ -void xbee_listen_stop(xbee_hnd xbee) { - ISREADYP(); - xbee->run = 0; -} - -/* ################################################################# - xbee_listen_wrapper - INTERNAL - the xbee_listen wrapper. Prints an error when xbee_listen ends */ -static void xbee_listen_wrapper(xbee_hnd xbee) { - int ret; - - /* just falls out if the proper 'go-ahead' isn't given */ - if (xbee->xbee_ready != -1) return; - /* now allow the parent to continue */ - xbee->xbee_ready = -2; - -#ifdef _WIN32 /* ---- */ - /* win32 requires this delay... no idea why */ - usleep(1000000); -#endif /* ----------- */ - - while (xbee->run) { - ret = xbee_listen(xbee); - if (!xbee->run) break; - xbee_log("xbee_listen() returned [%d]... Restarting in 25ms!",ret); - usleep(25000); - } -} - -/* xbee_listen - INTERNAL - the xbee xbee_listen thread - reads data from the xbee and puts it into a linked list to keep the xbee buffers free */ -static int xbee_listen(xbee_hnd xbee) { -#define LISTEN_BUFLEN 1024 - unsigned char c, t, d[LISTEN_BUFLEN]; - unsigned int l, i, chksum, o; - int j; - xbee_pkt *p = NULL, *q; - xbee_con *con; - int hasCon; - - /* do this forever :) */ - while (xbee->run) { - /* clean up any undesired storage */ - if (p) Xfree(p); - - /* wait for a valid start byte */ - if ((c = xbee_getrawbyte(xbee)) != 0x7E) { - if (xbee->log) xbee_log("***** Unexpected byte (0x%02X)... *****",c); - continue; - } - if (!xbee->run) return 0; - - xbee_logSf(); - if (xbee->log) { - struct timeval tv; - xbee_logI("--== RX Packet ===========--"); - gettimeofday(&tv,NULL); - xbee_logI("Got a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec); - } - - /* get the length */ - l = xbee_getbyte(xbee) << 8; - l += xbee_getbyte(xbee); - - /* check it is a valid length... */ - if (!l) { - if (xbee->log) { - xbee_logI("Recived zero length packet!"); - } - continue; - } - if (l > 100) { - if (xbee->log) { - xbee_logI("Recived oversized packet! Length: %d",l - 1); - } - } - if (l > LISTEN_BUFLEN) { - if (xbee->log) { - xbee_logI("Recived packet larger than buffer! Discarding..."); - } - continue; - } - - if (xbee->log) { - xbee_logI("Length: %d",l - 1); - } - - /* get the packet type */ - t = xbee_getbyte(xbee); - - /* start the checksum */ - chksum = t; - - /* suck in all the data */ - for (i = 0; l > 1 && i < LISTEN_BUFLEN; l--, i++) { - /* get an unescaped byte */ - c = xbee_getbyte(xbee); - d[i] = c; - chksum += c; - if (xbee->log) { - xbee_logIc("%3d | 0x%02X | ",i,c); - if ((c > 32) && (c < 127)) fprintf(xbee->log,"'%c'",c); else fprintf(xbee->log," _ "); - - if ((t == XBEE_LOCAL_AT && i == 4) || - (t == XBEE_REMOTE_AT && i == 14) || - (t == XBEE_64BIT_DATARX && i == 10) || - (t == XBEE_16BIT_DATARX && i == 4) || - (t == XBEE_64BIT_IO && i == 13) || - (t == XBEE_16BIT_IO && i == 7)) { - /* mark the beginning of the 'data' bytes */ - fprintf(xbee->log," <-- data starts"); - } else if (t == XBEE_64BIT_IO) { - if (i == 10) fprintf(xbee->log," <-- sample count"); - else if (i == 11) fprintf(xbee->log," <-- mask (msb)"); - else if (i == 12) fprintf(xbee->log," <-- mask (lsb)"); - } else if (t == XBEE_16BIT_IO) { - if (i == 4) fprintf(xbee->log," <-- sample count"); - else if (i == 5) fprintf(xbee->log," <-- mask (msb)"); - else if (i == 6) fprintf(xbee->log," <-- mask (lsb)"); - } - xbee_logIcf(); - } - } - i--; /* it went up too many times!... */ - - /* add the checksum */ - chksum += xbee_getbyte(xbee); - - /* check if the whole packet was recieved, or something else occured... unlikely... */ - if (l>1) { - if (xbee->log) { - xbee_logE("Didn't get whole packet... :("); - } - continue; - } - - /* check the checksum */ - if ((chksum & 0xFF) != 0xFF) { - if (xbee->log) { - chksum &= 0xFF; - xbee_logE("Invalid Checksum: 0x%02X",chksum); - } - continue; - } - - /* make a new packet */ - p = Xcalloc(sizeof(xbee_pkt)); - q = NULL; - p->datalen = 0; - - /* ########################################## */ - /* if: modem status */ - if (t == XBEE_MODEM_STATUS) { - if (xbee->log) { - xbee_logI("Packet type: Modem Status (0x8A)"); - xbee_logIc("Event: "); - switch (d[0]) { - case 0x00: fprintf(xbee->log,"Hardware reset"); break; - case 0x01: fprintf(xbee->log,"Watchdog timer reset"); break; - case 0x02: fprintf(xbee->log,"Associated"); break; - case 0x03: fprintf(xbee->log,"Disassociated"); break; - case 0x04: fprintf(xbee->log,"Synchronization lost"); break; - case 0x05: fprintf(xbee->log,"Coordinator realignment"); break; - case 0x06: fprintf(xbee->log,"Coordinator started"); break; - } - fprintf(xbee->log,"... (0x%02X)",d[0]); - xbee_logIcf(); - } - p->type = xbee_modemStatus; - - p->sAddr64 = FALSE; - p->dataPkt = FALSE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = TRUE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - - /* modem status can only ever give 1 'data' byte */ - p->datalen = 1; - p->data[0] = d[0]; - - /* ########################################## */ - /* if: local AT response */ - } else if (t == XBEE_LOCAL_AT) { - if (xbee->log) { - xbee_logI("Packet type: Local AT Response (0x88)"); - xbee_logI("FrameID: 0x%02X",d[0]); - xbee_logI("AT Command: %c%c",d[1],d[2]); - xbee_logIc("Status: "); - if (d[3] == 0x00) fprintf(xbee->log,"OK"); - else if (d[3] == 0x01) fprintf(xbee->log,"Error"); - else if (d[3] == 0x02) fprintf(xbee->log,"Invalid Command"); - else if (d[3] == 0x03) fprintf(xbee->log,"Invalid Parameter"); - fprintf(xbee->log," (0x%02X)",d[3]); - xbee_logIcf(); - } - p->type = xbee_localAT; - - p->sAddr64 = FALSE; - p->dataPkt = FALSE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - - p->frameID = d[0]; - p->atCmd[0] = d[1]; - p->atCmd[1] = d[2]; - - p->status = d[3]; - - /* copy in the data */ - p->datalen = i-3; - for (;i>3;i--) p->data[i-4] = d[i]; - - /* ########################################## */ - /* if: remote AT response */ - } else if (t == XBEE_REMOTE_AT) { - if (xbee->log) { - xbee_logI("Packet type: Remote AT Response (0x97)"); - xbee_logI("FrameID: 0x%02X",d[0]); - xbee_logIc("64-bit Address: "); - for (j=0;j<8;j++) { - fprintf(xbee->log,(j?":%02X":"%02X"),d[1+j]); - } - xbee_logIcf(); - xbee_logIc("16-bit Address: "); - for (j=0;j<2;j++) { - fprintf(xbee->log,(j?":%02X":"%02X"),d[9+j]); - } - xbee_logIcf(); - xbee_logI("AT Command: %c%c",d[11],d[12]); - xbee_logIc("Status: "); - if (d[13] == 0x00) fprintf(xbee->log,"OK"); - else if (d[13] == 0x01) fprintf(xbee->log,"Error"); - else if (d[13] == 0x02) fprintf(xbee->log,"Invalid Command"); - else if (d[13] == 0x03) fprintf(xbee->log,"Invalid Parameter"); - else if (d[13] == 0x04) fprintf(xbee->log,"No Response"); - fprintf(xbee->log," (0x%02X)",d[13]); - xbee_logIcf(); - } - p->type = xbee_remoteAT; - - p->sAddr64 = FALSE; - p->dataPkt = FALSE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = TRUE; - p->IOPkt = FALSE; - - p->frameID = d[0]; - - p->Addr64[0] = d[1]; - p->Addr64[1] = d[2]; - p->Addr64[2] = d[3]; - p->Addr64[3] = d[4]; - p->Addr64[4] = d[5]; - p->Addr64[5] = d[6]; - p->Addr64[6] = d[7]; - p->Addr64[7] = d[8]; - - p->Addr16[0] = d[9]; - p->Addr16[1] = d[10]; - - p->atCmd[0] = d[11]; - p->atCmd[1] = d[12]; - - p->status = d[13]; - - p->samples = 1; - - if (p->status == 0x00 && p->atCmd[0] == 'I' && p->atCmd[1] == 'S') { - /* parse the io data */ - xbee_logI("--- Sample -----------------"); - xbee_parse_io(xbee, p, d, 15, 17, 0); - xbee_logI("----------------------------"); - } else { - /* copy in the data */ - p->datalen = i-13; - for (;i>13;i--) p->data[i-14] = d[i]; - } - - /* ########################################## */ - /* if: TX status */ - } else if (t == XBEE_TX_STATUS) { - if (xbee->log) { - xbee_logI("Packet type: TX Status Report (0x89)"); - xbee_logI("FrameID: 0x%02X",d[0]); - xbee_logIc("Status: "); - if (d[1] == 0x00) fprintf(xbee->log,"Success"); - else if (d[1] == 0x01) fprintf(xbee->log,"No ACK"); - else if (d[1] == 0x02) fprintf(xbee->log,"CCA Failure"); - else if (d[1] == 0x03) fprintf(xbee->log,"Purged"); - fprintf(xbee->log," (0x%02X)",d[1]); - xbee_logIcf(); - } - p->type = xbee_txStatus; - - p->sAddr64 = FALSE; - p->dataPkt = FALSE; - p->txStatusPkt = TRUE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - - p->frameID = d[0]; - - p->status = d[1]; - - /* never returns data */ - p->datalen = 0; - - /* check for any connections waiting for a status update */ - /* lock the connection mutex */ - xbee_mutex_lock(xbee->conmutex); - xbee_logI("Looking for a connection that wants a status update..."); - con = xbee->conlist; - while (con) { - if ((con->frameID == p->frameID) && - (con->ACKstatus == 0xFF)) { - xbee_logI("Found @ 0x%08X!",con); - con->ACKstatus = p->status; - xbee_sem_post(con->waitforACKsem); - } - con = con->next; - } - - /* unlock the connection mutex */ - xbee_mutex_unlock(xbee->conmutex); - - /* ########################################## */ - /* if: 16 / 64bit data recieve */ - } else if ((t == XBEE_64BIT_DATARX) || - (t == XBEE_16BIT_DATARX)) { - int offset; - if (t == XBEE_64BIT_DATARX) { /* 64bit */ - offset = 8; - } else { /* 16bit */ - offset = 2; - } - if (xbee->log) { - xbee_logI("Packet type: %d-bit RX Data (0x%02X)",((t == XBEE_64BIT_DATARX)?64:16),t); - xbee_logIc("%d-bit Address: ",((t == XBEE_64BIT_DATARX)?64:16)); - for (j=0;jlog,(j?":%02X":"%02X"),d[j]); - } - xbee_logIcf(); - xbee_logI("RSSI: -%ddB",d[offset]); - if (d[offset + 1] & 0x02) xbee_logI("Options: Address Broadcast"); - if (d[offset + 1] & 0x04) xbee_logI("Options: PAN Broadcast"); - } - p->isBroadcastADR = !!(d[offset+1] & 0x02); - p->isBroadcastPAN = !!(d[offset+1] & 0x04); - p->dataPkt = TRUE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - - if (t == XBEE_64BIT_DATARX) { /* 64bit */ - p->type = xbee_64bitData; - - p->sAddr64 = TRUE; - - p->Addr64[0] = d[0]; - p->Addr64[1] = d[1]; - p->Addr64[2] = d[2]; - p->Addr64[3] = d[3]; - p->Addr64[4] = d[4]; - p->Addr64[5] = d[5]; - p->Addr64[6] = d[6]; - p->Addr64[7] = d[7]; - } else { /* 16bit */ - p->type = xbee_16bitData; - - p->sAddr64 = FALSE; - - p->Addr16[0] = d[0]; - p->Addr16[1] = d[1]; - } - - /* save the RSSI / signal strength - this can be used with printf as: - printf("-%ddB\n",p->RSSI); */ - p->RSSI = d[offset]; - - p->status = d[offset + 1]; - - /* copy in the data */ - p->datalen = i-(offset + 1); - for (;i>offset + 1;i--) p->data[i-(offset + 2)] = d[i]; - - /* ########################################## */ - /* if: 16 / 64bit I/O recieve */ - } else if ((t == XBEE_64BIT_IO) || - (t == XBEE_16BIT_IO)) { - int offset,i2; - if (t == XBEE_64BIT_IO) { /* 64bit */ - p->type = xbee_64bitIO; - - p->sAddr64 = TRUE; - - p->Addr64[0] = d[0]; - p->Addr64[1] = d[1]; - p->Addr64[2] = d[2]; - p->Addr64[3] = d[3]; - p->Addr64[4] = d[4]; - p->Addr64[5] = d[5]; - p->Addr64[6] = d[6]; - p->Addr64[7] = d[7]; - - offset = 8; - p->samples = d[10]; - } else { /* 16bit */ - p->type = xbee_16bitIO; - - p->sAddr64 = FALSE; - - p->Addr16[0] = d[0]; - p->Addr16[1] = d[1]; - - offset = 2; - p->samples = d[4]; - } - if (p->samples > 1) { - p = Xrealloc(p, sizeof(xbee_pkt) + (sizeof(xbee_sample) * (p->samples - 1))); - } - if (xbee->log) { - xbee_logI("Packet type: %d-bit RX I/O Data (0x%02X)",((t == XBEE_64BIT_IO)?64:16),t); - xbee_logIc("%d-bit Address: ",((t == XBEE_64BIT_IO)?64:16)); - for (j = 0; j < offset; j++) { - fprintf(xbee->log,(j?":%02X":"%02X"),d[j]); - } - xbee_logIcf(); - xbee_logI("RSSI: -%ddB",d[offset]); - xbee_logI("Samples: %d",d[offset + 2]); - } - i2 = offset + 5; - - /* never returns data */ - p->datalen = 0; - - p->dataPkt = FALSE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = TRUE; - - /* save the RSSI / signal strength - this can be used with printf as: - printf("-%ddB\n",p->RSSI); */ - p->RSSI = d[offset]; - - p->status = d[offset + 1]; - - /* each sample is split into its own packet here, for simplicity */ - for (o = 0; o < p->samples; o++) { - if (i2 >= i) { - xbee_logI("Invalid I/O data! Actually contained %d samples...",o); - p = Xrealloc(p, sizeof(xbee_pkt) + (sizeof(xbee_sample) * ((o>1)?o:1))); - p->samples = o; - break; - } - xbee_logI("--- Sample %3d -------------", o); - - /* parse the io data */ - i2 = xbee_parse_io(xbee, p, d, offset + 3, i2, o); - } - xbee_logI("----------------------------"); - - /* ########################################## */ - /* if: Series 2 Transmit status */ - } else if (t == XBEE2_TX_STATUS) { - if (xbee->log) { - xbee_logI("Packet type: Series 2 Transmit Status (0x%02X)", t); - xbee_logI("FrameID: 0x%02X",d[0]); - xbee_logI("16-bit Delivery Address: %02X:%02X",d[1],d[2]); - xbee_logI("Transmit Retry Count: %02X",d[3]); - xbee_logIc("Delivery Status: "); - if (d[4] == 0x00) fprintf(xbee->log,"Success"); - else if (d[4] == 0x02) fprintf(xbee->log,"CCA Failure"); - else if (d[4] == 0x15) fprintf(xbee->log,"Invalid Destination"); - else if (d[4] == 0x21) fprintf(xbee->log,"Network ACK Failure"); - else if (d[4] == 0x22) fprintf(xbee->log,"Not Joined to Network"); - else if (d[4] == 0x23) fprintf(xbee->log,"Self-Addressed"); - else if (d[4] == 0x24) fprintf(xbee->log,"Address Not Found"); - else if (d[4] == 0x25) fprintf(xbee->log,"Route Not Found"); - else if (d[4] == 0x74) fprintf(xbee->log,"Data Payload Too Large"); /* ??? */ - fprintf(xbee->log," (0x%02X)",d[4]); - xbee_logIcf(); - - xbee_logIc("Discovery Status: "); - if (d[5] == 0x00) fprintf(xbee->log,"No Discovery Overhead"); - else if (d[5] == 0x01) fprintf(xbee->log,"Address Discovery"); - else if (d[5] == 0x02) fprintf(xbee->log,"Route Discovery"); - else if (d[5] == 0x03) fprintf(xbee->log,"Address & Route Discovery"); - fprintf(xbee->log," (0x%02X)",d[5]); - xbee_logIcf(); - } - - p->type = xbee2_txStatus; - - p->sAddr64 = FALSE; - p->dataPkt = FALSE; - p->txStatusPkt = TRUE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - - p->frameID = d[0]; - - p->status = d[4]; - - /* never returns data */ - p->datalen = 0; - - /* ########################################## */ - /* if: Series 2 data recieve */ - } else if (t == XBEE2_DATARX) { - int offset; - offset = 10; - if (xbee->log) { - xbee_logI("Packet type: Series 2 Data Rx (0x%02X)", t); - - xbee_logIc("64-bit Address: "); - for (j=0;j<8;j++) { - fprintf(xbee->log,(j?":%02X":"%02X"),d[j]); - } - xbee_logIcf(); - - xbee_logIc("16-bit Address: "); - for (j=0;j<2;j++) { - fprintf(xbee->log,(j?":%02X":"%02X"),d[j+8]); - } - xbee_logIcf(); - - if (d[offset] & 0x01) xbee_logI("Options: Packet Acknowledged"); - if (d[offset] & 0x02) xbee_logI("Options: Packet was a broadcast packet"); - if (d[offset] & 0x20) xbee_logI("Options: Packet Encrypted"); /* ??? */ - if (d[offset] & 0x40) xbee_logI("Options: Packet from end device"); /* ??? */ - } - p->dataPkt = TRUE; - p->txStatusPkt = FALSE; - p->modemStatusPkt = FALSE; - p->remoteATPkt = FALSE; - p->IOPkt = FALSE; - p->type = xbee2_data; - p->sAddr64 = TRUE; - - p->Addr64[0] = d[0]; - p->Addr64[1] = d[1]; - p->Addr64[2] = d[2]; - p->Addr64[3] = d[3]; - p->Addr64[4] = d[4]; - p->Addr64[5] = d[5]; - p->Addr64[6] = d[6]; - p->Addr64[7] = d[7]; - - p->Addr16[0] = d[8]; - p->Addr16[1] = d[9]; - - p->status = d[offset]; - - /* copy in the data */ - p->datalen = i - (offset + 1); - for (;i>offset;i--) { - p->data[i-(offset + 1)] = d[i]; - } - - /* ########################################## */ - /* if: Unknown */ - } else { - xbee_logE("Packet type: Unknown (0x%02X)",t); - continue; - } - p->next = NULL; - - /* lock the connection mutex */ - xbee_mutex_lock(xbee->conmutex); - - hasCon = 0; - if (p->isBroadcastADR || p->isBroadcastPAN) { - unsigned char t[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - /* if the packet was broadcast, search for a broadcast accepting connection */ - con = xbee->conlist; - while (con) { - if (con->type == p->type && - (con->type == xbee_16bitData || con->type == xbee_64bitData) && - ((con->tAddr64 && !memcmp(con->tAddr,t,8)) || - (!con->tAddr64 && !memcmp(con->tAddr,t,2)))) { - hasCon = 1; - xbee_logI("Found broadcasting connection @ 0x%08X",con); - break; - } - con = con->next; - } - } - if (!hasCon || !con) { - con = xbee->conlist; - while (con) { - if (xbee_matchpktcon(xbee, p, con)) { - hasCon = 1; - break; - } - con = con->next; - } - } - - /* unlock the connection mutex */ - xbee_mutex_unlock(xbee->conmutex); - - /* if the packet doesn't have a connection, don't add it! */ - if (!hasCon) { - xbee_logE("Connectionless packet... discarding!"); - continue; - } - - /* if the connection has a callback function then it is passed the packet - and the packet is not added to the list */ - if (con && con->callback) { - t_callback_list *l, *q; - - xbee_mutex_lock(con->callbackListmutex); - l = con->callbackList; - q = NULL; - while (l) { - q = l; - l = l->next; - } - l = Xcalloc(sizeof(t_callback_list)); - l->pkt = p; - if (!con->callbackList || q == NULL) { - con->callbackList = l; - } else { - q->next = l; - } - xbee_mutex_unlock(con->callbackListmutex); - - xbee_logI("Using callback function!"); - xbee_logI(" info block @ 0x%08X",l); - xbee_logI(" function @ 0x%08X",con->callback); - xbee_logI(" connection @ 0x%08X",con); - xbee_logE(" packet @ 0x%08X",p); - - /* if the callback thread not still running, then start a new one! */ - if (!xbee_mutex_trylock(con->callbackmutex)) { - xbee_thread_t t; - int ret; - t_threadList *p, *q; - t_CBinfo info; - info.xbee = xbee; - info.con = con; - xbee_log("Starting new callback thread!"); - if ((ret = xbee_thread_create(t,xbee_callbackWrapper,&info)) != 0) { - xbee_mutex_unlock(con->callbackmutex); - /* this MAY help with future attempts... */ - xbee_sem_post(xbee->threadsem); - xbee_logS("An error occured while starting thread (%d)... Out of resources?", ret); - xbee_logE("This packet has been lost!"); - continue; - } - xbee_log("Started thread 0x%08X!", t); - xbee_mutex_lock(xbee->threadmutex); - p = xbee->threadList; - q = NULL; - while (p) { - q = p; - p = p->next; - } - p = Xcalloc(sizeof(t_threadList)); - if (q == NULL) { - xbee->threadList = p; - } else { - q->next = p; - } - p->thread = t; - p->next = NULL; - xbee_mutex_unlock(xbee->threadmutex); - } else { - xbee_logE("Using existing callback thread... callback has been scheduled."); - } - /* prevent the packet from being free'd */ - p = NULL; - continue; - } - - /* lock the packet mutex, so we can safely add the packet to the list */ - xbee_mutex_lock(xbee->pktmutex); - - /* if: the list is empty */ - if (!xbee->pktlist) { - /* start the list! */ - xbee->pktlist = p; - } else if (xbee->pktlast) { - /* add the packet to the end */ - xbee->pktlast->next = p; - } else { - /* pktlast wasnt set... look for the end and then set it */ - i = 0; - q = xbee->pktlist; - while (q->next) { - q = q->next; - i++; - } - q->next = p; - xbee->pktcount = i; - } - xbee->pktlast = p; - xbee->pktcount++; - - /* unlock the packet mutex */ - xbee_mutex_unlock(xbee->pktmutex); - - xbee_logI("--========================--"); - xbee_logE("Packets: %d",xbee->pktcount); - - p = q = NULL; - } - return 0; -} - -static void xbee_callbackWrapper(t_CBinfo *info) { - xbee_hnd xbee; - xbee_con *con; - xbee_pkt *pkt; - t_callback_list *temp; - xbee = info->xbee; - con = info->con; - /* dont forget! the callback mutex is already locked... by the parent thread :) */ - xbee_mutex_lock(con->callbackListmutex); - while (con->callbackList) { - /* shift the list along 1 */ - temp = con->callbackList; - con->callbackList = temp->next; - xbee_mutex_unlock(con->callbackListmutex); - /* get the packet */ - pkt = temp->pkt; - - xbee_logS("Starting callback function..."); - xbee_logI(" info block @ 0x%08X",temp); - xbee_logI(" function @ 0x%08X",con->callback); - xbee_logI(" connection @ 0x%08X",con); - xbee_logE(" packet @ 0x%08X",pkt); - Xfree(temp); - if (con->callback) { - con->callback(con,pkt); - xbee_log("Callback complete!"); - if (!con->noFreeAfterCB) Xfree(pkt); - } else { - xbee_pkt *q; - int i; - xbee_log("Callback function was removed! Appending packet to main list..."); - /* lock the packet mutex, so we can safely add the packet to the list */ - xbee_mutex_lock(xbee->pktmutex); - - /* if: the list is empty */ - if (!xbee->pktlist) { - /* start the list! */ - xbee->pktlist = pkt; - } else if (xbee->pktlast) { - /* add the packet to the end */ - xbee->pktlast->next = pkt; - } else { - /* pktlast wasnt set... look for the end and then set it */ - i = 0; - q = xbee->pktlist; - while (q->next) { - q = q->next; - i++; - } - q->next = pkt; - xbee->pktcount = i; - } - xbee->pktlast = pkt; - xbee->pktcount++; - - /* unlock the packet mutex */ - xbee_mutex_unlock(xbee->pktmutex); - } - - xbee_mutex_lock(con->callbackListmutex); - } - xbee_mutex_unlock(con->callbackListmutex); - - xbee_log("Callback thread ending..."); - /* releasing the thread mutex is the last thing we do! */ - xbee_mutex_unlock(con->callbackmutex); - - if (con->destroySelf) { - _xbee_endcon2(xbee,&con,1); - } - xbee_sem_post(xbee->threadsem); -} - -/* ################################################################# - xbee_thread_watch - INTERNAL - watches for dead threads and tidies up */ -static void xbee_thread_watch(xbee_hnd xbee) { - -#ifdef _WIN32 /* ---- */ - /* win32 requires this delay... no idea why */ - usleep(1000000); -#endif /* ----------- */ - - xbee_mutex_init(xbee->threadmutex); - xbee_sem_init(xbee->threadsem); - - while (xbee->run) { - t_threadList *p, *q, *t; - xbee_mutex_lock(xbee->threadmutex); - p = xbee->threadList; - q = NULL; - - while (p) { - t = p; - p = p->next; - if (!(xbee_thread_tryjoin(t->thread))) { - xbee_log("Joined with thread 0x%08X...",t->thread); - if (t == xbee->threadList) { - xbee->threadList = t->next; - } else if (q) { - q->next = t->next; - } - free(t); - } else { - q = t; - } - } - - xbee_mutex_unlock(xbee->threadmutex); - xbee_sem_wait(xbee->threadsem); - usleep(100000); /* 100ms to allow the thread to end before we try to join */ - } - - xbee_mutex_destroy(xbee->threadmutex); - xbee_sem_destroy(xbee->threadsem); -} - - -/* ################################################################# - xbee_getbyte - INTERNAL - waits for an escaped byte of data */ -static unsigned char xbee_getbyte(xbee_hnd xbee) { - unsigned char c; - - /* take a byte */ - c = xbee_getrawbyte(xbee); - /* if its escaped, take another and un-escape */ - if (c == 0x7D) c = xbee_getrawbyte(xbee) ^ 0x20; - - return (c & 0xFF); -} - -/* ################################################################# - xbee_getrawbyte - INTERNAL - waits for a raw byte of data */ -static unsigned char xbee_getrawbyte(xbee_hnd xbee) { - int ret; - unsigned char c = 0x00; - - /* the loop is just incase there actually isnt a byte there to be read... */ - do { - /* wait for a read to be possible */ - if ((ret = xbee_select(xbee,NULL)) == -1) { - xbee_perror("libxbee:xbee_getrawbyte()"); - exit(1); - } - if (!xbee->run) break; - if (ret == 0) continue; - - /* read 1 character */ - if (xbee_read(xbee,&c,1) == 0) { - /* for some reason no characters were read... */ - if (xbee_ferror(xbee) || xbee_feof(xbee)) { - xbee_log("Error or EOF detected"); - fprintf(stderr,"libxbee:xbee_read(): Error or EOF detected\n"); - exit(1); /* this should have something nicer... */ - } - /* no error... try again */ - usleep(10); - continue; - } - } while (0); - - return (c & 0xFF); -} - -/* ################################################################# - _xbee_send_pkt - INTERNAL - sends a complete packet of data */ -static int _xbee_send_pkt(xbee_hnd xbee, t_data *pkt, xbee_con *con) { - int retval = 0; - - /* lock connection mutex */ - xbee_mutex_lock(con->Txmutex); - /* lock the send mutex */ - xbee_mutex_lock(xbee->sendmutex); - - /* write and flush the data */ - xbee_write(xbee,pkt->data,pkt->length); - - /* unlock the mutex */ - xbee_mutex_unlock(xbee->sendmutex); - - xbee_logSf(); - if (xbee->log) { - int i,x,y; - /* prints packet in hex byte-by-byte */ - xbee_logIc("TX Packet:"); - for (i=0,x=0,y=0;ilength;i++,x--) { - if (x == 0) { - fprintf(xbee->log,"\n 0x%04X | ",y); - x = 0x8; - y += x; - } - if (x == 4) { - fprintf(xbee->log," "); - } - fprintf(xbee->log,"0x%02X ",pkt->data[i]); - } - xbee_logIcf(); - } - xbee_logEf(); - - if (con->waitforACK && - ((con->type == xbee_16bitData) || - (con->type == xbee_64bitData))) { - con->ACKstatus = 0xFF; /* waiting */ - xbee_log("Waiting for ACK/NAK response..."); - xbee_sem_wait1sec(con->waitforACKsem); - switch (con->ACKstatus) { - case 0: xbee_log("ACK recieved!"); break; - case 1: xbee_log("NAK recieved..."); break; - case 2: xbee_log("CCA failure..."); break; - case 3: xbee_log("Purged..."); break; - case 255: default: xbee_log("Timeout..."); - } - if (con->ACKstatus) retval = 1; /* error */ - } - - /* unlock connection mutex */ - xbee_mutex_unlock(con->Txmutex); - - /* free the packet */ - Xfree(pkt); - - return retval; -} - -/* ################################################################# - xbee_make_pkt - INTERNAL - adds delimiter field - calculates length and checksum - escapes bytes */ -static t_data *xbee_make_pkt(xbee_hnd xbee, unsigned char *data, int length) { - t_data *pkt; - unsigned int l, i, o, t, x, m; - char d = 0; - - /* check the data given isnt too long - 100 bytes maximum payload + 12 bytes header information */ - if (length > 100 + 12) return NULL; - - /* calculate the length of the whole packet - start, length (MSB), length (LSB), DATA, checksum */ - l = 3 + length + 1; - - /* prepare memory */ - pkt = Xcalloc(sizeof(t_data)); - - /* put start byte on */ - pkt->data[0] = 0x7E; - - /* copy data into packet */ - for (t = 0, i = 0, o = 1, m = 1; i <= length; o++, m++) { - /* if: its time for the checksum */ - if (i == length) d = M8((0xFF - M8(t))); - /* if: its time for the high length byte */ - else if (m == 1) d = M8(length >> 8); - /* if: its time for the low length byte */ - else if (m == 2) d = M8(length); - /* if: its time for the normal data */ - else if (m > 2) d = data[i]; - - x = 0; - /* check for any escapes needed */ - if ((d == 0x11) || /* XON */ - (d == 0x13) || /* XOFF */ - (d == 0x7D) || /* Escape */ - (d == 0x7E)) { /* Frame Delimiter */ - l++; - pkt->data[o++] = 0x7D; - x = 1; - } - - /* move data in */ - pkt->data[o] = ((!x)?d:d^0x20); - if (m > 2) { - i++; - t += d; - } - } - - /* remember the length */ - pkt->length = l; - - return pkt; -} diff --git a/libs/thirdParty/libxbee/api.h b/libs/thirdParty/libxbee/api.h deleted file mode 100644 index 692bd7b16..000000000 --- a/libs/thirdParty/libxbee/api.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include - -#include - -#include -#include -#include -#include - -#ifdef __GNUC__ /* ---- */ -#include -#include -#define __USE_GNU -#include -#undef __USE_GNU -#include -#else /* -------------- */ -#include -#include -#include -#include -#endif /* ------------- */ - -#ifdef __UMAKEFILE - #define HOST_OS "Embedded" -#elif defined(__GNUC__) - #define HOST_OS "Linux" -#elif defined(_WIN32) - #define HOST_OS "Win32" -#else - #define HOST_OS "UNKNOWN" -#endif - -#define TRUE 1 -#define FALSE 0 - -#define M8(x) (x & 0xFF) - -/* various connection types */ -#define XBEE_LOCAL_AT 0x88 -#define XBEE_LOCAL_ATREQ 0x08 -#define XBEE_LOCAL_ATQUE 0x09 - -#define XBEE_REMOTE_AT 0x97 -#define XBEE_REMOTE_ATREQ 0x17 - -#define XBEE_MODEM_STATUS 0x8A - -/* XBee Series 1 stuff */ -#define XBEE_TX_STATUS 0x89 -#define XBEE_64BIT_DATATX 0x00 -#define XBEE_64BIT_DATARX 0x80 -#define XBEE_16BIT_DATATX 0x01 -#define XBEE_16BIT_DATARX 0x81 -#define XBEE_64BIT_IO 0x82 -#define XBEE_16BIT_IO 0x83 - -/* XBee Series 2 stuff */ -#define XBEE2_DATATX 0x10 -#define XBEE2_DATARX 0x90 -#define XBEE2_TX_STATUS 0x8B - -typedef struct xbee_hnd* xbee_hnd; - -#define __LIBXBEE_API_H -#include "xbee.h" - -typedef struct t_threadList t_threadList; -struct t_threadList { - xbee_thread_t thread; - t_threadList *next; -}; - -struct xbee_hnd { - xbee_file_t tty; -#ifdef __GNUC__ /* ---- */ - int ttyfd; -#else /* -------------- */ - int ttyr; - int ttyw; - int ttyeof; - - OVERLAPPED ttyovrw; - OVERLAPPED ttyovrr; - OVERLAPPED ttyovrs; -#endif /* ------------- */ - - char *path; /* serial port path */ - - xbee_mutex_t logmutex; - FILE *log; - int logfd; - - xbee_mutex_t conmutex; - xbee_con *conlist; - - xbee_mutex_t pktmutex; - xbee_pkt *pktlist; - xbee_pkt *pktlast; - int pktcount; - - xbee_mutex_t sendmutex; - - xbee_thread_t listent; - - xbee_thread_t threadt; - xbee_mutex_t threadmutex; - xbee_sem_t threadsem; - t_threadList *threadList; - - int run; - - int oldAPI; - char cmdSeq; - int cmdTime; - - /* ready flag. - needs to be set to -1 so that the listen thread can begin. */ - volatile int xbee_ready; - - xbee_hnd next; -}; -xbee_hnd default_xbee = NULL; -xbee_mutex_t xbee_hnd_mutex; - -typedef struct t_data t_data; -struct t_data { - unsigned char data[128]; - unsigned int length; -}; - -typedef struct t_LTinfo t_LTinfo; -struct t_LTinfo { - int i; - xbee_hnd xbee; -}; - -typedef struct t_CBinfo t_CBinfo; -struct t_CBinfo { - xbee_hnd xbee; - xbee_con *con; -}; - -typedef struct t_callback_list t_callback_list; -struct t_callback_list { - xbee_pkt *pkt; - t_callback_list *next; -}; - -static void *Xmalloc2(xbee_hnd xbee, size_t size); -static void *Xcalloc2(xbee_hnd xbee, size_t size); -static void *Xrealloc2(xbee_hnd xbee, void *ptr, size_t size); -static void Xfree2(void **ptr); -#define Xmalloc(x) Xmalloc2(xbee,(x)) -#define Xcalloc(x) Xcalloc2(xbee,(x)) -#define Xrealloc(x,y) Xrealloc2(xbee,(x),(y)) -#define Xfree(x) Xfree2((void **)&x) - -/* usage: - xbee_logSf() lock the log - xbee_logEf() unlock the log - - xbee_log() lock print with \n unlock # to print a single line - xbee_logc() lock print with no \n # to print a single line with a custom ending - xbee_logcf() print \n unlock # to end a custom-ended single line - - xbee_logS() lock print with \n # to start a continuous block - xbee_logI() print with \n # to continue a continuous block - xbee_logIc() print with no \n # to continue a continuous block with a custom ending - xbee_logIcf() print \n # to continue a continuous block with ended custom-ended line - xbee_logE() print with \n unlock # to end a continuous block -*/ -static void xbee_logf(xbee_hnd xbee, const char *logformat, const char *file, - const int line, const char *function, char *format, ...); -#define LOG_FORMAT "[%s:%d] %s(): %s" - -#define xbee_logSf() if (xbee->log) { xbee_mutex_lock(xbee->logmutex); } -#define xbee_logEf() if (xbee->log) { xbee_mutex_unlock(xbee->logmutex); } - -#define xbee_log(...) if (xbee->log) { xbee_logSf(); xbee_logf(xbee,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); xbee_logEf(); } -#define xbee_logc(...) if (xbee->log) { xbee_logSf(); xbee_logf(xbee,LOG_FORMAT ,__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); } -#define xbee_logcf() if (xbee->log) { fprintf(xbee->log, "\n"); xbee_logEf(); } - -#define xbee_logS(...) if (xbee->log) { xbee_logSf(); xbee_logf(xbee,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); } -#define xbee_logI(...) if (xbee->log) { xbee_logf(xbee,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); } -#define xbee_logIc(...) if (xbee->log) { xbee_logf(xbee,LOG_FORMAT ,__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); } -#define xbee_logIcf() if (xbee->log) { fprintf(xbee->log, "\n"); } -#define xbee_logE(...) if (xbee->log) { xbee_logf(xbee,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__); xbee_logEf(); } - -#define xbee_perror(str) \ - if (xbee->log) xbee_logI("%s:%s",str,strerror(errno)); \ - perror(str); - -static int xbee_startAPI(xbee_hnd xbee); - -static int xbee_sendAT(xbee_hnd xbee, char *command, char *retBuf, int retBuflen); -static int xbee_sendATdelay(xbee_hnd xbee, int guardTime, char *command, char *retBuf, int retBuflen); - -static int xbee_parse_io(xbee_hnd xbee, xbee_pkt *p, unsigned char *d, - int maskOffset, int sampleOffset, int sample); - -static void xbee_thread_watch(xbee_hnd xbee); -static void xbee_listen_wrapper(xbee_hnd xbee); -static int xbee_listen(xbee_hnd xbee); -static unsigned char xbee_getbyte(xbee_hnd xbee); -static unsigned char xbee_getrawbyte(xbee_hnd xbee); -static int xbee_matchpktcon(xbee_hnd xbee, xbee_pkt *pkt, xbee_con *con); - -static t_data *xbee_make_pkt(xbee_hnd xbee, unsigned char *data, int len); -static int _xbee_send_pkt(xbee_hnd xbee, t_data *pkt, xbee_con *con); -static void xbee_callbackWrapper(t_CBinfo *info); - -/* these functions can be found in the xsys files */ -static int init_serial(xbee_hnd xbee, int baudrate); -static int xbee_select(xbee_hnd xbee, struct timeval *timeout); - -#ifdef __GNUC__ /* ---- */ -#include "xsys/linux.c" -#else /* -------------- */ -#include "xsys\win32.c" -#endif /* ------------- */ - -#ifndef Win32Message -#define Win32Message() -#endif - -#define ISREADY(a) if (!xbee || !xbee->xbee_ready) { \ - if (stderr) fprintf(stderr,"libxbee: Run xbee_setup() first!...\n"); \ - Win32Message(); \ - a; \ - } -#define ISREADYP() ISREADY(return) -#define ISREADYR(a) ISREADY(return a) diff --git a/libs/thirdParty/libxbee/doc/man/man3/libxbee.3.html b/libs/thirdParty/libxbee/doc/man/man3/libxbee.3.html deleted file mode 100644 index 57b7f6a0c..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/libxbee.3.html +++ /dev/null @@ -1,127 +0,0 @@ -Manpage of LIBXBEE - -

LIBXBEE

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -libxbee -  -

DESCRIPTION

- -libxbee is a C library to aid the use of Series 1 Digi XBee radios running in API mode (AP=2). -

-I have tried to keep flexibility to a maximum. -By allowing connections to individual nodes to be created you don't have to address each packet, -or filter through incomming packets to get at the one you are after. This is all taken care of -for you by -libxbee! - -

-libxbee is still in development, so if you find any bugs or have any enhancement requests, please -feel free to submit an issue on the project page: - -

-http://code.google.com/p/libxbee/
-
- - -or contact me (Attie) directly: - -
-attie@attie.co.uk
-
- - -  -

MAN PAGES

- -Documentation is avaliable via the following man pages, or by example in the 'sample' folder in the SVN repository - -

-xbee_pkt(3) - libxbee's packet structure - -xbee_con(3) - libxbee's connection structure - -

-xbee_setup(3) - function to setup libxbee (and its variants) - -xbee_end(3) - function to end the libxbee session and close any open handles - -

-xbee_logit(3) - function that allows the user to add to the xbee log output - -

-xbee_newcon(3) - function to create a new connection - -xbee_flushcon(3) - function to flush packets from a connection - -xbee_endcon(3) - function to end a connection - -

-xbee_senddata(3) - function to send data to a remote XBee (and its variants) - -xbee_getpacket(3) - function to get a packet from a connection (and its variants) - -

-xbee_hasdigital(3) - function to check if digital sample is in the packet - -xbee_getdigital(3) - function to get digital sample from the packet - -

-xbee_hasanalog(3) - function to check if analog sample is in the packet - -xbee_getanalog(3) - function to get the analog sample from the packet - - - -  -

SEE ALSO

- -xbee_pkt(3), - -xbee_con(3), - -xbee_setup(3), - -xbee_end(3), - -xbee_logit(3), - -xbee_newcon(3), - -xbee_flushcon(3), - -xbee_endcon(3), - -xbee_senddata(3), - -xbee_getpacket(3), - -xbee_hasdigital(3), - -xbee_getdigital(3), - -xbee_hasanalog(3), - -xbee_getanalog(3) - -

- -


- 

Index

-
-
NAME
-
DESCRIPTION
-
MAN PAGES
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_con.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_con.3.html deleted file mode 100644 index 9990d7f2e..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_con.3.html +++ /dev/null @@ -1,26 +0,0 @@ -Manpage of LIBXBEE - -

LIBXBEE

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -libxbee -

-This page has not been written yet... -

- -


- 

Index

-
-
NAME
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_end.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_end.3.html deleted file mode 100644 index 7eaa6c270..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_end.3.html +++ /dev/null @@ -1,27 +0,0 @@ -Manpage of LIBXBEE - -

LIBXBEE

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -libxbee -

-This page has not been written yet... -

-

- -


- 

Index

-
-
NAME
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_endcon.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_endcon.3.html deleted file mode 100644 index d7250a3cc..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_endcon.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_endcon.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_flushcon.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_flushcon.3.html deleted file mode 100644 index 1d046d792..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_flushcon.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_flushcon.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_getanalog.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_getanalog.3.html deleted file mode 100644 index 1883c3262..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_getanalog.3.html +++ /dev/null @@ -1,140 +0,0 @@ -Manpage of XBEE_GETPACKET - -

XBEE_GETPACKET

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_hasanalog, xbee_getanalog -  -

SYNOPSIS

- -#include <xbee.h> - -

-int xbee_hasanalog(xbee_pkt *pkt,int sample, int input); - -

-double xbee_getanalog(xbee_pkt *pkt,int sample, int input, double Vref); - - -  -

DESCRIPTION

- -The -xbee_hasanalog() - -function will check the packet for the presence of an analog sample on the specified input. -

-The -xbee_getanalog() - -function will read the packet and return the sample value for the specified analog input. -

-They both take 3 arguments, with the same purposes. -

-The argument -pkt - -points to a packet that was previously retrieved with -xbee_getpacket() - -

-The argument -sample - -selects the sample within the packet to use. -

-The argument -input - -specifies which input you are interested in testing. -

-xbee_getanalog() - -also takes a fourth argument that allows you to provide a -Vref - -value. This allows the function to convert the raw ADC value into a voltage for you. -  -

RETURN VALUE

- -The -xbee_hasanalog() - -function will return -1 - -if the provided packet has sample data for the specified input, otherwise -0. - -

-The -xbee_getanalog() - -function will return the raw ADC value (0 - 1023) if the provided packet has sample data for the specified input and Vref was given as zero. -If Vref was non-zero, then the return value will be the voltage read. -A --1 - -will be returned if the packet does not contain sample data. -

-  -

EXAMPLE

- -To read sample data from previously made connection: - -
-#include <xbee.h>
-xbee_pkt *pkt;
-double Vref = 3.3;
-if ((pkt = xbee_getpacket(con)) != NULL) {
-  if (xbee_hasanalog(pkt,0,0)) {
-    printf("A0 read %fv,xbee_getanalog(pkt,0,0,Vref));
-  } else {
-    printf("No A0 data);
-  }
-  free(pkt);
-}
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_pkt(3), - -xbee_getpacket(3), - -xbee_hasdigital(3), - -xbee_getdigital(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_getdigital.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_getdigital.3.html deleted file mode 100644 index 2df913622..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_getdigital.3.html +++ /dev/null @@ -1,134 +0,0 @@ -Manpage of XBEE_GETPACKET - -

XBEE_GETPACKET

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_hasdigital, xbee_getdigital -  -

SYNOPSIS

- -#include <xbee.h> - -

-int xbee_hasdigital(xbee_pkt *pkt, int sample, int input); - -

-int xbee_getdigital(xbee_pkt *pkt, int sample, int input); - - -  -

DESCRIPTION

- -The -xbee_hasdigital() - -function will check the packet for the presence of a given sample on the specified input. -

-The -xbee_getdigital() - -function will read the packet and return the sample value for the specified input. -

-They both take 3 arguments, with the same purposes. -

-The argument -pkt - -points to a packet that was previously retrieved with -xbee_getpacket() - -

-The argument -sample - -selects the sample within the packet to use. -

-The argument -input - -specifies which input you are interested in testing. -  -

RETURN VALUE

- -The -xbee_hasdigital() - -function will return -1 - -if the provided packet has sample data for the specified input, otherwise -0. - -

-The -xbee_getdigital() - -function will return -1 - -if the provided packet has sample data for the specified input and the sample was HIGH. -A -0 - -will be returned if the sample was LOW, or the packet does not contain sample data. -

-  -

EXAMPLE

- -To read sample data from previously made connection: - -
-#include <xbee.h>
-xbee_pkt *pkt;
-if ((pkt = xbee_getpacket(con)) != NULL) {
-  if (xbee_hasdigital(pkt,0,0)) {
-    printf("D0 read %d,xbee_getdigital(pkt,0));
-  } else {
-    printf("No D0 data);
-  }
-  free(pkt);
-}
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_pkt(3), - -xbee_getpacket(3), - -xbee_hasanalog(3), - -xbee_getanalog(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_getpacket.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_getpacket.3.html deleted file mode 100644 index 45063f773..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_getpacket.3.html +++ /dev/null @@ -1,130 +0,0 @@ -Manpage of XBEE_GETPACKET - -

XBEE_GETPACKET

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_getpacket -  -

SYNOPSIS

- -#include <xbee.h> - -

-xbee_pkt *xbee_getpacket(xbee_con *con); - -

-xbee_pkt *xbee_getpacketwait(xbee_con *con); - - -  -

DESCRIPTION

- -The -xbee_getpacket() - -function will return the next avaliable packet for the provided connection. -It takes 1 argument. -

-The argument -con - -points to a connection made previously with -xbee_newcon(). - -

-The -xbee_getpacketwait() - -function behaves the same, but will wait for an internally specified time for a packet to arrive (currently around 1 second). -  -

RETURN VALUE

- -Upon successful return, this function returns the packet, having unlinked it from the internal list of packets. -You must keep hold of the packet until you are finished with it, and then you must -free() - -it to prevent memory leaks. -

-If a packet was not avaliable for the provided connection, a -NULL - -is returned. -

-If an error occured a -NULL - -is also returned (though unlikely). -

-For more information on the structure of the packet, please see -xbee_pkt(3) - -

-For information on using callback functions with connections instead, please see -xbee_con(3) - -  -

EXAMPLE

- -To recieve a packet from a previously made connection: - -
-#include <xbee.h>
-xbee_pkt *pkt;
-if ((pkt = xbee_getpacket(con)) != NULL) {
-  /* process packet... */
-  free(pkt);
-}
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_setup(3), - -xbee_newcon(3), - -xbee_senddata(3), - -xbee_pkt(3), - -xbee_con(3), - -xbee_hasDigital(3), - -xbee_getDigital(3), - -xbee_hasAnalog(3), - -xbee_getAnalog(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_hasanalog.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_hasanalog.3.html deleted file mode 100644 index e2c495c41..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_hasanalog.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_hasanalog.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_hasdigital.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_hasdigital.3.html deleted file mode 100644 index 9774f4081..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_hasdigital.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_hasdigital.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_logit.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_logit.3.html deleted file mode 100644 index 7eaa6c270..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_logit.3.html +++ /dev/null @@ -1,27 +0,0 @@ -Manpage of LIBXBEE - -

LIBXBEE

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -libxbee -

-This page has not been written yet... -

-

- -


- 

Index

-
-
NAME
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_newcon.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_newcon.3.html deleted file mode 100644 index 32f085b26..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_newcon.3.html +++ /dev/null @@ -1,201 +0,0 @@ -Manpage of XBEE_NEWCON - -

XBEE_NEWCON

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_newcon -  -

SYNOPSIS

- -#include <xbee.h> - -

-xbee_con *xbee_newcon(unsigned char frameID, xbee_types type, ...); - -

-void xbee_flushcon(xbee_con *con); - -

-void xbee_endcon(xbee_con *con); - - -  -

DESCRIPTION

- -The -xbee_newcon() - -function will setup a new connection with the specified settings. -It takes at least 2 arguments, and possibly up to 4 depending on the -type. - -

-NOTE: - -Packets will only be collected when they match an active connection. -You must setup a connection in order to recieve packets. -

-The argument -frameID - -allows similar functionality to that of TCP/IP port numbers. This is 1 character (or 8-bit integer) that -identifies where the data is coming from or going to. - -The -type - -specifies the type of connection you would like. The following types are avaliable: -

-
xbee_localAT - -
-communicates AT commands with the local XBee -
xbee_txStatus - -
-recieves transmit status information from the local XBee -
xbee_modemStatus - -
-recieves modem status information from the local XBee -
xbee_16bitRemoteAT - -
-communicates AT commands with a remote node (using 16-bit addressing) -
xbee_64bitRemoteAT - -
-communicates AT commands with a remote node (using 64-bit addressing) -
xbee_16bitData - -
-sends/recieves data through a remote node (using 16-bit addressing) -
xbee_64bitData - -
-sends/recieves data through a remote node (using 64-bit addressing) -
xbee_16bitIO - -
-sends/recieves I/O data through a remote node (using 16-bit addressing) -
xbee_64bitIO - -
-sends/recieves I/O data through a remote node (using 64-bit addressing) -
-

- -If you are using -xbee_localAT, xbee_txStatus or xbee_modemStatus - -then only the -frameID - -and -type - -arguments are required. -

-If you are using any 16-bit connection, you must also specify 1 right aligned integer, -containing the 16-bit address (e.g. 0x1234). -

-If you are using any 64-bit connection, you must also specify 2 integers containing the -64-bit address, first the high 32-bits, then the low 32-bits. -

-The -xbee_flushcon() - -function is very basic. It removes any packets that have been collected in the buffer for the specified connection. -

-The -xbee_endcon() - -function is used to end a connection. This will stop collecting packets for the given connection, and remove any packets from the buffer. -  -

RETURN VALUE

- -A pointer to the connection is returned. A connection can only be made once, using the same -type - -, -frameID - -and address (if needed). The second call using the same parameters will return the same -connection. -

-For information on using callback functions for packet handling please see -xbee_con(3) - -  -

EXAMPLE

- -To create a local AT connection: - -
-#include <xbee.h>
-xbee_con *con;
-con = xbee_newcon('A', xbee_localAT);
-
- - -

-To create a 16-bit Data connection: - -

-#include <xbee.h>
-xbee_con *con;
-con = xbee_newcon('A', xbee_16bitData, 0x1234);
-
- - -

-To create a 64-bit Data connection: - -

-#include <xbee.h>
-xbee_con *con;
-con = xbee_newcon('A', xbee_64bitData, 0x0013A200, 0x40081826);
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_setup(3), - -xbee_getpacket(3), - -xbee_con(3), - -xbee_senddata(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_nsenddata.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_nsenddata.3.html deleted file mode 100644 index 3da3c6f21..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_nsenddata.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_nsenddata.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_pkt.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_pkt.3.html deleted file mode 100644 index 1859405aa..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_pkt.3.html +++ /dev/null @@ -1,107 +0,0 @@ -Manpage of XBEE_PKT - -

XBEE_PKT

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_pkt -  -

SYNOPSIS

- -#include <xbee.h> - - -  -

DESCRIPTION

- -This is the packet structure. If you want to get more advanced information from connections (such as RSSI) then this is where it lives. -

- -

-struct xbee_pkt {
-  unsigned char frameID;          /* AT        Status    */
-  unsigned char atCmd[2];         /* AT                  */
-  unsigned char status;           /* AT  Data  Status    */ /* status / options */
-  unsigned char Addr64[8];        /* AT  Data            */
-  unsigned char Addr16[2];        /* AT  Data            */
-  unsigned char data[128];        /* AT  Data            */
-  unsigned char RSSI;             /*     Data            */
-  unsigned int datalen;
-
-  /* X  A5 A4 A3 A2 A1 A0 D8    D7 D6 D5 D4 D3 D2 D1 D0 */
-  unsigned short IOmask;          /*                  IO */
-
-  /* X  X  X  X  X  X  X  D8    D7 D6 D5 D4 D3 D2 D1 D0 */
-  unsigned short IOdata;          /*                  IO */
-
-  /* X  X  X  X  X  D  D  D     D  D  D  D  D  D  D  D  */
-  unsigned short IOanalog[6];     /*                  IO */
-};
-typedef struct xbee_pkt xbee_pkt;
-
- - -

-Most of these fields are fairly self explanatory, however some need attention brought to them -and others need explaining. I will touch on the most important here: -

-
atCmd - -
-This is the 2 character identifier for the AT command response you just recieved. -Of course if you didnt setup an AT connection, you should never see, or try to see data here. -
Addr64 and Addr16 - -
-These contain the address of the XBee that you recieved the packet from. You should really know this -because you setup the connection. However remote AT packets will contain both 16 and 64 bit -addresses. -
data - -
-This is the data you just recieved. Either the AT reponse, or the data from the remote XBee node. -
datalen - -
-Would you be suprised if I told you this is how much data there is?... Dont try and -printf() - -the -data - -as it isn't null terminated. Use this for processing instead. - - -
-  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_getpacket(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_senddata.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_senddata.3.html deleted file mode 100644 index 31baf1c73..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_senddata.3.html +++ /dev/null @@ -1,129 +0,0 @@ -Manpage of XBEE_SENDDATA - -

XBEE_SENDDATA

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_senddata, xbee_vsenddata -  -

SYNOPSIS

- -#include <xbee.h> - -

-int xbee_senddata(xbee_con *con, char *format, ...); - -

-int xbee_nsenddata(xbee_con *con, char *data, int length); - -

-#include <stdarg.h> - -

-int xbee_vsenddata(xbee_con *con, char *format, va_list ap); - - -  -

DESCRIPTION

- -The -xbee_senddata() - -function will send data via a provided connection. -It takes at least 2 arguments, and possibly more depending on the format string. -

-The argument -con - -points to a connection made previously with -xbee_newcon(). - -

-The -format - -string and any following parameters are passed to -sprintf() - -within these functions. -Please see the -printf(3) - -man page for more information. -

-If you are using -xbee_nsenddata() - -you must provide a character array of the data, and the data's length. -

-If you are using -xbee_vsenddata() - -you must provide a va_list. See -stdarg(3). - -  -

RETURN VALUE

- -Upon successful completion, these functions return 0. -

-If an invalid packet or connection was provided, -1 is returned. -

-If an unknown error occured, -2 is returned. -

-If -con - -has -waitforACK - -enabled, then these functions return 1 when an ACK was not recieved within 1 second. -  -

EXAMPLE

- -To send the string "Hello World!" through a previously made connection: - -
-#include <xbee.h>
-xbee_senddata(con,"Hello World!");
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_setup(3), - -xbee_newcon(3), - -xbee_getpacket(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_setup.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_setup.3.html deleted file mode 100644 index 5b69945a5..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_setup.3.html +++ /dev/null @@ -1,142 +0,0 @@ -Manpage of XBEE_SETUP - -

XBEE_SETUP

-Section: Linux Programmer's Manual (3)
Updated: 2009-11-01
Index -Return to Main Contents
- -  -

NAME

- -xbee_setup -  -

SYNOPSIS

- -#include <xbee.h> - -

-int xbee_setup(char *path, int baudrate); - -

-int xbee_setuplog(char *path, int baudrate, int logfd); - -

-int xbee_setupAPI(char *path, char cmdSeq, int cmdTime); - -

-int xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime); - - -  -

DESCRIPTION

- -

-A VERSION OF THIS FUNCTION MUST BE CALLED BEFORE ANY OTHER libxbee FUNCTION! - -The -xbee_setup() - -function will setup libxbee so that it can handle an XBee. -It takes 2 arguments. -

-The argument -path - -is the path to the serial port that the XBee is connected to (e.g. /dev/ttyUSB0). -

-The -baudrate - -is the baud rate that the local XBee is configured to run at. The following are avaliable: - -

-1200
-2400
-4800
-9600
-19200
-38400
-57600
-115200 - this is potentially unstable (read the XBee manual to find out why...)
-
- - -

-Using -xbee_setuplog() - -is exactly the same, but instead you give an open file descriptor. All log messages will be written to this file (you can use stderr or stdout if you want!). -

-Using -xbee_setupAPI() - -is exactly the same, but instead you provide the 'Command Sequence' character and the 'Guard Time' that your local XBee has been configured with. -libxbee will then place your XBee in API mode 2, and when you call xbee_end() it will return your XBee to its previous API mode. -

-Using -xbee_setuplogAPI() - -is simply a combination of -xbee_setuplog() - -and -xbee_setupAPI() - -  -

RETURN VALUE

- -If any error occures, --1 - -is returned. Otherwise -0 - -is returned. -  -

EXAMPLE

- -To setup libxbee to use /dev/ttyUSB0 at 57600 baud: - -
-#include <xbee.h>
-if (xbee_setup("/dev/ttyUSB0",57600) == -1) {
-  printf("Oh no...);
-  exit(1);
-}
-
- - -  -

AUTHOR

- -Attie Grande <attie@attie.co.uk> -  -

SEE ALSO

- -libxbee(3), - -xbee_newcon(3), - -xbee_getpacket(3), - -xbee_senddata(3) - -

- -


- 

Index

-
-
NAME
-
SYNOPSIS
-
DESCRIPTION
-
RETURN VALUE
-
EXAMPLE
-
AUTHOR
-
SEE ALSO
-
-
-This document was created by -man2html, -using the manual pages.
-Time: 00:08:23 GMT, March 30, 2011 - - diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_setupAPI.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_setupAPI.3.html deleted file mode 100644 index 158f1857c..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_setupAPI.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_setupAPI.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplog.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplog.3.html deleted file mode 100644 index 16accc571..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplog.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_setuplog.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplogAPI.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplogAPI.3.html deleted file mode 100644 index 801cbfaf9..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_setuplogAPI.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_setuplogAPI.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/doc/man/man3/xbee_vsenddata.3.html b/libs/thirdParty/libxbee/doc/man/man3/xbee_vsenddata.3.html deleted file mode 100644 index 5a9b7d948..000000000 --- a/libs/thirdParty/libxbee/doc/man/man3/xbee_vsenddata.3.html +++ /dev/null @@ -1,4 +0,0 @@ -Invalid Manpage - -

Invalid Manpage

-The requested file ./man/man3/xbee_vsenddata.3 is not a valid (unformatted) man page. diff --git a/libs/thirdParty/libxbee/lib/libxbee.dll b/libs/thirdParty/libxbee/lib/libxbee.dll deleted file mode 100644 index cf1af098e6c3a5fb130ab8d248fe873fe594db94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151552 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 diff --git a/libs/thirdParty/libxbee/lib/libxbee.exp b/libs/thirdParty/libxbee/lib/libxbee.exp deleted file mode 100644 index 1ab830676fc609ead5c1aaab15a204bc98345123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7707 zcmeI1e{38_6~~`rr!iu$?tHe-At{&GgoHSb8z&)@Yn}7$+P>y| zcilVx2r5#cLFym*qeu}15kUn7BDH@+sE7iUh(uJiQY9)8sg0d8 z&CK5JZ1|HukXXgADH0 zT&Cf2d8kl1St(Zw&S-Uk%SQ{P3jZE=r%UyQ>y5kKyz4PJS@FVbg8P|pD-QXpSgoXW zY1(bn3dOT-!!9orN`{rI(_uZOD_7hFbY%*4oI_qcb)MRsH|;jvmbdOU=4!g7=>21d zgGHRvDmE+3`+e~}%k32!jY4r|s8B9X;%*e1oGX=`iBe^%s>+TVt>cvHg~_tJf9QzZ zuO6!ZfK2yw)t_78dc$tL;Z@I#yX7f^9bd>mOQ%zw>ne;9FH{PAf8s#VDPepIWxuQ+ zh;gf8^ZI5A^wi4cA!u#6=+bSv!+a{_T7U2Tk9@u@N+OUvw=!oG>C3;xYGBAV-{3T&O zE@~y%GEo`WoTzrNJ4Iz-_lxQT<1iDWy|4#Ftp{5nDhE3xstb&-Hb%p+{1>A**j=Iy z!G1ziH`ppsBd`ZW^?=J01=QG3BUMLDp1?il63)`*&cJt}G#Y^|s=EQiM!9Rgb?ss?*Z)CkymQ5?znx??m3 z#_={ri?EM~Isvv()Kjp>MV$uQBd=U_iAstgtvbrF`wD@HZ2t)ec$enwOS>^@PKVILK>2)0es^RS;4^%PjQ zs25;)tYh>v*!`lez&sY`dseU<;xy zg6$CXD(s}FOJEO)x&~Vmbs21@sOzwfsOQ0UiFzHDQ=%BX0G1H-ChU}`D`2}ty#+fh z>LswGs2i{|qOO9aM7;xB67@1zTGYF+Pl$R2tXI@c*t4Qu1 zbsel<)CaH?QLlq>ZWg0I!B$1R36>T0SJ;}Ux4;HPeF*!cs2gB=MEwhvv&k5}1NJdd z1XUOHF4SI8ZLojF-#4&~tf!U^NJ``p;rWjjsD!&%!}#y*;5DU{-E#0MQJrt0qotx( ztyiZS@t)(`<0FShkI=ViUk4Qz94hX~pR8l8%X@Q9sd6?yTrJMcx|K#fzu%j5OO5>4 z%y53(#Y$bT7QFnE(?$K~Rp%-W;uC^bIp3HmdCpkDYn(HeMDC(XLwoXLUUk|l%*GFv z%5FX1ypmI;`dpzLA8+8a)p#nINajxsX%FA z1zxu>5fx`xi&nhMYmaFp7sN}w&k-$Ao$s*S!U&Lj4tDVz47R|1@VIor#cRLi;)dxY zR^4m|M*;2iqbhg03cgPEA@FjUpBG00^*qAq86FK^0|y1=Zf0uwNpoyau5i+ZmfZ3B zraP3s!OeHT5yQ-bKq?3C7I-;!1idCnsbkdQ@N*mq`U43bdfO9q^rVXi?Bl2F>`!gD#!d@sr`Xyfx@kS)smTjyHaX&3sXxjw6rq z`l%y#$&a%;=n9;UeH@5Xvp`CTE_tqVK+-XqY1lnPV*#_?!!NDI`LgqBai^O-}N?bGiZcgj&N;MVUdFP9wa!k)mZ z%0W*%n+Y+dE6+d7jTZ# z|FyuAU#y?2=NC$q-ZZ|@Owk{I^3(Y4;gON}?qerU>@fk;!L~S8tj^XFC8s+7b-Hzn z184Bl58ps5)ju~{m_;ZWMX*uCo373C_4BkeMAd3T(U65>9x|SrKItA!;z-e>em{z& z=?RPUl4p?&owrCIU9w0&eaj*Pbk!nRx@M6ey|f%zhq`yU zuN2lPi}X>CMfxdYkpUXANS2OUWRRRNL11ZEBt?rBNz(<3^wMREWaz3z`slhv`sr

^4kc?5@=Sz$ zH9}sDkRL_JYZ3B`2zft3Zbir%tcB)KPG0rRp~zr_j7G>~5pp&{J|7`pj*u%6^5Y2k zd4#+dB7Z~aHe1l(*7LSoPKLMHYUuCgJ3ahW{`olbrw46=R;Fc}O!&^1u;Vqi4_0~+4^_EN(LR&E(1@=Tf^S53W zk>_fzG_=)H6`Nn`WuWDT!dojM<}o)AVnh Cp~Q>; diff --git a/libs/thirdParty/libxbee/lib/libxbee.lib b/libs/thirdParty/libxbee/lib/libxbee.lib deleted file mode 100644 index 7b42cadbaede1530f4c126e5c81306195d19784e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12566 zcmcgx&u<&Y6@HSPI+fK#k?q9RPfDVGOO!;BlpPcWWvhT4%Zed8xiXZvvY0TWAW}+u z>Mii0$SJ3whg_ON4mlJ#q(BZiWlni}HBxL#xY{|xnzm9LegK0rMrbL_H}PE17?dNXQ8KEM)$3C8KHZr%R?e2`vfT zs@3k5tM%eqR%?V2!B{#t_Pk<_N=`x9lu*-aog5Xf`wFI{G*l}Kx-EiMYK=#-KUPa4 zA#0|hovy_<@86Ll4mtnrXJgCTI|O*+zvn#`5T3c32VJl;P^&guUcJ<8HI9sH1Te-`_ukuY)ctwV1eN z61_x1C~HxvHlvz^Vb(_3+wz*NaQ4+bi$C(=_v@EIK%ARc^J) z`-i&LkcDB^rs74m3B#<-@k!l~h#(}VI|wp;cpuGGG%zJvKqjw z~|ri3nP{4qiU;Mvkx+WF>RO$X$LT--8`*}mwB@a7uJzH zfHCd8lWMI}s@4x0M%jg!F6M#7GgDF;%D)t7=GGSNoJ#D#UkkbrvH=BV@qjwdb8{xo z-9V=HnoDeTdBMn`xqtmVu2Ey}5#nFGX}7+23a zF_3*vFjXu#Ka2_Qj;e6u2lHO>YT_-ToZd}tRO6^%t!lzA8ing1?(*3*n{``)ZW60) zAa6$(Wn|*rmMu`>W{s**RP@(73uQyb$k=3|ZQO}X7ZT)1EE8jWiO+T-vn!{y%#&S2 z=OGN$yCep+eJiw&>DKu~@-3gDe&;Bsbl-RJFRJ!0n)a!>bXk!RF>=?9C8z7f3}Oj4 zv*4%Qkx8^)XH$FLPWw6YQAuXjh1=7Z>`_~CMiy8w(saanv3SDm7aM|JOu-Ro++Adf z$$haiV{G+_UAxrQp-mTTP?L!DDD$#5kO^c`Q$2d4WuCgMG!I;r^lZv~wd#j?B*ubP z(ml1aEseMnZ6Oe=$#NhwPZDJl!HzU!RXFrW*bO=o3Wpm7NAJ;8pkN_JDn|qth6lE0 zevPy7)}ch!=^!EtcuWy_W>kXMF74LALYi~)Sq+8E1ww<=Q=Xysx7smemG%o(U?G5` zcJXu6E_;P)pOGFQA>&t?Ke))HmTqUZUZFE7{R;HeQEwM>0Mou^VeOL+;;OleRw^7sq=B26s@SWhv%2asZvFxJj}M%~wMeU9`Fc3LRKI>)euij0ffgzCRWGiC$Q;G* zA{*2!T^hImOxP2@K1kzq@LqNQxY2AJv{G}s^QrASJ9lMm;7^c=hQa`nc6m2||L5c# zW1-@H3v`WgLGbfSg$iO%(s9&(XLcK1*ta&V%>IgR9m4gfMbd5Z`zG?|9ZWu7}pwzd)cKa`ddltgpPDDB+4?eeoY#Upb8ny?^S0 ze?8OLO**4(zbJG%qq&?vp(*h#N-WOp9uc*$O~-Dw-`V{!+r;h@`FqZM3v+pJ6oAkH z9-c8V_ZvFFBn~$d7J{@ zg$O=BkL-#ww-G%?9Fxd%4Sq`0?f7zqXf7Q4;QjTy_7gwR;lpD0iJn{>Q=et>vnXbr zKfpvxdH)nF74XOu#&pp>Gv}zGD?a^d3Gj&(&~N!n_U5`IV@@=>oDRtWHXM%Oq{=)o zlLa#S9nWN(C%#}urgt-(s`EG!v$9_daQO-BGvgZw@cB7!cYJLk|0JH9f%z1lHl`C$JjVJ=E%py}`oRZxq^3Z&nw_O?@dM&`HKgf!2 z#f*pFC*y1rukmpM-z2ohMxDKO!y1=o4l`D90Mivuo6(84(Fo`94|lraYO^jxPCIJ2 z{DUJ-bfznQ%rH5pQr+>zWGv&HkDXC*LUy0bC)-47k+GyWpE*ur4BTImvBj+Oi7=ag zRM!<_OfE5L7@cE4PS?4=#5}(#&UaSNFNQB|_}ueLK0tP#=n2Nte-OBfS#CD|PXBNjaYnURoBONp4WBh;4%${PhC&t~(VjSaqkkOn*KP$$CAfq|y z?us#HjW1e8y$=$n=$x+Orn>-+wwaAK*$89SK`z90CgY2FhIz~AeB)xH>iikT#Ib_mW1I9q G9Q!Xg(NR - 0000:00000003 ___safe_se_handler_count 00000003 - 0000:00000000 ___ImageBase 10000000 - 0001:00000000 _ver@16 10001000 f api.obj - 0001:000000b0 _xbee_UNLOADALL@0 100010b0 f api.obj - 0001:000000e0 _DllMain@12 100010e0 f api.obj - 0001:00000120 _DllCanUnloadNow@0 10001120 f api.obj - 0001:00000140 _RegWriteKey@24 10001140 f api.obj - 0001:00000210 _DllRegisterServer@0 10001210 f api.obj - 0001:000004b0 _DllUnregisterServer@0 100014b0 f api.obj - 0001:00000670 _xbee_write@12 10001670 f api.obj - 0001:00000720 _xbee_read@12 10001720 f api.obj - 0001:000007d0 _xbee_free@4 100017d0 f api.obj - 0001:00000800 _gettimeofday@8 10001800 f api.obj - 0001:000008a0 _xbee_setupDebugAPI@20 100018a0 f api.obj - 0001:000009b0 _xbee_setupDebug@12 100019b0 f api.obj - 0001:000009e0 _xbee_newcon_simple@8 100019e0 f api.obj - 0001:00000a00 _xbee_newcon_16bit@12 10001a00 f api.obj - 0001:00000a30 _xbee_newcon_64bit@16 10001a30 f api.obj - 0001:00000a60 _xbee_enableACKwait@4 10001a60 f api.obj - 0001:00000a80 _xbee_disableACKwait@4 10001a80 f api.obj - 0001:00000aa0 _xbee_enableDestroySelf@4 10001aa0 f api.obj - 0001:00000ac0 _xbee_callback@8 10001ac0 f api.obj - 0001:00000c70 _xbee_runCallback@12 10001c70 f api.obj - 0001:00000ca0 _xbee_enableCallbacks@8 10001ca0 f api.obj - 0001:00000df0 _xbee_attachCallback@4 10001df0 f api.obj - 0001:00000ea0 _xbee_detachCallback@4 10001ea0 f api.obj - 0001:00000f50 _xbee_svn_version@0 10001f50 f api.obj - 0001:00000fc0 _xbee_build_info@0 10001fc0 f api.obj - 0001:00000fd0 _xbee_hasdigital@12 10001fd0 f api.obj - 0001:00001030 _xbee_getdigital@12 10002030 f api.obj - 0001:00001090 _xbee_hasanalog@12 10002090 f api.obj - 0001:000010f0 _xbee_getanalog@20 100020f0 f api.obj - 0001:00001270 _xbee_logitf 10002270 f api.obj - 0001:00001320 __xbee_logitf 10002320 f api.obj - 0001:000013d0 _xbee_logit@4 100023d0 f api.obj - 0001:000013f0 __xbee_logit@8 100023f0 f api.obj - 0001:00001470 _xbee_end@0 10002470 f api.obj - 0001:00001490 __xbee_end@4 10002490 f api.obj - 0001:00001a30 _xbee_setup@8 10002a30 f api.obj - 0001:00001a60 __xbee_setup@8 10002a60 f api.obj - 0001:00001a90 _xbee_setuplog@12 10002a90 f api.obj - 0001:00001ac0 __xbee_setuplog@12 10002ac0 f api.obj - 0001:00001af0 _xbee_setupAPI@16 10002af0 f api.obj - 0001:00001b20 __xbee_setupAPI@16 10002b20 f api.obj - 0001:00001b50 _xbee_setuplogAPI@20 10002b50 f api.obj - 0001:00001ba0 __xbee_setuplogAPI@20 10002ba0 f api.obj - 0001:00003820 _xbee_newcon 10004820 f api.obj - 0001:00003870 __xbee_newcon 10004870 f api.obj - 0001:000038c0 __xbee_vnewcon@16 100048c0 f api.obj - 0001:000043f0 _xbee_purgecon@4 100053f0 f api.obj - 0001:00004410 __xbee_purgecon@8 10005410 f api.obj - 0001:00004590 _xbee_endcon2@8 10005590 f api.obj - 0001:000045c0 __xbee_endcon2@12 100055c0 f api.obj - 0001:00004880 _xbee_senddata 10005880 f api.obj - 0001:000048d0 __xbee_senddata 100058d0 f api.obj - 0001:00004920 _xbee_vsenddata@12 10005920 f api.obj - 0001:00004950 __xbee_vsenddata@16 10005950 f api.obj - 0001:00004a00 _xbee_nsenddata@12 10005a00 f api.obj - 0001:00004a30 __xbee_nsenddata@16 10005a30 f api.obj - 0001:000053a0 _xbee_getpacketwait@4 100063a0 f api.obj - 0001:000053c0 __xbee_getpacketwait@8 100063c0 f api.obj - 0001:00005440 _xbee_getpacket@4 10006440 f api.obj - 0001:00005460 __xbee_getpacket@8 10006460 f api.obj - 0001:00005ab0 _xbee_listen_stop@4 10006ab0 f api.obj - 0001:0000a47f _sprintf 1000b47f f LIBCMT:sprintf.obj - 0001:0000a503 @__security_check_cookie@4 1000b503 f LIBCMT:secchk.obj - 0001:0000a512 __RTC_CheckEsp 1000b512 f LIBCMT:_stack_.obj - 0001:0000a535 @_RTC_CheckStackVars@8 1000b535 f LIBCMT:_stack_.obj - 0001:0000a591 _free 1000b591 f LIBCMT:free.obj - 0001:0000a5cb __ftime64_s 1000b5cb f LIBCMT:ftime64.obj - 0001:0000a73b __ftime64 1000b73b f LIBCMT:ftime64.obj - 0001:0000a746 __get_errno_from_oserr 1000b746 f LIBCMT:dosmap.obj - 0001:0000a788 __errno 1000b788 f LIBCMT:dosmap.obj - 0001:0000a79b ___doserrno 1000b79b f LIBCMT:dosmap.obj - 0001:0000a7ae __dosmaperr 1000b7ae f LIBCMT:dosmap.obj - 0001:0000af05 __open 1000bf05 f LIBCMT:open.obj - 0001:0000afc0 _strrchr 1000bfc0 f LIBCMT:strrchr.obj - 0001:0000aff0 __cfltcvt_init 1000bff0 f LIBCMT:_fpinit_.obj - 0001:0000b050 __fpmath 1000c050 f LIBCMT:_fpinit_.obj - 0001:0000b069 _fprintf 1000c069 f LIBCMT:fprintf.obj - 0001:0000b175 __flush 1000c175 f LIBCMT:fflush.obj - 0001:0000b1dd __fflush_nolock 1000c1dd f LIBCMT:fflush.obj - 0001:0000b2ff _fflush 1000c2ff f LIBCMT:fflush.obj - 0001:0000b352 __flushall 1000c352 f LIBCMT:fflush.obj - 0001:0000b35b __fclose_nolock 1000c35b f LIBCMT:fclose.obj - 0001:0000b3c8 _fclose 1000c3c8 f LIBCMT:fclose.obj - 0001:0000b43c ___iob_func 1000c43c f LIBCMT:_file.obj - 0001:0000b442 ___initstdio 1000c442 f LIBCMT:_file.obj - 0001:0000b4f3 ___endstdio 1000c4f3 f LIBCMT:_file.obj - 0001:0000b513 __lock_file 1000c513 f LIBCMT:_file.obj - 0001:0000b554 __lock_file2 1000c554 f LIBCMT:_file.obj - 0001:0000b586 __unlock_file 1000c586 f LIBCMT:_file.obj - 0001:0000b5c2 __unlock_file2 1000c5c2 f LIBCMT:_file.obj - 0001:0000b600 _strcpy 1000c600 f LIBCMT:strcat.obj - 0001:0000b610 _strcat 1000c610 f LIBCMT:strcat.obj - 0001:0000b700 _strlen 1000c700 f LIBCMT:strlen.obj - 0001:0000b78b _setvbuf 1000c78b f LIBCMT:setvbuf.obj - 0001:0000b881 __get_sys_err_msg 1000c881 f i LIBCMT:perror.obj - 0001:0000b8a9 _perror 1000c8a9 f LIBCMT:perror.obj - 0001:0000b937 _strerror 1000c937 f LIBCMT:strerror.obj - 0001:0000b9a1 ___crtCorExitProcess 1000c9a1 f LIBCMT:crt0dat.obj - 0001:0000b9cc ___crtExitProcess 1000c9cc f LIBCMT:crt0dat.obj - 0001:0000b9e4 __lockexit 1000c9e4 f LIBCMT:crt0dat.obj - 0001:0000b9ed __unlockexit 1000c9ed f LIBCMT:crt0dat.obj - 0001:0000b9f6 __init_pointers 1000c9f6 f LIBCMT:crt0dat.obj - 0001:0000ba29 __initterm_e 1000ca29 f LIBCMT:crt0dat.obj - 0001:0000ba4d __cinit 1000ca4d f LIBCMT:crt0dat.obj - 0001:0000bc24 _exit 1000cc24 f LIBCMT:crt0dat.obj - 0001:0000bc3a __exit 1000cc3a f LIBCMT:crt0dat.obj - 0001:0000bc50 __cexit 1000cc50 f LIBCMT:crt0dat.obj - 0001:0000bc5f __amsg_exit 1000cc5f f LIBCMT:crt0dat.obj - 0001:0000bc7d _malloc 1000cc7d f LIBCMT:malloc.obj - 0001:0000bd11 _calloc 1000cd11 f LIBCMT:calloc.obj - 0001:0000bd51 _atol 1000cd51 f LIBCMT:atox.obj - 0001:0000bd67 _atoi 1000cd67 f LIBCMT:atox.obj - 0001:0000bd72 _strncmp 1000cd72 f LIBCMT:strncmp.obj - 0001:0000be40 _memset 1000ce40 f LIBCMT:memset.obj - 0001:0000bec0 _memcpy 1000cec0 f LIBCMT:memcpy.obj - 0001:0000c221 _memcmp 1000d221 f LIBCMT:memcmp.obj - 0001:0000d775 _realloc 1000e775 f LIBCMT:realloc.obj - 0001:0000d822 __CRT_INIT@12 1000e822 f LIBCMT:dllcrt0.obj - 0001:0000da7c __DllMainCRTStartup@12 1000ea7c f LIBCMT:dllcrt0.obj - 0001:0000da9f __flsbuf 1000ea9f f LIBCMT:_flsbuf.obj - 0001:0000dc03 ??0_LocaleUpdate@@QAE@PAUlocaleinfo_struct@@@Z 1000ec03 f i LIBCMT:output.obj - 0001:0000dd0a __output_l 1000ed0a f LIBCMT:output.obj - 0001:0000e8b6 __initp_misc_invarg 1000f8b6 f LIBCMT:invarg.obj - 0001:0000e8c5 __call_reportfault 1000f8c5 f LIBCMT:invarg.obj - 0001:0000e9ee __invoke_watson 1000f9ee f LIBCMT:invarg.obj - 0001:0000ea13 __invalid_parameter 1000fa13 f LIBCMT:invarg.obj - 0001:0000ea40 __invalid_parameter_noinfo 1000fa40 f LIBCMT:invarg.obj - 0001:0000ea50 ___report_gsfailure 1000fa50 f LIBCMT:gs_report.obj - 0001:0000ee0e ?_RTC_Failure@@YAXPAXH@Z 1000fe0e f LIBCMT:_error_.obj - 0001:0000ee49 ?_RTC_StackFailure@@YAXPAXPBD@Z 1000fe49 f LIBCMT:_error_.obj - 0001:0000ef3c ?_RTC_GetErrorFunc@@YAP6AHHPBDH00ZZPBX@Z 1000ff3c f LIBCMT:_userapi_.obj - 0001:0000ef42 ?_RTC_GetErrorFuncW@@YAP6AHHPB_WH00ZZPBX@Z 1000ff42 f LIBCMT:_userapi_.obj - 0001:0000ef48 __heap_init 1000ff48 f LIBCMT:heapinit.obj - 0001:0000ef66 __heap_term 1000ff66 f LIBCMT:heapinit.obj - 0001:0000ef7a __get_daylight 1000ff7a f LIBCMT:timeset.obj - 0001:0000efa7 __get_dstbias 1000ffa7 f LIBCMT:timeset.obj - 0001:0000efd4 __get_timezone 1000ffd4 f LIBCMT:timeset.obj - 0001:0000f001 ___daylight 10010001 f LIBCMT:timeset.obj - 0001:0000f007 ___dstbias 10010007 f LIBCMT:timeset.obj - 0001:0000f00d ___timezone 1001000d f LIBCMT:timeset.obj - 0001:0000f013 ___tzname 10010013 f LIBCMT:timeset.obj - 0001:0000f32c ___tzset 1001032c f LIBCMT:tzset.obj - 0001:0000f380 __aullrem 10010380 f LIBCMT:ullrem.obj - 0001:0000f400 __aulldiv 10010400 f LIBCMT:ulldiv.obj - 0001:0000f468 __encoded_null 10010468 f LIBCMT:tidtable.obj - 0001:0000f471 ___crtTlsAlloc@4 10010471 f LIBCMT:tidtable.obj - 0001:0000f47a ___set_flsgetvalue 1001047a f LIBCMT:tidtable.obj - 0001:0000f4ae __mtterm 100104ae f LIBCMT:tidtable.obj - 0001:0000f4eb __initptd 100104eb f LIBCMT:tidtable.obj - 0001:0000f59f __getptd_noexit 1001059f f LIBCMT:tidtable.obj - 0001:0000f618 __getptd 10010618 f LIBCMT:tidtable.obj - 0001:0000f632 __freefls@4 10010632 f LIBCMT:tidtable.obj - 0001:0000f761 __freeptd 10010761 f LIBCMT:tidtable.obj - 0001:0000f7cf __mtinit 100107cf f LIBCMT:tidtable.obj - 0001:0000f94a __set_osfhnd 1001094a f LIBCMT:osfinfo.obj - 0001:0000f9cb __free_osfhnd 100109cb f LIBCMT:osfinfo.obj - 0001:0000fa51 __get_osfhandle 10010a51 f LIBCMT:osfinfo.obj - 0001:0000faba ___lock_fhandle 10010aba f LIBCMT:osfinfo.obj - 0001:0000fb59 __unlock_fhandle 10010b59 f LIBCMT:osfinfo.obj - 0001:0000fb80 __alloc_osfhnd 10010b80 f LIBCMT:osfinfo.obj - 0001:0000fd19 __write_nolock 10010d19 f LIBCMT:write.obj - 0001:00010416 __write 10011416 f LIBCMT:write.obj - 0001:000104ea __lseeki64_nolock 100114ea f LIBCMT:lseeki64.obj - 0001:0001056f __lseeki64 1001156f f LIBCMT:lseeki64.obj - 0001:00010659 __chsize_nolock 10011659 f LIBCMT:chsize.obj - 0001:0001080f __read_nolock 1001180f f LIBCMT:read.obj - 0001:00010dc6 __close_nolock 10011dc6 f LIBCMT:close.obj - 0001:00010e62 __close 10011e62 f LIBCMT:close.obj - 0001:00010f26 __lseek_nolock 10011f26 f LIBCMT:lseek.obj - 0001:00010f9b __ioinit 10011f9b f LIBCMT:ioinit.obj - 0001:000111e0 __ioterm 100121e0 f LIBCMT:ioinit.obj - 0001:00011233 __setmode_nolock 10012233 f LIBCMT:setmode.obj - 0001:000112ee __get_fmode 100122ee f LIBCMT:setmode.obj - 0001:00011320 __SEH_prolog4 10012320 f LIBCMT:sehprolg4.obj - 0001:00011365 __SEH_epilog4 10012365 f LIBCMT:sehprolg4.obj - 0001:00011380 __except_handler4 10012380 f LIBCMT:chandler4.obj - 0001:0001150f __forcdecpt_l 1001250f f LIBCMT:cvt.obj - 0001:00011583 __cropzeros_l 10012583 f LIBCMT:cvt.obj - 0001:00011605 __positive 10012605 f LIBCMT:cvt.obj - 0001:00011621 __fassign_l 10012621 f LIBCMT:cvt.obj - 0001:00011663 __fassign 10012663 f LIBCMT:cvt.obj - 0001:0001169c __forcdecpt 1001269c f LIBCMT:cvt.obj - 0001:000116af __cropzeros 100126af f LIBCMT:cvt.obj - 0001:00011823 __cftoe_l 10012823 f LIBCMT:cvt.obj - 0001:000118ea __cftoe 100128ea f LIBCMT:cvt.obj - 0001:0001190a __cftoa_l 1001290a f LIBCMT:cvt.obj - 0001:00011d83 __cftof_l 10012d83 f LIBCMT:cvt.obj - 0001:00011e44 __cftog_l 10012e44 f LIBCMT:cvt.obj - 0001:00011f31 __cfltcvt_l 10012f31 f LIBCMT:cvt.obj - 0001:00011fb9 __cfltcvt 10012fb9 f LIBCMT:cvt.obj - 0001:00011fdc __initp_misc_cfltcvt_tab 10012fdc f LIBCMT:cmiscdat.obj - 0001:00011fff __setdefaultprecision 10012fff f LIBCMT:fp8.obj - 0001:00012027 __stbuf 10013027 f LIBCMT:_sftbuf.obj - 0001:000120c3 __ftbuf 100130c3 f LIBCMT:_sftbuf.obj - 0001:000120f7 __fileno 100130f7 f LIBCMT:fileno.obj - 0001:0001211d __vsnprintf_l 1001311d f LIBCMT:vsnprint.obj - 0001:000121c9 __vsnprintf 100131c9 f LIBCMT:vsnprint.obj - 0001:000121c9 _vsnprintf 100131c9 f LIBCMT:vsnprint.obj - 0001:000121e6 __commit 100131e6 f LIBCMT:commit.obj - 0001:000122bf __mtinitlocks 100132bf f LIBCMT:mlock.obj - 0001:00012309 __mtdeletelocks 10013309 f LIBCMT:mlock.obj - 0001:00012360 __unlock 10013360 f LIBCMT:mlock.obj - 0001:00012377 __mtinitlocknum 10013377 f LIBCMT:mlock.obj - 0001:00012439 __lock 10013439 f LIBCMT:mlock.obj - 0001:0001246c __freebuf 1001346c f LIBCMT:_freebuf.obj - 0001:0001249d __malloc_crt 1001349d f LIBCMT:crtheap.obj - 0001:000124e2 __calloc_crt 100134e2 f LIBCMT:crtheap.obj - 0001:0001252e __realloc_crt 1001352e f LIBCMT:crtheap.obj - 0001:0001257c __recalloc_crt 1001357c f LIBCMT:crtheap.obj - 0001:000125ce __fcloseall 100135ce f LIBCMT:closeall.obj - 0001:0001266a ___sys_nerr 1001366a f LIBCMT:syserr.obj - 0001:00012670 ___sys_errlist 10013670 f LIBCMT:syserr.obj - 0001:00012676 _strcpy_s 10013676 f LIBCMT:strcpy_s.obj - 0001:000126d5 _strncpy_s 100136d5 f LIBCMT:strncpy_s.obj - 0001:0001278a ?terminate@@YAXXZ 1001378a f LIBCMT:hooks.obj - 0001:000127c3 __initp_eh_hooks 100137c3 f LIBCMT:hooks.obj - 0001:000127d4 __initp_misc_winsig 100137d4 f LIBCMT:winsig.obj - 0001:00012829 ___get_sigabrt 10013829 f LIBCMT:winsig.obj - 0001:00012836 _raise 10013836 f LIBCMT:winsig.obj - 0001:000129d9 __initp_misc_rand_s 100139d9 f LIBCMT:rand_s.obj - 0001:000129e8 __initp_misc_purevirt 100139e8 f LIBCMT:inithelp.obj - 0001:000129f7 __initp_heap_handler 100139f7 f LIBCMT:handler.obj - 0001:00012a06 __callnewh 10013a06 f LIBCMT:handler.obj - 0001:00012ae4 ___onexitinit 10013ae4 f LIBCMT:onexit.obj - 0001:00012b15 __onexit 10013b15 f LIBCMT:onexit.obj - 0001:00012b51 _atexit 10013b51 f LIBCMT:onexit.obj - 0001:00012b68 __RTC_Initialize 10013b68 f LIBCMT:_initsect_.obj - 0001:00012b8e __RTC_Terminate 10013b8e f LIBCMT:_initsect_.obj - 0001:00012bc0 __ValidateImageBase 10013bc0 f LIBCMT:pesect.obj - 0001:00012c00 __FindPESection 10013c00 f LIBCMT:pesect.obj - 0001:00012c50 __IsNonwritableInCurrentImage 10013c50 f LIBCMT:pesect.obj - 0001:00012d0c __GET_RTERRMSG 10013d0c f LIBCMT:crt0msg.obj - 0001:00012d32 __NMSG_WRITE 10013d32 f LIBCMT:crt0msg.obj - 0001:00012ee1 __FF_MSGBANNER 10013ee1 f LIBCMT:crt0msg.obj - 0001:00012f1a __calloc_impl 10013f1a f LIBCMT:calloc_impl.obj - 0001:000131c7 _strtol 100141c7 f LIBCMT:strtol.obj - 0001:000131f2 __VEC_memzero 100141f2 f LIBCMT:p4_memset.obj - 0001:000132ac ___sse2_available_init 100142ac f LIBCMT:cpu_disp.obj - 0001:000132bc __VEC_memcpy 100142bc f LIBCMT:p4_memcpy.obj - 0001:000133bf __setenvp 100143bf f LIBCMT:stdenvp.obj - 0001:00013635 __setargv 10014635 f LIBCMT:stdargv.obj - 0001:000136f0 ___crtGetEnvironmentStringsA 100146f0 f LIBCMT:a_env.obj - 0001:00013787 __XcptFilter 10014787 f LIBCMT:winxfltr.obj - 0001:000138d1 ___CppXcptFilter 100148d1 f LIBCMT:winxfltr.obj - 0001:000138f1 ___security_init_cookie 100148f1 f LIBCMT:gs_support.obj - 0001:0001398c __getbuf 1001498c f LIBCMT:_getbuf.obj - 0001:000139d5 __isatty 100149d5 f LIBCMT:isatty.obj - 0001:00013c4e ___updatetmbcinfo 10014c4e f LIBCMT:mbctype.obj - 0001:00013d6e __setmbcp_nolock 10014d6e f LIBCMT:mbctype.obj - 0001:00013f57 __setmbcp 10014f57 f LIBCMT:mbctype.obj - 0001:000140f1 ___initmbctable 100150f1 f LIBCMT:mbctype.obj - 0001:0001410f ___addlocaleref 1001510f f LIBCMT:localref.obj - 0001:0001419e ___removelocaleref 1001519e f LIBCMT:localref.obj - 0001:00014237 ___freetlocinfo 10015237 f LIBCMT:localref.obj - 0001:00014382 __updatetlocinfoEx_nolock 10015382 f LIBCMT:localref.obj - 0001:000143cf ___updatetlocinfo 100153cf f LIBCMT:localref.obj - 0001:00014448 __get_printf_count_output 10015448 f LIBCMT:printf.obj - 0001:0001445e __wctomb_s_l 1001545e f LIBCMT:wctomb.obj - 0001:000145b3 _wctomb_s 100155b3 f LIBCMT:wctomb.obj - 0001:000145d0 __isleadbyte_l 100155d0 f i LIBCMT:_wctype.obj - 0001:00014608 _isleadbyte 10015608 f i LIBCMT:_wctype.obj - 0001:00014620 __aulldvrm 10015620 f LIBCMT:ulldvrm.obj - 0001:000146b5 __crt_debugger_hook 100156b5 f LIBCMT:dbghook.obj - 0001:00014849 ?_RTC_GetSrcLine@@YAHPAEPA_WKPAH1K@Z 10015849 f LIBCMT:_pdblkup_.obj - 0001:00014ad0 _strcmp 10015ad0 f LIBCMT:strcmp.obj - 0001:00014b58 __getenv_helper_nolock 10015b58 f LIBCMT:getenv.obj - 0001:00014bdf ____lc_codepage_func 10015bdf f LIBCMT:initctyp.obj - 0001:00014c05 __putwch_nolock 10015c05 f LIBCMT:putwch.obj - 0001:00014c47 __mbtowc_l 10015c47 f LIBCMT:mbtowc.obj - 0001:00014d5d _mbtowc 10015d5d f LIBCMT:mbtowc.obj - 0001:00014d80 __chkstk 10015d80 f LIBCMT:chkstk.obj - 0001:00014d80 __alloca_probe 10015d80 LIBCMT:chkstk.obj - 0001:00014db0 __local_unwind4 10015db0 f LIBCMT:exsup4.obj - 0001:00014e86 __seh_longjmp_unwind4@4 10015e86 f LIBCMT:exsup4.obj - 0001:00014ea2 @_EH4_CallFilterFunc@8 10015ea2 f LIBCMT:exsup4.obj - 0001:00014eb9 @_EH4_TransferToHandler@8 10015eb9 f LIBCMT:exsup4.obj - 0001:00014ed2 @_EH4_GlobalUnwind2@8 10015ed2 f LIBCMT:exsup4.obj - 0001:00014eeb @_EH4_LocalUnwind@16 10015eeb f LIBCMT:exsup4.obj - 0001:00014f02 __isdigit_l 10015f02 f i LIBCMT:_ctype.obj - 0001:00014f53 _isdigit 10015f53 f i LIBCMT:_ctype.obj - 0001:00014f81 __tolower_l 10015f81 f LIBCMT:tolower.obj - 0001:00015096 _tolower 10016096 f LIBCMT:tolower.obj - 0001:000150c2 __atodbl_l 100160c2 f LIBCMT:atodbl.obj - 0001:0001516a __atoflt_l 1001616a f LIBCMT:atodbl.obj - 0001:00015220 _memmove 10016220 f LIBCMT:memmove.obj - 0001:00015581 __fptostr 10016581 f LIBCMT:_fptostr.obj - 0001:00015634 ___dtold 10016634 f LIBCMT:_cfout_.obj - 0001:000156e7 __fltout2 100166e7 f LIBCMT:_cfout_.obj - 0001:00015780 __alldvrm 10016780 f LIBCMT:lldvrm.obj - 0001:00015860 __aullshr 10016860 f LIBCMT:ullshr.obj - 0001:0001587f __fptrap 1001687f f LIBCMT:crt0fp.obj - 0001:00015888 __controlfp_s 10016888 f LIBCMT:_contrlfp_.obj - 0001:000158e7 __recalloc 100168e7 f LIBCMT:recalloc.obj - 0001:00015955 _abort 10016955 f LIBCMT:abort.obj - 0001:00015988 __freea 10016988 f i LIBCMT:a_loc.obj - 0001:000159a8 __msize 100169a8 f LIBCMT:msize.obj - 0001:000159db ___crtMessageBoxW 100169db f LIBCMT:crtmboxw.obj - 0001:00015b47 _wcscat_s 10016b47 f LIBCMT:wcscat_s.obj - 0001:00015bbc _wcsncpy_s 10016bbc f LIBCMT:wcsncpy_s.obj - 0001:00015c89 _wcslen 10016c89 f LIBCMT:wcslen.obj - 0001:00015ca4 _wcscpy_s 10016ca4 f LIBCMT:wcscpy_s.obj - 0001:00015d07 __set_error_mode 10016d07 f LIBCMT:errmode.obj - 0001:00015d46 __isctype_l 10016d46 f LIBCMT:isctype.obj - 0001:00015e00 __allmul 10016e00 f LIBCMT:llmul.obj - 0001:00015e87 __ismbblead 10016e87 f LIBCMT:ismbbyte.obj - 0001:00016086 ___crtLCMapStringA 10017086 f LIBCMT:a_map.obj - 0001:000161b3 ___crtGetStringTypeA 100171b3 f LIBCMT:a_str.obj - 0001:000161f3 ___free_lc_time 100171f3 f LIBCMT:inittime.obj - 0001:0001656a ___free_lconv_num 1001756a f LIBCMT:initnum.obj - 0001:000165d3 ___free_lconv_mon 100175d3 f LIBCMT:initmon.obj - 0001:000166d1 __mbsnbicoll_l 100176d1 f LIBCMT:mbsnbico.obj - 0001:000167af __mbsnbicoll 100177af f LIBCMT:mbsnbico.obj - 0001:000167c9 ___wtomb_environ 100177c9 f LIBCMT:wtombenv.obj - 0001:00016860 ___initconout 10017860 f LIBCMT:initcon.obj - 0001:0001687f ___termconout 1001787f f LIBCMT:initcon.obj - 0001:000168a0 __global_unwind2 100178a0 f LIBCMT:exsup.obj - 0001:00016905 __local_unwind2 10017905 f LIBCMT:exsup.obj - 0001:00016989 __abnormal_termination 10017989 f LIBCMT:exsup.obj - 0001:000169ac __NLG_Notify1 100179ac f LIBCMT:exsup.obj - 0001:000169b5 __NLG_Notify 100179b5 f LIBCMT:exsup.obj - 0001:000169cc __NLG_Dispatch 100179cc LIBCMT:exsup.obj - 0001:000169cc __NLG_Dispatch2 100179cc LIBCMT:exsup.obj - 0001:000169d4 __NLG_Call 100179d4 f LIBCMT:exsup.obj - 0001:000169d6 __NLG_Return2 100179d6 LIBCMT:exsup.obj - 0001:000169d7 __ld12tod 100179d7 f LIBCMT:_intrncvt_.obj - 0001:00016f28 __ld12tof 10017f28 f LIBCMT:_intrncvt_.obj - 0001:00017479 ___strgtold12_l 10018479 f LIBCMT:_strgtold_.obj - 0001:00017b55 _$I10_OUTPUT 10018b55 f LIBCMT:_x10fout_.obj - 0001:0001857b __control87 1001957b f i LIBCMT:_ieee87_.obj - 0001:00018890 __alloca_probe_16 10019890 f LIBCMT:alloca16.obj - 0001:000188a6 __alloca_probe_8 100198a6 LIBCMT:alloca16.obj - 0001:000188c0 _strcspn 100198c0 f LIBCMT:strcspn.obj - 0001:00018910 _strpbrk 10019910 f LIBCMT:strpbrk.obj - 0001:00018bbe ___crtCompareStringA 10019bbe f LIBCMT:a_cmp.obj - 0001:00018c00 __strnicoll_l 10019c00 f LIBCMT:strnicol.obj - 0001:00018d9c ___crtsetenv 10019d9c f LIBCMT:setenv.obj - 0001:00018fde ___mtold12 10019fde f LIBCMT:_mantold_.obj - 0001:000191c2 ___set_fpsr_sse2 1001a1c2 f LIBCMT:_fpctrl_.obj - 0001:00019234 __strnicmp_l 1001a234 f LIBCMT:strnicmp.obj - 0001:00019316 __strdup 1001a316 f LIBCMT:strdup.obj - 0001:00019368 __mbschr_l 1001a368 f LIBCMT:mbschr.obj - 0001:00019418 __mbschr 1001a418 f LIBCMT:mbschr.obj - 0001:00019430 ___ascii_strnicmp 1001a430 f LIBCMT:_strnicm.obj - 0001:000194b0 _strchr 1001a4b0 f LIBCMT:strchr.obj - 0001:000194b6 ___from_strstr_to_strchr 1001a4b6 LIBCMT:strchr.obj - 0001:0001956e _RtlUnwind@16 1001a56e f kernel32:KERNEL32.dll - 0001:00019574 __fdopen 1001a574 f LIBCMT:fdopen.obj - 0001:00019574 _fdopen 1001a574 f LIBCMT:fdopen.obj - 0001:00019835 _dup 1001a835 f LIBCMT:dup.obj - 0001:00019835 __dup 1001a835 f LIBCMT:dup.obj - 0001:00019905 __getstream 1001a905 f LIBCMT:stream.obj - 0002:00000000 __imp__RegSetValueExA@24 1001b000 Advapi32:ADVAPI32.dll - 0002:00000004 __imp__RegCreateKeyA@12 1001b004 Advapi32:ADVAPI32.dll - 0002:00000008 __imp__RegDeleteKeyA@8 1001b008 Advapi32:ADVAPI32.dll - 0002:0000000c __imp__RegCloseKey@4 1001b00c Advapi32:ADVAPI32.dll - 0002:00000010 \177ADVAPI32_NULL_THUNK_DATA 1001b010 Advapi32:ADVAPI32.dll - 0002:00000014 __imp__SetEnvironmentVariableA@8 1001b014 kernel32:KERNEL32.dll - 0002:00000018 __imp__CompareStringW@24 1001b018 kernel32:KERNEL32.dll - 0002:0000001c __imp__lstrcpyA@8 1001b01c kernel32:KERNEL32.dll - 0002:00000020 __imp__GetModuleFileNameA@12 1001b020 kernel32:KERNEL32.dll - 0002:00000024 __imp__lstrlenA@4 1001b024 kernel32:KERNEL32.dll - 0002:00000028 __imp__GetOverlappedResult@16 1001b028 kernel32:KERNEL32.dll - 0002:0000002c __imp__GetLastError@0 1001b02c kernel32:KERNEL32.dll - 0002:00000030 __imp__WriteFile@20 1001b030 kernel32:KERNEL32.dll - 0002:00000034 __imp__ReadFile@20 1001b034 kernel32:KERNEL32.dll - 0002:00000038 __imp__SetEvent@4 1001b038 kernel32:KERNEL32.dll - 0002:0000003c __imp__WaitForSingleObject@8 1001b03c kernel32:KERNEL32.dll - 0002:00000040 __imp__TerminateThread@8 1001b040 kernel32:KERNEL32.dll - 0002:00000044 __imp__CloseHandle@4 1001b044 kernel32:KERNEL32.dll - 0002:00000048 __imp__Sleep@4 1001b048 kernel32:KERNEL32.dll - 0002:0000004c __imp__CreateThread@24 1001b04c kernel32:KERNEL32.dll - 0002:00000050 __imp__CreateEventA@16 1001b050 kernel32:KERNEL32.dll - 0002:00000054 __imp__SetCommMask@8 1001b054 kernel32:KERNEL32.dll - 0002:00000058 __imp__SetCommTimeouts@8 1001b058 kernel32:KERNEL32.dll - 0002:0000005c __imp__SetCommState@8 1001b05c kernel32:KERNEL32.dll - 0002:00000060 __imp__GetCommState@8 1001b060 kernel32:KERNEL32.dll - 0002:00000064 __imp__CreateFileA@28 1001b064 kernel32:KERNEL32.dll - 0002:00000068 __imp__WaitCommEvent@12 1001b068 kernel32:KERNEL32.dll - 0002:0000006c __imp__ClearCommError@12 1001b06c kernel32:KERNEL32.dll - 0002:00000070 __imp__HeapFree@12 1001b070 kernel32:KERNEL32.dll - 0002:00000074 __imp__GetTimeZoneInformation@4 1001b074 kernel32:KERNEL32.dll - 0002:00000078 __imp__GetSystemTimeAsFileTime@4 1001b078 kernel32:KERNEL32.dll - 0002:0000007c __imp__GetFileType@4 1001b07c kernel32:KERNEL32.dll - 0002:00000080 __imp__EnterCriticalSection@4 1001b080 kernel32:KERNEL32.dll - 0002:00000084 __imp__LeaveCriticalSection@4 1001b084 kernel32:KERNEL32.dll - 0002:00000088 __imp__GetProcAddress@8 1001b088 kernel32:KERNEL32.dll - 0002:0000008c __imp__GetModuleHandleW@4 1001b08c kernel32:KERNEL32.dll - 0002:00000090 __imp__ExitProcess@4 1001b090 kernel32:KERNEL32.dll - 0002:00000094 __imp__DecodePointer@4 1001b094 kernel32:KERNEL32.dll - 0002:00000098 __imp__HeapAlloc@12 1001b098 kernel32:KERNEL32.dll - 0002:0000009c __imp__HeapReAlloc@16 1001b09c kernel32:KERNEL32.dll - 0002:000000a0 __imp__GetCurrentThreadId@0 1001b0a0 kernel32:KERNEL32.dll - 0002:000000a4 __imp__GetCommandLineA@0 1001b0a4 kernel32:KERNEL32.dll - 0002:000000a8 __imp__UnhandledExceptionFilter@4 1001b0a8 kernel32:KERNEL32.dll - 0002:000000ac __imp__SetUnhandledExceptionFilter@4 1001b0ac kernel32:KERNEL32.dll - 0002:000000b0 __imp__IsDebuggerPresent@0 1001b0b0 kernel32:KERNEL32.dll - 0002:000000b4 __imp__EncodePointer@4 1001b0b4 kernel32:KERNEL32.dll - 0002:000000b8 __imp__TerminateProcess@8 1001b0b8 kernel32:KERNEL32.dll - 0002:000000bc __imp__GetCurrentProcess@0 1001b0bc kernel32:KERNEL32.dll - 0002:000000c0 __imp__RaiseException@16 1001b0c0 kernel32:KERNEL32.dll - 0002:000000c4 __imp__WideCharToMultiByte@32 1001b0c4 kernel32:KERNEL32.dll - 0002:000000c8 __imp__MultiByteToWideChar@24 1001b0c8 kernel32:KERNEL32.dll - 0002:000000cc __imp__LoadLibraryW@4 1001b0cc kernel32:KERNEL32.dll - 0002:000000d0 __imp__HeapCreate@12 1001b0d0 kernel32:KERNEL32.dll - 0002:000000d4 __imp__HeapDestroy@4 1001b0d4 kernel32:KERNEL32.dll - 0002:000000d8 __imp__TlsAlloc@0 1001b0d8 kernel32:KERNEL32.dll - 0002:000000dc __imp__TlsGetValue@4 1001b0dc kernel32:KERNEL32.dll - 0002:000000e0 __imp__TlsSetValue@8 1001b0e0 kernel32:KERNEL32.dll - 0002:000000e4 __imp__TlsFree@4 1001b0e4 kernel32:KERNEL32.dll - 0002:000000e8 __imp__InterlockedIncrement@4 1001b0e8 kernel32:KERNEL32.dll - 0002:000000ec __imp__SetLastError@4 1001b0ec kernel32:KERNEL32.dll - 0002:000000f0 __imp__InterlockedDecrement@4 1001b0f0 kernel32:KERNEL32.dll - 0002:000000f4 __imp__SetStdHandle@8 1001b0f4 kernel32:KERNEL32.dll - 0002:000000f8 __imp__InitializeCriticalSectionAndSpinCount@8 1001b0f8 kernel32:KERNEL32.dll - 0002:000000fc __imp__GetConsoleCP@0 1001b0fc kernel32:KERNEL32.dll - 0002:00000100 __imp__GetConsoleMode@8 1001b100 kernel32:KERNEL32.dll - 0002:00000104 __imp__SetFilePointer@16 1001b104 kernel32:KERNEL32.dll - 0002:00000108 __imp__SetEndOfFile@4 1001b108 kernel32:KERNEL32.dll - 0002:0000010c __imp__GetProcessHeap@0 1001b10c kernel32:KERNEL32.dll - 0002:00000110 __imp__SetHandleCount@4 1001b110 kernel32:KERNEL32.dll - 0002:00000114 __imp__GetStdHandle@4 1001b114 kernel32:KERNEL32.dll - 0002:00000118 __imp__GetStartupInfoW@4 1001b118 kernel32:KERNEL32.dll - 0002:0000011c __imp__DeleteCriticalSection@4 1001b11c kernel32:KERNEL32.dll - 0002:00000120 __imp__FlushFileBuffers@4 1001b120 kernel32:KERNEL32.dll - 0002:00000124 __imp__FreeLibrary@4 1001b124 kernel32:KERNEL32.dll - 0002:00000128 __imp__GetModuleFileNameW@12 1001b128 kernel32:KERNEL32.dll - 0002:0000012c __imp__IsProcessorFeaturePresent@4 1001b12c kernel32:KERNEL32.dll - 0002:00000130 __imp__FreeEnvironmentStringsW@4 1001b130 kernel32:KERNEL32.dll - 0002:00000134 __imp__GetEnvironmentStringsW@0 1001b134 kernel32:KERNEL32.dll - 0002:00000138 __imp__QueryPerformanceCounter@4 1001b138 kernel32:KERNEL32.dll - 0002:0000013c __imp__GetTickCount@0 1001b13c kernel32:KERNEL32.dll - 0002:00000140 __imp__GetCurrentProcessId@0 1001b140 kernel32:KERNEL32.dll - 0002:00000144 __imp__GetCPInfo@8 1001b144 kernel32:KERNEL32.dll - 0002:00000148 __imp__GetACP@0 1001b148 kernel32:KERNEL32.dll - 0002:0000014c __imp__GetOEMCP@0 1001b14c kernel32:KERNEL32.dll - 0002:00000150 __imp__IsValidCodePage@4 1001b150 kernel32:KERNEL32.dll - 0002:00000154 __imp__VirtualQuery@12 1001b154 kernel32:KERNEL32.dll - 0002:00000158 __imp__WriteConsoleW@20 1001b158 kernel32:KERNEL32.dll - 0002:0000015c __imp__RtlUnwind@16 1001b15c kernel32:KERNEL32.dll - 0002:00000160 __imp__HeapSize@12 1001b160 kernel32:KERNEL32.dll - 0002:00000164 __imp__LCMapStringW@24 1001b164 kernel32:KERNEL32.dll - 0002:00000168 __imp__GetStringTypeW@16 1001b168 kernel32:KERNEL32.dll - 0002:0000016c __imp__CreateFileW@28 1001b16c kernel32:KERNEL32.dll - 0002:00000170 __imp__DuplicateHandle@28 1001b170 kernel32:KERNEL32.dll - 0002:00000174 \177KERNEL32_NULL_THUNK_DATA 1001b174 kernel32:KERNEL32.dll - 0002:00000178 __imp__wsprintfA 1001b178 User32:USER32.dll - 0002:0000017c __imp__MessageBoxA@16 1001b17c User32:USER32.dll - 0002:00000180 __imp__SendMessageA@16 1001b180 User32:USER32.dll - 0002:00000184 \177USER32_NULL_THUNK_DATA 1001b184 User32:USER32.dll - 0002:00000188 ___xc_a 1001b188 LIBCMT:crt0init.obj - 0002:0000018c ___xc_z 1001b18c LIBCMT:crt0init.obj - 0002:00000190 ___xi_a 1001b190 LIBCMT:crt0init.obj - 0002:000001a4 ___xi_z 1001b1a4 LIBCMT:crt0init.obj - 0002:000001a8 ___xp_a 1001b1a8 LIBCMT:crt0init.obj - 0002:000001b4 ___xp_z 1001b1b4 LIBCMT:crt0init.obj - 0002:000001b8 ___xt_a 1001b1b8 LIBCMT:crt0init.obj - 0002:000001bc ___xt_z 1001b1bc LIBCMT:crt0init.obj - 0002:000001c0 __real@408ff80000000000 1001b1c0 api.obj - 0002:000001c8 __real@3f6a6d01a6d01a6d 1001b1c8 api.obj - 0002:000001d0 __FPinit 1001b1d0 LIBCMT:_fpinit_.obj - 0002:000001d4 ??_C@_01EEMJAFIK@?6?$AA@ 1001b1d4 LIBCMT:perror.obj - 0002:000001d8 ??_C@_02LMMGGCAJ@?3?5?$AA@ 1001b1d8 LIBCMT:perror.obj - 0002:000001e0 ??_C@_0EA@FCLIIPNN@Visual?5C?$CL?$CL?5CRT?3?5Not?5enough?5memor@ 1001b1e0 LIBCMT:strerror.obj - 0002:00000220 ??_C@_0P@MIGLKIOC@CorExitProcess?$AA@ 1001b220 LIBCMT:crt0dat.obj - 0002:00000230 ??_C@_1BI@BGOHAHKC@?$AAm?$AAs?$AAc?$AAo?$AAr?$AAe?$AAe?$AA?4?$AAd?$AAl?$AAl?$AA?$AA@ 1001b230 LIBCMT:crt0dat.obj - 0002:00000248 __pRawDllMain 1001b248 LIBCMT:dllcrt0.obj - 0002:00000248 __pDefaultRawDllMain 1001b248 LIBCMT:dllcrt0.obj - 0002:0000024c ??_C@_1O@CEDCILHN@?$AA?$CI?$AAn?$AAu?$AAl?$AAl?$AA?$CJ?$AA?$AA@ 1001b24c LIBCMT:output.obj - 0002:0000025c ??_C@_06OJHGLDPL@?$CInull?$CJ?$AA@ 1001b25c LIBCMT:output.obj - 0002:00000268 ___lookuptable 1001b268 LIBCMT:output.obj - 0002:000002cc ??_C@_0BO@GNIAFIKK@Unknown?5Runtime?5Check?5Error?6?$AN?$AA@ 1001b2cc LIBCMT:_error_.obj - 0002:000002ec ??_C@_0CM@NGINOKPC@Stack?5memory?5around?5_alloca?5was?5@ 1001b2ec LIBCMT:_error_.obj - 0002:00000318 ??_C@_0DG@HKJMLLLP@A?5local?5variable?5was?5used?5before@ 1001b318 LIBCMT:_error_.obj - 0002:00000350 ??_C@_0BN@FFOINMNJ@Stack?5memory?5was?5corrupted?6?$AN?$AA@ 1001b350 LIBCMT:_error_.obj - 0002:00000370 ??_C@_0BBN@GPMLNJCF@A?5cast?5to?5a?5smaller?5data?5type?5ha@ 1001b370 LIBCMT:_error_.obj - 0002:00000490 ??_C@_0NN@NGPKDKPD@The?5value?5of?5ESP?5was?5not?5properl@ 1001b490 LIBCMT:_error_.obj - 0002:0000060c ??_C@_0CA@IODNCDPG@Run?9Time?5Check?5Failure?5?$CD?$CFd?5?9?5?$CFs?$AA@ 1001b60c LIBCMT:_error_.obj - 0002:0000062c ??_C@_0BE@GNBOBNCK@Unknown?5Module?5Name?$AA@ 1001b62c LIBCMT:_error_.obj - 0002:00000640 ??_C@_0BB@PFFGGCJP@Unknown?5Filename?$AA@ 1001b640 LIBCMT:_error_.obj - 0002:00000658 ??_C@_1EA@NFKNIFJP@?$AAR?$AAu?$AAn?$AA?9?$AAT?$AAi?$AAm?$AAe?$AA?5?$AAC?$AAh?$AAe?$AAc?$AAk?$AA?5?$AAF?$AAa?$AAi?$AAl?$AAu?$AAr?$AAe?$AA?5?$AA?$CD?$AA?$CF?$AAd?$AA?5?$AA?9?$AA?5?$AA?$CF?$AAs?$AA?$AA@ 1001b658 LIBCMT:_error_.obj - 0002:00000698 ??_C@_1GM@OLMCBDMB@?$AAR?$AAu?$AAn?$AAt?$AAi?$AAm?$AAe?$AA?5?$AAC?$AAh?$AAe?$AAc?$AAk?$AA?5?$AAE?$AAr?$AAr?$AAo?$AAr?$AA?4?$AA?6?$AA?$AN?$AA?5?$AAU?$AAn?$AAa?$AAb?$AAl?$AAe?$AA?5?$AAt?$AAo@ 1001b698 LIBCMT:_error_.obj - 0002:00000704 ??_C@_0CG@IAFNJNEE@Stack?5corrupted?5near?5unknown?5var@ 1001b704 LIBCMT:_error_.obj - 0002:0000072c ??_C@_0BP@OGBCLIBO@Stack?5around?5_alloca?5corrupted?$AA@ 1001b72c LIBCMT:_userapi_.obj - 0002:0000074c ??_C@_0CK@CNLNOEPB@Local?5variable?5used?5before?5initi@ 1001b74c LIBCMT:_userapi_.obj - 0002:00000778 ??_C@_0BI@CIGMDCBH@Stack?5memory?5corruption?$AA@ 1001b778 LIBCMT:_userapi_.obj - 0002:00000790 ??_C@_0CK@FEGOIOPB@Cast?5to?5smaller?5type?5causing?5los@ 1001b790 LIBCMT:_userapi_.obj - 0002:000007bc ??_C@_0BJ@HEGAHDFO@Stack?5pointer?5corruption?$AA@ 1001b7bc LIBCMT:_userapi_.obj - 0002:000007ec ___dnames 1001b7ec LIBCMT:timeset.obj - 0002:00000804 ___mnames 1001b804 LIBCMT:timeset.obj - 0002:0000082c ??_C@_02CLFPBFFP@TZ?$AA@ 1001b82c LIBCMT:tzset.obj - 0002:00000830 ??_C@_1BK@DBDEIDLH@?$AAK?$AAE?$AAR?$AAN?$AAE?$AAL?$AA3?$AA2?$AA?4?$AAD?$AAL?$AAL?$AA?$AA@ 1001b830 LIBCMT:tidtable.obj - 0002:0000084c ??_C@_07PEJMOBNF@FlsFree?$AA@ 1001b84c LIBCMT:tidtable.obj - 0002:00000854 ??_C@_0M@JCPCPOEF@FlsSetValue?$AA@ 1001b854 LIBCMT:tidtable.obj - 0002:00000860 ??_C@_0M@GDNOONDI@FlsGetValue?$AA@ 1001b860 LIBCMT:tidtable.obj - 0002:0000086c ??_C@_08KNHFBNJ@FlsAlloc?$AA@ 1001b86c LIBCMT:tidtable.obj - 0002:00000878 ??_C@_05KLBDPFGC@e?$CL000?$AA@ 1001b878 LIBCMT:cvt.obj - 0002:00000880 ??_C@_0BG@KLEAJEFJ@Illegal?5byte?5sequence?$AA@ 1001b880 LIBCMT:syserr.obj - 0002:00000898 ??_C@_0BE@ICMCHPHH@Directory?5not?5empty?$AA@ 1001b898 LIBCMT:syserr.obj - 0002:000008ac ??_C@_0BJ@IHEHINLI@Function?5not?5implemented?$AA@ 1001b8ac LIBCMT:syserr.obj - 0002:000008c8 ??_C@_0BD@CLHBCGPB@No?5locks?5available?$AA@ 1001b8c8 LIBCMT:syserr.obj - 0002:000008dc ??_C@_0BC@BEDIHIDK@Filename?5too?5long?$AA@ 1001b8dc LIBCMT:syserr.obj - 0002:000008f0 ??_C@_0BK@JAEBMJJM@Resource?5deadlock?5avoided?$AA@ 1001b8f0 LIBCMT:syserr.obj - 0002:0000090c ??_C@_0BB@FCBJFCAJ@Result?5too?5large?$AA@ 1001b90c LIBCMT:syserr.obj - 0002:00000920 ??_C@_0N@MMJPGLJK@Domain?5error?$AA@ 1001b920 LIBCMT:syserr.obj - 0002:00000930 ??_C@_0M@LHEPIIOM@Broken?5pipe?$AA@ 1001b930 LIBCMT:syserr.obj - 0002:0000093c ??_C@_0P@PKCJJLLM@Too?5many?5links?$AA@ 1001b93c LIBCMT:syserr.obj - 0002:0000094c ??_C@_0BG@DDBFNKBH@Read?9only?5file?5system?$AA@ 1001b94c LIBCMT:syserr.obj - 0002:00000964 ??_C@_0N@FEHLOILP@Invalid?5seek?$AA@ 1001b964 LIBCMT:syserr.obj - 0002:00000974 ??_C@_0BI@FEALHKLD@No?5space?5left?5on?5device?$AA@ 1001b974 LIBCMT:syserr.obj - 0002:0000098c ??_C@_0P@LFMMIPAE@File?5too?5large?$AA@ 1001b98c LIBCMT:syserr.obj - 0002:0000099c ??_C@_0CE@ONOKNLPF@Inappropriate?5I?1O?5control?5operat@ 1001b99c LIBCMT:syserr.obj - 0002:000009c0 ??_C@_0BE@INBJMKGG@Too?5many?5open?5files?$AA@ 1001b9c0 LIBCMT:syserr.obj - 0002:000009d4 ??_C@_0BO@IIFBODJE@Too?5many?5open?5files?5in?5system?$AA@ 1001b9d4 LIBCMT:syserr.obj - 0002:000009f4 ??_C@_0BB@HMGGCEBG@Invalid?5argument?$AA@ 1001b9f4 LIBCMT:syserr.obj - 0002:00000a08 ??_C@_0P@NDHGCGKE@Is?5a?5directory?$AA@ 1001ba08 LIBCMT:syserr.obj - 0002:00000a18 ??_C@_0BA@CJBACOOL@Not?5a?5directory?$AA@ 1001ba18 LIBCMT:syserr.obj - 0002:00000a28 ??_C@_0P@NLEIANHE@No?5such?5device?$AA@ 1001ba28 LIBCMT:syserr.obj - 0002:00000a38 ??_C@_0O@OAMDNOCP@Improper?5link?$AA@ 1001ba38 LIBCMT:syserr.obj - 0002:00000a48 ??_C@_0M@NAAJNNGH@File?5exists?$AA@ 1001ba48 LIBCMT:syserr.obj - 0002:00000a54 ??_C@_0BA@BIBLIOEK@Resource?5device?$AA@ 1001ba54 LIBCMT:syserr.obj - 0002:00000a64 ??_C@_0O@NIPGCINC@Unknown?5error?$AA@ 1001ba64 LIBCMT:syserr.obj - 0002:00000a74 ??_C@_0M@LOEHLCJD@Bad?5address?$AA@ 1001ba74 LIBCMT:syserr.obj - 0002:00000a80 ??_C@_0BC@HFNFNKAI@Permission?5denied?$AA@ 1001ba80 LIBCMT:syserr.obj - 0002:00000a94 ??_C@_0BB@IMDKMPFB@Not?5enough?5space?$AA@ 1001ba94 LIBCMT:syserr.obj - 0002:00000aa8 ??_C@_0CB@EPFKGNAK@Resource?5temporarily?5unavailable@ 1001baa8 LIBCMT:syserr.obj - 0002:00000acc ??_C@_0BD@LOHELEP@No?5child?5processes?$AA@ 1001bacc LIBCMT:syserr.obj - 0002:00000ae0 ??_C@_0BE@NFGDDCEF@Bad?5file?5descriptor?$AA@ 1001bae0 LIBCMT:syserr.obj - 0002:00000af4 ??_C@_0BC@HKPNECK@Exec?5format?5error?$AA@ 1001baf4 LIBCMT:syserr.obj - 0002:00000b08 ??_C@_0BC@MFFGCDFL@Arg?5list?5too?5long?$AA@ 1001bb08 LIBCMT:syserr.obj - 0002:00000b1c ??_C@_0BK@DPKMCKJ@No?5such?5device?5or?5address?$AA@ 1001bb1c LIBCMT:syserr.obj - 0002:00000b38 ??_C@_0BD@KKNFOBBD@Input?1output?5error?$AA@ 1001bb38 LIBCMT:syserr.obj - 0002:00000b4c ??_C@_0BK@FJBOAFDK@Interrupted?5function?5call?$AA@ 1001bb4c LIBCMT:syserr.obj - 0002:00000b68 ??_C@_0BA@FKIAIBGA@No?5such?5process?$AA@ 1001bb68 LIBCMT:syserr.obj - 0002:00000b78 ??_C@_0BK@FMDHKPNF@No?5such?5file?5or?5directory?$AA@ 1001bb78 LIBCMT:syserr.obj - 0002:00000b94 ??_C@_0BI@BJFCGOHL@Operation?5not?5permitted?$AA@ 1001bb94 LIBCMT:syserr.obj - 0002:00000bac ??_C@_08INEPGKHH@No?5error?$AA@ 1001bbac LIBCMT:syserr.obj - 0002:00000bb8 ??_C@_1BK@GAEMIDIL@?$AAA?$AAD?$AAV?$AAA?$AAP?$AAI?$AA3?$AA2?$AA?4?$AAD?$AAL?$AAL?$AA?$AA@ 1001bbb8 LIBCMT:rand_s.obj - 0002:00000bd4 ??_C@_1BO@BKOMIGKJ@?$AAr?$AAu?$AAn?$AAt?$AAi?$AAm?$AAe?$AA?5?$AAe?$AAr?$AAr?$AAo?$AAr?$AA?5?$AA?$AA@ 1001bbd4 LIBCMT:crt0msg.obj - 0002:00000bf4 ??_C@_15JNBOKNOG@?$AA?$AN?$AA?6?$AA?$AA@ 1001bbf4 LIBCMT:crt0msg.obj - 0002:00000bfc ??_C@_1BM@JBBEPPHI@?$AAT?$AAL?$AAO?$AAS?$AAS?$AA?5?$AAe?$AAr?$AAr?$AAo?$AAr?$AA?$AN?$AA?6?$AA?$AA@ 1001bbfc LIBCMT:crt0msg.obj - 0002:00000c18 ??_C@_1BK@KMOMNAAI@?$AAS?$AAI?$AAN?$AAG?$AA?5?$AAe?$AAr?$AAr?$AAo?$AAr?$AA?$AN?$AA?6?$AA?$AA@ 1001bc18 LIBCMT:crt0msg.obj - 0002:00000c34 ??_C@_1BO@BFCDCGC@?$AAD?$AAO?$AAM?$AAA?$AAI?$AAN?$AA?5?$AAe?$AAr?$AAr?$AAo?$AAr?$AA?$AN?$AA?6?$AA?$AA@ 1001bc34 LIBCMT:crt0msg.obj - 0002:00000c58 ??_C@_1BOO@KGEDBGAJ@?$AAR?$AA6?$AA0?$AA3?$AA3?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAA?$AAt?$AAt?$AAe?$AAm?$AAp?$AAt?$AA?5?$AAt?$AAo?$AA?5?$AAu?$AAs?$AAe?$AA?5?$AAM?$AAS?$AAI?$AAL?$AA?5?$AAc?$AAo?$AAd@ 1001bc58 LIBCMT:crt0msg.obj - 0002:00000e48 ??_C@_1GG@GOPILAJP@?$AAR?$AA6?$AA0?$AA3?$AA2?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAl?$AAo@ 1001be48 LIBCMT:crt0msg.obj - 0002:00000eb0 ??_C@_1MG@ENCOOIDF@?$AAR?$AA6?$AA0?$AA3?$AA1?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAA?$AAt?$AAt?$AAe?$AAm?$AAp?$AAt?$AA?5?$AAt?$AAo?$AA?5?$AAi?$AAn?$AAi?$AAt?$AAi?$AAa?$AAl?$AAi?$AAz?$AAe?$AA?5?$AAt@ 1001beb0 LIBCMT:crt0msg.obj - 0002:00000f78 ??_C@_1DO@BMFCDCD@?$AAR?$AA6?$AA0?$AA3?$AA0?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAC?$AAR?$AAT?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAi?$AAn?$AAi?$AAt?$AAi?$AAa?$AAl?$AAi?$AAz?$AAe?$AAd?$AA?$AN?$AA?6?$AA?$AA@ 1001bf78 LIBCMT:crt0msg.obj - 0002:00000fb8 ??_C@_1EK@HHFLMAOL@?$AAR?$AA6?$AA0?$AA2?$AA8?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAu?$AAn?$AAa?$AAb?$AAl?$AAe?$AA?5?$AAt?$AAo?$AA?5?$AAi?$AAn?$AAi?$AAt?$AAi?$AAa?$AAl?$AAi?$AAz?$AAe?$AA?5?$AAh?$AAe@ 1001bfb8 LIBCMT:crt0msg.obj - 0002:00001008 ??_C@_1GK@MFGOKLAG@?$AAR?$AA6?$AA0?$AA2?$AA7?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAl?$AAo@ 1001c008 LIBCMT:crt0msg.obj - 0002:00001078 ??_C@_1GK@MCAAGJMO@?$AAR?$AA6?$AA0?$AA2?$AA6?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAs?$AAt@ 1001c078 LIBCMT:crt0msg.obj - 0002:000010e8 ??_C@_1EM@MAADIHMB@?$AAR?$AA6?$AA0?$AA2?$AA5?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAp?$AAu?$AAr?$AAe?$AA?5?$AAv?$AAi?$AAr?$AAt?$AAu?$AAa?$AAl?$AA?5?$AAf?$AAu?$AAn?$AAc?$AAt?$AAi?$AAo?$AAn?$AA?5?$AAc@ 1001c0e8 LIBCMT:crt0msg.obj - 0002:00001138 ??_C@_1GK@FHCKBEFA@?$AAR?$AA6?$AA0?$AA2?$AA4?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AA_?$AAo@ 1001c138 LIBCMT:crt0msg.obj - 0002:000011a8 ??_C@_1FC@ECHBIFBC@?$AAR?$AA6?$AA0?$AA1?$AA9?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAu?$AAn?$AAa?$AAb?$AAl?$AAe?$AA?5?$AAt?$AAo?$AA?5?$AAo?$AAp?$AAe?$AAn?$AA?5?$AAc?$AAo?$AAn?$AAs?$AAo?$AAl?$AAe?$AA?5@ 1001c1a8 LIBCMT:crt0msg.obj - 0002:00001200 ??_C@_1EC@JIBHAOPH@?$AAR?$AA6?$AA0?$AA1?$AA8?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAu?$AAn?$AAe?$AAx?$AAp?$AAe?$AAc?$AAt?$AAe?$AAd?$AA?5?$AAh?$AAe?$AAa?$AAp?$AA?5?$AAe?$AAr?$AAr?$AAo?$AAr?$AA?$AN?$AA?6@ 1001c200 LIBCMT:crt0msg.obj - 0002:00001248 ??_C@_1FK@BEOGODMC@?$AAR?$AA6?$AA0?$AA1?$AA7?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAu?$AAn?$AAe?$AAx?$AAp?$AAe?$AAc?$AAt?$AAe?$AAd?$AA?5?$AAm?$AAu?$AAl?$AAt?$AAi?$AAt?$AAh?$AAr?$AAe?$AAa?$AAd?$AA?5@ 1001c248 LIBCMT:crt0msg.obj - 0002:000012a8 ??_C@_1FI@LOGNIKDM@?$AAR?$AA6?$AA0?$AA1?$AA6?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAt?$AAh@ 1001c2a8 LIBCMT:crt0msg.obj - 0002:00001300 ??_C@_1EG@BEHAGFJD@?$AAR?$AA6?$AA0?$AA1?$AA0?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAa?$AAb?$AAo?$AAr?$AAt?$AA?$CI?$AA?$CJ?$AA?5?$AAh?$AAa?$AAs?$AA?5?$AAb?$AAe?$AAe?$AAn?$AA?5?$AAc?$AAa?$AAl?$AAl?$AAe?$AAd@ 1001c300 LIBCMT:crt0msg.obj - 0002:00001348 ??_C@_1FI@HONFMGBI@?$AAR?$AA6?$AA0?$AA0?$AA9?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAe?$AAn@ 1001c348 LIBCMT:crt0msg.obj - 0002:000013a0 ??_C@_1FE@LLNEDJMD@?$AAR?$AA6?$AA0?$AA0?$AA8?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAn?$AAo?$AAt?$AA?5?$AAe?$AAn?$AAo?$AAu?$AAg?$AAh?$AA?5?$AAs?$AAp?$AAa?$AAc?$AAe?$AA?5?$AAf?$AAo?$AAr?$AA?5?$AAa?$AAr@ 1001c3a0 LIBCMT:crt0msg.obj - 0002:000013f8 ??_C@_1FK@PGACCAFB@?$AAR?$AA6?$AA0?$AA0?$AA2?$AA?$AN?$AA?6?$AA?9?$AA?5?$AAf?$AAl?$AAo?$AAa?$AAt?$AAi?$AAn?$AAg?$AA?5?$AAp?$AAo?$AAi?$AAn?$AAt?$AA?5?$AAs?$AAu?$AAp?$AAp?$AAo?$AAr?$AAt?$AA?5@ 1001c3f8 LIBCMT:crt0msg.obj - 0002:00001508 ??_C@_1EK@MBDPDCGA@?$AAM?$AAi?$AAc?$AAr?$AAo?$AAs?$AAo?$AAf?$AAt?$AA?5?$AAV?$AAi?$AAs?$AAu?$AAa?$AAl?$AA?5?$AAC?$AA?$CL?$AA?$CL?$AA?5?$AAR?$AAu?$AAn?$AAt?$AAi?$AAm?$AAe?$AA?5?$AAL?$AAi?$AAb@ 1001c508 LIBCMT:crt0msg.obj - 0002:00001554 ??_C@_15IABLJNFO@?$AA?6?$AA?6?$AA?$AA@ 1001c554 LIBCMT:crt0msg.obj - 0002:0000155c ??_C@_17LGKOMLJ@?$AA?4?$AA?4?$AA?4?$AA?$AA@ 1001c55c LIBCMT:crt0msg.obj - 0002:00001564 ??_C@_1CO@EAEJAADC@?$AA?$DM?$AAp?$AAr?$AAo?$AAg?$AAr?$AAa?$AAm?$AA?5?$AAn?$AAa?$AAm?$AAe?$AA?5?$AAu?$AAn?$AAk?$AAn?$AAo?$AAw?$AAn?$AA?$DO?$AA?$AA@ 1001c564 LIBCMT:crt0msg.obj - 0002:00001594 ??_C@_1DE@JNGNBFGO@?$AAR?$AAu?$AAn?$AAt?$AAi?$AAm?$AAe?$AA?5?$AAE?$AAr?$AAr?$AAo?$AAr?$AA?$CB?$AA?6?$AA?6?$AAP?$AAr?$AAo?$AAg?$AAr?$AAa?$AAm?$AA?3?$AA?5?$AA?$AA@ 1001c594 LIBCMT:crt0msg.obj - 0002:000015c8 __XcptActTab 1001c5c8 LIBCMT:winxfltr.obj - 0002:00001658 __First_FPE_Indx 1001c658 LIBCMT:winxfltr.obj - 0002:0000165c __Num_FPE 1001c65c LIBCMT:winxfltr.obj - 0002:00001660 __XcptActTabSize 1001c660 LIBCMT:winxfltr.obj - 0002:00001664 __XcptActTabCount 1001c664 LIBCMT:winxfltr.obj - 0002:00001668 ??_C@_1BC@GDGBMEMK@?$AAH?$AAH?$AA?3?$AAm?$AAm?$AA?3?$AAs?$AAs?$AA?$AA@ 1001c668 LIBCMT:nlsdata2.obj - 0002:0000167c ??_C@_1CI@KNAKOEBC@?$AAd?$AAd?$AAd?$AAd?$AA?0?$AA?5?$AAM?$AAM?$AAM?$AAM?$AA?5?$AAd?$AAd?$AA?0?$AA?5?$AAy?$AAy?$AAy?$AAy?$AA?$AA@ 1001c67c LIBCMT:nlsdata2.obj - 0002:000016a4 ??_C@_1BC@IEBCMHCM@?$AAM?$AAM?$AA?1?$AAd?$AAd?$AA?1?$AAy?$AAy?$AA?$AA@ 1001c6a4 LIBCMT:nlsdata2.obj - 0002:000016b8 ??_C@_15CLMNNGEL@?$AAP?$AAM?$AA?$AA@ 1001c6b8 LIBCMT:nlsdata2.obj - 0002:000016c0 ??_C@_15ODEHAHHF@?$AAA?$AAM?$AA?$AA@ 1001c6c0 LIBCMT:nlsdata2.obj - 0002:000016c8 ??_C@_1BC@FEMKIFH@?$AAD?$AAe?$AAc?$AAe?$AAm?$AAb?$AAe?$AAr?$AA?$AA@ 1001c6c8 LIBCMT:nlsdata2.obj - 0002:000016dc ??_C@_1BC@BGLIFPF@?$AAN?$AAo?$AAv?$AAe?$AAm?$AAb?$AAe?$AAr?$AA?$AA@ 1001c6dc LIBCMT:nlsdata2.obj - 0002:000016f0 ??_C@_1BA@EPANDLNG@?$AAO?$AAc?$AAt?$AAo?$AAb?$AAe?$AAr?$AA?$AA@ 1001c6f0 LIBCMT:nlsdata2.obj - 0002:00001700 ??_C@_1BE@DKAAMBJL@?$AAS?$AAe?$AAp?$AAt?$AAe?$AAm?$AAb?$AAe?$AAr?$AA?$AA@ 1001c700 LIBCMT:nlsdata2.obj - 0002:00001714 ??_C@_1O@PAHLKOAC@?$AAA?$AAu?$AAg?$AAu?$AAs?$AAt?$AA?$AA@ 1001c714 LIBCMT:nlsdata2.obj - 0002:00001724 ??_C@_19BIFMLPCD@?$AAJ?$AAu?$AAl?$AAy?$AA?$AA@ 1001c724 LIBCMT:nlsdata2.obj - 0002:00001730 ??_C@_19EPFLPGAP@?$AAJ?$AAu?$AAn?$AAe?$AA?$AA@ 1001c730 LIBCMT:nlsdata2.obj - 0002:0000173c ??_C@_1M@GJNLMHFD@?$AAA?$AAp?$AAr?$AAi?$AAl?$AA?$AA@ 1001c73c LIBCMT:nlsdata2.obj - 0002:00001748 ??_C@_1M@IKEENEDF@?$AAM?$AAa?$AAr?$AAc?$AAh?$AA?$AA@ 1001c748 LIBCMT:nlsdata2.obj - 0002:00001754 ??_C@_1BC@JGDDFFAM@?$AAF?$AAe?$AAb?$AAr?$AAu?$AAa?$AAr?$AAy?$AA?$AA@ 1001c754 LIBCMT:nlsdata2.obj - 0002:00001768 ??_C@_1BA@EFMEIEBA@?$AAJ?$AAa?$AAn?$AAu?$AAa?$AAr?$AAy?$AA?$AA@ 1001c768 LIBCMT:nlsdata2.obj - 0002:00001778 ??_C@_17EGKACKIF@?$AAD?$AAe?$AAc?$AA?$AA@ 1001c778 LIBCMT:nlsdata2.obj - 0002:00001780 ??_C@_17BBDMLCIG@?$AAN?$AAo?$AAv?$AA?$AA@ 1001c780 LIBCMT:nlsdata2.obj - 0002:00001788 ??_C@_17FNLKOI@?$AAO?$AAc?$AAt?$AA?$AA@ 1001c788 LIBCMT:nlsdata2.obj - 0002:00001790 ??_C@_17HCHCOKMG@?$AAS?$AAe?$AAp?$AA?$AA@ 1001c790 LIBCMT:nlsdata2.obj - 0002:00001798 ??_C@_17ICPELBCN@?$AAA?$AAu?$AAg?$AA?$AA@ 1001c798 LIBCMT:nlsdata2.obj - 0002:000017a0 ??_C@_17IJPCKHK@?$AAJ?$AAu?$AAl?$AA?$AA@ 1001c7a0 LIBCMT:nlsdata2.obj - 0002:000017a8 ??_C@_17KCJGOCPB@?$AAJ?$AAu?$AAn?$AA?$AA@ 1001c7a8 LIBCMT:nlsdata2.obj - 0002:000017b0 ??_C@_17PNNKMEED@?$AAM?$AAa?$AAy?$AA?$AA@ 1001c7b0 LIBCMT:nlsdata2.obj - 0002:000017b8 ??_C@_17LFPOIHDD@?$AAA?$AAp?$AAr?$AA?$AA@ 1001c7b8 LIBCMT:nlsdata2.obj - 0002:000017c0 ??_C@_17CKNLEDEC@?$AAM?$AAa?$AAr?$AA?$AA@ 1001c7c0 LIBCMT:nlsdata2.obj - 0002:000017c8 ??_C@_17LMDJEKJN@?$AAF?$AAe?$AAb?$AA?$AA@ 1001c7c8 LIBCMT:nlsdata2.obj - 0002:000017d0 ??_C@_17DKNBKCHM@?$AAJ?$AAa?$AAn?$AA?$AA@ 1001c7d0 LIBCMT:nlsdata2.obj - 0002:000017d8 ??_C@_1BC@ENMNNPAJ@?$AAS?$AAa?$AAt?$AAu?$AAr?$AAd?$AAa?$AAy?$AA?$AA@ 1001c7d8 LIBCMT:nlsdata2.obj - 0002:000017ec ??_C@_1O@PDICJHAG@?$AAF?$AAr?$AAi?$AAd?$AAa?$AAy?$AA?$AA@ 1001c7ec LIBCMT:nlsdata2.obj - 0002:000017fc ??_C@_1BC@HHMNLIHE@?$AAT?$AAh?$AAu?$AAr?$AAs?$AAd?$AAa?$AAy?$AA?$AA@ 1001c7fc LIBCMT:nlsdata2.obj - 0002:00001810 ??_C@_1BE@EBOGMDOH@?$AAW?$AAe?$AAd?$AAn?$AAe?$AAs?$AAd?$AAa?$AAy?$AA?$AA@ 1001c810 LIBCMT:nlsdata2.obj - 0002:00001824 ??_C@_1BA@ENFBFFEK@?$AAT?$AAu?$AAe?$AAs?$AAd?$AAa?$AAy?$AA?$AA@ 1001c824 LIBCMT:nlsdata2.obj - 0002:00001834 ??_C@_1O@MMNBFLIA@?$AAM?$AAo?$AAn?$AAd?$AAa?$AAy?$AA?$AA@ 1001c834 LIBCMT:nlsdata2.obj - 0002:00001844 ??_C@_1O@IHNHDHPB@?$AAS?$AAu?$AAn?$AAd?$AAa?$AAy?$AA?$AA@ 1001c844 LIBCMT:nlsdata2.obj - 0002:00001854 ??_C@_17GGIBDPIH@?$AAS?$AAa?$AAt?$AA?$AA@ 1001c854 LIBCMT:nlsdata2.obj - 0002:0000185c ??_C@_17HFOLPPLP@?$AAF?$AAr?$AAi?$AA?$AA@ 1001c85c LIBCMT:nlsdata2.obj - 0002:00001864 ??_C@_17PDPHAADD@?$AAT?$AAh?$AAu?$AA?$AA@ 1001c864 LIBCMT:nlsdata2.obj - 0002:0000186c ??_C@_17CJEDCEPE@?$AAW?$AAe?$AAd?$AA?$AA@ 1001c86c LIBCMT:nlsdata2.obj - 0002:00001874 ??_C@_17BMKGEGOJ@?$AAT?$AAu?$AAe?$AA?$AA@ 1001c874 LIBCMT:nlsdata2.obj - 0002:0000187c ??_C@_17KBOMKBF@?$AAM?$AAo?$AAn?$AA?$AA@ 1001c87c LIBCMT:nlsdata2.obj - 0002:00001884 ??_C@_17MBGCMIPB@?$AAS?$AAu?$AAn?$AA?$AA@ 1001c884 LIBCMT:nlsdata2.obj - 0002:0000188c ??_C@_08JCCMCCIL@HH?3mm?3ss?$AA@ 1001c88c LIBCMT:nlsdata2.obj - 0002:00001898 ??_C@_0BE@CKGJFCPC@dddd?0?5MMMM?5dd?0?5yyyy?$AA@ 1001c898 LIBCMT:nlsdata2.obj - 0002:000018ac ??_C@_08BPBNCDIB@MM?1dd?1yy?$AA@ 1001c8ac LIBCMT:nlsdata2.obj - 0002:000018b8 ??_C@_02CJNFDJBF@PM?$AA@ 1001c8b8 LIBCMT:nlsdata2.obj - 0002:000018bc ??_C@_02DEDBPAFC@AM?$AA@ 1001c8bc LIBCMT:nlsdata2.obj - 0002:000018c0 ??_C@_08EDHMEBNP@December?$AA@ 1001c8c0 LIBCMT:nlsdata2.obj - 0002:000018cc ??_C@_08HCHEGEOA@November?$AA@ 1001c8cc LIBCMT:nlsdata2.obj - 0002:000018d8 ??_C@_07JJNFCEND@October?$AA@ 1001c8d8 LIBCMT:nlsdata2.obj - 0002:000018e0 ??_C@_09BHHEALKD@September?$AA@ 1001c8e0 LIBCMT:nlsdata2.obj - 0002:000018ec ??_C@_06LBBHFDDG@August?$AA@ 1001c8ec LIBCMT:nlsdata2.obj - 0002:000018f4 ??_C@_04MIEPOIFP@July?$AA@ 1001c8f4 LIBCMT:nlsdata2.obj - 0002:000018fc ??_C@_04CNLMGBGM@June?$AA@ 1001c8fc LIBCMT:nlsdata2.obj - 0002:00001904 ??_C@_05DMJDNLEJ@April?$AA@ 1001c904 LIBCMT:nlsdata2.obj - 0002:0000190c ??_C@_05HPCKOFNC@March?$AA@ 1001c90c LIBCMT:nlsdata2.obj - 0002:00001914 ??_C@_08GNJGEPFN@February?$AA@ 1001c914 LIBCMT:nlsdata2.obj - 0002:00001920 ??_C@_07CGJPFGJA@January?$AA@ 1001c920 LIBCMT:nlsdata2.obj - 0002:00001928 ??_C@_03MKABNOCG@Dec?$AA@ 1001c928 LIBCMT:nlsdata2.obj - 0002:0000192c ??_C@_03JPJOFNIA@Nov?$AA@ 1001c92c LIBCMT:nlsdata2.obj - 0002:00001930 ??_C@_03BMAOKBAD@Oct?$AA@ 1001c930 LIBCMT:nlsdata2.obj - 0002:00001934 ??_C@_03GGCAPAJC@Sep?$AA@ 1001c934 LIBCMT:nlsdata2.obj - 0002:00001938 ??_C@_03IFJFEIGA@Aug?$AA@ 1001c938 LIBCMT:nlsdata2.obj - 0002:0000193c ??_C@_03LBGABGKK@Jul?$AA@ 1001c93c LIBCMT:nlsdata2.obj - 0002:00001940 ??_C@_03IDFGHECI@Jun?$AA@ 1001c940 LIBCMT:nlsdata2.obj - 0002:00001944 ??_C@_03CNMDKL@May?$AA@ 1001c944 LIBCMT:nlsdata2.obj - 0002:00001948 ??_C@_03LEOLGMJP@Apr?$AA@ 1001c948 LIBCMT:nlsdata2.obj - 0002:0000194c ??_C@_03ODNJBKGA@Mar?$AA@ 1001c94c LIBCMT:nlsdata2.obj - 0002:00001950 ??_C@_03HJBDCHOM@Feb?$AA@ 1001c950 LIBCMT:nlsdata2.obj - 0002:00001954 ??_C@_03JIHJHPIE@Jan?$AA@ 1001c954 LIBCMT:nlsdata2.obj - 0002:00001958 ??_C@_08INBOOONO@Saturday?$AA@ 1001c958 LIBCMT:nlsdata2.obj - 0002:00001964 ??_C@_06JECMNKMI@Friday?$AA@ 1001c964 LIBCMT:nlsdata2.obj - 0002:0000196c ??_C@_08HACCIKIA@Thursday?$AA@ 1001c96c LIBCMT:nlsdata2.obj - 0002:00001978 ??_C@_09DLIGFAKA@Wednesday?$AA@ 1001c978 LIBCMT:nlsdata2.obj - 0002:00001984 ??_C@_07BAAGCFCM@Tuesday?$AA@ 1001c984 LIBCMT:nlsdata2.obj - 0002:0000198c ??_C@_06JLEDEDGH@Monday?$AA@ 1001c98c LIBCMT:nlsdata2.obj - 0002:00001994 ??_C@_06OOPIFAJ@Sunday?$AA@ 1001c994 LIBCMT:nlsdata2.obj - 0002:0000199c ??_C@_03FEFJNEK@Sat?$AA@ 1001c99c LIBCMT:nlsdata2.obj - 0002:000019a0 ??_C@_03IDIOELNC@Fri?$AA@ 1001c9a0 LIBCMT:nlsdata2.obj - 0002:000019a4 ??_C@_03IOFIKPDN@Thu?$AA@ 1001c9a4 LIBCMT:nlsdata2.obj - 0002:000019a8 ??_C@_03MHOMLAJA@Wed?$AA@ 1001c9a8 LIBCMT:nlsdata2.obj - 0002:000019ac ??_C@_03NAGEINEP@Tue?$AA@ 1001c9ac LIBCMT:nlsdata2.obj - 0002:000019b0 ??_C@_03PDAGKDH@Mon?$AA@ 1001c9b0 LIBCMT:nlsdata2.obj - 0002:000019b4 ??_C@_03KOEHGMDN@Sun?$AA@ 1001c9b4 LIBCMT:nlsdata2.obj - 0002:000019b8 ___lookuptable_s 1001c9b8 LIBCMT:outputs.obj - 0002:00001a30 ??_C@_1CK@HHHCOJPD@?$AAE?$AAn?$AAv?$AAi?$AAr?$AAo?$AAn?$AAm?$AAe?$AAn?$AAt?$AAD?$AAi?$AAr?$AAe?$AAc?$AAt?$AAo?$AAr?$AAy?$AA?$AA@ 1001ca30 LIBCMT:_pdblkup_.obj - 0002:00001a60 ??_C@_1FM@DNCABAP@?$AAS?$AAO?$AAF?$AAT?$AAW?$AAA?$AAR?$AAE?$AA?2?$AAM?$AAi?$AAc?$AAr?$AAo?$AAs?$AAo?$AAf?$AAt?$AA?2?$AAV?$AAi?$AAs?$AAu?$AAa?$AAl?$AAS?$AAt?$AAu?$AAd?$AAi?$AAo?$AA?2@ 1001ca60 LIBCMT:_pdblkup_.obj - 0002:00001abc ??_C@_0M@HLOHPNFA@RegCloseKey?$AA@ 1001cabc LIBCMT:_pdblkup_.obj - 0002:00001ac8 ??_C@_0BB@GLNAEDBD@RegQueryValueExW?$AA@ 1001cac8 LIBCMT:_pdblkup_.obj - 0002:00001adc ??_C@_0O@COHOBMLB@RegOpenKeyExW?$AA@ 1001cadc LIBCMT:_pdblkup_.obj - 0002:00001aec ??_C@_01KDCPPGHE@r?$AA@ 1001caec LIBCMT:_pdblkup_.obj - 0002:00001af0 ??_C@_0BB@KCIACLNC@PDBOpenValidate5?$AA@ 1001caf0 LIBCMT:_pdblkup_.obj - 0002:00001b04 ??_C@_0BI@DFKBFLJE@GetProcessWindowStation?$AA@ 1001cb04 LIBCMT:crtmboxw.obj - 0002:00001b1c ??_C@_0BK@DEKFELLI@GetUserObjectInformationW?$AA@ 1001cb1c LIBCMT:crtmboxw.obj - 0002:00001b38 ??_C@_0BD@HHGDFDBJ@GetLastActivePopup?$AA@ 1001cb38 LIBCMT:crtmboxw.obj - 0002:00001b4c ??_C@_0BA@HNOPNCHB@GetActiveWindow?$AA@ 1001cb4c LIBCMT:crtmboxw.obj - 0002:00001b5c ??_C@_0M@DLDCCGNP@MessageBoxW?$AA@ 1001cb5c LIBCMT:crtmboxw.obj - 0002:00001b68 ??_C@_1BG@GOEBHBDC@?$AAU?$AAS?$AAE?$AAR?$AA3?$AA2?$AA?4?$AAD?$AAL?$AAL?$AA?$AA@ 1001cb68 LIBCMT:crtmboxw.obj - 0002:00001c00 ___newctype 1001cc00 LIBCMT:ctype.obj - 0002:00001f00 __wctype 1001cf00 LIBCMT:ctype.obj - 0002:00002108 ___newclmap 1001d108 LIBCMT:ctype.obj - 0002:00002288 ___newcumap 1001d288 LIBCMT:ctype.obj - 0002:00002408 ??_C@_1BA@BALFACEM@?$AAC?$AAO?$AAN?$AAO?$AAU?$AAT?$AA$?$AA?$AA@ 1001d408 LIBCMT:initcon.obj - 0002:00002418 ??_C@_06IMKFLFPG@1?$CDQNAN?$AA@ 1001d418 LIBCMT:_x10fout_.obj - 0002:00002420 ??_C@_05DNEBIAHO@1?$CDINF?$AA@ 1001d420 LIBCMT:_x10fout_.obj - 0002:00002428 ??_C@_05PHHOCPM@1?$CDIND?$AA@ 1001d428 LIBCMT:_x10fout_.obj - 0002:00002430 ??_C@_06PGGFOGJG@1?$CDSNAN?$AA@ 1001d430 LIBCMT:_x10fout_.obj - 0002:00002438 __load_config_used 1001d438 LIBCMT:loadcfg.obj - 0002:00002480 ___safe_se_handler_table 1001d480 - 0002:0000248c ___rtc_iaa 1001d48c LIBCMT:_initsect_.obj - 0002:00002490 ___rtc_izz 1001d490 LIBCMT:_initsect_.obj - 0002:00002494 ___rtc_taa 1001d494 LIBCMT:_initsect_.obj - 0002:00002498 ___rtc_tzz 1001d498 LIBCMT:_initsect_.obj - 0002:0000293c __IMPORT_DESCRIPTOR_ADVAPI32 1001d93c Advapi32:ADVAPI32.dll - 0002:00002950 __IMPORT_DESCRIPTOR_USER32 1001d950 User32:USER32.dll - 0002:00002964 __IMPORT_DESCRIPTOR_KERNEL32 1001d964 kernel32:KERNEL32.dll - 0002:00002978 __NULL_IMPORT_DESCRIPTOR 1001d978 Advapi32:ADVAPI32.dll - 0003:00000038 _SVN_REV 1001f038 api.obj - 0003:00003f20 ___security_cookie 10022f20 LIBCMT:gs_cookie.obj - 0003:00003f24 ___security_cookie_complement 10022f24 LIBCMT:gs_cookie.obj - 0003:000040a0 __fltused 100230a0 LIBCMT:_fpinit_.obj - 0003:000040a4 __ldused 100230a4 LIBCMT:_fpinit_.obj - 0003:000040a8 __iob 100230a8 LIBCMT:_file.obj - 0003:00004330 ___nullstring 10023330 LIBCMT:output.obj - 0003:00004334 ___wnullstring 10023334 LIBCMT:output.obj - 0003:00004338 ?_RTC_ErrorLevels@@3PAHA 10023338 LIBCMT:_error_.obj - 0003:00004350 __timezone 10023350 LIBCMT:timeset.obj - 0003:00004354 __daylight 10023354 LIBCMT:timeset.obj - 0003:00004358 __dstbias 10023358 LIBCMT:timeset.obj - 0003:000043e0 __tzname 100233e0 LIBCMT:timeset.obj - 0003:00004400 ___flsindex 10023400 LIBCMT:tidtable.obj - 0003:00004404 ___getvalueindex 10023404 LIBCMT:tidtable.obj - 0003:00004408 __lookuptrailbytes 10023408 LIBCMT:read.obj - 0003:00004508 ___badioinfo 10023508 LIBCMT:ioinit.obj - 0003:00004550 __cfltcvt_tab 10023550 LIBCMT:cmiscdat.obj - 0003:00004698 __sys_errlist 10023698 LIBCMT:syserr.obj - 0003:00004748 __sys_nerr 10023748 LIBCMT:syserr.obj - 0003:00004750 ___initialmbcinfo 10023750 LIBCMT:mbctype.obj - 0003:00004970 __mbctype 10023970 LIBCMT:mbctype.obj - 0003:00004a78 __mbcasemap 10023a78 LIBCMT:mbctype.obj - 0003:00004b78 ___ptmbcinfo 10023b78 LIBCMT:mbctype.obj - 0003:00004c70 ___globallocalestatus 10023c70 LIBCMT:glstatus.obj - 0003:00004c74 ___clocalestr 10023c74 LIBCMT:nlsdata2.obj - 0003:00004c78 ___lc_time_c 10023c78 LIBCMT:nlsdata2.obj - 0003:00004de0 ___initiallocinfo 10023de0 LIBCMT:nlsdata2.obj - 0003:00004eb8 ___ptlocinfo 10023eb8 LIBCMT:nlsdata2.obj - 0003:00004ebc ___initiallocalestructinfo 10023ebc LIBCMT:nlsdata2.obj - 0003:00004ed0 __lpdays 10023ed0 LIBCMT:days.obj - 0003:00004f04 __days 10023f04 LIBCMT:days.obj - 0003:00004f40 ___abort_behavior 10023f40 LIBCMT:abort.obj - 0003:00004f50 ___lconv_static_decimal 10023f50 LIBCMT:lconv.obj - 0003:00004f54 ___lconv_static_W_decimal 10023f54 LIBCMT:lconv.obj - 0003:00004f58 ___lconv_c 10023f58 LIBCMT:lconv.obj - 0003:00004fa8 ___lconv 10023fa8 LIBCMT:lconv.obj - 0003:00004fac __pctype 10023fac LIBCMT:ctype.obj - 0003:00004fb0 __pwctype 10023fb0 LIBCMT:ctype.obj - 0003:00004fb4 __confh 10023fb4 LIBCMT:initcon.obj - 0003:00004fc0 __NLG_Destination 10023fc0 LIBCMT:exsup.obj - 0003:00005000 ___mb_cur_max 10024000 LIBCMT:nlsdata1.obj - 0003:00005004 ___decimal_point 10024004 LIBCMT:nlsdata1.obj - 0003:00005008 ___decimal_point_length 10024008 LIBCMT:nlsdata1.obj - 0003:00005010 __pow10pos 10024010 LIBCMT:_constpow_.obj - 0003:00005170 __pow10neg 10024170 LIBCMT:_constpow_.obj - 0003:00005300 _svn_rev 10024300 api.obj - 0003:00005380 _default_xbee 10024380 api.obj - 0003:00005384 _glob_hModule 10024384 api.obj - 0003:00005388 _win32_hWnd 10024388 api.obj - 0003:0000538c _win32_MessageID 1002438c api.obj - 0003:00005398 ___fastflag 10024398 LIBCMT:_fpinit_.obj - 0003:0000539c __cflush 1002439c LIBCMT:_file.obj - 0003:000053a0 __umaskval 100243a0 LIBCMT:crt0dat.obj - 0003:000053a4 ___argc 100243a4 LIBCMT:crt0dat.obj - 0003:000053a8 ___argv 100243a8 LIBCMT:crt0dat.obj - 0003:000053ac ___wargv 100243ac LIBCMT:crt0dat.obj - 0003:000053b0 __environ 100243b0 LIBCMT:crt0dat.obj - 0003:000053b4 ___initenv 100243b4 LIBCMT:crt0dat.obj - 0003:000053b8 __wenviron 100243b8 LIBCMT:crt0dat.obj - 0003:000053bc ___winitenv 100243bc LIBCMT:crt0dat.obj - 0003:000053c0 __pgmptr 100243c0 LIBCMT:crt0dat.obj - 0003:000053c4 __wpgmptr 100243c4 LIBCMT:crt0dat.obj - 0003:000053c8 __exitflag 100243c8 LIBCMT:crt0dat.obj - 0003:000053cc __C_Termination_Done 100243cc LIBCMT:crt0dat.obj - 0003:000053d0 __C_Exit_Done 100243d0 LIBCMT:crt0dat.obj - 0003:000053d8 __aenvptr 100243d8 LIBCMT:dllcrt0.obj - 0003:000053dc __wenvptr 100243dc LIBCMT:dllcrt0.obj - 0003:000053e0 ___error_mode 100243e0 LIBCMT:dllcrt0.obj - 0003:000053e4 ___app_type 100243e4 LIBCMT:dllcrt0.obj - 0003:000053e8 ___pInvalidArgHandler 100243e8 LIBCMT:invarg.obj - 0003:00005714 ?wsprintffp@@3P6AHPADPBDZZA 10024714 LIBCMT:_error_.obj - 0003:00005720 __crtheap 10024720 LIBCMT:heapinit.obj - 0003:000057e0 _gpFlsAlloc 100247e0 LIBCMT:tidtable.obj - 0003:000057e4 _gpFlsGetValue 100247e4 LIBCMT:tidtable.obj - 0003:000057e8 _gpFlsSetValue 100247e8 LIBCMT:tidtable.obj - 0003:000057ec _gpFlsFree 100247ec LIBCMT:tidtable.obj - 0003:000057f0 __stdbuf 100247f0 LIBCMT:_sftbuf.obj - 0003:00005948 __maxwait 10024948 LIBCMT:crtheap.obj - 0003:0000594c ?__pInconsistency@@3P6AXXZA 1002494c LIBCMT:hooks.obj - 0003:00005968 ___pPurecall 10024968 LIBCMT:inithelp.obj - 0003:0000596c ?_pnhHeap@@3P6AHI@ZA 1002496c LIBCMT:handler.obj - 0003:00005f98 __newmode 10024f98 LIBCMT:_newmode.obj - 0003:000060ac ___mbulinfo 100250ac LIBCMT:mbctype.obj - 0003:000060b8 ___mbcodepage 100250b8 LIBCMT:mbctype.obj - 0003:000060bc ___ismbcodepage 100250bc LIBCMT:mbctype.obj - 0003:000060c0 ___mblcid 100250c0 LIBCMT:mbctype.obj - 0003:000060d0 __fmode 100250d0 LIBCMT:txtmode.obj - 0003:000060d4 __outputformat 100250d4 LIBCMT:outputformat.obj - 0003:000060ec ___locale_changed 100250ec LIBCMT:setlocal.obj - 0003:000060f0 ___lconv_static_null 100250f0 LIBCMT:lconv.obj - 0003:000060f4 ___lconv_static_W_null 100250f4 LIBCMT:lconv.obj - 0003:000060f8 __commode 100250f8 LIBCMT:ncommode.obj - 0003:000060fc _xbee_hnd_mutex 100250fc - 0003:00006100 __debugger_hook_dummy 10025100 - 0003:00006104 ___sse2_available 10025104 - 0003:00006108 __pDestructExceptionObject 10025108 - 0003:0000610c __nhandle 1002510c - 0003:00006120 ___pioinfo 10025120 - 0003:00006220 __acmdln 10025220 - 0003:00006224 ___env_initialized 10025224 - 0003:00006228 ___onexitend 10025228 - 0003:0000622c ___onexitbegin 1002522c - 0003:00006230 ___mbctype_initialized 10025230 - 0003:00006234 ___dyn_tls_init_callback 10025234 - 0003:00006238 ___piob 10025238 - 0003:00006240 __bufin 10025240 - 0003:00007240 __nstream 10026240 - - entry point at 0001:0000da7c - - Static symbols - - 0001:00001190 _xbee_logf 10002190 f api.obj - 0001:00001a00 _Xfree2@4 10002a00 f api.obj - 0001:00002af0 _init_serial@8 10003af0 f api.obj - 0001:00002db0 _Xmalloc2@8 10003db0 f api.obj - 0001:00002e50 _Xcalloc2@8 10003e50 f api.obj - 0001:00002ef0 _xbee_startAPI@4 10003ef0 f api.obj - 0001:00003110 _xbee_sendAT@16 10004110 f api.obj - 0001:00003140 _xbee_sendATdelay@20 10004140 f api.obj - 0001:00003630 _xbee_select@8 10004630 f api.obj - 0001:00005820 _xbee_matchpktcon@12 10006820 f api.obj - 0001:00005b20 _xbee_listen_wrapper@4 10006b20 f api.obj - 0001:00005c30 _xbee_listen@4 10006c30 f api.obj - 0001:00008d10 _Xrealloc2@12 10009d10 f api.obj - 0001:00008d70 _xbee_parse_io@24 10009d70 f api.obj - 0001:00009480 _xbee_callbackWrapper@4 1000a480 f api.obj - 0001:00009970 _xbee_thread_watch@4 1000a970 f api.obj - 0001:00009bb0 _xbee_getbyte@4 1000abb0 f api.obj - 0001:00009c00 _xbee_getrawbyte@4 1000ac00 f api.obj - 0001:00009dc0 __xbee_send_pkt@12 1000adc0 f api.obj - 0001:0000a2e0 _xbee_make_pkt@12 1000b2e0 f api.obj - 0001:0000a7d1 __tsopen_nolock 1000b7d1 f LIBCMT:open.obj - 0001:0000b225 _flsall 1000c225 f LIBCMT:fflush.obj - 0001:0000bae4 _doexit 1000cae4 f LIBCMT:crt0dat.obj - 0001:0000d986 ___DllMainCRTStartup 1000e986 f LIBCMT:dllcrt0.obj - 0001:0000dc8a _write_char 1000ec8a f LIBCMT:output.obj - 0001:0000dcbd _write_string 1000ecbd f LIBCMT:output.obj - 0001:0000eb56 ?DebuggerProbe@@YAHK@Z 1000fb56 f LIBCMT:_error_.obj - 0001:0000eba6 ?DebuggerRuntime@@YAHKHPAXPB_W@Z 1000fba6 f LIBCMT:_error_.obj - 0001:0000ec08 ?failwithmessage@@YAXPAXHHPBD@Z 1000fc08 f LIBCMT:_error_.obj - 0001:0000f019 __tzset_nolock 10010019 f LIBCMT:tzset.obj - 0001:0001167d __shift 1001267d f LIBCMT:cvt.obj - 0001:000116c2 __cftoe2_l 100126c2 f LIBCMT:cvt.obj - 0001:00011c80 __cftof2_l 10012c80 f LIBCMT:cvt.obj - 0001:000127f2 _siglookup 100137f2 f LIBCMT:winsig.obj - 0001:00012a2e __onexit_nolock 10013a2e f LIBCMT:onexit.obj - 0001:00012f9c ?strtoxl@@YAKPAUlocaleinfo_struct@@PBDPAPBDHH@Z 10013f9c f LIBCMT:strtol.obj - 0001:0001349b _parse_cmdline 1001449b f LIBCMT:stdargv.obj - 0001:00013a2b ?CPtoLCID@@YAHH@Z 10014a2b f LIBCMT:mbctype.obj - 0001:00013a5a ?setSBCS@@YAXPAUthreadmbcinfostruct@@@Z 10014a5a f LIBCMT:mbctype.obj - 0001:00013abe ?setSBUpLow@@YAXPAUthreadmbcinfostruct@@@Z 10014abe f LIBCMT:mbctype.obj - 0001:00013cf2 ?getSystemCP@@YAHH@Z 10014cf2 f LIBCMT:mbctype.obj - 0001:000146bd ?GetPdbDll@@YAPAUHINSTANCE__@@XZ 100156bd f LIBCMT:_pdblkup_.obj - 0001:00014e40 __unwind_handler4 10015e40 f LIBCMT:exsup4.obj - 0001:00015e34 ?x_ismbbtype_l@@YAHPAUlocaleinfo_struct@@IHH@Z 10016e34 f LIBCMT:ismbbyte.obj - 0001:00015e9f ?__crtLCMapStringA_stat@@YAHPAUlocaleinfo_struct@@KKPBDHPADHHH@Z 10016e9f f LIBCMT:a_map.obj - 0001:000160cc ?__crtGetStringTypeA_stat@@YAHPAUlocaleinfo_struct@@KPBDHPAGHHH@Z 100170cc f LIBCMT:a_str.obj - 0001:000168c0 __unwind_handler 100178c0 f LIBCMT:exsup.obj - 0001:0001844d __hw_cw 1001944d f LIBCMT:_ieee87_.obj - 0001:000184db ___hw_cw_sse2 100194db f LIBCMT:_ieee87_.obj - 0001:00018950 ?__crtCompareStringA_stat@@YAHPAUlocaleinfo_struct@@KKPBDH1HH@Z 10019950 f LIBCMT:a_cmp.obj - 0001:00018cea _findenv 10019cea f LIBCMT:setenv.obj - 0001:00018d3c _copy_environ 10019d3c f LIBCMT:setenv.obj - 0001:000196ea __dup_nolock 1001a6ea f LIBCMT:dup.obj diff --git a/libs/thirdParty/libxbee/main.c b/libs/thirdParty/libxbee/main.c deleted file mode 100644 index 7dcacafa7..000000000 --- a/libs/thirdParty/libxbee/main.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -#include "xbee.h" - -int main(int argc, char *argv[]) { - xbee_con *con, *con2; - xbee_pkt *pkt, *p; - - if (xbee_setuplog("/dev/ttyUSB0",57600,2) == -1) { - perror("xbee_setuplog()"); - exit(1); - } - if (argc >= 2 && !strcmp(argv[1],"sleep")) { - for (;;) { - sleep(86400); /* sleep for a day... forever :) */ - } - } - - /*if ((con = xbee_newcon(NULL,'X',xbee_localAT)) == (void *)-1) { - printf("error creating connection...\n"); - exit(1); - } - - while(1){sleep(10);} - - xbee_senddata(con,"CH%c",0x0C); - sleep(1); - xbee_senddata(con,"ID%c%c",0x33, 0x32); - sleep(1); - xbee_senddata(con,"DH%c%c%c%c",0x00,0x00,0x00,0x00); - sleep(1); - xbee_senddata(con,"DL%c%c%c%c",0x00,0x00,0x00,0x00); - sleep(1); - xbee_senddata(con,"MY%c%c",0x00,0x00); - sleep(1); - // SH - read only - // SL - read only - xbee_senddata(con,"RR%c",0x00); - sleep(1); - xbee_senddata(con,"RN%c",0x00); - sleep(1); - xbee_senddata(con,"MM%c",0x00); - sleep(1); - xbee_senddata(con,"NT%c",0x19); - sleep(1); - xbee_senddata(con,"NO%c",0x00); - sleep(1); - xbee_senddata(con,"CE%c",0x00); - sleep(1); - xbee_senddata(con,"SC%c%c",0x1F,0xFE); - sleep(1); - xbee_senddata(con,"SD%c",0x04); - sleep(1); - xbee_senddata(con,"A1%c",0x00); - sleep(1); - xbee_senddata(con,"A2%c",0x00); - sleep(1); - // AI - read only - xbee_senddata(con,"EE%c",0x00); - sleep(1); - //xbee_senddata(con,"KY%c",0x00); - //sleep(1); - xbee_senddata(con,"NI%s","TIGGER"); - sleep(1); - xbee_senddata(con,"PL%c",0x04); - sleep(1); - xbee_senddata(con,"CA%c",0x2C); - sleep(1); - xbee_senddata(con,"SM%c",0x00); - sleep(1); - xbee_senddata(con,"ST%c%c",0x13,0x88); - sleep(1); - xbee_senddata(con,"SP%c%c",0x00,0x00); - sleep(1); - xbee_senddata(con,"DP%c%c",0x03,0xE8); - sleep(1); - xbee_senddata(con,"SO%c",0x00); - sleep(1); - xbee_senddata(con,"BD%c",0x06); - sleep(1); - xbee_senddata(con,"RO%c",0x03); - sleep(1); - xbee_senddata(con,"AP%c",0x02); - sleep(1); - xbee_senddata(con,"PR%c",0xFF); - sleep(1); - xbee_senddata(con,"D8%c",0x00); - sleep(1); - xbee_senddata(con,"D7%c",0x01); - sleep(1); - xbee_senddata(con,"D6%c",0x00); - sleep(1); - xbee_senddata(con,"D5%c",0x01); - sleep(1); - xbee_senddata(con,"D4%c",0x00); - sleep(1); - xbee_senddata(con,"D3%c",0x00); - sleep(1); - xbee_senddata(con,"D2%c",0x00); - sleep(1); - xbee_senddata(con,"D1%c",0x00); - sleep(1); - xbee_senddata(con,"D0%c",0x00); - sleep(1); - xbee_senddata(con,"IU%c",0x00); - sleep(1); - xbee_senddata(con,"IT%c",0x01); - sleep(1); - xbee_senddata(con,"IC%c",0x00); - sleep(1); - xbee_senddata(con,"IR%c%c",0x00,0x00); - sleep(1); - xbee_senddata(con,"IA%c%c%c%c%c%c%c%c",0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF); - sleep(1); - xbee_senddata(con,"T0%c",0xFF); - sleep(1); - xbee_senddata(con,"T1%c",0xFF); - sleep(1); - xbee_senddata(con,"T2%c",0xFF); - sleep(1); - xbee_senddata(con,"T3%c",0xFF); - sleep(1); - xbee_senddata(con,"T4%c",0xFF); - sleep(1); - xbee_senddata(con,"T5%c",0xFF); - sleep(1); - xbee_senddata(con,"T6%c",0xFF); - sleep(1); - xbee_senddata(con,"T7%c",0xFF); - sleep(1); - xbee_senddata(con,"P0%c",0x01); - sleep(1); - xbee_senddata(con,"P1%c",0x00); - sleep(1); - xbee_senddata(con,"PT%c",0xFF); - sleep(1); - xbee_senddata(con,"RP%c",0x28); - sleep(1); - // VR - read only - // HV - read only - // DB - read only - // EC - read only - // EA - read only - // DD - read only - xbee_senddata(con,"CT%c",0x64); - sleep(1); - xbee_senddata(con,"GT%c%c",0x03,0xE8); - sleep(1); - xbee_senddata(con,"CC%c",0x2B); - sleep(1); - - sleep(10); - */ - - /* test 64bit IO and Data */ - con = xbee_newcon('I',xbee_64bitIO, 0x0013A200, 0x403af247); - con2 = xbee_newcon('I',xbee_64bitData, 0x0013A200, 0x403af247); - - while (1) { - while ((pkt = xbee_getpacket(con)) != NULL) { - int i; - for (i = 0; i < pkt->samples; i++) { - int m; - for (m = 0; m <= 8; m++) { - if (xbee_hasdigital(pkt,i,m)) printf("D%d: %d ",m,xbee_getdigital(pkt,i,m)); - } -#define Vref 3.23 - for (m = 0; m <= 5; m++) { - if (xbee_hasanalog(pkt,i,m)) printf("A%d: %.2fv ",m,xbee_getanalog(pkt,i,m,Vref)); - } - printf("\n"); - } - if (xbee_senddata(con2, "the time is %d\r", time(NULL))) { - printf("Error: xbee_senddata\n"); - return 1; - } - free(pkt); - if (p) { - switch (p->status) { - case 0x01: printf("XBee: txStatus: No ACK\n"); break; - case 0x02: printf("XBee: txStatus: CCA Failure\n"); break; - case 0x03: printf("XBee: txStatus: Purged\n"); break; - } - free(p); - } - } - while ((pkt = xbee_getpacket(con2)) != NULL) { - printf("he said '%s'\n", pkt->data); - if (xbee_senddata(con2, "you said '%s'\r", pkt->data)) { - printf("Error: xbee_senddata\n"); - return 1; - } - free(pkt); - if (p) { - switch (p->status) { - case 0x01: printf("XBee: txStatus: No ACK\n"); break; - case 0x02: printf("XBee: txStatus: CCA Failure\n"); break; - case 0x03: printf("XBee: txStatus: Purged\n"); break; - } - free(p); - } - } - usleep(100); - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/makefile b/libs/thirdParty/libxbee/makefile deleted file mode 100644 index 11b9d3fa6..000000000 --- a/libs/thirdParty/libxbee/makefile +++ /dev/null @@ -1,226 +0,0 @@ -#-- set this to the man directory you would like to use -MANPATH:=/usr/share/man - -#-- uncomment this to enable debugging -#DEBUG:=-g -DDEBUG - - -###### YOU SHOULD NOT CHANGE BELOW THIS LINE ###### - -VERSION:=1.4.1 -SHELL:=/bin/bash -SRCS:=api.c -MANS:=man3/libxbee.3 \ - man3/xbee_con.3 \ - man3/xbee_end.3 \ - man3/xbee_endcon.3 \ - man3/xbee_flushcon.3 \ - man3/xbee_purgecon.3 \ - man3/xbee_getanalog.3 \ - man3/xbee_getdigital.3 \ - man3/xbee_getpacket.3 \ - man3/xbee_hasanalog.3 \ - man3/xbee_hasdigital.3 \ - man3/xbee_logit.3 \ - man3/xbee_newcon.3 \ - man3/xbee_nsenddata.3 \ - man3/xbee_pkt.3 \ - man3/xbee_senddata.3 \ - man3/xbee_setup.3 \ - man3/xbee_setupAPI.3 \ - man3/xbee_setuplog.3 \ - man3/xbee_setuplogAPI.3 \ - man3/xbee_vsenddata.3 -MANPATHS:=$(foreach dir,$(shell ls man -ln | grep ^d | tr -s ' ' | cut -d ' ' -f 9),${MANPATH}/$(dir)) - -PDFS:=${SRCS} ${SRCS:.c=.h} makefile main.c xbee.h - -CC:=gcc -CFLAGS:=-Wall -Wstrict-prototypes -Wno-variadic-macros -pedantic -c -fPIC ${DEBUG} -CLINKS:=-lpthread -lrt ${DEBUG} -DEFINES:= - -ifeq ($(strip $(wildcard ${MANPATH}/man3/libxbee.3.bz2)),) -FIRSTTIME:=TRUE -else -FIRSTTIME:=FALSE -endif - -ENSCRIPT:=-MA4 --color -f Courier8 -C --margins=15:15:0:20 -ifneq ($(strip $(wildcard /usr/share/enscript/mine-web.hdr)),) - ENSCRIPT+= --fancy-header=mine-web -else - ENSCRIPT+= --fancy-header=a2ps -endif - -SRCS:=${sort ${SRCS}} -PDFS:=${sort ${PDFS}} - -.PHONY: FORCE -.PHONY: all run new clean cleanpdfs main pdfs html -.PHONY: install install_su install_man -.PHONY: uninstall uninstall_su uninstall_man/ - - -# all - do everything (default) # -all: ./lib/libxbee.so.$(VERSION) - @echo "*** Done! ***" - - -# run - remake main and then run # -run: all main - ./bin/main - - -# new - clean and do everything again # -new: clean all - - -# clean - remove any compiled files and PDFs # -clean: - rm -f ./*~ - rm -f ./sample/*~ - rm -f ./obj/*.o - rm -f ./lib/libxbee.so* - rm -f ./bin/main - -cleanpdfs: - rm -f ./pdf/*.pdf - - -# install - installs library # -install: ./lib/libxbee.so.$(VERSION) - @echo - @echo -ifneq ($(shell echo $$USER),root) - @echo "#######################################################################################" - @echo "### To Install this library I need the root password please!" - @echo "#######################################################################################" -endif - su -c "make install_su --no-print-directory" - @echo -ifeq (${FIRSTTIME},TRUE) - @echo "#######################################################################################" - @echo - @pr -h "" -o 3 -w 86 -tT ./README - @echo - @echo "#######################################################################################" -endif - -install_su: /usr/lib/libxbee.so.$(VERSION) /usr/include/xbee.h install_man - -/usr/lib/libxbee.so.$(VERSION): ./lib/libxbee.so.$(VERSION) - cp ./lib/libxbee.so.$(VERSION) /usr/lib/libxbee.so.$(VERSION) -f - @chmod 755 /usr/lib/libxbee.so.$(VERSION) - @chown root:root /usr/lib/libxbee.so.$(VERSION) - ln ./libxbee.so.$(VERSION) /usr/lib/libxbee.so.1 -sf - @chown root:root /usr/lib/libxbee.so.1 - ln ./libxbee.so.$(VERSION) /usr/lib/libxbee.so -sf - @chown root:root /usr/lib/libxbee.so - -/usr/include/xbee.h: ./xbee.h - cp ./xbee.h /usr/include/xbee.h -f - @chmod 644 /usr/include/xbee.h - @chown root:root /usr/include/xbee.h - -install_man: ${MANPATH} ${MANPATHS} ${addsuffix .bz2,${addprefix ${MANPATH}/,${MANS}}} - -${MANPATH} ${MANPATHS}: - @echo "#######################################################################################" - @echo "### $@ does not exist... cannot install man files here!" - @echo "### Please check the directory and the MANPATH variable in the makefile" - @echo "#######################################################################################" - @false - -${MANPATH}/%.bz2: ./man/% - @echo "cat $< | bzip2 -z > $@" - @cat $< | bzip2 -z > $@ || ( \ - echo "#######################################################################################"; \ - echo "### Installing man page '$*' to '$@' failed..."; \ - echo "#######################################################################################"; ) - @chmod 644 $@ - @chown root:root $@ - -./doc/: - mkdir ./doc/ - -html: ./doc/ ./man/ - cd ./doc/; mkdir -p `find ../man/ -type d -not -path *.svn* | cut -b 2-`; - find ./man/ -type f -not -path *.svn* | cut -d / -f 3- | sort > .html_todo - for item in `cat .html_todo`; do \ - man2html -r ./man/$$item | tail -n +3 > ./doc/man/$$item.html; \ - done 2> /dev/null - rm .html_todo - -uninstall: - @echo - @echo -ifneq ($(shell echo $$USER),root) - @echo "#######################################################################################" - @echo "### To Uninstall this library I need the root password please!" - @echo "#######################################################################################" -endif - su -c "make uninstall_su --no-print-directory" - @echo - @echo - -uninstall_su: ${addprefix uninstall_man/,${MANS}} - rm /usr/lib/libxbee.so.$(VERSION) -f - rm /usr/lib/libxbee.so.1 -f - rm /usr/lib/libxbee.so -f - rm /usr/include/xbee.h -f - -uninstall_man/%: - rm ${MANPATH}/$*.bz2 -f - -# main - compile & link objects # -main: ./bin/main - -./bin/main: ./obj/api.o ./bin/ ./main.c - ${CC} ${CLINKS} ./main.c ./obj/api.o -o ./bin/main ${DEBUG} - -./bin/: - mkdir ./bin/ - -./lib/libxbee.so.$(VERSION): ./lib/ ${addprefix ./obj/,${SRCS:.c=.o}} ./xbee.h - gcc -shared -Wl,-soname,libxbee.so.1 $(CLINKS) -o ./lib/libxbee.so.$(VERSION) ./obj/*.o - ln ./libxbee.so.$(VERSION) ./lib/libxbee.so.1 -sf - ln ./libxbee.so.$(VERSION) ./lib/libxbee.so -sf - -./lib/: - mkdir ./lib/ - -./obj/: - mkdir ./obj/ - -./obj/%.o: ./obj/ %.c %.h xbee.h - ${CC} ${CFLAGS} ${DEFINES} ${DEBUG} $*.c -o $@ - -./obj/%.o: ./obj/ %.c xbee.h - ${CC} ${CFLAGS} ${DEFINES} ${DEBUG} $*.c -o $@ - - -# pdfs - generate PDFs for each source file # -ifneq ($(strip $(wildcard /usr/bin/ps2pdf)),) -ifneq ($(strip $(wildcard /usr/bin/enscript)),) -pdfs: ./pdf/ ${addprefix ./pdf/,${addsuffix .pdf,${PDFS}}} - -./pdf/: - mkdir ./pdf/ - -./pdf/makefile.pdf: ./makefile - enscript ${ENSCRIPT} -Emakefile $< -p - | ps2pdf - $@ - -./pdf/%.pdf: % - enscript ${ENSCRIPT} -Ec $< -p - | ps2pdf - $@ - -./pdf/%.pdf: - @echo "*** Cannot make $@ - '$*' does not exist ***" -else -pdfs: - @echo "WARNING: enscript is not installed - cannot generate PDF files" -endif -else -pdfs: - @echo "WARNING: ps2pdf is not installed - cannot generate PDF files" -endif diff --git a/libs/thirdParty/libxbee/man/man3/libxbee.3 b/libs/thirdParty/libxbee/man/man3/libxbee.3 deleted file mode 100644 index 6bcb978f6..000000000 --- a/libs/thirdParty/libxbee/man/man3/libxbee.3 +++ /dev/null @@ -1,91 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH LIBXBEE 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -libxbee -.SH DESCRIPTION -libxbee is a C library to aid the use of Series 1 Digi XBee radios running in API mode (AP=2). -.sp -I have tried to keep flexibility to a maximum. -By allowing connections to individual nodes to be created - you don't have to address each packet, -or filter through incomming packets to get at the one you are after. This is all taken care of -for you by -.BR libxbee -! -.sp -libxbee is still in development, so if you find any bugs or have any enhancement requests, please -feel free to submit an issue on the project page: -.in +4n -.nf -http://code.google.com/p/libxbee/ -.fi -.in -or contact me (Attie) directly: -.in +4n -.nf -attie@attie.co.uk -.fi -.in -.SH "MAN PAGES" -Documentation is avaliable via the following man pages, or by example in the 'sample' folder in the SVN repository -.in +4n -.sp -.BR xbee_pkt "(3) - libxbee's packet structure" -.sp 0 -.BR xbee_con "(3) - libxbee's connection structure" -.sp -.BR xbee_setup "(3) - function to setup libxbee (and its variants)" -.sp 0 -.BR xbee_end "(3) - function to end the libxbee session and close any open handles" -.sp -.BR xbee_logit "(3) - function that allows the user to add to the xbee log output" -.sp -.BR xbee_newcon "(3) - function to create a new connection" -.sp 0 -.BR xbee_purgecon "(3) - function to purge packets from a connection" -.sp 0 -.BR xbee_endcon "(3) - function to end a connection" -.sp -.BR xbee_senddata "(3) - function to send data to a remote XBee (and its variants)" -.sp 0 -.BR xbee_getpacket "(3) - function to get a packet from a connection (and its variants)" -.sp -.BR xbee_hasdigital "(3) - function to check if digital sample is in the packet" -.sp 0 -.BR xbee_getdigital "(3) - function to get digital sample from the packet" -.sp -.BR xbee_hasanalog "(3) - function to check if analog sample is in the packet" -.sp 0 -.BR xbee_getanalog "(3) - function to get the analog sample from the packet" -.fi -.in -.SH "SEE ALSO" -.BR xbee_pkt (3), -.BR xbee_con (3), -.BR xbee_setup (3), -.BR xbee_end (3), -.BR xbee_logit (3), -.BR xbee_newcon (3), -.BR xbee_flushcon (3), -.BR xbee_endcon (3), -.BR xbee_senddata (3), -.BR xbee_getpacket (3), -.BR xbee_hasdigital (3), -.BR xbee_getdigital (3), -.BR xbee_hasanalog (3), -.BR xbee_getanalog (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_con.3 b/libs/thirdParty/libxbee/man/man3/xbee_con.3 deleted file mode 100644 index 37e770504..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_con.3 +++ /dev/null @@ -1,22 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH LIBXBEE 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -libxbee -.sp -This page has not been written yet... diff --git a/libs/thirdParty/libxbee/man/man3/xbee_end.3 b/libs/thirdParty/libxbee/man/man3/xbee_end.3 deleted file mode 100644 index 5fd1de512..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_end.3 +++ /dev/null @@ -1,23 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH LIBXBEE 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -libxbee -.sp -This page has not been written yet... - diff --git a/libs/thirdParty/libxbee/man/man3/xbee_endcon.3 b/libs/thirdParty/libxbee/man/man3/xbee_endcon.3 deleted file mode 100644 index e64ccedb2..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_endcon.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_newcon.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_flushcon.3 b/libs/thirdParty/libxbee/man/man3/xbee_flushcon.3 deleted file mode 100644 index e64ccedb2..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_flushcon.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_newcon.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_getanalog.3 b/libs/thirdParty/libxbee/man/man3/xbee_getanalog.3 deleted file mode 100644 index f7bc1d0c7..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_getanalog.3 +++ /dev/null @@ -1,96 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_GETPACKET 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_hasanalog, xbee_getanalog -.SH SYNOPSIS -.B #include -.sp -.BI "int xbee_hasanalog(xbee_pkt *" pkt ", int " sample ", int " input ");" -.sp -.BI "double xbee_getanalog(xbee_pkt *" pkt ", int " sample ", int " input ", double " Vref ");" -.ad b -.SH DESCRIPTION -The -.BR xbee_hasanalog () -function will check the packet for the presence of an analog sample on the specified input. -.sp -The -.BR xbee_getanalog () -function will read the packet and return the sample value for the specified analog input. -.sp -They both take 3 arguments, with the same purposes. -.sp -The argument -.I pkt -points to a packet that was previously retrieved with -.BR xbee_getpacket () -.sp -The argument -.I sample -selects the sample within the packet to use. -.sp -The argument -.I input -specifies which input you are interested in testing. -.sp -.BR xbee_getanalog () -also takes a fourth argument that allows you to provide a -.I Vref -value. This allows the function to convert the raw ADC value into a voltage for you. -.SH "RETURN VALUE" -The -.BR xbee_hasanalog () -function will return -.B 1 -if the provided packet has sample data for the specified input, otherwise -.BR 0 . -.sp -The -.BR xbee_getanalog () -function will return the raw ADC value (0 - 1023) if the provided packet has sample data for the specified input and Vref was given as zero. -If Vref was non-zero, then the return value will be the voltage read. -A -.B -1 -will be returned if the packet does not contain sample data. -.sp -.SH EXAMPLE -To read sample data from previously made connection: -.in +4n -.nf -#include -xbee_pkt *pkt; -double Vref = 3.3; -if ((pkt = xbee_getpacket(con)) != NULL) { - if (xbee_hasanalog(pkt,0,0)) { - printf("A0 read %fv\\n",xbee_getanalog(pkt,0,0,Vref)); - } else { - printf("No A0 data\\n"); - } - free(pkt); -} -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_pkt (3), -.BR xbee_getpacket (3), -.BR xbee_hasdigital (3), -.BR xbee_getdigital (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_getdigital.3 b/libs/thirdParty/libxbee/man/man3/xbee_getdigital.3 deleted file mode 100644 index 4a0af65b5..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_getdigital.3 +++ /dev/null @@ -1,91 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_GETPACKET 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_hasdigital, xbee_getdigital -.SH SYNOPSIS -.B #include -.sp -.BI "int xbee_hasdigital(xbee_pkt *" pkt ", int " sample ", int " input ");" -.sp -.BI "int xbee_getdigital(xbee_pkt *" pkt ", int " sample ", int " input ");" -.ad b -.SH DESCRIPTION -The -.BR xbee_hasdigital () -function will check the packet for the presence of a given sample on the specified input. -.sp -The -.BR xbee_getdigital () -function will read the packet and return the sample value for the specified input. -.sp -They both take 3 arguments, with the same purposes. -.sp -The argument -.I pkt -points to a packet that was previously retrieved with -.BR xbee_getpacket () -.sp -The argument -.I sample -selects the sample within the packet to use. -.sp -The argument -.I input -specifies which input you are interested in testing. -.SH "RETURN VALUE" -The -.BR xbee_hasdigital () -function will return -.B 1 -if the provided packet has sample data for the specified input, otherwise -.BR 0 . -.sp -The -.BR xbee_getdigital () -function will return -.B 1 -if the provided packet has sample data for the specified input and the sample was HIGH. -A -.B 0 -will be returned if the sample was LOW, or the packet does not contain sample data. -.sp -.SH EXAMPLE -To read sample data from previously made connection: -.in +4n -.nf -#include -xbee_pkt *pkt; -if ((pkt = xbee_getpacket(con)) != NULL) { - if (xbee_hasdigital(pkt,0,0)) { - printf("D0 read %d\n",xbee_getdigital(pkt,0)); - } else { - printf("No D0 data\n"); - } - free(pkt); -} -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_pkt (3), -.BR xbee_getpacket (3), -.BR xbee_hasanalog (3), -.BR xbee_getanalog (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_getpacket.3 b/libs/thirdParty/libxbee/man/man3/xbee_getpacket.3 deleted file mode 100644 index a458e4960..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_getpacket.3 +++ /dev/null @@ -1,88 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_GETPACKET 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_getpacket, xbee_getpacketwait -.SH SYNOPSIS -.B #include -.sp -.BI "xbee_pkt *xbee_getpacket(xbee_con *" con ");" -.sp -.BI "xbee_pkt *xbee_getpacketwait(xbee_con *" con ");" -.ad b -.SH DESCRIPTION -The -.BR xbee_getpacket () -function will return the next avaliable packet for the provided connection. -It takes 1 argument. -.sp -The argument -.I con -points to a connection made previously with -.BR xbee_newcon (). -.sp -The -.BR xbee_getpacketwait () -function behaves the same, but will wait for an internally specified time for a packet to arrive (currently around 1 second). -.SH "RETURN VALUE" -Upon successful return, this function returns the packet, having unlinked it from the internal list. -You must keep hold of the packet until you are finished with it, and then you must -.BR free () -it to prevent memory leaks. -.sp -If a packet was not avaliable for the provided connection, a -.B NULL -is returned. -.sp -If an error occured a -.B NULL -is also returned (though unlikely). -.sp -For more information on the structure of the packet, please see -.BR xbee_pkt (3) -.sp -For information on using callback functions with connections instead, please see -.BR xbee_con (3) -or -.B callback.c -in the SVN sample directory. -.SH EXAMPLE -To recieve a packet from a previously made connection: -.in +4n -.nf -#include -xbee_pkt *pkt; -if ((pkt = xbee_getpacket(con)) != NULL) { - /* process packet... */ - free(pkt); -} -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_setup (3), -.BR xbee_newcon (3), -.BR xbee_senddata (3), -.BR xbee_pkt (3), -.BR xbee_con (3), -.BR xbee_hasDigital (3), -.BR xbee_getDigital (3), -.BR xbee_hasAnalog (3), -.BR xbee_getAnalog (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_hasanalog.3 b/libs/thirdParty/libxbee/man/man3/xbee_hasanalog.3 deleted file mode 100644 index 402a3f266..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_hasanalog.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_getanalog.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_hasdigital.3 b/libs/thirdParty/libxbee/man/man3/xbee_hasdigital.3 deleted file mode 100644 index 555711112..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_hasdigital.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_getdigital.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_logit.3 b/libs/thirdParty/libxbee/man/man3/xbee_logit.3 deleted file mode 100644 index 5fd1de512..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_logit.3 +++ /dev/null @@ -1,23 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH LIBXBEE 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -libxbee -.sp -This page has not been written yet... - diff --git a/libs/thirdParty/libxbee/man/man3/xbee_newcon.3 b/libs/thirdParty/libxbee/man/man3/xbee_newcon.3 deleted file mode 100644 index 2ec511278..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_newcon.3 +++ /dev/null @@ -1,152 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_NEWCON 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_newcon, xbee_purgecon, xbee_endcon -.SH SYNOPSIS -.B #include -.sp -.BI "xbee_con *xbee_newcon(unsigned char " frameID ", xbee_types " type ", ...);" -.sp -.BI "void xbee_purgecon(xbee_con *" con ");" -.sp -.BI "void xbee_endcon(xbee_con *" con ");" -.ad b -.SH DESCRIPTION -The -.BR xbee_newcon () -function will setup a new connection with the specified settings. -It takes at least 2 arguments, and potentially up to 4 depending on the -.I type. -.sp -.B NOTE: -Packets will only be collected when they match an active connection. -You must setup a connection in order to recieve packets. -.sp -The argument -.I frameID -allows similar functionality to that of TCP/IP port numbers. This is 1 -.I unsigned char -(or 8-bit integer) that identifies where the data is coming from or going to. -.s -The -.I type -specifies the type of connection you would like. The following types are avaliable: -.TP -.B xbee_localAT -communicates AT commands with the local XBee -.TP -.B xbee_txStatus -recieves transmit status information from the local XBee -.TP -.B xbee_modemStatus -recieves modem status information from the local XBee -.TP -.B xbee_16bitRemoteAT -communicates AT commands with a remote node (using 16-bit addressing) -.TP -.B xbee_64bitRemoteAT -communicates AT commands with a remote node (using 64-bit addressing) -.TP -.B xbee_16bitData -sends/recieves data through a remote node (using 16-bit addressing) -.TP -.B xbee_64bitData -sends/recieves data through a remote node (using 64-bit addressing) -.TP -.B xbee_16bitIO -sends/recieves I/O data through a remote node (using 16-bit addressing) -.TP -.B xbee_64bitIO -sends/recieves I/O data through a remote node (using 64-bit addressing) -.TP -.B xbee2_data -sends/recieves data using a Series 2 XBee (uses 64-bit addressing) -.TP -.B xbee2_txStatus -recieves transmit status information from the local Series 2 XBee -.PP -If you are using -.BR xbee_localAT ", " xbee_txStatus ", " xbee2_txStatus " or " xbee_modemStatus -then only the -.I frameID -and -.I type -arguments are required. -.sp -If you are using any 16-bit connection, you must also specify 1 right aligned integer, -containing the 16-bit address (e.g. 0x1234). -.sp -If you are using any 64-bit connection, you must also specify 2 integers containing the -64-bit address, first the high 32-bits, then the low 32-bits. -.sp -The -.BR xbee_purgecon () -function is very basic. It removes any packets that have been collected in the buffer for the specified connection. -.sp -The -.BR xbee_endcon () -function is used to end a connection. This will stop collecting packets for the given connection, and remove any packets from the buffer. -.SH "RETURN VALUE" -A pointer to the connection is returned. A connection can only be made once, using the same -.I type -, -.I frameID -and address (if needed). The second call using the same parameters will return the same -connection. -.sp -For information on using callback functions for packet handling please see -.BR xbee_con (3) -or -.B callback.c -in the SVN sample directory. -.SH EXAMPLE -To create a local AT connection: -.in +4n -.nf -#include -xbee_con *con; -con = xbee_newcon('A', xbee_localAT); -.fi -.in -.sp -To create a 16-bit Data connection: -.in +4n -.nf -#include -xbee_con *con; -con = xbee_newcon('A', xbee_16bitData, 0x1234); -.fi -.in -.sp -To create a 64-bit Data connection: -.in +4n -.nf -#include -xbee_con *con; -con = xbee_newcon('A', xbee_64bitData, 0x0013A200, 0x40081826); -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_setup (3), -.BR xbee_getpacket (3), -.BR xbee_con (3), -.BR xbee_senddata (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_nsenddata.3 b/libs/thirdParty/libxbee/man/man3/xbee_nsenddata.3 deleted file mode 100644 index b36885d85..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_nsenddata.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_senddata.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_pkt.3 b/libs/thirdParty/libxbee/man/man3/xbee_pkt.3 deleted file mode 100644 index d5e269c13..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_pkt.3 +++ /dev/null @@ -1,79 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_PKT 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_pkt -.SH SYNOPSIS -.B #include -.ad b -.SH DESCRIPTION -This is the packet structure. If you want to get more advanced information from connections (such as RSSI) then this is where it lives. -.sp -.in +4n -.nf -struct xbee_pkt { - unsigned char frameID; /* AT Status */ - unsigned char atCmd[2]; /* AT */ - unsigned char status; /* AT Data Status */ /* status / options */ - unsigned char Addr64[8]; /* AT Data */ - unsigned char Addr16[2]; /* AT Data */ - unsigned char data[128]; /* AT Data */ - unsigned char RSSI; /* Data */ - unsigned int datalen; - - /* X A5 A4 A3 A2 A1 A0 D8 D7 D6 D5 D4 D3 D2 D1 D0 */ - unsigned short IOmask; /* IO */ - - /* X X X X X X X D8 D7 D6 D5 D4 D3 D2 D1 D0 */ - unsigned short IOdata; /* IO */ - - /* X X X X X D D D D D D D D D D D */ - unsigned short IOanalog[6]; /* IO */ -}; -typedef struct xbee_pkt xbee_pkt; -.fi -.in -.sp -Most of these fields are fairly self explanatory, however some need attention brought to them -and others need explaining. I will touch on the most important here: -.TP -.B atCmd -This is the 2 character identifier for the AT command response you just recieved. -Of course if you didnt setup an AT connection, you should never see, or try to see data here. -.TP -.BR Addr64 " and " Addr16 -These contain the address of the XBee that you recieved the packet from. You should really know this -because you setup the connection. However remote AT packets will contain both 16 and 64 bit -addresses. -.TP -.B data -This is the data you just recieved. Either the AT reponse, or the data from the remote XBee node. -.TP -.B datalen -Would you be suprised if I told you this is how much data there is?... Dont try and -.BR printf () -the -.B data -as it isn't null terminated. Use this for processing instead. -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_getpacket (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_purgecon.3 b/libs/thirdParty/libxbee/man/man3/xbee_purgecon.3 deleted file mode 100644 index e64ccedb2..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_purgecon.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_newcon.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_senddata.3 b/libs/thirdParty/libxbee/man/man3/xbee_senddata.3 deleted file mode 100644 index 10296bb8c..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_senddata.3 +++ /dev/null @@ -1,86 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_SENDDATA 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_senddata, xbee_nsenddata, xbee_vsenddata -.SH SYNOPSIS -.B #include -.sp -.BI "int xbee_senddata(xbee_con *" con ", char *" format ", ...);" -.sp -.BI "int xbee_nsenddata(xbee_con *" con ", char *" data ", int " length ");" -.sp -.B #include -.sp -.BI "int xbee_vsenddata(xbee_con *" con ", char *" format ", va_list " ap "); -.ad b -.SH DESCRIPTION -The -.BR xbee_senddata () -function will send data via a provided connection. -It takes at least 2 arguments, and possibly more depending on the format string. -.sp -The argument -.I con -points to a connection made previously with -.BR xbee_newcon (). -.sp -The -.I format -string and any following parameters are passed to -.BR snprintf () -within these functions. -Please see the -.BR printf (3) -man page for more information. -.sp -If you are using -.BR xbee_nsenddata () -you must provide a character array of the data, and the data's length. -.sp -If you are using -.BR xbee_vsenddata () -you must provide a va_list. See -.BR stdarg (3). -.SH "RETURN VALUE" -Upon successful completion, these functions return 0. -.sp -If an invalid packet or connection was provided, -1 is returned. -.sp -If an unknown error occured, -2 is returned. -.sp -If -.I con -has -.I waitforACK -enabled, then these functions return 1 when an ACK was not recieved within 1 second. -.SH EXAMPLE -To send the string "Hello World!" through a previously made connection: -.in +4n -.nf -#include -xbee_senddata(con,"Hello World!"); -.fi -.in -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_setup (3), -.BR xbee_newcon (3), -.BR xbee_getpacket (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_setup.3 b/libs/thirdParty/libxbee/man/man3/xbee_setup.3 deleted file mode 100644 index 2628c46f7..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_setup.3 +++ /dev/null @@ -1,108 +0,0 @@ -.\" libxbee - a C library to aid the use of Digi's Series 1 XBee modules -.\" running in API mode (AP=2). -.\" -.\" Copyright (C) 2009 Attie Grande (attie@attie.co.uk) -.\" -.\" This program is free software: you can redistribute it and/or modify -.\" it under the terms of the GNU General Public License as published by -.\" the Free Software Foundation, either version 3 of the License, or -.\" (at your option) any later version. -.\" -.\" This program is distributed in the hope that it will be useful, -.\" but WITHOUT ANY WARRANTY; without even the implied warranty of -.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -.\" GNU General Public License for more details. -.\" -.\" You should have received a copy of the GNU General Public License -.\" along with this program. If not, see . -.TH XBEE_SETUP 3 2009-11-01 "GNU" "Linux Programmer's Manual" -.SH NAME -xbee_setup, xbee_setuplog, xbee_setupAPI, xbee_setuplogAPI -.SH SYNOPSIS -.B #include -.sp -.BI "int xbee_setup(char *" path ", int " baudrate ");" -.sp -.BI "int xbee_setuplog(char *" path ", int " baudrate ", int " logfd ");" -.sp -.BI "int xbee_setupAPI(char *" path ", int " baudrate ", char " cmdSeq ", int " cmdTime ");" -.sp -.BI "int xbee_setuplogAPI(char *" path ", int " baudrate ", int " logfd ", char " cmdSeq ", int " cmdTime ");" -.ad b -.SH DESCRIPTION -.sp -.B A VERSION OF THIS FUNCTION MUST BE CALLED BEFORE ANY OTHER libxbee FUNCTION! -The -.BR xbee_setup () -function will setup libxbee so that it can handle an XBee. -It takes 2 arguments. -.sp -The argument -.I path -is the path to the serial port that the XBee is connected to (e.g. /dev/ttyUSB0). -.sp -The -.I baudrate -is the baud rate that the local XBee is configured to run at. The following are avaliable: -.in +2n -.nf -.B 1200 -.B 2400 -.B 4800 -.B 9600 -.B 19200 -.B 38400 -.B 57600 -.BR 115200 " - this is potentially unstable (read the XBee manual to find out why...)" -.fi -.in -.sp -Using -.BR xbee_setuplog () -is exactly the same, but instead you give an open file descriptor. All log messages will be written to this file (you can use stderr or stdout if you want!). NOTE: The file descriptor is -.BR dup ()'ed -before use, so you may close it immediately. Providing a value of -.B 0 -will disable the log output. -.sp -Using -.BR xbee_setupAPI () -is exactly the same, but instead you provide the 'Command Sequence' character and the 'Guard Time' that your local XBee has been configured with. -libxbee will then place your XBee in API mode 2, and when you call -.BR xbee_end () -it will return your XBee to its previous API mode. -.sp -Using -.BR xbee_setuplogAPI () -is simply a combination of -.BR xbee_setuplog () -and -.BR xbee_setupAPI () -.SH "RETURN VALUE" -If any error occures, -.B -1 -is returned. Otherwise -.B 0 -is returned. -.SH EXAMPLE -To setup libxbee to use /dev/ttyUSB0 at 57600 baud: -.in +4n -.nf -#include -if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - printf("Oh no...\n"); - exit(1); -} -.fi -.in -.SH NOTE -If libxbee is compiled with -.B DEBUG -defined, then the log output will ALWAYS be enabled. If no file descriptor is provided, then stderr will be used. -.SH AUTHOR -Attie Grande -.SH "SEE ALSO" -.BR libxbee (3), -.BR xbee_newcon (3), -.BR xbee_getpacket (3), -.BR xbee_senddata (3) diff --git a/libs/thirdParty/libxbee/man/man3/xbee_setupAPI.3 b/libs/thirdParty/libxbee/man/man3/xbee_setupAPI.3 deleted file mode 100644 index 56a5fcac5..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_setupAPI.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_setup.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_setuplog.3 b/libs/thirdParty/libxbee/man/man3/xbee_setuplog.3 deleted file mode 100644 index 56a5fcac5..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_setuplog.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_setup.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_setuplogAPI.3 b/libs/thirdParty/libxbee/man/man3/xbee_setuplogAPI.3 deleted file mode 100644 index 56a5fcac5..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_setuplogAPI.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_setup.3 diff --git a/libs/thirdParty/libxbee/man/man3/xbee_vsenddata.3 b/libs/thirdParty/libxbee/man/man3/xbee_vsenddata.3 deleted file mode 100644 index b36885d85..000000000 --- a/libs/thirdParty/libxbee/man/man3/xbee_vsenddata.3 +++ /dev/null @@ -1 +0,0 @@ -.so man3/xbee_senddata.3 diff --git a/libs/thirdParty/libxbee/notes/Notepad++ Style.xml b/libs/thirdParty/libxbee/notes/Notepad++ Style.xml deleted file mode 100644 index 5e56ab9ce..000000000 --- a/libs/thirdParty/libxbee/notes/Notepad++ Style.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - 000000 - - - - - _xbee_nsenddata(): xbee_send_pkt(): - xbee_listen(): - _xbee_logit(): - _xbee_getpacket(): - - - - - - - - - - - - - - - - - - - diff --git a/libs/thirdParty/libxbee/notes/v1-v2.txt b/libs/thirdParty/libxbee/notes/v1-v2.txt deleted file mode 100644 index cb2e1c923..000000000 --- a/libs/thirdParty/libxbee/notes/v1-v2.txt +++ /dev/null @@ -1,29 +0,0 @@ -XBee -0x8A* Modem Status -0x08* AT Command -0x09* AT Command (Queue) -0x88* AT Command Response -0x17* Remote AT Command Request -0x97* Remote AT Command Response -0x00* Tx Request (64-bit) -0x01* Tx Request (16-bit) -0x89* Tx Status -0x80* Rx Data (64-bit) -0x81* Rx Data (16-bit) - -XBee 2.5 -0x8A* Modem Status -0x08* AT Command -0x09* AT Command (Queue) -0x88* AT Command Response -0x17* Remote AT Command Request -0x97* Remote AT Command Response -0x10* ZigBee Transmit Request -0x11 Explicit Addressing ZigBee Command Frame -0x8B* ZigBee Transmit Status -0x90* ZigBee Recieve Packet -0x91 ZigBee Explicit Rx -0x92 ZigBee IO Data Sample Rx -0x94 Xbee Sensor Read -0x95 Node Identification - diff --git a/libs/thirdParty/libxbee/pdf/api.c.pdf b/libs/thirdParty/libxbee/pdf/api.c.pdf deleted file mode 100644 index dc8c75af3cb7130fa8716de8a733fd3a3fe13ba3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75665 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�s>|Zu(JPU5 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 JRkTr{{{nlBI;Q{t diff --git a/libs/thirdParty/libxbee/pdf/main.c.pdf b/libs/thirdParty/libxbee/pdf/main.c.pdf deleted file mode 100644 index 4410e6b8e4d9f3a95779694316ce6943da6202e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8923 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.c ../obj/api.o -o -lpthread -lrt diff --git a/libs/thirdParty/libxbee/sample/analog.c b/libs/thirdParty/libxbee/sample/analog.c deleted file mode 100644 index 84069e39a..000000000 --- a/libs/thirdParty/libxbee/sample/analog.c +++ /dev/null @@ -1,71 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will output the voltage read from analog 0 */ - -#include -#include -#include - -/* set this to the voltage measured between GND and Vref - 3.3 is a good place to start */ -#define Vref 3.3 - -int main(int argc, char *argv[]) { - xbee_con *con; - xbee_pkt *pkt; - int i; - - /* setup libxbee */ - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - return 1; - } - - /* get a connection to the remote XBee */ - con = xbee_newcon('I',xbee_64bitIO, 0x13A200, 0x403CB26A); - /* do this forever! */ - while (1) { - /* get as many packets as we can */ - while ((pkt = xbee_getpacket(con)) != NULL) { - for (i = 0; i < pkt->samples; i++) { - /* did we get a value for A0? */ - if (!xbee_hasanalog(pkt,i,0)) { - /* there was no data for A0 in the packet */ - printf("A0: -- No Data --\n"); - continue; - } - /* print out the reading in raw, and adjusted */ - printf("A0: %.0f (~%.2fv)\n", - xbee_getanalog(pkt,i,0,0), - xbee_getanalog(pkt,i,0,Vref)); - fflush(stdout); - } - /* release the packet */ - free(pkt); - } - usleep(100); - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/api.c b/libs/thirdParty/libxbee/sample/api.c deleted file mode 100644 index cff70bf1a..000000000 --- a/libs/thirdParty/libxbee/sample/api.c +++ /dev/null @@ -1,42 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will show you how to seup the xbee and ensure that it is in API mode */ - -#include -#include -#include - -int main(int argc, char *argv[]) { - /* the extra arguments are the CC ('+' by default) and GT (1000) by default AT values */ - xbee_setuplogAPI("/dev/ttyUSB0",57600,2,'+',1000); - - /* now we can do our stuff! */ - sleep(10); - - /* calling xbee_end() will return the xbee to its previous API mode */ - xbee_end(); - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/atis.c b/libs/thirdParty/libxbee/sample/atis.c deleted file mode 100644 index 66946f184..000000000 --- a/libs/thirdParty/libxbee/sample/atis.c +++ /dev/null @@ -1,81 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will output the voltage read from analog 0 */ - -#include -#include -#include - -/* set this to the voltage measured between GND and Vref - 3.3 is a good place to start */ -#define Vref 3.3 - -int main(int argc, char *argv[]) { - xbee_con *con; - xbee_pkt *pkt; - - /* setup libxbee */ - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - printf("xbee_setup failed...\n"); - return 1; - } - - /* create an AT connection */ - con = xbee_newcon('I',xbee_64bitRemoteAT,0x13A200,0x403CB26A); - - /* do this forever! */ - for (;;) { - /* request samples now! */ - xbee_senddata(con,"IS"); - /* get as many packets as we can */ - while ((pkt = xbee_getpacketwait(con)) != NULL) { - if (pkt) { - if (pkt->status != 0x00) { - /* if the return status was not 0x00 (OK) then the request failed... */ - printf("Sample A0: -- Request Failed --\n"); - } else { - if (!xbee_hasanalog(pkt,0,0)) { - /* there was no data for A0 in the packet */ - printf("Sample A0: -- No Data --\n"); - } else { - /* it appears that there is sample data for A0! */ - printf("Sample A0: %.0f (~%.2fv)\n", - xbee_getanalog(pkt,0,0,0), - xbee_getanalog(pkt,0,0,Vref)); - } - } - /* dont forget to free the packet! */ - free(pkt); - } else { - /* couldnt get a packet */ - printf("Sample A0: -- No Packet Returned --\n"); - } - } - /* wait a second for the next sample */ - sleep(1); - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/atsetup.c b/libs/thirdParty/libxbee/sample/atsetup.c deleted file mode 100644 index 13afbe416..000000000 --- a/libs/thirdParty/libxbee/sample/atsetup.c +++ /dev/null @@ -1,157 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will setup certain AT parameters of the local XBee unit */ - -#include -#include -#include -#include - -/* con = connection to use - cmd = 2 character command string, eg NI - parameter = NULL - no parameter - !NULL - command parameter, either NULL terminated string, or block of memory - length = 0 - use parameter as NULL terminated string - !0 - use 'length' bytes from parameter - ret = NULL - don't return anything - !NULL - pointer to pointer. doAT will allocate memory, you must free it! - str = return data pointer - - returns the length of the data in ret, or -ve for error */ -int doAT(xbee_con *con, char *cmd, char *parameter, int length, unsigned char **ret) { - xbee_pkt *pkt; - if (con->type != xbee_localAT && con->type != xbee_16bitRemoteAT && con->type != xbee_64bitRemoteAT) { - printf("Thats not an AT connection!...\n"); - return -1; - } - if (strlen(cmd) != 2) { - printf("Invalid command: \"%s\"\n",cmd); - return -2; - } - if (parameter == NULL) { - xbee_senddata(con,"%s",cmd); - } else if (length != 0) { - char *tmp; - if ((tmp = malloc(1024)) == NULL) { - printf("Failed to get memory!\n"); - return -3; - } - snprintf(tmp,1024,"%s",cmd); - memcpy(&(tmp[2]),parameter,(length>1022)?1022:length); - xbee_nsenddata(con,tmp,length+2); - free(tmp); - } else { - xbee_senddata(con,"%s%s",cmd,parameter); - } - pkt = xbee_getpacketwait(con); - if (pkt == NULL) { - printf("Failed to set %s!\n",cmd); - return -4; - } - if (pkt->status != 0) { - printf("An error occured while setting %s!\n",cmd); - return -5; - } - if (ret && pkt->datalen > 0) { - *ret = realloc(*ret,sizeof(char) * (pkt->datalen + 1)); - memcpy(*ret,pkt->data,pkt->datalen); - (*ret)[pkt->datalen] = '\0'; - free(pkt); - return pkt->datalen; - } - free(pkt); - return 0; -} - -int main(int argc, char *argv[]) { - xbee_con *con; - int ret,i; - unsigned char *str = NULL; - - if (argc != 2) { - printf("Usage: %s \n",argv[0]); - return 1; - } - - /* setup libxbee */ - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - printf("xbee_setup failed...\n"); - return 1; - } - - /* create an AT connection */ - con = xbee_newcon('I',xbee_localAT); - /*con = xbee_newcon('I',xbee_64bitRemoteAT,0x13A200,0x403CB26A);*/ - - - /* get the node's address! */ - if ((ret = doAT(con,"SH",NULL,0,&str)) < 0) return 1; - if (ret == 4) { - printf("SH: 0x%02X%02X%02X%02X\n", str[0], str[1], str[2], str[3]); - } - if ((ret = doAT(con,"SL",NULL,0,&str)) < 0) return 1; - if (ret == 4) { - printf("SL: 0x%02X%02X%02X%02X\n", str[0], str[1], str[2], str[3]); - } - - /* set the power level - 2 methods, i prefer the first but it generates compile warnings :( */ - /*if ((ret = doAT(con,"PL",&((unsigned char[]){4}),1,&str)) < 0) return 1;*/ - /*{ - char t[] = {0}; - if ((ret = doAT(con,"PL",t,1,&str)) < 0) return 1; - }*/ - - /* get the power level */ - if ((ret = doAT(con,"PL",NULL,0,&str)) < 0) return 1; - if (ret == 1) { - printf("PL: 0x%02X\n", str[0]); - } - - /* get NI */ - if ((ret = doAT(con,"NI",NULL,0,&str)) < 0) return 1; - if (ret > 0) { - printf("NI: "); - for (i = 0; i < ret; i++) { - printf("%c",(str[i]>=32 && str[i]<=126)?str[i]:'.'); - } - printf("\n"); - } - - printf("Setting NI to '%s': ",(argc!=2)?"MyNode":argv[1]); - if ((ret = doAT(con,"NI",(argc!=2)?"MyNode":argv[1],0,NULL)) < 0) return 1; - printf("OK\n"); - - if ((ret = doAT(con,"NI",NULL,0,&str)) < 0) return 1; - if (ret > 0) { - printf("NI: "); - for (i = 0; i < ret; i++) { - printf("%c",(str[i]>=32 && str[i]<=126)?str[i]:'.'); - } - printf("\n"); - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/callback.c b/libs/thirdParty/libxbee/sample/callback.c deleted file mode 100644 index 9e48276b0..000000000 --- a/libs/thirdParty/libxbee/sample/callback.c +++ /dev/null @@ -1,88 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will return any recieved data, using a callback function */ - -#include -#include -#include -#include - -void sighandler(int sig) { - xbee_pkt *pkt; - if (sig == SIGINT) { - xbee_end(); - exit(0); - } -} - -void callback(xbee_con *con, xbee_pkt *pkt) { - int ret; - /* print the recieved data */ - printf("Rx: %s\n",pkt->data); - /* say thank you */ - if ((ret = xbee_senddata(con,"%s",pkt->data)) != 0) { - printf("xbee_senddata: Error %d... Retrying!\n",ret); - if ((ret = xbee_senddata(con,"%s",pkt->data)) != 0) { - printf("xbee_senddata: Error %d... Data lost!\n",ret); - } else { - printf("xbee_senddata: Success after retry!\n",ret); - } - } -free(pkt); -} - -int main(int argc, char *argv[]) { - xbee_con *con; - xbee_pkt *pkt, *rpkt; - - /* setup the xbee */ - //if (xbee_setupAPI("/dev/ttyUSB0",57600,'+',250) == -1) { - //if (xbee_setuplogAPI("/dev/ttyUSB0",57600,2,'+',250) == -1) { - if (xbee_setuplog("/dev/ttyUSB0",57600,2) == -1) { - /* oh no... it failed */ - printf("xbee_setup() failed...\n"); - exit(1); - } - - /* handle ^C */ - signal(SIGINT, sighandler); - - /* setup a connection */ - con = xbee_newcon('I',xbee_64bitData, 0x0013A200, 0x40081826); - con->waitforACK = 1; - con->callback = callback; - - printf("Waiting...\n"); - - /* do nothing forever! */ - for (;;) { - sleep(86400); /* 24hrs */ - } - - /* shouldn't ever get here but... */ - return 0; -} - diff --git a/libs/thirdParty/libxbee/sample/digital.c b/libs/thirdParty/libxbee/sample/digital.c deleted file mode 100644 index 4b0c9f633..000000000 --- a/libs/thirdParty/libxbee/sample/digital.c +++ /dev/null @@ -1,146 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will control digital output pins of a chosen node */ - -#include -#include -#include -#include - -int mode; -char bitmask; -char outputs; - -void sighandler(int sig) { - if (sig == SIGINT) { - xbee_end(); - exit(0); - } -} - -void doneCB(xbee_con *con, xbee_pkt *pkt) { - /* this packet should be confirmation... */ - xbee_end(); - exit(0); -} -void doCB(xbee_con *con, xbee_pkt *pkt) { - int i,m; - outputs = pkt->IOdata[0].IOdigital; - printf("\n 7 6 5 4 3 2 1 0\n"); - printf("Current output state: "); - for (i = 0; i < 8; i++) { - if (xbee_hasdigital(pkt,0,7-i)) { - if (xbee_getdigital(pkt,0,7-i)) { - printf(" 1"); - } else { - printf(" 0"); - } - } else { - printf(" x"); - } - } - printf("\n"); - switch (mode) { - case 0: outputs |= bitmask; break; - case 1: outputs &= ~bitmask; break; - case 2: default: - xbee_end(); - exit(0); - } - m = outputs; - printf("New output state: "); - for (i = 0; i < 8; i++) { - if (xbee_hasdigital(pkt,0,7-i)) { - if (m & 0x80) { - printf(" 1"); - } else { - printf(" 0"); - } - } else { - printf(" x"); - } - m <<= 1; - } - printf("\n\n"); - con->callback = doneCB; - xbee_senddata(con,"IO%c",outputs); -} - -void usage(char *argv0) { - printf("Usage: %s \n",argv0); - printf("Usage: %s [port[0-7]]...\n",argv0); - exit(1); -} -int main(int argc, char *argv[]) { - xbee_con *con; - - if (argc < 2) usage(argv[0]); - - if (!strcasecmp(argv[1],"on")) { - mode = 0; - } else if (!strcasecmp(argv[1],"off")) { - mode = 1; - } else if (!strcasecmp(argv[1],"query")) { - mode = 2; - } else usage(argv[0]); - - if (mode != 2) { - int i; - char *c; - bitmask = 0; - c = argv[2]; - while (*c != '\0') { - *c -= '0'; - if ((*c >= 0) && (*c <= 7)) { - bitmask |= 0x01 << *c; - } else { - usage(argv[0]); - } - c++; - } - } - - /* setup libxbee */ - if (xbee_setupAPI("/dev/ttyUSB0",57600,'+',250) == -1) { - return 1; - } - - /* handle ^C */ - signal(SIGINT, sighandler); - - /* get a connection to the remote XBee */ - con = xbee_newcon('I',xbee_64bitRemoteAT, 0x0013A200, 0x403CB26B); - con->waitforACK = 1; - con->callback = doCB; - - xbee_senddata(con,"IS"); - - /* timeout after 1 second... */ - sleep(1); - - xbee_end(); - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/digitalout.c b/libs/thirdParty/libxbee/sample/digitalout.c deleted file mode 100644 index 46db084f1..000000000 --- a/libs/thirdParty/libxbee/sample/digitalout.c +++ /dev/null @@ -1,128 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will control the digital 0 output from the keyboard. Type: - 0, - off - 1, - on - q, - quit */ - -#include -#include -#include - -#include - -int main(int argc,char *argv[]) { - xbee_con *con; - xbee_pkt *pkt; - - printf("Hello\n"); - - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - printf("failed to setup xbee\n"); - return 1; - } - - con = xbee_newcon('R',xbee_64bitRemoteAT,0x0013a200,0x403af247); - if (!con) { - printf("no connection returned\n"); - return 1; - } - - for (;;) { - - xbee_senddata(con,"D0"); - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("no packet returned from state probe\n"); - return 1; - } - - if (pkt->status != 0) { - printf("state probe failed (ret=0x%02X - ",pkt->status); - switch (pkt->status) { - case 0x1: printf("Error"); break; - case 0x2: printf("Invalid Command"); break; - case 0x3: printf("Invalid Parameter"); break; - case 0x4: printf("No Response"); break; - default: printf("Unknown"); break; - } - printf(")\n"); - return 1; - } - - if (pkt->datalen != 1) { - printf("unexpected datalen from state probe\n"); - return 1; - } - - if (pkt->data[0] == 0x05) { - printf("this port is currently ON\n"); - } else if (pkt->data[0] == 0x04) { - printf("this port is currently OFF\n"); - } else { - printf("this port is currently in an unknown state\n"); - return 1; - } - free(pkt); - pkt = NULL; - - recharprompt: - printf("--> "); - rechar: - switch(getchar()) { - case 'q': case 'Q': - printf("byebye\n"); - return 0; - case '0': - printf("turning off...\n"); - xbee_senddata(con,"D0%c",0x04); - break; - case '1': - printf("turning on...\n"); - xbee_senddata(con,"D0%c",0x05); - break; - case '\n': goto rechar; - default: goto recharprompt; - } - - if ((pkt = xbee_getpacketwait(con)) != NULL) { - if (pkt->status != 0) { - printf("state set failed (ret=0x%02X - ",pkt->status); - switch (pkt->status) { - case 0x1: printf("Error"); break; - case 0x2: printf("Invalid Command"); break; - case 0x3: printf("Invalid Parameter"); break; - case 0x4: printf("No Response"); break; - default: printf("Unknown"); break; - } - printf(")\n"); - return 1; - } - } - - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/multi.c b/libs/thirdParty/libxbee/sample/multi.c deleted file mode 100644 index 107f88f6e..000000000 --- a/libs/thirdParty/libxbee/sample/multi.c +++ /dev/null @@ -1,100 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will make use of multiple instances of libxbee and send messages between the attached XBees */ - -#include -#include -#include -#include - -int mode; -char bitmask; -char outputs; - -xbee_hnd xbee1; -xbee_hnd xbee2; - -void sighandler(int sig) { - if (sig == SIGINT) { - _xbee_end(xbee1); - _xbee_end(xbee2); - exit(0); - } -} - -void xbee1CB(xbee_con *con, xbee_pkt *pkt) { - char data[128]; - snprintf(data,pkt->datalen+1,"%s",pkt->data); - printf("XBee1: Rx[%3d]: %s\n",pkt->datalen,data); -} - -void xbee2CB(xbee_con *con, xbee_pkt *pkt) { - char data[128]; - snprintf(data,pkt->datalen+1,"%s",pkt->data); - printf("XBee2: Rx[%3d]: %s\n",pkt->datalen,data); -} - -int main(int argc, char *argv[]) { - xbee_con *con1; - xbee_con *con2; - - if (!(xbee1 = _xbee_setuplogAPI("/dev/ttyUSB0",57600,3,'+',250))) { - //if (!(xbee1 = _xbee_setupAPI("/dev/ttyUSB0",57600,'+',250))) { - printf("xbee1: setup error...\n"); - return 1; - } - if (!(xbee2 = _xbee_setuplogAPI("/dev/ttyUSB1",57600,4,'+',250))) { - //if (!(xbee2 = _xbee_setupAPI("/dev/ttyUSB1",57600,'+',250))) { - printf("xbee2: setup error...\n"); - return 1; - } - - /* handle ^C */ - signal(SIGINT, sighandler); - - con1 = _xbee_newcon(xbee1,'1',xbee_64bitData, 0x0013A200, 0x40081826); - con1->waitforACK = 1; - con1->callback = xbee1CB; - - con2 = _xbee_newcon(xbee2,'2',xbee_64bitData, 0x0013A200, 0x404B75DE); - con2->waitforACK = 1; - con2->callback = xbee2CB; - - while (1) { - printf("xbee1: Tx\n"); - _xbee_logit(xbee1,"xbee1: Tx"); - _xbee_logit(xbee2,"xbee1: Tx"); - _xbee_senddata(xbee1,con1,"Hello"); - usleep(1000000); - printf("xbee2: Tx\n"); - _xbee_logit(xbee1,"xbee2: Tx"); - _xbee_logit(xbee2,"xbee2: Tx"); - _xbee_senddata(xbee2,con2,"Hi There!"); - usleep(1000000); - } - - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/scan.c b/libs/thirdParty/libxbee/sample/scan.c deleted file mode 100644 index 4546887f9..000000000 --- a/libs/thirdParty/libxbee/sample/scan.c +++ /dev/null @@ -1,144 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will scan the currently configured channel for all nodes, - returning the values of a few useful settings */ - -#include -#include -#include -#include -#include -#include - -#define MAXNODES 100 - -int main(int argc, char *argv[]) { - int i; - int saidfull = 0; - int ATNT = 0x19; /* node discover timeout */ - int ATNTc; /* counter */ - - int nodes = 0; - char addrs[MAXNODES][8]; - - xbee_con *con; - xbee_pkt *pkt, *rpkt; - - time_t ttime; - char stime[32]; - - /* setup libxbee */ - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - return 1; - } - - /* grab a local AT connection */ - con = xbee_newcon('I',xbee_localAT); - - /* get the ND timeout */ - xbee_senddata(con,"NT"); - if ((rpkt = xbee_getpacketwait(con)) == NULL) { - printf("XBee didnt return a result for NT\n"); - return 1; - } - ATNT = rpkt->data[0]; - free(rpkt); - - while (1) { - /* send a ND - Node Discover request */ - xbee_senddata(con,"ND"); - /* only wait for a bit longer than the ND timeout */ - ATNTc = ATNT + 10; - /* loop until the end packet has been received or timeout reached */ - while (ATNTc--) { - /* get a packet */ - pkt = xbee_getpacketwait(con); - /* check a packet was returned, and that its one we are after... */ - if (pkt && !memcmp(pkt->atCmd,"ND",2)) { - /* is this the end packet? you can tell from the 0 datalen */ - if (pkt->datalen == 0) { - /* free the packet */ - free(pkt); - break; - } else { - /* check if we know this node already */ - for (i = 0; i < nodes; i++) { - /* the 64bit address will match one in the list */ - if (!memcmp(&(pkt->data[2]),&(addrs[i]),8)) break; - } - ttime = time(NULL); - strftime(stime,32,"%I:%M:%S %p",gmtime(&ttime)); - /* is there space for another? */ - if ((i == nodes) && - (nodes == MAXNODES) && - (!saidfull)) { - printf("MAXNODES reached... Can't add more...\r"); - /* flush so the change is seen! */ - fflush(stdout); - saidfull = 1; - } else { - /* is this a rewrite? */ - if (i != nodes) { - /* find the line to edit */ - printf("%c[%dA",27,nodes-i); - /* clear the line */ - printf("%c[2K",27); - } - /* write out the info */ - memcpy(&(addrs[nodes]),&(pkt->data[2]),8); - printf("MY: 0x%02X%02X ",pkt->data[0],pkt->data[1]); - printf("SH: 0x%02X%02X%02X%02X ",pkt->data[2],pkt->data[3],pkt->data[4],pkt->data[5]); - printf("SL: 0x%02X%02X%02X%02X ",pkt->data[6],pkt->data[7],pkt->data[8],pkt->data[9]); - printf("dB: -%2d ",pkt->data[10]); - printf("NI: %-20s ",&(pkt->data[11])); - printf("@: %s",stime); - /* is this a rewrite? */ - if (i != nodes) { - /* go back the the bottom */ - printf("%c[%dB\r",27,nodes-i); - } else { - /* new line is only wanted for new nodes */ - printf("\n"); - /* if not, then add 1 to the number of nodes! */ - nodes++; - } - } - /* flush so the change is seen! */ - fflush(stdout); - } - /* free the packet */ - free(pkt); - } - /* sleep for 100ms (same as NT steps) */ - usleep(100000); - } - /* try again! */ - usleep(100000); - } - - return 0; -} - diff --git a/libs/thirdParty/libxbee/sample/scan_adv.c b/libs/thirdParty/libxbee/sample/scan_adv.c deleted file mode 100644 index d3360222c..000000000 --- a/libs/thirdParty/libxbee/sample/scan_adv.c +++ /dev/null @@ -1,589 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will scan all possible channels for remote nodes and return - the value of a few useful settings */ - -#include -#include -#include -#include -#include -#include -#include - -#define MAXNODES 100 - -int ATCH = 0x0C; /* origional channel number */ -int ATNT = 0x19; /* node discover timeout */ -int ATNTc; /* node discover timeout counter */ -int BREAK = 0; -xbee_con *con; - -void sighandler(int sig) { - xbee_pkt *pkt; - if (sig == SIGINT) { - BREAK = 1; - /* wait for the rest of the timeout... */ - printf("\r%c[2KWaiting for node discover command to timeout...",27); - fflush(stdout); - for (; ATNTc; ATNTc--) { - usleep(100000); - } - /* Restore the XBee's channel setting */ - printf("\r%c[2KRestoring channel to 0x%02X...",27,ATCH); - fflush(stdout); - if (xbee_senddata(con,"CH%c",ATCH)) { - printf("xbee_senddata: Error\n"); - exit(1); - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\r%c[2K*** XBee didnt return a result for CH... ***\nPlease manually reset your channel to 0x%02X\n",27,ATCH); - } - if (pkt->status) { - printf("\r%c[2K*** An error occured while restoring the channel setting... ***\nPlease manually reset your channel to 0x%02X\n",27,ATCH); - } else { - printf("\nDone!\n"); - } - free(pkt); - /* Restore the terminal */ - printf("%c[?25h%c[0m",27,27); - fflush(stdout); - exit(0); - } -} - -int main(int argc, char *argv[]) { - int i; - int saidfull = 0; - int ATCHc; /* current channel number */ - int XBeePro = 0; /* XBee pro? */ - - int nodes = 0; - unsigned char addrs[MAXNODES][19]; /* 0-7 : 64 bit address - 8 : channel - 9-10 : id - 11 : baud - 12 : API - 13-14: HV - 15-16: VR - 17 : CC - 18 : mask - not address */ - - xbee_pkt *pkt, *rpkt; - - time_t ttime; - char stime[32]; - - /* handle ^C */ - signal(SIGINT, sighandler); - - /* setup libxbee */ - if (xbee_setupAPI("/dev/ttyUSB0",57600,'+',250) == -1) { - return 1; - } - - /* grab a local AT connection */ - con = xbee_newcon('I',xbee_localAT); - - /* get the current channel */ - if (xbee_senddata(con,"CH")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("XBee didnt return a result for CH\n"); - return 1; - } - ATCH = pkt->data[0]; - free(pkt); - - /* XBee - 0x0B - 0x1A - XBee Pro - 0x0C - 0x17 */ - if (xbee_senddata(con,"CH%c",0x0B)) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("XBee didnt return a result for CH\n"); - return 1; - } - /* did that fail? */ - if (pkt->status == 0) { - /* nope.. its not a pro */ - printf("Using XBee (not Pro) channels (0x0B - 0x1A)...\n"); - XBeePro = 0; - ATCHc = 0x0B; - } else { - /* yup... its a pro */ - printf("Using XBee Pro channels (0x0C - 0x17)...\n"); - XBeePro = 1; - ATCHc = 0x0C; - } - free(pkt); - - /* find and print data for the local node */ - printf("\n%c[31mCH:%c[32m 0x%02X ",27,27,ATCH); - if (xbee_senddata(con,"ID")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for ID\n"); - return 1; - } - printf("%c[31mID:%c[32m 0x%02X%02X ",27,27,pkt->data[0],pkt->data[1]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"MY")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for MY\n"); - return 1; - } - printf("%c[31mMY:%c[32m 0x%02X%02X ",27,27,pkt->data[0],pkt->data[1]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"SH")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for SH\n"); - return 1; - } - printf("%c[31mSH:%c[32m 0x%02X%02X%02X%02X ",27,27,pkt->data[0],pkt->data[1],pkt->data[2],pkt->data[3]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"SL")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for SL\n"); - return 1; - } - printf("%c[31mSL:%c[32m 0x%02X%02X%02X%02X ",27,27,pkt->data[0],pkt->data[1],pkt->data[2],pkt->data[3]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"BD")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for BD\n"); - return 1; - } - printf("%c[31mBD:%c[32m ",27,27); - /* print the baud rate */ - switch (pkt->data[3]) { - case 0: printf(" 1200"); break; - case 1: printf(" 2400"); break; - case 2: printf(" 4800"); break; - case 3: printf(" 9600"); break; - case 4: printf(" 19200"); break; - case 5: printf(" 38400"); break; - case 6: printf(" 57600"); break; - case 7: printf("115200"); break; - default: printf(" other"); break; - } - printf(" "); - free(pkt); - /* ### */ - if (xbee_senddata(con,"AP")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for AP\n"); - return 1; - } - printf("%c[31mAP:%c[32m 0x%02X ",27,27,pkt->data[0]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"HV")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for HV\n"); - return 1; - } - printf("%c[31mHV:%c[32m 0x%02X%02X ",27,27,pkt->data[0],pkt->data[1]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"VR")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for VR\n"); - return 1; - } - printf("%c[31mVR:%c[32m 0x%02X%02X ",27,27,pkt->data[0],pkt->data[1]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"CC")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for CC\n"); - return 1; - } - printf("%c[31mCC:%c[32m '%c' (0x%02X) ",27,27,pkt->data[0],pkt->data[0]); - free(pkt); - /* ### */ - if (xbee_senddata(con,"NI")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("\nXBee didnt return a result for NI\n"); - return 1; - } - printf("%c[31mNI:%c[32m %-20s ",27,27,pkt->data); - free(pkt); - /* ### */ - printf("%c[95m* This is the lobal XBee *",27); - - printf("%c[34m\n---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------%c[0m\n\n",27,27); - - /* get the ND timeout */ - if (xbee_senddata(con,"NT")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if ((pkt = xbee_getpacketwait(con)) == NULL) { - printf("XBee didnt return a result for NT\n"); - return 1; - } - ATNT = pkt->data[0]; - free(pkt); - - printf("%c[?25l",27); - fflush(stdout); - - usleep(100000); - - while (!BREAK) { - /* set the channel to scan */ - if (xbee_senddata(con,"CH%c",ATCHc)) { - printf("xbee_senddata: Error\n"); - return 1; - } - pkt = xbee_getpacketwait(con); - if (!pkt || pkt->status) { - printf("\nXBee didnt return a result for CH\n"); - return 1; - } - free(pkt); - printf("%c[2KScanning channel 0x%02X...\r",27,ATCHc); - fflush(stdout); - /* send a ND - Node Discover request */ - if (!xbee_senddata(con,"ND")) { - /* only wait for a bit longer than the ND timeout */ - ATNTc = ATNT + 10; - /* loop until the end packet has been received or timeout reached */ - while (!BREAK && ATNTc--) { - /* get a packet */ - pkt = xbee_getpacket(con); - /* check a packet was returned, and that its one we are after... */ - if (pkt && !memcmp(pkt->atCmd,"ND",2)) { - /* is this the end packet? you can tell from the 0 datalen */ - if (pkt->datalen == 0) { - /* free the packet */ - free(pkt); - break; - } else { - /* check if we know this node already */ - for (i = 0; i < nodes; i++) { - /* the 64bit address will match one in the list */ - if (!memcmp(&(pkt->data[2]),&(addrs[i][0]),8)) break; - } - ttime = time(NULL); - strftime(stime,32,"%I:%M:%S %p",gmtime(&ttime)); - /* is there space for another? */ - if ((i == nodes) && - (nodes == MAXNODES) && - (!saidfull)) { - printf("%c[2KMAXNODES reached... Can't add more...\r",27); - /* flush so the change is seen! */ - fflush(stdout); - saidfull = 1; - } else { - /* is this a rewrite? */ - if (i != nodes) { - /* find the line to edit */ - printf("%c[%dA",27,nodes-i+1); - /* clear the line */ - printf("%c[2K",27); - } else { - /* fill the blank line */ - printf("%c[%dA",27,1); - } - /* save the channel */ - addrs[i][8] = ATCHc; - /* write out the info */ - printf("%c[31mCH:%c[32m 0x%02X ",27,27,ATCHc); - printf("%c[31mID:%c[32m 0x",27,27); - if (i == nodes || !(addrs[i][18] & 0x80)) { - printf("...."); - } else { - printf("%02X%02X",addrs[i][9],addrs[i][10]); - } - printf(" "); - printf("%c[31mMY:%c[32m 0x%02X%02X ",27,27,pkt->data[0],pkt->data[1]); - printf("%c[31mSH:%c[32m 0x%02X%02X%02X%02X ",27,27,pkt->data[2],pkt->data[3],pkt->data[4],pkt->data[5]); - printf("%c[31mSL:%c[32m 0x%02X%02X%02X%02X ",27,27,pkt->data[6],pkt->data[7],pkt->data[8],pkt->data[9]); - printf("%c[31mBD:%c[32m ",27,27); - if (i == nodes || !(addrs[i][18] & 0x40)) { - printf("......"); - } else { - switch (addrs[i][11]) { - case 0: printf(" 1200"); break; - case 1: printf(" 2400"); break; - case 2: printf(" 4800"); break; - case 3: printf(" 9600"); break; - case 4: printf(" 19200"); break; - case 5: printf(" 38400"); break; - case 6: printf(" 57600"); break; - case 7: printf("115200"); break; - default: printf(" other"); break; - } - } - printf(" "); - printf("%c[31mAP:%c[32m 0x",27,27); - if (i == nodes || !(addrs[i][18] & 0x20)) { - printf(".."); - } else { - printf("%02X",addrs[i][12]); - } - printf(" "); - printf("%c[31mHV:%c[32m 0x",27,27); - if (i == nodes || !(addrs[i][18] & 0x10)) { - printf("...."); - } else { - printf("%02X%02X",addrs[i][13],addrs[i][14]); - } - printf(" "); - printf("%c[31mVR:%c[32m 0x",27,27); - if (i == nodes || !(addrs[i][18] & 0x08)) { - printf("...."); - } else { - printf("%02X%02X",addrs[i][15],addrs[i][16]); - } - printf(" "); - printf("%c[31mCC:%c[32m ",27,27); - if (i == nodes || !(addrs[i][18] & 0x04)) { - printf(" . (0x..)"); - } else { - printf("'%c' (0x%02X)",addrs[i][17],addrs[i][17]); - } - printf(" "); - printf("%c[31mNI:%c[32m %-20s ",27,27,&(pkt->data[11])); - printf("%c[31mdB:%c[32m -%2d ",27,27,pkt->data[10]); - printf("%c[31m@:%c[32m %s",27,27,stime); - /* is this a rewrite? */ - if (i != nodes) { - /* go back the the bottom */ - printf("%c[%dB\r",27,nodes-i+1); - } else { - /* if its new... save the address */ - memcpy(&(addrs[nodes][0]),&(pkt->data[2]),8); - /* turn off all the flags */ - addrs[nodes][18] = 0; - /* new line is only wanted for new nodes */ - printf("\n%c[2K\n%c[0m",27,27); - /* if not, then add 1 to the number of nodes! */ - nodes++; - } - printf("%c[0m%c[2KScanning channel 0x%02X...\r",27,27,ATCHc); - fflush(stdout); - } - /* flush so the change is seen! */ - fflush(stdout); - } - /* free the packet */ - free(pkt); - } - /* sleep for 100ms (same as NT steps */ - usleep(100000); - } - } - fflush(stdout); - /* check for all nodes on this channel, and get thier pan id */ - for (i = 0; i < nodes; i++) { - int first = 1; - if (addrs[i][8] == ATCHc) { - xbee_con *tcon; - unsigned int dh,dl; - if (first) { - printf("%c[2KGathering settings for nodes on channel 0x%02X...\r",27,ATCHc); - first = 0; - } - /* get the address, and turn it the right way up! */ - memcpy(&dh,&(addrs[i][0]),4); - dh = ((dh & 0xFF) << 24) | ((dh & 0xFF00) << 8) | ((dh & 0xFF0000) >> 8) | ((dh & 0xFF000000) >> 24); - memcpy(&dl,&(addrs[i][4]),4); - dl = ((dl & 0xFF) << 24) | ((dl & 0xFF00) << 8) | ((dl & 0xFF0000) >> 8) | ((dl & 0xFF000000) >> 24); - /* setup a connection the the remote node */ - if ((tcon = xbee_newcon('I',xbee_64bitRemoteAT,dh,dl)) != NULL) { - /* find the line to edit */ - printf("\r%c[%dA",27,nodes-i+1); - - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"ID")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the ID column */ - printf("\r%c[18C",27); - /* print the ID */ - printf("%c[32m%02X%02X%c[0m",27,rpkt->data[0],rpkt->data[1],27); - addrs[i][9] = rpkt->data[0]; - addrs[i][10] = rpkt->data[1]; - /* turn on the flag */ - addrs[i][18] |= 0x80; - free(rpkt); - } - - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"BD")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the BD column */ - printf("\r%c[80C",27); - if ((rpkt->data[0] != 0x00) || (rpkt->data[1] != 0x00) || (rpkt->data[2] != 0x00) || ((rpkt->data[3] & 0xF8) != 0x00)) { - addrs[i][11] = 8; - } else { - addrs[i][11] = rpkt->data[3]; - } - /* turn on the flag */ - addrs[i][18] |= 0x40; - /* print the baud rate */ - printf("%c[32m",27); - switch (addrs[i][11]) { - case 0: printf(" 1200"); break; - case 1: printf(" 2400"); break; - case 2: printf(" 4800"); break; - case 3: printf(" 9600"); break; - case 4: printf(" 19200"); break; - case 5: printf(" 38400"); break; - case 6: printf(" 57600"); break; - case 7: printf("115200"); break; - default: printf(" other"); break; - } - printf("%c[0m",27); - free(rpkt); - } - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"AP")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the AP column */ - printf("\r%c[96C",27); - /* print the ID */ - printf("%c[32m%02X%c[0m",27,rpkt->data[0],27); - addrs[i][12] = rpkt->data[0]; - /* turn on the flag */ - addrs[i][18] |= 0x20; - free(rpkt); - } - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"HV")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the HV column */ - printf("\r%c[108C",27); - /* print the ID */ - printf("%c[32m%02X%02X%c[0m",27,rpkt->data[0],rpkt->data[1],27); - addrs[i][13] = rpkt->data[0]; - addrs[i][14] = rpkt->data[1]; - /* turn on the flag */ - addrs[i][18] |= 0x10; - free(rpkt); - } - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"VR")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the VR column */ - printf("\r%c[122C",27); - /* print the ID */ - printf("%c[32m%02X%02X%c[0m",27,rpkt->data[0],rpkt->data[1],27); - addrs[i][15] = rpkt->data[0]; - addrs[i][16] = rpkt->data[1]; - /* turn on the flag */ - addrs[i][18] |= 0x08; - free(rpkt); - } - /* in this case we dont care if we dont get a response packet... */ - if (xbee_senddata(tcon,"CC")) { - printf("xbee_senddata: Error\n"); - return 1; - } - if (((rpkt = xbee_getpacketwait(tcon)) != NULL) && (rpkt->status == 0)) { - /* move over the CC column */ - printf("\r%c[134C",27); - /* print the ID */ - printf("%c[32m'%c' (0x%02X)%c[0m",27,rpkt->data[0],rpkt->data[0],27); - addrs[i][17] = rpkt->data[0]; - /* turn on the flag */ - addrs[i][18] |= 0x04; - free(rpkt); - } - /* go back the the bottom */ - printf("%c[%dB\r",27,nodes-i+1); - fflush(stdout); - } - } - } - /* fall back to the first channel if that was the last */ - if (XBeePro && ATCHc == 0x17) { - ATCHc = 0x0C; - } else if (!XBeePro && ATCHc == 0x1A) { - ATCHc = 0x0B; - } else { - /* else move onto next channel */ - ATCHc++; - } - usleep(100000); - } - - return 0; -} - diff --git a/libs/thirdParty/libxbee/sample/simple.c b/libs/thirdParty/libxbee/sample/simple.c deleted file mode 100644 index 8ab776f12..000000000 --- a/libs/thirdParty/libxbee/sample/simple.c +++ /dev/null @@ -1,68 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will politely return any recieved data */ - -#include -#include -#include - -int main(int argc, char *argv[]) { - xbee_con *con; - xbee_pkt *pkt, *rpkt; - - /* setup the xbee */ - if (xbee_setup("/dev/ttyUSB0",57600) == -1) { - /* oh no... it failed */ - printf("xbee_setup() failed...\n"); - exit(1); - } - - /* setup a connection */ - con = xbee_newcon('I',xbee_64bitData, 0x0013A200, 0x40081826); - - printf("Waiting...\n"); - - /* just wait for data, and echo it back! */ - while (1) { - /* while there are packets avaliable... */ - while ((pkt = xbee_getpacket(con)) != NULL) { - /* print the recieved data */ - printf("Rx: %s\n",pkt->data); - /* say thank you */ - if (xbee_senddata(con,"thank you for saying '%s'\r\n",pkt->data)) { - printf("xbee_senddata: Error\n"); - return 1; - } - /* free the packet */ - free(pkt); - } - usleep(100000); - } - - /* shouldn't ever get here but... */ - return 0; -} - diff --git a/libs/thirdParty/libxbee/sample/talk_to_me.c b/libs/thirdParty/libxbee/sample/talk_to_me.c deleted file mode 100644 index 26750988a..000000000 --- a/libs/thirdParty/libxbee/sample/talk_to_me.c +++ /dev/null @@ -1,82 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample will make the remote XBee talk to us! */ - -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - union { - unsigned char as8[8]; - unsigned int as32[2]; - } addr; - xbee_con *atCon, *rCon; - xbee_pkt *pkt; - - /* the extra arguments are the CC ('+' by default) and GT (1000) by default AT values */ - xbee_setuplogAPI("/dev/ttyUSB0",57600,2,'+',1000); - - atCon = xbee_newcon('@', xbee_localAT); - - xbee_senddata(atCon, "SH"); - pkt = xbee_getpacketwait(atCon); - if (!pkt || pkt->status || pkt->atCmd[0] != 'S' || pkt->atCmd[1] != 'H') { - printf("Missing SH Packet!\n"); - return 1; - } - addr.as8[3] = pkt->data[0]; - addr.as8[2] = pkt->data[1]; - addr.as8[1] = pkt->data[2]; - addr.as8[0] = pkt->data[3]; - free(pkt); - - xbee_senddata(atCon, "SL"); - pkt = xbee_getpacketwait(atCon); - if (!pkt || pkt->status || pkt->atCmd[0] != 'S' || pkt->atCmd[1] != 'L') { - printf("Missing SL Packet!\n"); - return 1; - } - addr.as8[7] = pkt->data[0]; - addr.as8[6] = pkt->data[1]; - addr.as8[5] = pkt->data[2]; - addr.as8[4] = pkt->data[3]; - free(pkt); - - printf("Local XBee address is: 0x%08X %08X\n", addr.as32[0], addr.as32[1]); - - rCon = xbee_newcon('#', xbee_64bitRemoteAT, 0x13A200, 0x403CB26A); - - xbee_senddata(rCon, "DH%c%c%c%c", addr.as8[3], addr.as8[2], addr.as8[1], addr.as8[0]); - usleep(250000); - xbee_senddata(rCon, "DL%c%c%c%c", addr.as8[7], addr.as8[6], addr.as8[5], addr.as8[4]); - usleep(250000); - - /* calling xbee_end() will return the xbee to its previous API mode */ - xbee_end(); - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/vb6/README.txt b/libs/thirdParty/libxbee/sample/vb6/README.txt deleted file mode 100644 index fe13a7dfc..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/README.txt +++ /dev/null @@ -1,8 +0,0 @@ -These sample projects provide a quick demo of how to use various functions - -Running these assume that you have first compiled libxbee.dll successfully -You will also need to either copy libxbee.dll into this folder or into a - directory in your PATH - -If you want to use libxbee in your own projects, you must include libxbee.bas - which will provide you with access to the functions and type declarations diff --git a/libs/thirdParty/libxbee/sample/vb6/demo/Form1.frm b/libs/thirdParty/libxbee/sample/vb6/demo/Form1.frm deleted file mode 100644 index 4c7c6655b..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/demo/Form1.frm +++ /dev/null @@ -1,64 +0,0 @@ -VERSION 5.00 -Begin VB.Form Form1 - Caption = "Form1" - ClientHeight = 2250 - ClientLeft = 120 - ClientTop = 450 - ClientWidth = 3855 - LinkTopic = "Form1" - ScaleHeight = 2250 - ScaleWidth = 3855 - StartUpPosition = 3 'Windows Default - Begin VB.TextBox tb - Height = 1995 - Left = 120 - MultiLine = -1 'True - TabIndex = 0 - Top = 120 - Width = 3615 - End -End -Attribute VB_Name = "Form1" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = True -Attribute VB_Exposed = False -Option Explicit - -Dim myCon As Long -Dim myDataCon As Long - -Private Sub Form_Load() - Dim i As Long - Dim x As Byte - Dim ctype, addrH, addrL As Long - Me.Show - DoEvents - - ' Connect to the XBee on COM1 with a baud rate of 57600 - ' The XBee should be in API mode 2 (ATAP2) - If xbee_setupDebug("COM8", 57600, "xbee.log") <> 0 Then - MsgBox "Error while setting up the local XBee module", vbCritical, "xbee_setup()" - End - End If - xbee_logit "Hello!" - - ' Enable callbacks, this only needs to be done ONCE - ' The window handle provided must remain in memory (dont unload the form - callbacks will automatically be disabled) - xbee_enableCallbacks Me.hWnd - - ' Create a Remote AT connection to a node using 64-bit addressing - myCon = xbee_newcon_64bit(&H30, xbee_64bitRemoteAT, &H13A200, &H404B75DE) - xbee_enableACKwait myCon - - myDataCon = xbee_newcon_64bit(&H31, xbee_64bitData, &H13A200, &H404B75DE) - - ' Setup callbacks - xbee_attachCallback myCon, AddressOf Module1.callback1 - xbee_attachCallback myDataCon, AddressOf Module1.callback2 - - ' Send the AT command NI (Node Identifier) - tb.text = "Sending 'ATNI'..." - xbee_sendstring myCon, "NI" -End Sub - diff --git a/libs/thirdParty/libxbee/sample/vb6/demo/demo.bas b/libs/thirdParty/libxbee/sample/vb6/demo/demo.bas deleted file mode 100644 index b1e51fd90..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/demo/demo.bas +++ /dev/null @@ -1,19 +0,0 @@ -Attribute VB_Name = "Module1" -Public Function callback1(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - ' Check the returned status, if it isnt 0 then an error occured - If pkt.status <> 0 Then - Form1.tb.Text = Form1.tb.Text & vbNewLine & "An error occured (" & pkt.status & ")" - Exit Function - End If - - ' Display the Node Identifier - Form1.tb.Text = Form1.tb.Text & vbNewLine & "Node Identifier:" & StrConv(pkt.data, vbUnicode) - Form1.tb.SelStart = Len(Form1.tb.Text) -End Function - -Public Function callback2(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - ' Display the data - Form1.tb.Text = Form1.tb.Text & vbNewLine & "Rx:" & StrConv(pkt.data, vbUnicode) - Form1.tb.SelStart = Len(Form1.tb.Text) -End Function - diff --git a/libs/thirdParty/libxbee/sample/vb6/demo/demo.vbp b/libs/thirdParty/libxbee/sample/vb6/demo/demo.vbp deleted file mode 100644 index 815e949e7..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/demo/demo.vbp +++ /dev/null @@ -1,33 +0,0 @@ -Type=Exe -Form=Form1.frm -Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\..\Windows\SysWOW64\stdole2.tlb#OLE Automation -Module=Module1; demo.bas -Module=libxbee; ..\libxbee.bas -IconForm="Form1" -Startup="Form1" -ExeName32="demo.exe" -Command32="" -Name="Project1" -HelpContextID="0" -CompatibleMode="0" -MajorVer=1 -MinorVer=0 -RevisionVer=0 -AutoIncrementVer=0 -ServerSupportFiles=0 -VersionCompanyName="Microsoft" -CompilationType=0 -OptimizationType=0 -FavorPentiumPro(tm)=0 -CodeViewDebugInfo=0 -NoAliasing=0 -BoundsCheck=0 -OverflowCheck=0 -FlPointCheck=0 -FDIVCheck=0 -UnroundedFP=0 -StartMode=0 -Unattended=0 -Retained=0 -ThreadPerObject=0 -MaxNumberOfThreads=1 diff --git a/libs/thirdParty/libxbee/sample/vb6/libxbee.bas b/libs/thirdParty/libxbee/sample/vb6/libxbee.bas deleted file mode 100644 index 0875907f9..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/libxbee.bas +++ /dev/null @@ -1,285 +0,0 @@ -Attribute VB_Name = "libxbee" -Option Explicit - -Enum xbee_types - xbee_unknown - - xbee_localAT - xbee_remoteAT - xbee_modemStatus - xbee_txStatus - - ' XBee Series 1 stuff - xbee_16bitRemoteAT - xbee_64bitRemoteAT - - xbee_16bitData - xbee_64bitData - - xbee_16bitIO - xbee_64bitIO - - ' XBee Series 2 stuff - xbee2_data - xbee2_txStatus -End Enum - -Type xbee_sample - '# X A5 A4 A3 A2 A1 A0 D8 D7 D6 D5 D4 D3 D2 D1 D0 - IOmask As Integer - '# X X X X X X X D8 D7 D6 D5 D4 D3 D2 D1 D0 - IOdigital As Integer - '# X X X X X D D D D D D D D D D D - IOanalog(0 To 5) As Integer -End Type - -Type xbee_pkt - flags As Long '# bit 0 - is64 - '# bit 1 - dataPkt - '# bit 2 - txStatusPkt - '# bit 3 - modemStatusPkt - '# bit 4 - remoteATPkt - '# bit 5 - IOPkt - frameID As Byte - atCmd(0 To 1) As Byte - - status As Byte - samples As Byte - RSSI As Byte - - Addr16(0 To 1) As Byte - - Addr64(0 To 7) As Byte - - data(0 To 127) As Byte - - datalen As Long - - type As Long ' enum xbee_types - - SPARE As Long ' IGNORE THIS (is the pointer to the next packet in C... this will ALWAYS be 0 in VB) - - IOdata As xbee_sample -End Type - -Private OldhWndHandler As Long -Private ActivehWnd As Long -Private callbackMessageID As Long -Private Callbacks As New Collection - -Public Declare Sub xbee_free Lib "libxbee.dll" (ByVal ptr As Long) - -Public Declare Function xbee_setup Lib "libxbee.dll" (ByVal port As String, ByVal baudRate As Long) As Long -Public Declare Function xbee_setupDebug Lib "libxbee.dll" (ByVal port As String, ByVal baudRate As Long, ByVal logfile As String) As Long -Private Declare Function xbee_setupDebugAPIRaw Lib "libxbee.dll" Alias "xbee_setupDebugAPI" (ByVal port As String, ByVal baudRate As Long, ByVal logfile As String, ByVal cmdSeq As Byte, ByVal cmdTime As Long) As Long -Private Declare Function xbee_setupAPIRaw Lib "libxbee.dll" Alias "xbee_setupAPI" (ByVal port As String, ByVal baudRate As Long, ByVal cmdSeq As Byte, ByVal cmdTime As Long) As Long - -Public Declare Function xbee_end Lib "libxbee.dll" () As Long - -Public Declare Function xbee_newcon_simple Lib "libxbee.dll" (ByVal frameID As Byte, ByVal conType As Long) As Long 'xbee_con * -Public Declare Function xbee_newcon_16bit Lib "libxbee.dll" (ByVal frameID As Byte, ByVal conType As Long, ByVal addr16bit As Long) As Long 'xbee_con * -Public Declare Function xbee_newcon_64bit Lib "libxbee.dll" (ByVal frameID As Byte, ByVal conType As Long, ByVal addr64bitLow As Long, ByVal addr64bitHigh As Long) As Long 'xbee_con * -Public Declare Sub xbee_enableACKwait Lib "libxbee.dll" (ByVal con As Long) -Public Declare Sub xbee_disableACKwait Lib "libxbee.dll" (ByVal con As Long) -Public Declare Sub xbee_enableDestroySelf Lib "libxbee.dll" (ByVal con As Long) - -Private Declare Sub xbee_enableCallbacksRaw Lib "libxbee.dll" Alias "xbee_enableCallbacks" (ByVal hWnd As Long, ByVal uMsg As Long) -Private Declare Sub xbee_attachCallbackRaw Lib "libxbee.dll" Alias "xbee_attachCallback" (ByVal con As Long) -Private Declare Sub xbee_detachCallbackRaw Lib "libxbee.dll" Alias "xbee_detachCallback" (ByVal con As Long) -Private Declare Function xbee_runCallback Lib "libxbee.dll" (ByVal func As Long, ByVal con As Long, ByVal pkt As Long) As Long - -Public Declare Sub xbee_endcon2 Lib "libxbee.dll" (ByVal con As Long) -Public Declare Sub xbee_flushcon Lib "libxbee.dll" (ByVal con As Long) - -Public Declare Function xbee_senddata Lib "libxbee.dll" Alias "xbee_nsenddata" (ByVal con As Long, ByRef data As Byte, ByVal Length As Long) As Long -Private Declare Function xbee_senddata_str Lib "libxbee.dll" Alias "xbee_nsenddata" (ByVal con As Long, ByVal data As String, ByVal Length As Long) As Long - -Public Declare Function xbee_getpacketRaw Lib "libxbee.dll" Alias "xbee_getpacket" (ByVal con As Long) As Long 'xbee_pkt * - -Public Declare Function xbee_hasanalog Lib "libxbee.dll" (ByRef pkt As xbee_pkt, ByVal sample As Long, ByVal inputPin As Long) As Long -Public Declare Function xbee_getanalog Lib "libxbee.dll" (ByRef pkt As xbee_pkt, ByVal sample As Long, ByVal inputPin As Long, ByVal Vref As Double) As Double - -Public Declare Function xbee_hasdigital Lib "libxbee.dll" (ByRef pkt As xbee_pkt, ByVal sample As Long, ByVal inputPin As Long) As Long -Public Declare Function xbee_getdigital Lib "libxbee.dll" (ByRef pkt As xbee_pkt, ByVal sample As Long, ByVal inputPin As Long) As Long - -Private Declare Function xbee_svn_versionRaw Lib "libxbee.dll" Alias "xbee_svn_version" () As Long -Public Declare Sub xbee_logit Lib "libxbee.dll" (ByVal text As String) - -'########################################################################################################################################################################### - -Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) -Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long -Private Declare Function RegisterWindowMessage Lib "user32" Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long -Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long -Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long -Private Const WM_DESTROY = &H2 -Private Const GWL_WNDPROC = -4 - -Public Function PointerToString(lngPtr As Long) As String - Dim strTemp As String - Dim lngLen As Long - If lngPtr Then - lngLen = lstrlenW(lngPtr) * 2 - If lngLen Then - strTemp = Space(lngLen) - CopyMemory ByVal strTemp, ByVal lngPtr, lngLen - PointerToString = Replace(strTemp, Chr(0), "") - End If - End If -End Function - -Public Function ArrayToString(data() As Byte, Optional lb As Integer = -1, Optional ub As Integer = -1) As String - Dim tmp As String - Dim i - If lb = -1 Then lb = LBound(data) - If ub = -1 Then ub = UBound(data) - tmp = "" - For i = lb To ub - If (data(i) = 0) Then Exit For - tmp = tmp & Chr(data(i)) - Next - ArrayToString = tmp -End Function - -Public Function xbee_pointerToPacket(lngPtr As Long) As xbee_pkt - Dim p As xbee_pkt - CopyMemory p, ByVal lngPtr, Len(p) - xbee_pointerToPacket = p -End Function - -Public Sub libxbee_load() - ' this function is simply to get VB6 to call a libxbee function - ' if you are using any C DLLs that make use of libxbee, then you should call this function first so that VB6 will load libxbee - xbee_svn_versionRaw -End Sub - -Public Function xbee_svn_version() As String - xbee_svn_version = PointerToString(xbee_svn_versionRaw()) -End Function - -Public Function xbee_setupAPI(ByVal port As String, ByVal baudRate As Long, ByVal cmdSeq As String, ByVal cmdTime As Long) - xbee_setupAPI = xbee_setupAPIRaw(port, baudRate, Asc(cmdSeq), cmdTime) -End Function - -Public Function xbee_setupDebugAPI(ByVal port As String, ByVal baudRate As Long, ByVal logfile As String, ByVal cmdSeq As String, ByVal cmdTime As Long) - xbee_setupDebugAPI = xbee_setupDebugAPIRaw(port, baudRate, logfile, Asc(cmdSeq), cmdTime) -End Function - -Private Sub xbee_ensureMessageID() - If callbackMessageID = 0 Then - callbackMessageID = RegisterWindowMessage("libxbee") - End If - xbee_enableCallbacksRaw ActivehWnd, callbackMessageID -End Sub - -Public Sub xbee_attachCallback(ByVal con As Long, ByVal func As Long) - Dim t(0 To 1) As Long - Dim c As String - If ActivehWnd = 0 Then - Debug.Print "Callbacks not enabled!" - Exit Sub - End If - xbee_ensureMessageID - c = CStr(con) - t(0) = con - t(1) = func - On Error Resume Next - Callbacks.Remove c - Callbacks.Add t, c - On Error GoTo 0 - xbee_attachCallbackRaw con -End Sub - -Public Sub xbee_detachCallback(ByVal con As Long) - If ActivehWnd = 0 Then - Debug.Print "Callbacks not enabled!" - Exit Sub - End If - On Error Resume Next - xbee_detachCallbackRaw con - Callbacks.Remove CStr(con) -End Sub - -Public Sub xbee_enableCallbacks(ByVal hWnd As Long) - If ActivehWnd <> 0 Then - Debug.Print "Callbacks already enabled!" - Exit Sub - End If - ActivehWnd = hWnd - OldhWndHandler = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf libxbee.xbee_messageHandler) - xbee_ensureMessageID -End Sub - -Public Sub xbee_disableCallbacks() - Dim id As Variant - If ActivehWnd = 0 Then - Debug.Print "Callbacks not enabled!" - Exit Sub - End If - For Each id In Callbacks - xbee_detachCallback id(0) - Next - SetWindowLong ActivehWnd, GWL_WNDPROC, OldhWndHandler - ActivehWnd = 0 - OldhWndHandler = 0 -End Sub - -Private Function xbee_messageHandler(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long - If uMsg = callbackMessageID Then - Dim t As Long - On Error Resume Next - Err.Clear - t = Callbacks.Item(CStr(wParam))(1) - If Err.Number = 0 Then - On Error GoTo 0 - xbee_messageHandler = xbee_runCallback(t, wParam, lParam) - Exit Function - End If - On Error GoTo 0 - xbee_logit "Unable to match Connection with active callback!" - End If - xbee_messageHandler = CallWindowProc(OldhWndHandler, hWnd, uMsg, wParam, lParam) - If uMsg = WM_DESTROY And ActivehWnd <> 0 Then - ' Disable the MessageHandler if the form "unload" event is detected - xbee_disableCallbacks - End If -End Function - -Public Sub xbee_endcon(ByRef con As Long) - xbee_endcon2 con - con = 0 -End Sub - -Public Function xbee_sendstring(ByVal con As Long, ByVal str As String) - xbee_sendstring = xbee_senddata_str(con, str, Len(str)) -End Function - -Public Function xbee_getpacketPtr(ByVal con As Long, ByRef pkt As Long) As Integer - Dim ptr As Long - - ptr = xbee_getpacketRaw(con) - If ptr = 0 Then - pkt = 0 - xbee_getpacketPtr = 0 - Exit Function - End If - - pkt = ptr - xbee_getpacketPtr = 1 -End Function - -Public Function xbee_getpacket(ByVal con As Long, ByRef pkt As xbee_pkt) As Integer - Dim ptr As Long - - ptr = xbee_getpacketRaw(con) - If ptr = 0 Then - xbee_getpacket = 0 - Exit Function - End If - - pkt = xbee_pointerToPacket(ptr) - xbee_free ptr - - xbee_getpacket = 1 -End Function - diff --git a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/Form1.frm b/libs/thirdParty/libxbee/sample/vb6/talk_to_me/Form1.frm deleted file mode 100644 index 426a9df93..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/Form1.frm +++ /dev/null @@ -1,1197 +0,0 @@ -VERSION 5.00 -Begin VB.Form Form1 - BorderStyle = 1 'Fixed Single - Caption = "Talk to Me" - ClientHeight = 7875 - ClientLeft = 45 - ClientTop = 375 - ClientWidth = 7515 - LinkTopic = "Form1" - MaxButton = 0 'False - ScaleHeight = 7875 - ScaleWidth = 7515 - StartUpPosition = 1 'CenterOwner - Begin VB.Timer tmr_timeout - Enabled = 0 'False - Interval = 5000 - Left = 3720 - Top = 1380 - End - Begin VB.Frame Frame2 - Caption = " Actions " - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 1335 - Left = 180 - TabIndex = 1 - Top = 6420 - Width = 7215 - Begin VB.CommandButton write_settings - Caption = "Write Settings" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 1920 - TabIndex = 50 - Top = 780 - Width = 1935 - End - Begin VB.CommandButton set_default - Caption = "Set Default" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 180 - TabIndex = 49 - Top = 780 - Width = 1575 - End - Begin VB.CommandButton reset_node - Caption = "Reset Node" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 4020 - TabIndex = 47 - Top = 780 - Width = 1575 - End - Begin VB.CommandButton set_dest - Caption = "Set destination" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 1920 - TabIndex = 46 - Top = 300 - Width = 1935 - End - Begin VB.CommandButton talk_to_me - Caption = "Talk to Me" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 180 - TabIndex = 45 - Top = 300 - Width = 1575 - End - Begin VB.CommandButton set_ni - Caption = "Set Node Identifier" - Enabled = 0 'False - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 375 - Left = 4020 - TabIndex = 51 - Top = 300 - Width = 2355 - End - End - Begin VB.Frame Frame1 - Caption = " Settings " - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 3315 - Left = 180 - TabIndex = 0 - Top = 3000 - Width = 7215 - Begin VB.Label ni - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 24 - Top = 300 - Width = 3915 - End - Begin VB.Label sl - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 22 - Top = 1020 - Width = 3915 - End - Begin VB.Label sh - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 20 - Top = 780 - Width = 3915 - End - Begin VB.Label my - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 18 - Top = 540 - Width = 3915 - End - Begin VB.Label ap - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 17 - Top = 1260 - Width = 3915 - End - Begin VB.Label bd - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 16 - Top = 1500 - Width = 3915 - End - Begin VB.Label ch - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 15 - Top = 1740 - Width = 3915 - End - Begin VB.Label dh - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 14 - Top = 1980 - Width = 3915 - End - Begin VB.Label dl - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 13 - Top = 2220 - Width = 3915 - End - Begin VB.Label ia - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 11 - Top = 2460 - Width = 3915 - End - Begin VB.Label vr - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 10 - Top = 2940 - Width = 3915 - End - Begin VB.Label hv - BackStyle = 0 'Transparent - Caption = "-" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Left = 3180 - TabIndex = 12 - Top = 2700 - Width = 3915 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "NI - Node Identifier" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 17 - Left = 180 - TabIndex = 25 - Top = 300 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "MY - 16-bit Address" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 13 - Left = 180 - TabIndex = 19 - Top = 540 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "VR - Firmware Version" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 14 - Left = 180 - TabIndex = 9 - Top = 2940 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "IA - I/O Address" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 12 - Left = 180 - TabIndex = 8 - Top = 2460 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "DL - Destination Low" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 10 - Left = 180 - TabIndex = 6 - Top = 2220 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "DH - Destination High" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 9 - Left = 180 - TabIndex = 5 - Top = 1980 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "CH - Channel" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 8 - Left = 180 - TabIndex = 4 - Top = 1740 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "BD - Interface Rate" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 7 - Left = 180 - TabIndex = 3 - Top = 1500 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "AP - API Enable" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 6 - Left = 180 - TabIndex = 2 - Top = 1260 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "HV - Hardware Version" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 11 - Left = 180 - TabIndex = 7 - Top = 2700 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " .....:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 29 - Left = 180 - TabIndex = 44 - Top = 2700 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ...........:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 28 - Left = 180 - TabIndex = 43 - Top = 1260 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " .......:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 27 - Left = 180 - TabIndex = 42 - Top = 1500 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ..............:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 26 - Left = 180 - TabIndex = 41 - Top = 1740 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " .....:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 25 - Left = 180 - TabIndex = 40 - Top = 1980 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ......:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 24 - Left = 180 - TabIndex = 39 - Top = 2220 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ..........:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 23 - Left = 180 - TabIndex = 38 - Top = 2460 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " .....:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 22 - Left = 180 - TabIndex = 37 - Top = 2940 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " .......:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 21 - Left = 180 - TabIndex = 36 - Top = 540 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ......:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 18 - Left = 180 - TabIndex = 33 - Top = 300 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "SL - 64-bit Address (Lo)" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 16 - Left = 180 - TabIndex = 23 - Top = 1020 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "SH - 64-bit Address (Hi)" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 255 - Index = 15 - Left = 180 - TabIndex = 21 - Top = 780 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ..:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 20 - Left = 180 - TabIndex = 35 - Top = 780 - Width = 2955 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = " ..:" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 255 - Index = 19 - Left = 180 - TabIndex = 34 - Top = 1020 - Width = 2955 - End - End - Begin VB.Timer tmr_refresh - Enabled = 0 'False - Interval = 500 - Left = 3240 - Top = 1380 - End - Begin VB.Frame Frame3 - Caption = " Node List " - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 2775 - Left = 180 - TabIndex = 26 - Top = 120 - Width = 7215 - Begin VB.ListBox nodelist - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 2085 - Left = 180 - TabIndex = 27 - Top = 540 - Width = 6855 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "16-bit" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 195 - Index = 0 - Left = 240 - TabIndex = 32 - Top = 300 - Width = 675 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "RSSI" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 195 - Index = 3 - Left = 3660 - TabIndex = 30 - Top = 300 - Width = 435 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "@" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 195 - Index = 4 - Left = 4440 - TabIndex = 29 - Top = 300 - Width = 135 - End - Begin VB.Label Label - BackStyle = 0 'Transparent - Caption = "Node Name" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 195 - Index = 5 - Left = 4740 - TabIndex = 28 - Top = 300 - Width = 915 - End - Begin VB.Label Label - Alignment = 2 'Center - BackStyle = 0 'Transparent - Caption = "- -" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - ForeColor = &H00C0C0C0& - Height = 195 - Index = 2 - Left = 1020 - TabIndex = 48 - Top = 300 - Width = 2355 - End - Begin VB.Label Label - Alignment = 2 'Center - BackStyle = 0 'Transparent - Caption = "(Hi) 64-bit (Lo)" - BeginProperty Font - Name = "Courier New" - Size = 9 - Charset = 0 - Weight = 400 - Underline = 0 'False - Italic = 0 'False - Strikethrough = 0 'False - EndProperty - Height = 195 - Index = 1 - Left = 1020 - TabIndex = 31 - Top = 300 - Width = 2355 - End - End -End -Attribute VB_Name = "Form1" -Attribute VB_GlobalNameSpace = False -Attribute VB_Creatable = False -Attribute VB_PredeclaredId = True -Attribute VB_Exposed = False -Dim dieNow As Boolean - -Private Sub Form_Load() - Me.Show - DoEvents - dieNow = False - - ' setup libxbee - If (xbee_setupDebugAPI("COM8", 57600, "xbee.log", "+", 250) = -1) Then - MsgBox "libxbee setup failed...", vbCritical - Unload Me - End - End If - - ' enable callback functions - xbee_enableCallbacks Me.hWnd - - ' setup a local at connection - atcon = xbee_newcon_simple(Asc("A"), xbee_localAT) - xbee_enableACKwait atcon - xbee_attachCallback atcon, AddressOf localCB - - ' set off the chain reaction! - xbeesend atcon, "MY" -End Sub - -Private Sub Form_Unload(Cancel As Integer) - Static c As Integer - dieNow = 1 - Cancel = 1 - Me.Caption = "Waiting for command to complete..." - c = c + 1 - If (c >= 2) Then - Cancel = 0 - End If -End Sub - -Private Sub nodelist_Click() - Dim tmp() As String - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - If set_dest.Tag = "yes" Then - nodelist.Enabled = False - Else - If nodelist.ListIndex = 0 Then - remoteCon = atcon - Else - str2 = Split(nodelist.text, " ") - remoteCon = xbee_newcon_64bit(Asc("2"), xbee_64bitRemoteAT, CLng("&H" & Right(str2(1), 8)), CLng("&H" & Right(str2(2), 8))) - End If - setButtons False - nodelist.Enabled = False - tmp = Split(nodelist.List(nodelist.ListIndex), " ") - ni = tmp(5) - my = tmp(0) - sh = tmp(1) - sl = tmp(2) - ap = "-" - bd = "-" - ch = "-" - dh = "-" - dl = "-" - ia = "-" - hv = "-" - vr = "-" - End If -End Sub - -Private Sub reset_node_Click() - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - nodelist.Enabled = False - setButtons False - reset_node.Tag = "yes" -End Sub - -Private Sub set_default_Click() - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - nodelist.Enabled = False - setButtons False - set_default.Tag = "yes" -End Sub - -Private Sub set_dest_Click() - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - nodelist.Tag = nodelist.ListIndex - setButtons False - set_dest.Tag = "yes" -End Sub - -Private Sub set_ni_Click() - Dim newni As String - Dim oldni As String - oldni = Split(nodelist.text, " ")(5) - newni = InputBox("New node identifier:", "Set Node Identifier", oldni) - If newni = oldni Then Exit Sub - nodelist.Enabled = False - setButtons False - set_ni.Tag = newni -End Sub - -Private Sub talk_to_me_Click() - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - nodelist.Enabled = False - setButtons False - talk_to_me.Tag = "yes" -End Sub - -Private Sub tmr_refresh_Timer() - Dim str As String - Dim str2() As String - tmr_refresh.Enabled = False - If atcon = 0 Then Exit Sub - - If (dieNow) Then - xbee_end - DoEvents - xbee_disableCallbacks - Unload Me - End - End If - - If nodelist.Enabled = False Then - xbee_attachCallback remoteCon, AddressOf remoteCB - If talk_to_me.Tag = "yes" Then - str2 = Split(Form1.nodelist.text, " ") - xbee_attachCallback remoteCon, AddressOf setupCB_TTM - str2 = Split(nodelist.List(0), " ") - str = Chr(CInt("&H" & Mid(str2(1), 9, 2))) - str = Chr(CInt("&H" & Mid(str2(1), 7, 2))) & str - str = Chr(CInt("&H" & Mid(str2(1), 5, 2))) & str - str = Chr(CInt("&H" & Mid(str2(1), 3, 2))) & str - str = "DH" & str - xbeesend remoteCon, str - ElseIf set_dest.Tag = "yes" Then - str2 = Split(Form1.nodelist.text, " ") - xbee_attachCallback remoteCon, AddressOf setupCB_SDEST - str2 = Split(nodelist.text, " ") - str = Chr(CInt("&H" & Mid(str2(1), 9, 2))) - str = Chr(CInt("&H" & Mid(str2(1), 7, 2))) & str - str = Chr(CInt("&H" & Mid(str2(1), 5, 2))) & str - str = Chr(CInt("&H" & Mid(str2(1), 3, 2))) & str - str = "DH" & str - xbeesend remoteCon, str - ElseIf reset_node.Tag = "yes" Then - xbee_sendstring remoteCon, "FR" - setButtons True - reset_node.Tag = "" - tmr_refresh.Enabled = True - ElseIf write_settings.Tag = "yes" Then - xbee_sendstring remoteCon, "WR" - setButtons True - write_settings.Tag = "" - tmr_refresh.Enabled = True - ElseIf set_default.Tag = "yes" Then - setupCB_Default_Start - ElseIf set_ni.Tag <> "" Then - xbeesend remoteCon, "NI" & set_ni.Tag - set_ni.Tag = "" - Else - xbeesend remoteCon, "AP" - End If - Exit Sub - End If - ' initiate network scan - xbee_attachCallback atcon, AddressOf localCB - xbeesend atcon, "MY" -End Sub - -Private Sub tmr_timeout_Timer() - Dim con As Long - Dim str As String - Dim str2() As String - tmr_timeout.Enabled = False - str2 = Split(tmr_timeout.Tag, Chr(1), 2) - con = CStr(str2(0)) - str = str2(1) - If MsgBox("Request timed out... Retry?", vbYesNo + vbQuestion, "Retry?") = vbNo Then - setButtons True - nodelist.Enabled = True - tmr_refresh.Enabled = True - Exit Sub - End If - xbeesend con, str -End Sub - -Private Sub write_settings_Click() - If nodelist.ListCount = 0 Or nodelist.ListIndex = -1 Then Exit Sub - nodelist.Enabled = False - setButtons False - write_settings.Tag = "yes" -End Sub diff --git a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.bas b/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.bas deleted file mode 100644 index dc9f5f76d..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.bas +++ /dev/null @@ -1,431 +0,0 @@ -Attribute VB_Name = "Module1" -Public atcon As Long -Public remoteCon As Long - -Public Sub setButtons(ByVal state As Boolean) - Form1.talk_to_me.Tag = "" - Form1.talk_to_me.Enabled = state - Form1.set_dest.Tag = "" - Form1.set_dest.Enabled = state - Form1.reset_node.Tag = "" - Form1.reset_node.Enabled = state - Form1.set_default.Tag = "" - Form1.set_default.Enabled = state - Form1.write_settings.Tag = "" - Form1.write_settings.Enabled = state - Form1.set_ni.Tag = "" - Form1.set_ni.Enabled = state -End Sub - -Public Function xbeesend(ByVal con As Long, ByVal str As String) As Long - Form1.tmr_timeout.Enabled = False - Form1.tmr_timeout.Tag = CStr(con) & Chr(1) & str - Form1.tmr_timeout.Enabled = True - xbee_sendstring con, str -End Function - -Public Sub setupCB_Default_Start() - xbee_attachCallback remoteCon, AddressOf setupCB_Default - xbee_attachCallback atcon, AddressOf setupCB_Default - xbeesend remoteCon, "CH" & Chr(16) -End Sub -Public Function setupCB_Default(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - Dim str As String - Dim str2() As String - ' default values (in order of setting): - ' CH = 10 - ' local CH = 10 - ' MY = FF - ' T3 = 1 - ' BD = 6 - ' AP = 0 - ' RO = 1 - ' D0 = 5 (turn on rest of system) - ' D1 = 2 (battery reading) - ' D2 = 0 - ' D3 = 5 (reset) - ' D4 = 4 (battery reading power) - ' D5 = 0 - ' D6 = 0 - ' D7 = 0 - ' D8 = 0 - ' IA = 0xFFFF (accept inputs from anyone) - ' IU = 0 - Debug.Print ArrayToString(pkt.atCmd) - If con = atcon Then - xbee_attachCallback con, AddressOf localCB - xbeesend remoteCon, "MY" & Chr(255) & Chr(255) - Exit Function - End If - Select Case ArrayToString(pkt.atCmd) - Case "CH" - xbeesend atcon, "CH" & Chr(16) - Case "MY" - xbeesend con, "T3" & Chr(1) - Case "T3" - xbeesend con, "BD" & Chr(0) & Chr(0) & Chr(0) & Chr(6) - Case "BD" - xbeesend con, "AP" & Chr(0) - Case "AP" - xbeesend con, "RO" & Chr(1) - Case "RO" - xbeesend con, "D0" & Chr(5) - Case "D0" - xbeesend con, "D1" & Chr(2) - Case "D1" - xbeesend con, "D2" & Chr(0) - Case "D2" - xbeesend con, "D3" & Chr(4) - Case "D3" - xbeesend con, "D4" & Chr(4) - Case "D4" - xbeesend con, "D5" & Chr(0) - Case "D5" - xbeesend con, "D6" & Chr(0) - Case "D6" - xbeesend con, "D7" & Chr(0) - Case "D7" - xbeesend con, "D8" & Chr(0) - Case "D8" - xbeesend con, "IA" & Chr(0) & Chr(0) & Chr(0) & Chr(0) & Chr(0) & Chr(0) & Chr(255) & Chr(255) - Case "IA" - xbeesend con, "IU" & Chr(0) - Case "IU" - Form1.set_default.Tag = "" - Form1.tmr_refresh.Enabled = True - Form1.tmr_timeout.Enabled = False - End Select -End Function - -Public Function setupCB_TTM(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - Dim str As String - Dim str2() As String - Select Case ArrayToString(pkt.atCmd) - Case "DH" - str2 = Split(Form1.nodelist.List(0), " ") - str = Chr(CInt("&H" & Mid(str2(2), 9, 2))) - str = Chr(CInt("&H" & Mid(str2(2), 7, 2))) & str - str = Chr(CInt("&H" & Mid(str2(2), 5, 2))) & str - str = Chr(CInt("&H" & Mid(str2(2), 3, 2))) & str - str = "DL" & str - xbeesend con, str - Case "DL" - Form1.talk_to_me.Tag = "" - Form1.tmr_refresh.Enabled = True - End Select -End Function - -Public Function setupCB_SDEST(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - Dim str As String - Dim str2() As String - Select Case ArrayToString(pkt.atCmd) - Case "DH" - str2 = Split(Form1.nodelist.text, " ") - str = Chr(CInt("&H" & Mid(str2(2), 9, 2))) - str = Chr(CInt("&H" & Mid(str2(2), 7, 2))) & str - str = Chr(CInt("&H" & Mid(str2(2), 5, 2))) & str - str = Chr(CInt("&H" & Mid(str2(2), 3, 2))) & str - str = "DL" & str - xbeesend con, str - Case "DL" - Form1.set_dest.Tag = "" - Form1.nodelist.ListIndex = Form1.nodelist.Tag - Form1.tmr_refresh.Enabled = True - End Select -End Function - -Public Function remoteCB(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - Dim t As String - Dim i As Long - Debug.Print "<+>", ArrayToString(pkt.atCmd) - Form1.tmr_timeout.Enabled = False - Select Case ArrayToString(pkt.atCmd) - Case "AP" - Select Case pkt.data(0) - Case 0 - Form1.ap.Caption = "0 - API Disabled" - Case 1 - Form1.ap.Caption = "1 - API Enabled (no escapes)" - Case 2 - Form1.ap.Caption = "2 - API Enabled (with escapes)" - Case Default - Form1.ap.Caption = "0x" & Hex(pkt.data(0)) & " - Unknown..." - End Select - xbeesend con, "BD" - Case "BD" - t = Hex(pkt.data(3)) - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(2)) & t - If (Len(t) < 4) Then t = "0" & t - t = Hex(pkt.data(1)) & t - If (Len(t) < 6) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 8) Then t = "0" & t - i = CStr("&H" & t) - Select Case i - Case 0 - Form1.bd.Caption = "0 - 1200 bps" - Case 1 - Form1.bd.Caption = "1 - 2400 bps" - Case 2 - Form1.bd.Caption = "2 - 4800 bps" - Case 3 - Form1.bd.Caption = "3 - 9600 bps" - Case 4 - Form1.bd.Caption = "4 - 19200 bps" - Case 5 - Form1.bd.Caption = "5 - 38400 bps" - Case 6 - Form1.bd.Caption = "6 - 57600 bps" - Case 7 - Form1.bd.Caption = "7 - 115200 bps" - Case Default - Form1.bd.Caption = "0x" & Hex(i) & " - Unknwon..." - End Select - xbeesend con, "CH" - Case "CH" - t = Hex(pkt.data(0)) - If (Len(t) < 2) Then t = "0" & t - Form1.ch.Caption = "0x" & t - xbeesend con, "DH" - Case "DH" - t = Hex(pkt.data(3)) - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(2)) & t - If (Len(t) < 4) Then t = "0" & t - t = Hex(pkt.data(1)) & t - If (Len(t) < 6) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 8) Then t = "0" & t - Form1.dh.Caption = "0x" & t - xbeesend con, "DL" - Case "DL" - t = Hex(pkt.data(3)) - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(2)) & t - If (Len(t) < 4) Then t = "0" & t - t = Hex(pkt.data(1)) & t - If (Len(t) < 6) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 8) Then t = "0" & t - Form1.dl.Caption = "0x" & t - xbeesend con, "IA" - Case "IA" - t = Hex(pkt.data(7)) & t - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(6)) & t - If (Len(t) < 4) Then t = "0" & t - t = Hex(pkt.data(5)) & t - If (Len(t) < 6) Then t = "0" & t - t = Hex(pkt.data(4)) & t - If (Len(t) < 8) Then t = "0" & t - t = Hex(pkt.data(3)) & t - If (Len(t) < 10) Then t = "0" & t - t = Hex(pkt.data(2)) & t - If (Len(t) < 12) Then t = "0" & t - t = Hex(pkt.data(1)) & t - If (Len(t) < 14) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 16) Then t = "0" & t - Form1.ia.Caption = "0x" & t - xbeesend con, "HV" - Case "HV" - t = Hex(pkt.data(1)) - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 4) Then t = "0" & t - Form1.hv.Caption = "0x" & t - xbeesend con, "VR" - Case "VR" - t = Hex(pkt.data(1)) - If (Len(t) < 2) Then t = "0" & t - t = Hex(pkt.data(0)) & t - If (Len(t) < 4) Then t = "0" & t - Form1.vr.Caption = "0x" & t - If con = atcon Then - xbee_attachCallback con, AddressOf localCB - End If - setButtons True - Form1.nodelist.Enabled = True - Form1.tmr_refresh.Enabled = True - Form1.tmr_timeout.Enabled = False - Case Else - If con = atcon Then - xbee_attachCallback con, AddressOf localCB - End If - setButtons True - Form1.nodelist.Enabled = True - Form1.tmr_refresh.Enabled = True - Form1.tmr_timeout.Enabled = False - End Select -End Function - -Public Function localCB(ByVal con As Long, ByRef pkt As xbee_pkt) As Long - Dim nodeinfo As String - Dim nodename As String - Dim tmp As String - Dim tmp2() As String - Dim sh, sl As String - Dim i, m As Integer - Dim AT As String - Form1.tmr_timeout.Enabled = False - AT = ArrayToString(pkt.atCmd) - ' handle initial stuff - Select Case AT - Case "MY" - nodeinfo = "0x" - tmp = Hex(pkt.data(0)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(1)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - If Form1.nodelist.ListCount > 0 Then - Form1.nodelist.List(0) = nodeinfo - Else - Form1.nodelist.AddItem nodeinfo - End If - ' issue next command - xbeesend con, "SH" - Case "SH" - nodeinfo = Form1.nodelist.List(0) & " 0x" - tmp = Hex(pkt.data(0)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(1)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(2)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(3)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - Form1.nodelist.List(0) = nodeinfo - ' issue next command - xbeesend con, "SL" - Case "SL" - nodeinfo = Form1.nodelist.List(0) & " 0x" - tmp = Hex(pkt.data(0)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(1)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(2)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - tmp = Hex(pkt.data(3)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - nodeinfo = nodeinfo & tmp - Form1.nodelist.List(0) = nodeinfo - ' issue next command - xbeesend con, "NI" - Case "NI" - nodeinfo = Form1.nodelist.List(0) & " -***dB * " - tmp = ArrayToString(pkt.data) - nodeinfo = nodeinfo & tmp - Form1.nodelist.List(0) = nodeinfo - ' issue next command - xbeesend con, "ND" - End Select - - If (AT <> "ND") Then Exit Function - If (pkt.status <> 0) Then - MsgBox "An error occured when attempting to scan!", vbCritical - Exit Function - End If - - If (pkt.datalen = 0) Then - ' increment the counter for each node - For i = 0 To Form1.nodelist.ListCount - 1 - tmp2 = Split(Form1.nodelist.List(i), " ") - If tmp2(4) <> "+" And tmp2(4) <> "*" Then - tmp2(4) = CInt(tmp2(4)) + 1 - If (CInt(tmp2(4)) > 9) Then tmp2(4) = "+" - End If - tmp = "" - For m = LBound(tmp2) To UBound(tmp2) - If m > 0 Then tmp = tmp & " " - tmp = tmp & tmp2(m) - Next - Form1.nodelist.List(i) = tmp - Next - - ' restart the refresh timer - Form1.tmr_refresh.Enabled = True - Exit Function - End If - - ' extract the 16-bit address - nodeinfo = "" - tmp = Hex(pkt.data(1)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - tmp = Hex(pkt.data(0)) & tmp - If (Len(tmp) < 4) Then tmp = "0" & tmp - tmp = "0x" & tmp - nodeinfo = nodeinfo & tmp - - nodeinfo = nodeinfo & " " - - ' extract the high portion of the 64-bit address - nodeinfo = nodeinfo - tmp = Hex(pkt.data(5)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - tmp = Hex(pkt.data(4)) & tmp - If (Len(tmp) < 4) Then tmp = "0" & tmp - tmp = Hex(pkt.data(3)) & tmp - If (Len(tmp) < 6) Then tmp = "0" & tmp - tmp = Hex(pkt.data(2)) & tmp - If (Len(tmp) < 8) Then tmp = "0" & tmp - tmp = "0x" & tmp - nodeinfo = nodeinfo & tmp - sh = tmp - - nodeinfo = nodeinfo & " " - - ' extract the low portion of the 64-bit address - nodeinfo = nodeinfo - tmp = Hex(pkt.data(9)) - If (Len(tmp) < 2) Then tmp = "0" & tmp - tmp = Hex(pkt.data(8)) & tmp - If (Len(tmp) < 4) Then tmp = "0" & tmp - tmp = Hex(pkt.data(7)) & tmp - If (Len(tmp) < 6) Then tmp = "0" & tmp - tmp = Hex(pkt.data(6)) & tmp - If (Len(tmp) < 8) Then tmp = "0" & tmp - tmp = "0x" & tmp - nodeinfo = nodeinfo & tmp - sl = tmp - - nodeinfo = nodeinfo & " " - - ' extract the rssi (signal strength) - tmp = "-" & CStr(pkt.data(10)) - If Len(tmp) < 3 Then tmp = " " & tmp - If Len(tmp) < 4 Then tmp = " " & tmp - tmp = tmp & "dB" - nodeinfo = nodeinfo & tmp - - nodeinfo = nodeinfo & " " - ' add a number of scans - nodeinfo = nodeinfo & 0 - - nodeinfo = nodeinfo & " " - - ' extract the node name - nodename = ArrayToString(pkt.data, 11) - nodeinfo = nodeinfo & nodename - - ' see if we have already got this node - For i = 0 To Form1.nodelist.ListCount - 1 - tmp2 = Split(Form1.nodelist.List(i), " ") - If tmp2(1) = sh And tmp2(2) = sl Then - Form1.nodelist.List(i) = nodeinfo - Exit Function - End If - Next - - ' otherwise add the info to the list - Form1.nodelist.AddItem nodeinfo -End Function diff --git a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.vbp b/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.vbp deleted file mode 100644 index a80cc9bf2..000000000 --- a/libs/thirdParty/libxbee/sample/vb6/talk_to_me/talk_to_me.vbp +++ /dev/null @@ -1,33 +0,0 @@ -Type=Exe -Form=Form1.frm -Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#..\..\..\..\..\..\..\..\Windows\SysWOW64\stdole2.tlb#OLE Automation -Module=libxbee; ..\libxbee.bas -Module=Module1; talk_to_me.bas -IconForm="Form1" -Startup="Form1" -ExeName32="talk_to_me.exe" -Command32="" -Name="Project1" -HelpContextID="0" -CompatibleMode="0" -MajorVer=1 -MinorVer=0 -RevisionVer=0 -AutoIncrementVer=0 -ServerSupportFiles=0 -VersionCompanyName="Microsoft" -CompilationType=0 -OptimizationType=0 -FavorPentiumPro(tm)=0 -CodeViewDebugInfo=0 -NoAliasing=0 -BoundsCheck=0 -OverflowCheck=0 -FlPointCheck=0 -FDIVCheck=0 -UnroundedFP=0 -StartMode=0 -Unattended=0 -Retained=0 -ThreadPerObject=0 -MaxNumberOfThreads=1 diff --git a/libs/thirdParty/libxbee/sample/xbee2_rx.c b/libs/thirdParty/libxbee/sample/xbee2_rx.c deleted file mode 100644 index 223ddcf63..000000000 --- a/libs/thirdParty/libxbee/sample/xbee2_rx.c +++ /dev/null @@ -1,60 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample demonstrates how to communicate using series 2 radios */ - -#include -#include -#include -#include - -void callback(xbee_con *con, xbee_pkt *pkt) { - int ret; - /* print the recieved data */ - printf("Rx: %s\n",pkt->data); -} - -int main(int argc, char *argv[]) { - union { - unsigned char as8[8]; - unsigned int as32[2]; - } addr; - xbee_con *rCon; - xbee_pkt *pkt; - - xbee_setuplog("/dev/ttyUSB1",57600, 2); - - rCon = xbee_newcon('#', xbee2_data, 0x13A200, 0x403302B1); - rCon->waitforACK = 1; - rCon->callback = callback; - - for (;;) { - sleep(60); - } - - /* calling xbee_end() will return the xbee to its previous API mode */ - xbee_end(); - return 0; -} diff --git a/libs/thirdParty/libxbee/sample/xbee2_tx.c b/libs/thirdParty/libxbee/sample/xbee2_tx.c deleted file mode 100644 index db49fe6fa..000000000 --- a/libs/thirdParty/libxbee/sample/xbee2_tx.c +++ /dev/null @@ -1,54 +0,0 @@ -#ifdef shell -gcc -o ${0//.c/} $0 -lxbee -g -exit -} -#endif -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this sample demonstrates how to communicate using series 2 radios */ - -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - union { - unsigned char as8[8]; - unsigned int as32[2]; - } addr; - xbee_con *rCon; - xbee_pkt *pkt; - - xbee_setuplog("/dev/ttyUSB0",57600, 2); - - rCon = xbee_newcon('#', xbee2_data, 0x13A200, 0x403302BF); - - for (;;) { - xbee_senddata(rCon, "Hello!"); - printf("Sent!\n"); - sleep(1); - } - - /* calling xbee_end() will return the xbee to its previous API mode */ - xbee_end(); - return 0; -} diff --git a/libs/thirdParty/libxbee/umakefile b/libs/thirdParty/libxbee/umakefile deleted file mode 100644 index 597a32377..000000000 --- a/libs/thirdParty/libxbee/umakefile +++ /dev/null @@ -1,91 +0,0 @@ -#-- uncomment this to enable debugging -#DEBUG:=-g -DDEBUG - -#-- what compiler are you using? -CC:=gcc - - -###### YOU SHOULD NOT CHANGE BELOW THIS LINE ###### - -VERSION:=1.4.0 -SRCS:=api.c - -CFLAGS:=-Wall -Wstrict-prototypes -Wno-variadic-macros -pedantic -c -fPIC ${DEBUG} -CLINKS:=-lpthread -lrt ${DEBUG} -DEFINES:=-D__UMAKEFILE - -SRCS:=${sort ${SRCS}} - -.PHONY: all run new clean main - - -# all - do everything (default) # -all: ./lib/libxbee.so.$(VERSION) main - @echo "*** Done! ***" - - -# run - remake main and then run # -run: main - ./bin/main - - -# new - clean and do everything again # -new: clean all - - -# clean - remove any compiled files and PDFs # -clean: - rm -f ./*~ - rm -f ./sample/*~ - rm -f ./obj/*.o - rm -f ./lib/libxbee.so* - rm -f ./bin/main - -# install - installs library # -install: /usr/lib/libxbee.so.$(VERSION) /usr/include/xbee.h - -/usr/lib/libxbee.so.$(VERSION): ./lib/libxbee.so.$(VERSION) - cp ./lib/libxbee.so.$(VERSION) /usr/lib/libxbee.so.$(VERSION) -f - @chmod 755 /usr/lib/libxbee.so.$(VERSION) - @chown root:root /usr/lib/libxbee.so.$(VERSION) - ln ./libxbee.so.$(VERSION) /usr/lib/libxbee.so.1 -sf - @chown root:root /usr/lib/libxbee.so.1 - ln ./libxbee.so.$(VERSION) /usr/lib/libxbee.so -sf - @chown root:root /usr/lib/libxbee.so - -/usr/include/xbee.h: ./xbee.h - cp ./xbee.h /usr/include/xbee.h -f - @chmod 644 /usr/include/xbee.h - @chown root:root /usr/include/xbee.h - -uninstall: - rm /usr/lib/libxbee.so.$(VERSION) -f - rm /usr/lib/libxbee.so.1 -f - rm /usr/lib/libxbee.so -f - rm /usr/include/xbee.h -f - -# main - compile & link objects # -main: ./bin/main - -./bin/main: ./obj/api.o ./bin/ ./main.c - ${CC} ${CLINKS} ./main.c ./obj/api.o -o ./bin/main ${DEBUG} - -./bin/: - mkdir ./bin/ - -./lib/libxbee.so.$(VERSION): ./lib/ ${addprefix ./obj/,${SRCS:.c=.o}} ./xbee.h - gcc -shared -Wl,-soname,libxbee.so.1 -o ./lib/libxbee.so.$(VERSION) ./obj/*.o -lrt - ln ./libxbee.so.$(VERSION) ./lib/libxbee.so.1 -sf - ln ./libxbee.so.$(VERSION) ./lib/libxbee.so -sf - -./lib/: - mkdir ./lib/ - -./obj/: - mkdir ./obj/ - -./obj/%.o: ./obj/ %.c %.h xbee.h - ${CC} ${CFLAGS} ${DEFINES} ${DEBUG} $*.c -o $@ - -./obj/%.o: ./obj/ %.c xbee.h - ${CC} ${CFLAGS} ${DEFINES} ${DEBUG} $*.c -o $@ diff --git a/libs/thirdParty/libxbee/win32.README.txt b/libs/thirdParty/libxbee/win32.README.txt deleted file mode 100644 index f16dc0d94..000000000 --- a/libs/thirdParty/libxbee/win32.README.txt +++ /dev/null @@ -1,31 +0,0 @@ -Welcome to libxbee! - -I have proveded sample code in the ./sample directory. Hopefully this will help -get you up and running with libxbee. If you would like samples showing a different -aspect of libxbee, then please do not hesitate to file an 'issue' on the project -site, and I will get to it ASAP: - http://code.google.com/p/libxbee/issues/list - - -Documentation is avaliable as HTML in the 'doc' folder. - - -Please note that this project is still in development, so should not be used for -any purpose other than learning/playing/testing etc... Basically don't use it to -make money, and then hold me responsible if it breaks! - -Feel free to contact me directly with any queries: - attie@attie.co.uk - - -=== Compiling & Using === - -To compile you must: - 1) Have Visual C++ 2008 (v9.0) installed (Express or otherwise) - 2) Have make installed - see GnuWin - 3) Modify win32.makefile so that the paths at the top are correct - -Compile using `make -f win32.makefile` from the command prompt - -For C/C++ (and probrably others) just include xbee.h and link with libxbee.dll -For VB6 see ./sample/vb6/ for more info & examples diff --git a/libs/thirdParty/libxbee/win32.makefile b/libs/thirdParty/libxbee/win32.makefile deleted file mode 100644 index d90965681..000000000 --- a/libs/thirdParty/libxbee/win32.makefile +++ /dev/null @@ -1,48 +0,0 @@ -#-- uncomment this to enable debugging -#DEBUG:=/Zi /DDEBUG /MTd -#LDBUG:=/DEBUG - -#-- you may need to edit these lines if your installation is different -VCPath:=C:\Program Files\Microsoft Visual Studio 10.0\VC -SDKPath:=C:\Program Files\Microsoft SDKs\Windows\v7.1 - -#!! if using visual studio 2010, you may need to run the following in a shell, -# and then within the same shell run `make -f win32.makefile` -# C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\vcvars32.bat - -###### YOU SHOULD NOT CHANGE BELOW THIS LINE ###### -SHELL:=cmd -DEBUG?=/MT - -SRCS:=api.c - -CC:="${VCPath}\bin\cl.exe" -LINK:="${VCPath}\bin\link.exe" -RC:="${SDKPath}\bin\rc.exe" - -.PHONY: all new clean - -all: .\lib\libxbee.dll - -new: clean all - -clean: - -rmdir /Q /S lib - -rmdir /Q /S obj - -.\obj: - mkdir obj - -.\lib: - mkdir lib - -.\lib\libxbee.dll: .\lib .\obj\api.obj .\obj\win32.res - ${LINK} ${LDBUG} /nologo /DLL /MAP:lib\libxbee.map /DEF:xsys\win32.def \ - "/LIBPATH:${SDKPath}\Lib" "/LIBPATH:${VCPath}\lib" \ - /OUT:.\lib\libxbee.dll .\obj\api.obj .\obj\win32.res - -.\obj\api.obj: .\obj api.c api.h xbee.h - ${CC} ${DEBUG} /nologo "/I${SDKPath}\Include" "/I${VCPath}\include" /RTCs /Gz /c /Fd.\lib\libxbee.pdb /Fo.\obj\api.obj ${SRCS} - -.\obj\win32.res: .\xsys\win32.rc - ${RC} "/I${SDKPath}\Include" "/I${VCPath}\include" /n /fo.\obj\win32.res .\xsys\win32.rc diff --git a/libs/thirdParty/libxbee/xbee.h b/libs/thirdParty/libxbee/xbee.h deleted file mode 100644 index 61331d844..000000000 --- a/libs/thirdParty/libxbee/xbee.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ -#ifndef XBEE_H -#define XBEE_H - -#if !defined(__GNUC__) && !defined(_WIN32) -#error "This library is only currently compatible with Linux and Win32" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __LIBXBEE_API_H -typedef void* xbee_hnd; -#endif - -#include - -#ifdef __GNUC__ /* ---- */ -#include -typedef pthread_mutex_t xbee_mutex_t; -typedef pthread_cond_t xbee_cond_t; -typedef pthread_t xbee_thread_t; -typedef sem_t xbee_sem_t; -typedef FILE* xbee_file_t; -#elif (defined(WIN32) || defined(_WIN32)) /* -------------- */ -#include -#define CALLTYPE __stdcall -#define CALLTYPEVA __cdecl -typedef HANDLE xbee_mutex_t; -typedef CONDITION_VARIABLE xbee_cond_t; -typedef HANDLE xbee_thread_t; -typedef HANDLE xbee_sem_t; -typedef HANDLE xbee_file_t; -#else -#error "Unknown operating system or compiler" -#endif /* ------------- */ - -#ifndef CALLTYPE -#define CALLTYPE -#endif - -#ifndef CALLTYPEVA -#define CALLTYPEVA -#endif - -enum xbee_types { - xbee_unknown, - - xbee_localAT, /* frame ID */ - xbee_remoteAT, - xbee_modemStatus, - xbee_txStatus, - - /* XBee Series 1 stuff */ - xbee_16bitRemoteAT, /* frame ID */ - xbee_64bitRemoteAT, /* frame ID */ - - xbee_16bitData, /* frame ID for ACKs */ - xbee_64bitData, /* frame ID for ACKs */ - - xbee_16bitIO, - xbee_64bitIO, - - /* XBee Series 2 stuff */ - xbee2_data, - xbee2_txStatus -}; -typedef enum xbee_types xbee_types; - -typedef struct xbee_sample xbee_sample; -struct xbee_sample { - /* X A5 A4 A3 A2 A1 A0 D8 D7 D6 D5 D4 D3 D2 D1 D0 */ - unsigned short IOmask; /* IO */ - /* X X X X X X X D8 D7 D6 D5 D4 D3 D2 D1 D0 */ - unsigned short IOdigital; /* IO */ - /* X X X X X D D D D D D D D D D D */ - unsigned short IOanalog[6]; /* IO */ -}; - -typedef struct xbee_pkt xbee_pkt; -struct xbee_pkt { - unsigned int sAddr64 : 1; /* yes / no */ - unsigned int dataPkt : 1; /* if no - AT packet */ - unsigned int txStatusPkt : 1; - unsigned int modemStatusPkt : 1; - unsigned int remoteATPkt : 1; - unsigned int IOPkt : 1; - unsigned int isBroadcastADR : 1; - unsigned int isBroadcastPAN : 1; - - unsigned char frameID; /* AT Status */ - unsigned char atCmd[2]; /* AT */ - - unsigned char status; /* AT Data Status */ /* status / options */ - unsigned char samples; - unsigned char RSSI; /* Data */ - - unsigned char Addr16[2]; /* AT Data */ - - unsigned char Addr64[8]; /* AT Data */ - - unsigned char data[128]; /* AT Data */ - - unsigned int datalen; - xbee_types type; - - xbee_pkt *next; - - xbee_sample IOdata[1]; /* this array can be extended by using a this trick: - p = calloc(sizeof(xbee_pkt) + (sizeof(xbee_sample) * (samples - 1))) */ -}; - -typedef struct xbee_con xbee_con; -struct xbee_con { - unsigned int tAddr64 : 1; - unsigned int atQueue : 1; /* queues AT commands until AC is sent */ - unsigned int txDisableACK : 1; - unsigned int txBroadcastPAN: 1; /* broadcasts to PAN */ - unsigned int destroySelf : 1; /* if set, the callback thread will destroy the connection - after all of the packets have been processed */ - unsigned int waitforACK : 1; /* waits for the ACK or NAK after transmission */ - unsigned int noFreeAfterCB : 1; /* prevents libxbee from free'ing the packet after the callback has completed */ - unsigned int __spare__ : 1; - xbee_types type; - unsigned char frameID; - unsigned char tAddr[8]; /* 64-bit 0-7 16-bit 0-1 */ - void *customData; /* can be used to store data related to this connection */ - void (CALLTYPE *callback)(xbee_con*,xbee_pkt*); /* call back function */ - void *callbackList; - xbee_mutex_t callbackmutex; - xbee_mutex_t callbackListmutex; - xbee_mutex_t Txmutex; - xbee_sem_t waitforACKsem; - volatile unsigned char ACKstatus; /* 255 = waiting, 0 = success, 1 = no ack, 2 = cca fail, 3 = purged */ - xbee_con *next; -}; - -int CALLTYPE xbee_setup(char *path, int baudrate); -int CALLTYPE xbee_setuplog(char *path, int baudrate, int logfd); -int CALLTYPE xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime); -int CALLTYPE xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime); -xbee_hnd CALLTYPE _xbee_setup(char *path, int baudrate); -xbee_hnd CALLTYPE _xbee_setuplog(char *path, int baudrate, int logfd); -xbee_hnd CALLTYPE _xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime); -xbee_hnd CALLTYPE _xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime); - -int CALLTYPE xbee_end(void); -int CALLTYPE _xbee_end(xbee_hnd xbee); - -void CALLTYPE xbee_logitf(char *format, ...); -void CALLTYPE _xbee_logitf(xbee_hnd xbee, char *format, ...); -void CALLTYPE xbee_logit(char *str); -void CALLTYPE _xbee_logit(xbee_hnd xbee, char *str); - -xbee_con * CALLTYPEVA xbee_newcon(unsigned char frameID, xbee_types type, ...); -xbee_con * CALLTYPEVA _xbee_newcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, ...); -xbee_con * CALLTYPE _xbee_vnewcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, va_list ap); - -void CALLTYPE xbee_purgecon(xbee_con *con); -void CALLTYPE _xbee_purgecon(xbee_hnd xbee, xbee_con *con); - -void CALLTYPE xbee_endcon2(xbee_con **con, int alreadyUnlinked); -void CALLTYPE _xbee_endcon2(xbee_hnd xbee, xbee_con **con, int alreadyUnlinked); -#define xbee_endcon(x) xbee_endcon2(&(x),0) -#define _xbee_endcon(xbee,x) _xbee_endcon2((xbee),&(x),0) - -int CALLTYPE xbee_nsenddata(xbee_con *con, char *data, int length); -int CALLTYPE _xbee_nsenddata(xbee_hnd xbee, xbee_con *con, char *data, int length); -int CALLTYPEVA xbee_senddata(xbee_con *con, char *format, ...); -int CALLTYPEVA _xbee_senddata(xbee_hnd xbee, xbee_con *con, char *format, ...); -int CALLTYPE xbee_vsenddata(xbee_con *con, char *format, va_list ap); -int CALLTYPE _xbee_vsenddata(xbee_hnd xbee, xbee_con *con, char *format, va_list ap); - -#if defined(WIN32) -/* oh and just 'cos windows has rubbish memory management rules... this too */ -void CALLTYPE xbee_free(void *ptr); -#endif /* ------------- */ - -xbee_pkt * CALLTYPE xbee_getpacket(xbee_con *con); -xbee_pkt * CALLTYPE _xbee_getpacket(xbee_hnd xbee, xbee_con *con); -xbee_pkt * CALLTYPE xbee_getpacketwait(xbee_con *con); -xbee_pkt * CALLTYPE _xbee_getpacketwait(xbee_hnd xbee, xbee_con *con); - -int CALLTYPE xbee_hasdigital(xbee_pkt *pkt, int sample, int input); -int CALLTYPE xbee_getdigital(xbee_pkt *pkt, int sample, int input); - -int CALLTYPE xbee_hasanalog(xbee_pkt *pkt, int sample, int input); -double CALLTYPE xbee_getanalog(xbee_pkt *pkt, int sample, int input, double Vref); - -const char * CALLTYPE xbee_svn_version(void); -const char * CALLTYPE xbee_build_info(void); - -void CALLTYPE xbee_listen_stop(xbee_hnd xbee); - -#ifdef __cplusplus -} /* cplusplus */ -#endif - -#endif diff --git a/libs/thirdParty/libxbee/xsys/README b/libs/thirdParty/libxbee/xsys/README deleted file mode 100644 index 48d392850..000000000 --- a/libs/thirdParty/libxbee/xsys/README +++ /dev/null @@ -1 +0,0 @@ -This directory contains cross-system support files. \ No newline at end of file diff --git a/libs/thirdParty/libxbee/xsys/linux.c b/libs/thirdParty/libxbee/xsys/linux.c deleted file mode 100644 index 768d5b3d4..000000000 --- a/libs/thirdParty/libxbee/xsys/linux.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* ################################################################# */ -/* ### Linux Code ################################################## */ -/* ################################################################# */ - -/* this file contains code that is used by Linux ONLY */ -#ifndef __GNUC__ -#error "This file should only be used on a Linux system" -#endif - -#include "linux.h" - -int init_serial(xbee_hnd xbee, int baudrate) { - struct flock fl; - struct termios tc; - speed_t chosenbaud; - - /* select the baud rate */ - switch (baudrate) { - case 1200: chosenbaud = B1200; break; - case 2400: chosenbaud = B2400; break; - case 4800: chosenbaud = B4800; break; - case 9600: chosenbaud = B9600; break; - case 19200: chosenbaud = B19200; break; - case 38400: chosenbaud = B38400; break; - case 57600: chosenbaud = B57600; break; - case 115200:chosenbaud = B115200; break; - default: - fprintf(stderr,"%s(): Unknown or incompatiable baud rate specified... (%d)\n",__FUNCTION__,baudrate); - return -1; - }; - - /* open the serial port as a file descriptor */ - if ((xbee->ttyfd = open(xbee->path,O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) { - xbee_perror("xbee_setup():open()"); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); - return -1; - } - - /* lock the file */ - fl.l_type = F_WRLCK | F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_pid = getpid(); - if (fcntl(xbee->ttyfd, F_SETLK, &fl) == -1) { - xbee_perror("xbee_setup():fcntl()"); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); - close(xbee->ttyfd); - return -1; - } - - /* open the serial port as a FILE* */ - if ((xbee->tty = fdopen(xbee->ttyfd,"r+")) == NULL) { - xbee_perror("xbee_setup():fdopen()"); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); - close(xbee->ttyfd); - return -1; - } - - /* flush the serial port */ - fflush(xbee->tty); - - /* disable buffering */ - setvbuf(xbee->tty,NULL,_IONBF,BUFSIZ); - - /* setup the baud rate and other io attributes */ - tcgetattr(xbee->ttyfd, &tc); - /* input flags */ - tc.c_iflag &= ~ IGNBRK; /* enable ignoring break */ - tc.c_iflag &= ~(IGNPAR | PARMRK); /* disable parity checks */ - tc.c_iflag &= ~ INPCK; /* disable parity checking */ - tc.c_iflag &= ~ ISTRIP; /* disable stripping 8th bit */ - tc.c_iflag &= ~(INLCR | ICRNL); /* disable translating NL <-> CR */ - tc.c_iflag &= ~ IGNCR; /* disable ignoring CR */ - tc.c_iflag &= ~(IXON | IXOFF); /* disable XON/XOFF flow control */ - /* output flags */ - tc.c_oflag &= ~ OPOST; /* disable output processing */ - tc.c_oflag &= ~(ONLCR | OCRNL); /* disable translating NL <-> CR */ - tc.c_oflag &= ~ OFILL; /* disable fill characters */ - /* control flags */ - tc.c_cflag |= CREAD; /* enable reciever */ - tc.c_cflag &= ~ PARENB; /* disable parity */ - tc.c_cflag &= ~ CSTOPB; /* disable 2 stop bits */ - tc.c_cflag &= ~ CSIZE; /* remove size flag... */ - tc.c_cflag |= CS8; /* ...enable 8 bit characters */ - tc.c_cflag |= HUPCL; /* enable lower control lines on close - hang up */ - /* local flags */ - tc.c_lflag &= ~ ISIG; /* disable generating signals */ - tc.c_lflag &= ~ ICANON; /* disable canonical mode - line by line */ - tc.c_lflag &= ~ ECHO; /* disable echoing characters */ - tc.c_lflag &= ~ ECHONL; /* ??? */ - tc.c_lflag &= ~ NOFLSH; /* disable flushing on SIGINT */ - tc.c_lflag &= ~ IEXTEN; /* disable input processing */ - /* control characters */ - memset(tc.c_cc,0,sizeof(tc.c_cc)); - /* i/o rates */ - cfsetspeed(&tc, chosenbaud); /* set i/o baud rate */ - tcsetattr(xbee->ttyfd, TCSANOW, &tc); - tcflow(xbee->ttyfd, TCOON|TCION); /* enable input & output transmission */ - - return 0; -} - -static int xbee_select(xbee_hnd xbee, struct timeval *timeout) { - fd_set fds; - - FD_ZERO(&fds); - FD_SET(xbee->ttyfd, &fds); - - return select(xbee->ttyfd+1, &fds, NULL, NULL, timeout); -} - -#define xbee_sem_wait1sec(a) xbee_sem_wait1sec2(&(a)) -static inline int xbee_sem_wait1sec2(xbee_sem_t *sem) { - struct timespec to; - clock_gettime(CLOCK_REALTIME,&to); - to.tv_sec++; - return sem_timedwait(sem,&to); -} diff --git a/libs/thirdParty/libxbee/xsys/linux.h b/libs/thirdParty/libxbee/xsys/linux.h deleted file mode 100644 index 886bbf5bf..000000000 --- a/libs/thirdParty/libxbee/xsys/linux.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this file contains code that is used by Linux ONLY */ -#ifndef __GNUC__ -#error "This file should only be used on a Linux system" -#endif - -/* ################################################################# */ -/* ### Linux Code ################################################## */ -/* ################################################################# */ - -#define xbee_thread_create(a,b,c) pthread_create(&(a),NULL,(void *(*)(void *))(b),(void *)(c)) -#define xbee_thread_cancel(a,b) pthread_cancel((a)) -#define xbee_thread_join(a) pthread_join((a),NULL) -#define xbee_thread_tryjoin(a) pthread_tryjoin_np((a),NULL) - -#define xbee_mutex_init(a) pthread_mutex_init(&(a),NULL) -#define xbee_mutex_destroy(a) pthread_mutex_destroy(&(a)) -#define xbee_mutex_lock(a) pthread_mutex_lock(&(a)) -#define xbee_mutex_trylock(a) pthread_mutex_trylock(&(a)) -#define xbee_mutex_unlock(a) pthread_mutex_unlock(&(a)) - -#define xbee_sem_init(a) sem_init(&(a),0,0) -#define xbee_sem_destroy(a) sem_destroy(&(a)) -#define xbee_sem_wait(a) sem_wait(&(a)) -#define xbee_sem_post(a) sem_post(&(a)) - -#define xbee_cond_init(a) pthread_cond_init(&(a),NULL) -#define xbee_cond_destroy(a) pthread_cond_destroy(&(a)) -#define xbee_cond_wait(a,b) pthread_cond_wait(&(a),&(b)) -#define xbee_cond_signal(a) pthread_cond_signal(&(a)) -#define xbee_cond_broadcast(a) pthread_cond_broadcast(&(a)) - -#define xbee_write(xbee,a,b) fwrite((a),1,(b),(xbee)->tty) -#define xbee_read(xbee,a,b) fread((a),1,(b),(xbee)->tty) -#define xbee_ferror(xbee) ferror((xbee)->tty) -#define xbee_feof(xbee) feof((xbee)->tty) -#define xbee_close(a) fclose((a)) - diff --git a/libs/thirdParty/libxbee/xsys/pdf/linux.c.pdf b/libs/thirdParty/libxbee/xsys/pdf/linux.c.pdf deleted file mode 100644 index 75a9a38b29af858ad4bb773704982d44fa400b32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8394 zcmcgy2{=^k+iw#lOK2n22wB2xh8dYDdzP_|eH~^nwi#or*+SMzNg`XaE0q>RMY0u9 zw(LY9`{SpF z3^Bi3WzKx)A6jWs)6*NWx-49&{Rs;0p3dd2a4( z&~tp(>a?}{VS_t@x4~_NvRzt|1n`qErrN}3x^oGd^{&^W&dJB|D^5)Z4fJq!&2Q!` zI*n9kB6?3?b<$6m7k=Itu3b48)IKx&=-zQH499sp=ytZWCH~(Sxz6S;zA-I4(Z?P+BO#zzaY}0$r1(da}JiOys;O}7S0NA4nRm)g8!b|WMChFH6kf}ZkZ`cUrvoR1$6>9+^ouRI^C+b?`} zn$Wo%nP?@x`@#!D9c?%!^{T*$>YaD$H>Oj2$*a+(?l08pUi-@8TEVR`?>`x4cC{*~ zMm$IYo0o;2yrmMkY~9SOCeasyvEHM~*=8elGWO}Q4b?BFUYXb0aBe~krQZgh2s?NR zJgx*ILYsKAHh&N+iwUfM9ZlkD&|9U<%pN-Z5oNyu>KhU7YdzY9Qos6!^O953V~4BF73Hnc?@52M8I8!5-j|(OCsDR{m*`Snd1$?~f3OnA2A_1MDBY6a z=;`-ks(h3lOi)SkILxPO**j?g$y*OlMGdnCAy&L*IUv|AO0+#d6?fJTCj zdAx7cQvB$CimAV#^J)sL>|zxEE{XDH-(4#a%cHMm(}X3q*_QGT)atP~=+$#KdIXQm zQ|1LGo=l1NeG%`oXkcAl(pUkBi1G34(7QajDTXQ^3Xy1-LnLNfK8v(Ge#f`wk+x<{ zb>gc=X3gjFQSvu7 zvC4K(PEI}kCxs^Jq3#dLHCC%=`Qs|=vvYSB7HT}1IhrYh!-VnxpZ@#4p?uMV=*-D& z7d~cw(mUM{ocB6Dl=WL`E_~goz^Dy9A<6pBcpZ3##GwNaHUdt z7jNy@8_gU&RNuZw4@%`aHb#Xo2N$+D-E1k_l)bqKwcRklXp3ITO|3k$9AaRqk!Rna zVk0cr{XqER-QnBd#Fx9-tTza7*QG;J47W;LZz&i7YoA~8`KbG3hvA-z){R2sqHXBf zxdTtG?%6jvb5ve_%=@W{&YkjA6Q`!-Ad7cjs^n6gf*u|%H0~6#rx$z~Z zoLT{Kq}zr3^G1#m4Md(&EBnUiyrjm&v+9`res#Abz5N7u7TU&SeFPLT@D<})35nxU zEkc3#RPfi})l(LhD|S}5J{cnoBKO)SDc*d#L*uY?!r*jvj#7dcxi!7TesoFXx%B15 z=6Fk;_^`Rs=8pI0ZrTVtm2Y3N>`TrI6h7d}HlY~%jH6NL;wFa{jg!fEsh->Z8l#}z z3z-9P`}77QGjAJQpE^IRQ;mlUhL;u%4egzz;r7o&S?axPFx|y8*ZrPr94|lR^mz_P zNnAW>J!$lKab6)ee6X8cIhDJFTcnh|c9qS0s`TX7SRc3J@^Zc(gLl}7gEr*RDjSYW znxqV}cUcS-2l3ySf#+@zX5H$}7g(E4d84Gi8zDc}?$l!3uq^Y&SxjC2Omqu>G&QN@ zeRYq2wMm8XF_e}^0#*3|*GApNA*vtOQl%x|l}j`Eq+kpE_igAHQa6D3x=w;E6P3K#+=Rc0P`&mG|iXh1^s{W#Vm(hyEj?b$5 zib=~{TUS0BTHU>xoh6Q}@Gm)N_EI025ndGd7}guTn_1WN-0ZG?_b$qYg8qm8(V~Uj z^yH$!g^$^FhjyCAT@XzgdI9IF$soCcB06jq+Mij#h)x>6(M60bc|oKLpbb#&Q& zs@6c%;tT%6TOLupv8TCJ0WvJL?q^0vKI5}?9O8KOK4n}X5*Z-M2Hg(P=PWa`S~N|V zGFlB>301M}>NPUP_5~~j^tYECVWy_~Yqvy%RmkiSCioo%MT?f6xfWZ$z_qQWr>E?O zlJTcU1AXtjE-W<*Noq=1lb*WW^ri&_wxJ)QW|q8iV^f5QKIqgftv9!Ll(z|Mv%*-b zhEDGsmdG!HWR)Sfww_S-8!x@Eyy+VcgG*L zi8td2@%BtX2IzA)jTTM!+rP{^=HHHSg5@8MHJeoRPI$&vSo?{GTU&!eO3YW|!Lps6 zJO^rZAu(a#bqN2o)nfP20GB60udjLsy0^XS&1+cjcG#_UfQp%}>?vW%eyV=vouNs6 zvG_K%6t&pu!}6A{4LgX@myZ!6K5Ll74cqduipObtdv}QJa*D3iehLu_`*0R3%+-?? zTeDhfV|^mB^`Pc~hP(*!!AGNthZ=OQ9~m`Wj>YaqEXSDlIrK$a9t)c5cM@1R^Ht}R zIM&`?rZKLq53HF`m2fA^fbbNgF2@r`=Mz?U!X4LtfUQ*5r`lQ`%*r{a=%5i6Y7%>* zW;jwHAY~C-+2HW|=_c^~Wju6mP3>-1!pUNw69>`tGT?FF5ld>ADWY#&R}CEB@8W`ce-V)9W8`Y}s-K3Zopu1Cr;z1VW${@p1fXZoID zE9zDmgu#?OZ<=n92b(p>ylM8vo%q+5B}AWP*4j>vCz;!V`nRU1r0uJ?@qFcxnAm{p zV&Pu0ifcID*8Yg~b=J_pdkYu+@3)sw&|G4}R-~Yt3Xa8yGe%u$>DK9~xX3j9%$AFO z+ZH>7T2FM)uFg=dsFbjj74Fc=@6X3$W_*i6)Y9CJ- zNW+Ypl7rL=Le4)6YSr;>M;%CVgF8s>D|wRZ_{x&le@0@l{ewzj8ji zAm`Le=a=>R88zb06)vZJWeYT1Q@jMt#llR&UwpsXn8R4wKtTw+31SMP2pMY--R=vJN;l$g@yJNjfhZ}lzDXQ9Dkmq`NH_s2SqWxmo$ zWsivpO*Kj@Ro}TSZ!E;Seh4?_;e6qf(gt~?UyDH3Zlfq$V(Q5gmuv^w3Gn#653}NjZ|J;GMj}KlmYL-a=IU{)@)C$Qn*+1b<@3pk>8C*%Ujo z@vFjX-mixHBh;?nJ$=#Y@Zsy~MuRo>l0#!nU5&b9)okc5Y)n*9WvgIta#(-gA$vXF z=z=u;Ui%$KS+Dy`?A`6S)jCyDb{YHDd-go>>b zSIlizR7QpdUuj~k4}83@4U6M8Sdcz=JYxbmYE!d3)^yl7kBM`i=KeQP_u*sSAMo!y zRYS4qVo0u+%Nid1a)Ed0woSwrY6Ha;YBprDINy2vn8DM#h=QOdp`4K|cf1C7 zH_zN$AZqaST%b=tYM2^Eg4=FEqVkr9+s++bgFLj&M>#@CzT(Ok@J{X4`TKfb+wd4? z3Wo)mT>8RWp1I#;ILHUVuQML|wYu$dVy4@ z%2dAur(Pp|Hg3YI!umvf7675~;ahI{DIPhu#;SP_)e;Hy%#M2jPR!nhIpt1qA#NH1 zQ3qJnjoFqj&Dc% z=V~fJvz)54wktZw^Y*uKKJIKiy@gJ{H8OucL_I?@JMtZmkl%r_B^?mPAX>njiSt&< z>=)uev}m+`TG*kB2Rou$pLBM=*5zT9Y>gW9y(}P6lvZ#XtSiP{WX1C4d3kcvR|Vg! z?o5AHC*HVh39x3f0~g5ujZ|o4hD@3FF?E-Yx4c|p(nWgCFNKn#EwDQFX69w*%{dXO@3JXDI-?_daQe0TDIQk^riak8Eds{@A2(M)wr+;ij zop|CLxpI`L>{E(=0x7dg|Cnw0Ib6wty-Cn5Ab?%aW$fU2eZr=82{BaEN1<~H71K8A zxVOiw9{HP|9MvasTa2yP`1Bp9$RAk1oEPg~m@^3u*?7NDHZh*rR^zeyargeQN9lCu z=Dqp(M>yYnj!su|49BO2b(qQ+9GQ21Es)Um9`05)=|^}KQorfJj6s{Qqs!)B51xk z+C&J;MB3dUF9>;=Z#TFi7x2Xj9Z>4O7=LrP+VD*>a`VL>x?C~UKh#AIdMKRu3<0UDe|+CkQVEJ(x&wIpqw-Z z!E4T!arK!SMCCv2Oq-A&)aWmO*M{p$=A^@`V04+C|yj+J@fIgwzmqp-(-WQKJr?8BkJPtLrP6TxhJs zHM^b-(mg2|`?RvZe%>C1*U&OkQ5iNsO{o))n<(EG<=_~ppR#SS+&W#?!xXb3+-jeD zbn?N&eV*1!A`jEc3NN5Kt?eA|y9%7-KDxnXxhBR!y%kj4xjz@q^*k>R{xt1eBzO=p z%q8bq6(pRr*`+Y`qu}(a$1sKH$dUU;^Rm{JtaUFiY24)04PL39ZYBnXZ~ zBoh=VK45bwFpWeb!6<3CB?zKVp#vv&U?{*vBe~JR2u7O&kVzgCPnsRc1L%NsX%ssH z65SjE90h_Qc#;ntkW=xYs~N0`!oG{BQUGxn!?->OIQxSr0r!AL4>04*aE-LyUHx*1 z`2PYB_06Ym0MYAcega%;_kKml@aWqqC*Us-LhxD)J-`r6GSLHU4*KRK?3-hjU_g^6 z;5(e*_PUQhG<;W}M4%H~C=TnIzQ+tlq7#U~3>XeuhxJ_nquu-lVNJo$c!MB1x;QWl zi(R+$6D&ku7PQu;{(|F|V$er{)*=DXhrvNGIKv=B9}QXy2tb2^V2siLXfPNEwua{y zJq*e3c>cx;L&y5;|G~;HMidDiBu2s@$4M?;Bs$rS@GTAu8VF+GX-j96HX}JeD1Jnp z;na5=KNJATp^-^6Nkxha@gL~;#g!i_eqi{gO4cIzAAtOdLB$O?DJQ!*fZ^Xl{~!EX zoBghCZN;q1L6ph%_9S580xQ)V$f*p3PIe)ZEPn|JBa#0BiKLFL3)$WCTNok#HL`2< z`746;%Ke!O6P(uiHvt#|1;=8)zh}uG^`R2%oJn-BEy;oGh7p-5uMh!~i5L-MBo2zB zDv=z?$Ngv|13zuNou9KEnkaG<3z9!d0A9~Xl0F2gr2X2*1rIRE!wz#$NncC#AcMh2 z&c~JN3M?S7kE@HDhnx>aWKBd47&FKa5%3xd-5Dcdg3|@lDHIoHGO$dg&{8l-7(xPU zPb0XJyeTwiFic8D1W-vM+RNz!o7^{rz#T@!kxr+|K_K4V-csHODGJR20z;$G5GWi1 zhf4w+k{-Tpbb^nhn};A^Zw(09htwJ-DLaZQglqzVNkJj&hLiz!Xk;p#OmPD< z3=?cAo^*@|njj5Fph>clNEtL*5(XpLN2P$XHRtqc^gZi)H}!rx2*6M!uQ z5CUK%2W;R(PdgF~Bci6O1y*yUc+kI@2BV~8VIq+4p8pGgfQBo;+AM6b6%o0ttcRVJJByTn;L0CJ*@w6Oe5{N(uOy1bkik?^3@o{USx7;eqsH z)wB)4B)9L*14);X{$^@T=sS-*gprM3g7Ghghd{;Q{(FF2T^V)D;FD9P*m=4FPf&H` ze^?d&Vj_ulKVVWlX)bGpPqc%OTu2N=9zZT(08XNvoIQo+N&w21K&83>n>3@EAwbQE zVC8{HI+^Z5!mceV1sD3afeV?jOypb$ZVng`A4wv~p5W<17s0Bmr$xt;PW7aJ%Yq9T z`)#l$4*97P7}+tZfkOMH)qv!_t{$sEr;|y4kzbo<$p4V~n=G%9fb|a~9P(p#!Ggg5 z()I5t`njb7O#nv3o3UX6qu&pxaeO}2}mk<(j6%@(Y4I1+XL?ZT#jNt`Whb^$sPm+!{H#t z_V?`q%OH^mB-kGOoraV~0FO7u1$JAf$;vPuzQ5Cu2pOPP`wI;!4RlVw)1XjBPxm_w zE{pnuhLlDFCxpNAA!Viiz=A}{{$T@&mIgBOyQ~Zp^(ST&^iR7;1km68E{jCU{7HjD z|F8@HJAF|2A6Sqw2%rV}iwzl>KWH*gMo;iNp9~E73yn@AkX=YL;C}_c5CgIw3CJZF zc;BD^ZwWw8q7R0syV+B~j4}mYIMkKF=JE&?w4#ctvNBv&28EVIs;VeUD|NqryV|3k0x-2LA)lkNnGdDAXk;Wx5?j%x%Im0kx#>|mR*`ivCYAd9K zLN|6-n$?A3OHoAW;<7Fk(oITgwc7YTXV69a`|keHdClwPna}fif3DBxnfIaZY-K}2 zjA;b@%I=-n1Uf{9r2fGKGc(u$4GqMCAO?`Q!Zso?hRPw>M$E@hD>OhVKsg+O0+XYB z2_f=8&Hflg$EoS@Lk&lN`wvy)!7js%(3|#C`<}EYJCdF%1W`BB-72}ulP_)7o3;C- z)7?_dAAdYJTe*F!#?y4A;kW_PiK$wR`maqdF0@P=${%!0%^194-FI#i@9rO@_ME7A za?&xip>LB`HB=aAnkuJo3vOI3Z+xv@td(Il$vm}rFzjJ$wVy+b>(vCEYq5)~^RN>G z{G!K}=Xl*ueA_*AnXiqt$7_~FB&1Es8+1s0RIa|Tv)9Dt#nqG3k!q8ehNXUizjkZ) zUof-Ebt=wTWBPDXbN1x@M>60FkD2j7Db{w=9pPz(H=Hcv)YVzDuPx0-`wvWOF68-N zaydB|Y+O8~^KQbXu$U^fs_T`Os}G{=?ZUc3!u>^lO=}D7e7)x~;<6JimL=Zp`VYOK ztk?M6q3aftsI1coA>IdNj#d$(Rao5BXq5P5e<}CSp2g9-G~4cX9P6XX!u(QqlwWnT zN1A*m6+HD;o^G8~-up+3IDHB;PJ`HgeahFi;#)QkOSRL&UFh~|ZJzA$hnUfW1M`1) zG+*t}?y{;l@uqDv)#_%>N}Tp{-?YQjS384rVzP4L$%z-|UmJS*)q3@Fr8~;cRWN>5 zo_~9_-tHTH#Z3Jm*@p6PE{*YZ{|04+uhOPX>!mEF8ELfce|y@<_eefZt7f*^vDG_D zPB030-q;BLAC{rEl^pTwqKekJw|0;vXXu``i3afxGHR86TkpwJW3Ru&JkNc-GBTF& zcrMTHxbQx$D$yu<_3Q=P2ouU5A6?QA2ipgFru)X* zrKussYb+}3CbZh@A&;vaH)(I#9{8eR^e@V14NYC1zr0zcn|C%lf9BUSX1eTu<*aSF z?uXSEjp7>;s=_=rJvu})rFEyL(i~nKLfZ^TiiDPeIrb}B<{q4~d$M@?<=XbTXxBT{ z5F;UcqX%7{5N<22noZ8C*7J>Rm`&Ec_hRm4BwA}1&8PA_yU+H1_U~$%kM^e5?ab1# z4U+ZGc}LNnGBG0O`i60um-V*PztA^&W?Stssbs{n+HdN!V@U6il% zR@w*ZG5tJv<$4M2=d^-m9qA-HdiZ25n5K~VnOP^^)8D-Dz~(y5Gw_lbjm*Jy_ij(V zXJGG9ZF%70FO|KqTUztR54B!%-mPVxyh(Sc*kIco>$0JNf#BLbMf-$(->>qneQR;s zamc_iqW=0oNgv_l=Hl;G2UjcODG6&UPhM^c;G;86srUC~ty5=P>BJp-)t+=zZ4WK> z&aO@wl2Vw~r`>5fZ{w!#zKtm^d6zWz?Ksnxzh;-m@j6;AG`{i7HB6atEClH)@1Ea2 zBi-p%;ry(+n;Ozn^{fkQSzOt?Ez@iK8nVu>v&o$JlpDN2|4YCOMJ>k9ETRm(riC?|}UpjB*y=?X*7g*O3KFujfzu zU7}}X%ipZgDVR*`9C&c*>TSubKl@Jey8O>dzHfeYw0B|EeAnp9l0(~eS1fmO&+p%@ zJ5Lfhe$kry8U>S`+$)AG>NoS2JH07cHYXznF$h%<52E#7#fy_S*VL{&ez?0Ud)H2{ zRe!}^xX@(yAkuuu-RLAajl;Va(L2chW<>_oeZ~2QZ4*(v9oqSF2XbGUIFEZN^|1a6eaXwBG)`$ss&Q#bi_TqW-{vs4 z(v%bTG_krJ1wZP}8~@zUU2^1H5_73Q~!)~`e6>OY_O;LVZKDYKY)K7os^ zOULWCJKxMMDPjDUf5%L_WFYc>j@6mz1@?{)JOt^&D`q}(9%(fNuNgF4nUO7Ax6!AK zTQs%SqF&Fcxv$7F*?T`^&l(qY*7#z9lgl@=Cj0M@(C;KP>$NmBnvT=wR>vm=&x>-* zzj4AQ$P1>eSC`D~vQ7=1s}t-RX&b8RgIe!44L@^MqNmu(5r9ktDF@<=9EoN0M5VoQ zVeOgiT<^CFx{9t=7ryGv`xWcW>zuh-_J+po%2Ry5_|l@q;S0J>Z%uNExw44ldu0(z zt%)sKmekPix>fT_3L;iz7z_k=y+)`FZl7&Whud=se30YrdK3C?s>Lb z-R`wSD{ft63AbHxqDi6^a_Vq_*B~&5uSK}B%*RHYgmhJJIH?5EFZg%xP92H=n(xqX)>K)Ji zhngw1`3z0u$2Ygr^G)`2+lKKEBcjvzRBAq>QQ(=u`A?0R^gyh*;ZsC6< zQ+WRQrCVvY3hRX~Hs8ir@4J2Ta?PQMEiZ#ICrr*PO)zt*$}B!!;B~xa_R92~R))9w zm$`$IRCIqOxW00(1JQENdaIU(y;ZjTuls+V-}=_M=PC7M*1i*rr&RmbcLH6P&$i0D z9aymPc4rjh%(ANOTl}1wj02ah;_O&p{^xJ|`{zYm-(aw9duPHvdB?7zl|Q>IUsJTO z2#ZxF2XCKP`Sz@f?VL7*HF#&hZ=h^fd6i4$??2r$s9SNtj^x{!;Ccb@hp=6ZNm=qs zZjFYQmmKFX953p5nRouazQ?Yx^kF9OZ`7TC;%5H_%R&ORJIm%W|FF`;`B|k$zBQxX zOAv70n&Fw)&8(THgT8T(A}!JA-30d-#HU_JhP?+{{+yaTH7W=V6^sxTu);|WPHphN z6beEojL2vo2#|Y}427Nffhf#F1tR`ZX(Z%B2HSKN9b(cbz697+iUB$Vk%5*R4aFcT z&Xs@|RY=3-0jL77C}+7ezzxNGV8F8=*d2|;0M0rRvvpHJkr9ZE6u=SOxhnw>E0_zu z1B(g>CuAzoNPM{dFhK*>K!A`cLIngb7YP)Q4`JAZF9a-v<0f%yBT(c$bOg!eV|=kR zaMZ*|C_EJ73&0M9jP-5useun^5MU=~9)xf>qnsQaGdK$aeP!`S0;gp@4~JK zg@90omn;HnL`Ehcc*XH;gn-})d}Id)4wnL6%2%Lx3E^d^crA*F0{E&FVO&Ol-NOAb zyq9>rK#=e2fx8=P0F+HGLgl2TQnBD)*uVoFYvO$j|JH~q$FWvM0{Is)sOs|nz)4R2 zVo_N5aPI$5j*qy%>gbOigbxMu;yH}I79dn6g~J({Co_vk89yKd#UOt)P!wuTd|pyU zghT>!;tD#C%#(4^AkngDIqDYe=pGOq62KA=EjR=-3qBY_loZLAk%X%E3k8HK0?hTe zt`0VOxI!d5QX-Rp^9V&s#Gwjyq&ZOqVS_a;hKZ0$1q(4JdhwhgOez(Jh%ks^%rZtu z2-OG@%J~vBLMjh|5Mu@rn3M~IY*#Rf!v?`Ob7ByN$=EO)5fNb=K{b}j17U>4V!>nz zOrel~21yYWit!^!p^EQ-KUE+iulKM}g|R9o;{d4y7J0#lF&Q3pWChZZi)5Hc8Vcc# z`To*y%$&#q^CciNOh|MFi$y{Z)SpBK4~b4QVX_dE#h}p0qn>0RMfiy;umOBgK?%T$ z4XznMcmOIlC)zqYK(;|r1vcy&Vj7zuM0h0gFAxyu$lvD<+BlND8PH+#qymvJ$_m_% z910mhNMz6uiaWw&(R{!!;e8ENv0^Wxy z3zv&k2VW2Xqhb_~NCA3@fH(yKY@t*x;e%t#m&wH7X2nl449*;pV+J-c5hg}Cs(KwPK+c8P$55Dj1f83qit~t$7JEya2LcP&hSD7hd-DEs{s5oNae#; z-O#X6^BhYI6QN`9s(l>)9`!d}R*As;gNDQJZx;>$`a;i_GxXs~1;450#0dPt1glR8 z%tt?9qZfd|-_5@vAQX5hLFe$%GO%LfBMCkfgh{7>X(W@0-~-ga)c%8pJKk`EF|7nh z%Wy16DmPGdX4D_}{`oi>zNZP1a#TnlLkNR_Uw^|7#GuoubVvw|$S8Cw_>ILMD0Eat zqtgHt_*6!rF#%H;BcoD2laa|dtr@EatdEs3$bhDd(KDd|DlV-o9W!(&EhQx=f`}lEbO?eXEg>C(fHabd zN+|dr@OrQJ`hM^CuJwP{ch;=MKF@w?KhLxG`JH_>9NO}V{9pknF-P~-?MK8y01&|4 z#)()$0;rB~b3i!)gfSFdpd!)*h42CZ6#{jvs3zk0t> zraaAl{)pEM9j8}ccF_KGnHg5Y!(R$w2M|O1A8Ck1FtfK?J?M~(;U&m(SyjGQ= z?~;3O$NhM-`;+o1mAnjg@`+mLMTa}KGdEPsDe%pSS<{seQWVGY zGDs}2>rs>Gh3(s=a4*JBJGEME2nszaGS)4*b}x6skE5@c84S!~dC%FvA=JWqulR+n zF9jjRX_@jZaMCHm;KG2|`Eg3REbQHWfvPni!laQ**`lQ(U6qd$IjzkGLMywpPCGQ_On{&6BaeX>Tx; zOvulAbhzEMm;}9?SY`+Rs3LY3X>FTDcwxTzYk(5rm765!FZ^D497C7P-EmQ>))Z8P z-9vlV<|@FSRF;$J-c@Mn5SmyWvJ12szN(zeaiqYt1YrShm{xRtqHHXm2-sbj!O4|x z$t>#L^m$y8edSvdNip+aMl$T4?wILw$rn@9{5Kd#E{NQo1>Bs8jhzXnbdY88`hMb`)I+1-(Bw;aM{t$AZp_l`GK{7dH^S#QhLA zo!8g{v6L*jlmj-a3SU;9c@wR%1Clr8+&$L<VGKM8DGHq29BAi-n1k(TR( zDB{o&(@eq1bCNIGJn}4-8-?Bx-iO{shu@-OyLn1jup`+PafNc1dBYnX6S=g}!EN@! zFnu-ILb{`*dp4SIvq<*T)Rs^-fDrug@PPaAe(KUvbTk2+k}m?3dFAUBQK?gHr&GfR zPlo52D-L+KC|}hds{7U-EDRQg36%EEvBf>T%BL2;mhl!B%{_zrE+bgF62|*z&?MO^ zm#OE4D3wR&G0p5(58pt%YtX(0b*_pR^7yA2F+>AUQK};!g$YY3J!tzY<9DC!`x(~Z zbkRl{t0GVOEGO?E0g zf_fQfH9Zj`aiu=xB$#sLL}^ig?bhwC*Y!QnsucSs0PVXdK8C9gptu?nQ`73y@&?IJ zi7gJFEoK2hJDe-8rRhH{l#J^vOETrD72C#7qDZY2?#NTPW96*Tauk*<&?*?&rBYPH zWjd7s8_j$YLZUBAu=B|={L%boV22NS*&BkVcIci{mO_whA&ql5~Hzid( zQpJ{N-a;mo>^(x3#}({<@iw5a8kLGXkgx=OqGkK&LSk}SP+}=8&o2iu8gf4jPuOV9 z<#d+#M7<$o(QfoX&OEgwfNU~t|hM>zqZN~SS@QQukMZrM}n|@JP)fo`eHxG`DF-kvq<>v*ib)Bn`M3xnzA+u zStXVhvlY@Cb;3^qI!MnOTPVyK^v%wgws!#fsM|aky4bC+Jy&xvxHImJ4K*~QpDD`0 zW@=S)vD|GsE=_^QO2@?OQzQijyz8=JwtZ=AV!CT-E>a$6e=xFv^Vy!=d`Lt|-2iNI zS>v#Zd1Y-aXH!HvGbMZTrT;Nul7n=Ad(2L-hn~Tm-Mx}VNUFijyo+f9?Ba#KTw7rMtCaYxmz3NwNk_IKc=Uw%;5*{924` zaBi`zNhXmyVVv~{+;ii%maydZn~clv%XF$X9MR=47}J~IVmO&oExD_Eyti}|soy3g zG>T(V!Wp?BSu2*~v~6g+S2ckIxE`)lKYe3y+50m#kw3`0gQF#qMboX+Uo#}+H9tIm zktm{JwtQjka|?xZjn-T1Wh$9V59WjWre5^%e&)lcBT9M97RpOg_MDicDW&=n=Zw%-!?@Nj%I(yqBt3bkMT zhz4B+C%i<^M74vIcO2*&RJ}EhuN@uHnkH$(>%+`{wlbnhSNr>)8; zCzZ(KnB!E85O`$$I#r{9G?9Hr{cTU3@tdaQQ{bcqGGIajxyj(K=_r{saGX}Rv1#f} z7@F6P9lYr!8~u{o=c%x2S>N7QgPNSws*jK*J)m{y*Q5;3t-jTORsovTgIPPJzSq*F zbzl9liUC6wLltO-oTGQ`0$BcP4=I!o*s3xQW%2^(to4seIfSkRYVZ17X`W$*nx4Et ziUXg}bS@QXg-zwnNjA7JYVi_^Op*?g(Bw0kM=yO~!#*xhn2g-^G<(5CSR1zZO$nFy z0Sjz;bm@33!H0zU?VQPBh-jG3r)G^MjTG0y=lAXM=q%hKNhOxDPs$x>$taBl>D z7wD+Ici%kbM)}>2-fXvusFr;V&4Pt5QeI(==$OYcFqpdJst&cX0R~xw(Jp5-Q-CjJFSrZAKqbt%26V6W^fexSxan1$|;W zU>R32uXt*CByGO0IErwy`vaI^T4!MH48ooLg+QS&;y-BMpTxhA9vJqYK)rFZ?|L(0 zl;0&oBacadd$N^wX7%vnRguj4XSqXBpFZ0Qz~WV?JvKYWd{Rd4pYArK@P(=^Jeitm zsfV^z8@(A!0*|>u*PQn{J6KOp zo)M}c{(YbYxaVEq{Ad{aqb&7=MB*bcVS=M&_sZ8)NH?{`sn?JZN91IOjVkH^WCebk0;CC z-Ev!5pc3FMQYe~k5FG8E%PP0_-~Iq>gG6y0y1z4uAt#5T`N-$qOXgU4Z%AsCLzel> z0Gh!|*y}5LA z<(6BxP-b&nAS#%phVUt?+?;I0$U7LSC2PRAN6umVx(d^)W_(8Di_;6!Yi3gbdgaOc z1|)Y?$<+^Ty`J4;zx$d1P8Ts!wfmi-`=Dk2wu3_p)6+35N^xl&;^o3@*<|%#KBDaGpM!Lm7}B$^xBD9}IS@pT8IwQe zDUE{S8@5nJ?deE0N`GI5Gp(USr+BaK6uk;+yFl?cF$-9Rd`k3n_*F!273v;KRqV3P zEuQ}I8fR8y!TaVgy_ZQD1w8T1L|+#LIFE>#k$BWTVV2lR+*Vd4QDaoo(C&%KC^KJ0 zeO@s(p^#c1wno=1KGWraHJA5K!TY;1^o^c6afOsUuqs(8c9yWWu-%?qfg@~Nv&UfJVbsw}>d zREY8xzHKbAv$HI6y^@&q(?P9e4P9Gec0vuK|Gdc_YF;+RL-$st=fPq{+uqjU$=5bs zO$&CrO1ZB-LE686??O}oI=NGLh4Q1``kcjKT{ zMNio$GVU>Y;qUfr{LlfOpF=!O-+%h1;Y6nT5l-Ta=M4DtezbG%PV5!lN#GL6(g@A~ zLlUnW@=o!M1wqwI(Z)|{Y?HGRlvP#=-ugn!WAA)#wQtyo&G33&X|4gY>fs09Xuh$_ z9T7(4@mz=JZQ+}+>SOrGT~h2vQM2sVkEFwdVivHFkZ^O8p-1c0 zLJuB1my(obA`{+2ray4bM02J-W)vE73wx@hAg8x$E`t<_zaS311W+}7DWY)GK-3S6A6U_FJz7WTxt zihaPbgT0i~*m`^Mf`X@q5&c}$V@iQ)`o{wI)-wh3C7fl$pFfHEF5(|3F^dZv4kgpm zP3c3tg(l*q_CoANEv1|zyLrrp_2JPjQQ1^1&&JI>^rdE6xf+bCu0&*7Cf&MBmX}Uy ztlyUgx`P$I-#6-{OD-?{N|?K8zMviAs}nJCDOExA`YqkZ4`qdJq~ToO&$KB`zeW2^ zj!5q7p(6F0;O;a7+m7GK;~twvsbw{` zHNE|o%$S|ueEmZ?Emn7S6^L?J+G|bS-kG_gN&&PPcj7F4HwddFTJxg75amcANM(V? z7OOAi!bCO=`ApGVsY{|B=ViTaipx65y4an_6!dzh0TqO!@+}Tebx3g+_O!14y9i=d zQ4=zjT8hVd;|Dx@1zJY!5B-I{>?~GB`ymc^XjrCI7_&$w6=`xuhS~vI!MEhI>-x9j zx7WNfy=qKX7BkGLO%1;ucTD0+iMnhj?<|T|6`H6ks@&OfVP1Tp2);b6`!J}g z{5sL({kkHFy9!yRUv9;;XFsGYFoxtl9YVNBKO4J*iqUYonV8a^i$fJyWs|!*_qJ*y zNkQ$ocFug*y+t9!rF|oZ@Y1$U=SEX6#aUG) zvoC`21PzxNA(zTId#UM5DGSoIT3^`?QEtCJvMU{rL=0sXRiva1oCGl932(VlPkFH8 z3D>Q1gF?|u84q9J_BBRn%wppiwixBVP*r!OHYTDD60+MF<))FA&&#hH2;I*#SV}N{ z?Q#lo?(l(M&Rj0^%bRxE>(-QPv+8}<7D{;i82N@yN}W+->H95V8?%br!aQ&@B!&l0 zV5TV9Qx#u0(p9O>*#2FWae&}r3amXAB;=f%psKlDJM40lCfctZT&qJgqsXZ7RpyPO zSh)aZw0UFK8Smt~?6kYqCOgeIvUR2i>yAjccF@r}vVCi|_uAEGW^2T2rNg@pnCvGh z{B~%4X>Pv(Jj-}wXxsRSURt#BgMd@O!$>KghX|_&DNk3j2&HD}o=R!!!R+;huZnNw z`mz9(9BCj?&rB1Q#75rQI@P|=O&#RbxmFcLB~v$~FV1+^`p)ds<;~h#jh+oAma2{# z!ck*V4t9fTf;_PULA8115Zq?wFl&cz*Gt}PIpf<{O+G11hg)-SPxE%0woy1NN3;jc zUOrBz!Z!`7xgs!itI+LTotIn-Pk2Y)_n2Ud0N%+p0XoMnuXn+6Fg}`YsJ-5Sa~m4z_PLG(|~w4{)g5b#FCmw$y)Cw8>K3pj%EYsY6hKn;p^Zy8pKM0wHk< zM~p!i_T zIxTi0zjK(3^dtC7P*{j|cn~Su>dIl`XUmkfyLkD4JVg)M5FOK@wlD2p$`9T{JY!2c zfXq|q%h$r_rD|hr@Go2Ci5|OMAU*o{^1_2L7)7Cs^d483KmNYq*9;GyOM#0h{LtEf zE8_2&XBiq!r%$C~@k|SJQsO@7$#Wfxs$asA-OZ16% zw5m6GICsF!cWLQaZXZ?YZ0UMFKgGe<)bUsN&~&gvyH_%~-t3fM^5BW$QQJ34^{;L! zq!!D==H^#FAE|q8PfG~Kt;OBCBINSjgpAqe0XOT5p~>>LTUWM>!}mAm_P-rprD8*s z6d$Yhn{c>V(uK55Ny;UtA z&2>{sRAv}zl8?ReT`^T5Hl>qH%-^fBi7x$(jXq_D`mquk){^MM^U)ylN$)n%+&Xus z>$rw)U$u;-4t}nx=wGW0xiJKTr*$;EI{yA8JMKj>mM)7rU8@mcIFlS*=ayBpgCT(+b!p^$wrFXDxS@HlM!UJ-99C>69ATH(^2W@Fc5E29@40%S4ExZv_ z7sQR;U(CoMzsP4!Y8JsN;R54vS^Vhrewa`F;@V4JK7pZLNWX){7#A>YB$F>1;y&;S z(a2B;7sy=gy2uw;n$H-;uO)JeR*#60tft*umr*=vHGM07b?IPHuDxGmO!x~=`YLF? z+N5{j`YJKvTI2~DwN`XgMD9jtM51` zv~9aJ`$jsnxhbz;+dvns?8k-*}nk)&?*&)v_-pjnAi#w;2vU2rG`)7Av7ZTNgPTi)i&*NnyVV~wI zxnFEACL-X0JQ8HnDQZqUA#|$Fhw~q`LXD2t2+N>Paz90imnDAcOe*FN2IDr2WVGjn zM4Rc~AG8pum$%>#^u&p~y<`WR${5rQf444CWVdR36xZDba~>uqbIIWmyz9l8yTgFX z)un_dzB-2IgU5kt7^`HmySt6$TQz+}_(rl#H4YtjL5YAm*tvDE)6?WTW54`0ZJN#x z{IPiw(HcIwGz#td7v6*#D-_e+f4QDrQx~SQAV0Y~;r&uFIF>+0;i7_QX8zS@uk5(V z-uBCfxzX}MiI?aiSMs@XBA@cL+OLZ}BOFCuYd9=ffp%Z~q=VaeZA2%QsV#ytJ(oFt z_c*BJuHvX>2U*hdo<~OzDG8paXC>@2(3b>Wqma~!6u!)BHs-lya%~D^Pov%?`LFbk zzes8prb>lo#pPJ~2&peOsDaFmp5iOLY7>~yUSOtTW@4Gdy&Xka&Bv5N%0|W?mTkQL zwo{g#qy6-2Qpm=Lc!Z}z=P0tnYIsW0y;K`Phu^ww#_TgDmep=zaY;|RiNK~Q65xYH))sgpDk&P5 zp=IVsX3kT2C=)I zSLR2WY*xF`SZRJfc8cpd@YE)Bo4w3NP32LOGj27N)bx|AJ0fBZ9M6s(F_WPBgIi+A zy8#AKf@x00n@1BPX+7wrp17Lla}fsC^D7+Fxpf9At_&pC`lq$0#Tsttw5)lB^i0D! z)~S;nW@Z-b0pzsb*w)AL&>LgTRL>2^iX9%4E^(DA72m8IJ8>c_OT|+S3ml7W7+3v} zZpu9@pteCV);HTUW~N=D)q*F}hp>v})1qzIRR}t)x}P{f`k{qkwSO*vYi4ZEuO-Ri zfm*if<$c*sxBWZvQB}q4uZ|Z!Kj!U2>m<585>dRT+{GpDos9VSHY_Ojc%xvrPI+j{ zx6xNHPEim_OhwJ1>=x_@rOM+~;Fa(5KyiG0)hW%e9?na6{q~TWgMGygZj`R8rZu4x zFMlQ-Z#A!q<&Psr(xwfKE07CvP~H1TO&Ol&aqZ3dn3? zzHyMv>9Pk3?Pe#Mi`K(G2b_HKJaZn}T^d-m(tOSca1&VTR?Ev-Mte{ky}8)AMg!z|0fhF!c=QJYMnzTcLs;|E zg%NTNM)x7;Zxx+!;I~Vi?(<}6oPGKy4R*`|88+r!-San?!bBe|-o9P={QzDl%gCR6 zRAHiMgs1;T;I2Cf-xM);LY!s(iT#I1nVlP8OLC~$T%EFe^3JA=gW=i;HvecR8X8q8 zhtIgGNT+mE{*2d(x4a4H)$&1iFj2dY>srMdqs{SI!_D$ix(y!DQuCgL^oNYQB?iIw z$5+fhjWJx)%mv$NS@p#@>MTc^w2gz7!s0~e5d7mP2u$LtEF)#ur ztnz40N~vbBS1nS&i0Mj%L_WKG2GdoL4fUy7eA*%%|I%PS+mMoDKG-nxVRwCU*N}~2 zZH|xSJrG?@QX{B7GRZ@sI4=C;{z-{Z{ap_u)!OFVC)8qL%yYgm*`!`9dGyD~3sfTo z))##3?rl+6wEGWoq+Y9y{q-!CQO+d-b&%esD z+B-$cvB|{cVRrhFXeF7N1VusQ4_ALoU(s?Ho6EgTwteh+#p=Sx;q~`xj$*-#33G4# z^W~%Fw4+V7h9wk~_6l!@gr0n(MST`yIv74;c}?KB?}8TH%KgqqMMQ!Vo7f&7qrW~n zSJlxNmz3ajCvh83Om4}K(zRfdWdCz6$HRfWg`Qa&ng){;^Z3~;ffN)(NoiC{%+wAx z{e_ZtyG2%)AC;9{shuy+EEzPoRo21}&X3JmzHpOrT1-+@HluHy-_)wpvEHP6a(#Vy z@E&|BKZ}ZLxY|<*u!1p1aC#tOA8xt$HjLks>s z8+h}s>L>I#GIx6*P2K3Jo49iK8%TZ2?hOJ<^>x)DPS@DkY0U4?`pk(tKaWz?mz9ki zr2dGN&|Z%yu*YDaWYQIsMK;OF6>Z@ofiYz`E?$F(MPmBHxJheQi>Nm;PLDayLu z&ctxK4ff#4PRL8ZU50NKT;0?w$i#w!TbwES{*-O4*beyDSZLhunb&h0aBk#Vjea6^E7$w?Y~e86G7)yqed4WOKzJN7{ba zM!MC*W&y=S9mJadlC};F-6yPGB3c2h3Eng~s;!^n8zOo`7h}WQ8s|tqu!w$Uz8$Q) zle+Oq-I#5dX@Hnt+eyfe}(G3d!Pp!dTo&>bzL52~BtpKdo zoa97HP{bpAr-@6H0s-?Kb9i`9VcH8a-RBq)=mrL&(e048gwzOZ%9u% zBHMO+z4frA-KEEw^pSQgw`kUW$uiwz?(K&fZ{i6$?sQY0&D; zkq*-qLiMor@;7twb?$+J?_J!Z%<5X+iey%{>}xl>V(#i&=#gDt2A5_DX2e-ZZp$b9l@hKpqT8zc~2p8(862lJ$K+x%9Mwv3u^+ zm$|TdmiG&c?a|v6O%8Xvr)!>WRSxvYm+u!lDNY>D#2=pe@P@FjRC7y1E~2fEYHr{3 zpAWQ~KGf`_jZ5`%td|-?1|l z=!^S4aLP3^nOgk&u|&2gK{NeYrs?WpiMNLAh8i*A02hyP#C@sNg!<$9pmt}4l-*`!QYGily&A+18_ zEK=4W^bl`tAall~^!XFE+C#L(emxcI4uMbn`00xs5?9VH%r{J?*Kvd2nZoNlet!a= zJz3A5z+e%G=+Dh5=wEwN`u-jWpf=nA0n|X)A>p#_egHENrdvo<2p|H5SP%ns-BFk$ zd;k!J%M0O#0tjMybunaww>#R)7U7Ng9<1%_$}dJ_;ogX|=?qjxxcDGYNL%=id3r`82I`@0 zP-hF@S)E|g`m-X>6ZD54e=5Kfnimq`#V_mbV)q~D_$4iWs`%4~|5V9&Rs0W*{0Baq zSJM9=&aY+Tg7ifH7(4LahU0vK{GsM7QkX}?kE!!l0F5=&f1djW3yMIbr2aTaBq8nR z0k?HVpa3=q2c(-M=Wg?JP5{zQlG8{?1Ek?0hj2tH2Y4a$0yOn)1DtI|?Kq{Sh$W=q znDa*nem}Sezy0|Ea&G{_+g9?boUXd!)iVY^F+W!iSBx6~KUWtwZ!tef&T|nlO#6%s z1Agxri*!Q z{ZNRxljL+np*+NZKwn>90bfA@cP|GZSX5LL2!a415Pl2?zqh{|3hu}6=FNt&ckT$% z_)jr6Z-H~41Z>@1fkf`z(~0%$-dwE_t!DDT(<(z#>8r z%xnaKI59sA8)nx2FC6-33FrDpDlw8WXq2P77uR`ae%iy_|2`eL{{&WkUI=?)5I_)2 ze75}kxB$XJLV`j7d%zDG7$ge9Ajz`};P#Uy2sr~?f2V;&&JgE6XkZZx-ufF2b8`kU z|4xGl31TAoHyXw+3qP6I>FaOpp2LPEdEf`!lU`9JuCMSk-c4E+~0&>1ZGhb##C z8x14~{iiP=$nU;@gak!@w+sF~ei+hkd>|pvKkb5qpufjY1R{t5#sBaba`qPWPa5<$ z8c109pLQ{+6vAM;zsq9MhQV5Yr-22*zwvvdFj!VtNL~n{C@Tn36p@t^ mlu-l;%ZLg~{qGzWXKyavD7Y8uyy!7rf`o`UI25!LiT@uS$*$4> diff --git a/libs/thirdParty/libxbee/xsys/pdf/win32.def.pdf b/libs/thirdParty/libxbee/xsys/pdf/win32.def.pdf deleted file mode 100644 index 99b0548e4d2703b57cf69ec0fde18166639f570e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3538 zcmb_fdvFuS8MkY~;{r_!A?4Yw)xk1Oq`T8wiX?2wmW`2Y;|F$ZAUT~@_F+jU->qy} zDQR$;LKs452-I#VEl?9?2m>L|Oexq7EiXfp))1g}rXc|`JVS^BA+$jE^zf4={K1W9 zy!-9<``+KTs~JVv4h5~$5Hk*4d2J)11r*@jiwLusbizP2ssTDoDJC794?zKt4j+SH zHe`7Zv|0%f36Sv<;dP-mdW3H32fw^_=kYyTFN|GTV67_~-&N4PYr`4TeFRNn9^L3T z{Wsl)#sBF%_^ZbZVZ*V{-~9RR_Ic(M5Bp;0n|D{%pFFTXrRO7CeaH8jQg-a#@lw)^ z+gD|08pkeiR!lvboICED(T&!Tzt1?F`Vc~# zb`tEo+_#@4CoR8Z+q3_jm$!N|?@YYf0k=&3@@VFk^7XHGq%N5r9&y3+$@@)W+r*@2 z&&<7bXv)*e%8b8TzHQX4xf#hfTb#rX3zKBerESb#UodUtt$^lWDAmo|Y%q zesEAqc6!Y8{O!V@d!GDf zk#E)h&nur8cW$grc{%gdIj&=u8#+(rJGZw@Yka=-<5qXeWA5rdtvTU5m$UY66%pEV zdzDOF5^Pw~_IGn+@2C0O8o%-&?t?=WV_D0rckEpZfSY%S^&Q=0SKf`0i#C1#o3oey zm33+I^!8b0v#0eo>1~!dudADGe0H(bp6T8E%8mJ_m%F!DO?dU*ingrglBQ!79g!!@ zU#^qyU94?+KJAnYsFl<`XU5v9XM!KrJW^fhA7S#Qt^2q09C;-FUfKDbA#vUMiI-n0 zy`G=;+g(|ag%|v%emsBa$%Z@s9aZ?q#PcxViPt?|NxdRShBwTU7n5dhHQv+d;PRU{;VT>(kilC)14?svPae$2xp%g4K z5BlmM;#ejc22!FCWJ$=4q^wE)VRI5elO+FNyu!x&JP#lwYSd6EILZPCg$IH6XD-H_ zh;zTy6)tCDcGGHu%4&^Qt9e#9$gs5#0XMAX0v7qTzqHE%=dsAkwJypPw8I)MFCxH_ zNI@wZsb!5G`7A48p2grIhl(&0RCuG8fC!+-T4eTOr$Z(&giT?8(2vs%!hT;sG=(kl zD8z)<5}A~PC=05!$SYh$0P(!9mP0_LG%9HYt$qr41;!5>c%c^1N}U{w6g*y2F|PEO zK)kcaYY+;WNV1`!LD`^I@Gjtd3QvwNRs=Y3No^_)}QcugtxaSYSAe@o= z`;OBXcixP7OfKHTc^k9w__3-eno`gz1*Iyb)F!RQq@pX$mV`biStR0I(%4Q9Yx44fpTT9z1cN?2&ZKH4am~rC zW_*Y^20;4#`hOu@C69TD2Bca!u00p#p}mj|&|896%}RGAIx zEWN{yb=y>GdyY+GG-TOqM!Qa{&(S$(o5T9u91El;nTQwxMH8f^DJ?N$MoyuF_#frn Bi5~y} diff --git a/libs/thirdParty/libxbee/xsys/pdf/win32.dll.c.pdf b/libs/thirdParty/libxbee/xsys/pdf/win32.dll.c.pdf deleted file mode 100644 index 97d7acb34cfe49904b5c08b6c227512fc8bc6048..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7893 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 JFtam9{2yx~BG~`{ diff --git a/libs/thirdParty/libxbee/xsys/pdf/win32.rc.pdf b/libs/thirdParty/libxbee/xsys/pdf/win32.rc.pdf deleted file mode 100644 index 8ff8a7956bff9e4e4ff4b5cb33683835c635a36c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4744 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 DyZfM7 diff --git a/libs/thirdParty/libxbee/xsys/win32.c b/libs/thirdParty/libxbee/xsys/win32.c deleted file mode 100644 index a05950a94..000000000 --- a/libs/thirdParty/libxbee/xsys/win32.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* ################################################################# */ -/* ### Win32 Code ################################################## */ -/* ################################################################# */ - -/* this file contains code that is used by Win32 ONLY */ -#ifndef _WIN32 -#error "This file should only be used on a Win32 system" -#endif - -#include "win32.h" -#include "win32.dll.c" - -static int init_serial(xbee_hnd xbee, int baudrate) { - int chosenbaud; - DCB tc; - int evtMask; - COMMTIMEOUTS timeouts; - - /* open the serial port */ - xbee->tty = CreateFile(TEXT(xbee->path), - GENERIC_READ | GENERIC_WRITE, - 0, /* exclusive access */ - NULL, /* default security attributes */ - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, - NULL); - if (xbee->tty == INVALID_HANDLE_VALUE) { - xbee_logS("Invalid file handle..."); - xbee_logE("Is the XBee plugged in and avaliable on the correct port?"); - xbee_mutex_destroy(xbee->conmutex); - xbee_mutex_destroy(xbee->pktmutex); - xbee_mutex_destroy(xbee->sendmutex); - Xfree(xbee->path); - return -1; - } - - GetCommState(xbee->tty, &tc); - tc.BaudRate = baudrate; - tc.fBinary = TRUE; - tc.fParity = FALSE; - tc.fOutxCtsFlow = FALSE; - tc.fOutxDsrFlow = FALSE; - tc.fDtrControl = DTR_CONTROL_DISABLE; - tc.fDsrSensitivity = FALSE; - tc.fTXContinueOnXoff = FALSE; - tc.fOutX = FALSE; - tc.fInX = FALSE; - tc.fErrorChar = FALSE; - tc.fNull = FALSE; - tc.fRtsControl = RTS_CONTROL_DISABLE; - tc.fAbortOnError = FALSE; - tc.ByteSize = 8; - tc.Parity = NOPARITY; - tc.StopBits = ONESTOPBIT; - SetCommState(xbee->tty, &tc); - - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = 0; - timeouts.ReadTotalTimeoutConstant = 0; - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - SetCommTimeouts(xbee->tty, &timeouts); - - SetCommMask(xbee->tty, EV_RXCHAR); - - return 0; -} - -/* a replacement for the linux select() function... for a serial port */ -static int xbee_select(xbee_hnd xbee, struct timeval *timeout) { - int evtMask = 0; - COMSTAT status; - int ret; - - for (;;) { - /* find out how many bytes are in the Rx buffer... */ - if (ClearCommError(xbee->tty,NULL,&status) && (status.cbInQue > 0)) { - /* if there is data... return! */ - return 1; /*status.cbInQue;*/ - } else if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) { - /* if the timeout was 0 (return immediately) then return! */ - return 0; - } - - /* otherwise wait for an Rx event... */ - memset(&(xbee->ttyovrs),0,sizeof(OVERLAPPED)); - xbee->ttyovrs.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - if (!WaitCommEvent(xbee->tty,&evtMask,&(xbee->ttyovrs))) { - if (GetLastError() == ERROR_IO_PENDING) { - DWORD timeoutval; - if (!timeout) { - /* behave like the linux function... if the timeout pointer was NULL - then wait indefinately */ - timeoutval = INFINITE; - } else { - /* Win32 doesn't give the luxury of microseconds and seconds... just miliseconds! */ - timeoutval = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000); - } - ret = WaitForSingleObject(xbee->ttyovrs.hEvent,timeoutval); - if (ret == WAIT_TIMEOUT) { - /* cause the WaitCommEvent() call to stop */ - SetCommMask(xbee->tty, EV_RXCHAR); - /* if a timeout occured, then return 0 */ - CloseHandle(xbee->ttyovrs.hEvent); - return 0; - } - } else { - return -1; - } - } - CloseHandle(xbee->ttyovrs.hEvent); - } - - /* always return -1 (error) for now... */ - return -1; -} - -/* this offers the same behavior as non-blocking I/O under linux */ -int xbee_write(xbee_hnd xbee, const void *ptr, size_t size) { - xbee->ttyeof = FALSE; - if (!WriteFile(xbee->tty, ptr, size, NULL, &(xbee->ttyovrw)) && - (GetLastError() != ERROR_IO_PENDING)) return 0; - if (!GetOverlappedResult(xbee->tty, &(xbee->ttyovrw), &(xbee->ttyw), TRUE)) { - if (GetLastError() == ERROR_HANDLE_EOF) xbee->ttyeof = TRUE; - return 0; - } - return xbee->ttyw; -} - -/* this offers the same behavior as non-blocking I/O under linux */ -int xbee_read(xbee_hnd xbee, void *ptr, size_t size) { - xbee->ttyeof = FALSE; - if (!ReadFile(xbee->tty, ptr, size, NULL, &(xbee->ttyovrr)) && - (GetLastError() != ERROR_IO_PENDING)) return 0; - if (!GetOverlappedResult(xbee->tty, &(xbee->ttyovrr), &(xbee->ttyr), TRUE)) { - if (GetLastError() == ERROR_HANDLE_EOF) xbee->ttyeof = TRUE; - return 0; - } - return xbee->ttyr; -} - -/* this is because Win32 has some weird memory management rules... - - the thread that allocated the memory, MUST free it... */ -void xbee_free(void *ptr) { - if (!ptr) return; - free(ptr); -} - -/* win32 equivalent of unix gettimeofday() */ -int gettimeofday(struct timeval *tv, struct timezone *tz) { - if (tv) { - struct _timeb timeb; - _ftime(&timeb); - tv->tv_sec = timeb.time; - tv->tv_usec = timeb.millitm * 1000; - } - /* ignore tz for now */ - return 0; -} - -/* ################################################################# */ -/* ### Helper Functions (Mainly for VB6 use) ####################### */ -/* ################################################################# */ - -/* enable the debug output to a custom file or fallback to stderr */ -int xbee_setupDebugAPI(char *path, int baudrate, char *logfile, char cmdSeq, int cmdTime) { - xbee_hnd xbee = NULL; - int fd, ret; - if ((fd = _open(logfile,_O_WRONLY | _O_CREAT | _O_TRUNC)) == -1) { - fd = 2; - } - ret = xbee_setuplogAPI(path,baudrate,fd,cmdSeq,cmdTime); - if (fd > 2) { /* close fd, as libxbee dup'ed it */ - //_close(fd); - } - if (!ret) { /* libxbee started correctly */ - xbee = default_xbee; - if (fd == -1) { - xbee_log("Error opening logfile '%s' (errno=%d)... using stderr instead!",logfile,errno); - } - } - return ret; -} -int xbee_setupDebug(char *path, int baudrate, char *logfile) { - return xbee_setupDebugAPI(path,baudrate,logfile,0,0); -} - -/* These silly little functions are required for VB6 - - it freaks out when you call a function that uses va_args... */ -xbee_con *xbee_newcon_simple(unsigned char frameID, xbee_types type) { - return xbee_newcon(frameID, type); -} -xbee_con *xbee_newcon_16bit(unsigned char frameID, xbee_types type, int addr) { - return xbee_newcon(frameID, type, addr); -} -xbee_con *xbee_newcon_64bit(unsigned char frameID, xbee_types type, int addrL, int addrH) { - return xbee_newcon(frameID, type, addrL, addrH); -} - -void xbee_enableACKwait(xbee_con *con) { - con->waitforACK = 1; -} -void xbee_disableACKwait(xbee_con *con) { - con->waitforACK = 0; -} - -void xbee_enableDestroySelf(xbee_con *con) { - con->destroySelf = 1; -} - -/* for vb6... it will send a message to the given hWnd which can in turn check for a packet */ -void xbee_callback(xbee_con *con, xbee_pkt *pkt) { - xbee_hnd xbee = default_xbee; - - if (!win32_hWnd) { - xbee_log("*** Cannot do callback! No hWnd set... ***"); - return; - } - if (!win32_MessageID) { - xbee_log("*** Cannot do callback! No MessageID set... ***"); - return; - } - - xbee_log("Callback message sent!"); - SendMessage(win32_hWnd, win32_MessageID, (int)con, (int)pkt); -} - -/* very simple C function to provide more functionality to VB6 */ -int xbee_runCallback(int(*func)(xbee_con*,xbee_pkt*), xbee_con *con, xbee_pkt *pkt) { - return func(con,pkt); -} - -void xbee_enableCallbacks(HWND hWnd, UINT uMsg) { - xbee_hnd xbee = default_xbee; - if (!win32_MessageID || win32_MessageID != uMsg) { - xbee_log("Configuring libxbee to use MessageID = 0x%08X", uMsg); - win32_MessageID = uMsg; - } - if (!win32_hWnd || win32_hWnd != hWnd) { - xbee_log("Configuring libxbee to use hWnd = 0x%08X", hWnd); - win32_hWnd = hWnd; - } -} - -void xbee_attachCallback(xbee_con *con) { - xbee_hnd xbee = default_xbee; - - /* setup the callback function */ - xbee_log("Setting callback for connection @ 0x%08X",con); - con->callback = xbee_callback; -} - -void xbee_detachCallback(xbee_con *con) { - xbee_hnd xbee = default_xbee; - - /* un-setup the callback function */ - xbee_log("Unsetting callback for connection @ 0x%08X",con); - con->callback = NULL; -} diff --git a/libs/thirdParty/libxbee/xsys/win32.def b/libs/thirdParty/libxbee/xsys/win32.def deleted file mode 100644 index 888040ee3..000000000 --- a/libs/thirdParty/libxbee/xsys/win32.def +++ /dev/null @@ -1,71 +0,0 @@ -LIBRARY libxbee - -EXPORTS - ver - DllMain - DllCanUnloadNow PRIVATE - DllRegisterServer PRIVATE - DllUnregisterServer PRIVATE - - xbee_free - - xbee_setup - _xbee_setup - xbee_setuplog - _xbee_setuplog - xbee_setupAPI - _xbee_setupAPI - xbee_setuplogAPI - _xbee_setuplogAPI - xbee_setupDebug - xbee_setupDebugAPI - - xbee_end - _xbee_end - xbee_listen_stop - - xbee_newcon - _xbee_newcon - xbee_newcon_simple - xbee_newcon_16bit - xbee_newcon_64bit - - xbee_enableACKwait - xbee_disableACKwait - xbee_enableDestroySelf - - xbee_enableCallbacks - xbee_attachCallback - xbee_detachCallback - xbee_runCallback - - xbee_endcon2 - _xbee_endcon2 - xbee_purgecon - _xbee_purgecon - - xbee_senddata - _xbee_senddata - xbee_nsenddata - _xbee_nsenddata - xbee_vsenddata - _xbee_vsenddata - - xbee_getpacket - _xbee_getpacket - xbee_getpacketwait - _xbee_getpacketwait - - xbee_hasanalog - xbee_getanalog - - xbee_hasdigital - xbee_getdigital - - xbee_svn_version - xbee_build_info - - xbee_logit - _xbee_logit - xbee_logitf - _xbee_logitf diff --git a/libs/thirdParty/libxbee/xsys/win32.dll.c b/libs/thirdParty/libxbee/xsys/win32.dll.c deleted file mode 100644 index 1a9ebcaa8..000000000 --- a/libs/thirdParty/libxbee/xsys/win32.dll.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* ################################################################# */ -/* ### Win32 DLL Code ############################################## */ -/* ################################################################# */ - -/* this file contains code that is used by Win32 ONLY */ -#ifndef _WIN32 -#error "This file should only be used on a Win32 system" -#endif - -int ver(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine, int nCmdShow) { - char t[256]; - sprintf(t,"libxbee.dll\n%s\n%s",xbee_svn_version(),xbee_build_info()); - MessageBox(NULL, t, "libxbee Win32 DLL", MB_OK); - return 0; -} - -void xbee_UNLOADALL(void) { - while (default_xbee) { - _xbee_end(default_xbee); - } -} - -/* this gets called when the dll is loaded and unloaded... */ -BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { - if (dwReason == DLL_PROCESS_DETACH) { - /* ensure that libxbee has been shut down nicely */ - xbee_UNLOADALL(); - } else if (dwReason == DLL_PROCESS_ATTACH || dwReason == DLL_THREAD_ATTACH) { - if (!glob_hModule) { - /* keep a handle on the module */ - glob_hModule = (HMODULE)hModule; - } - } - return TRUE; -} - -HRESULT DllCanUnloadNow(void) { - if (default_xbee) return 0; - return 1; -} - -/* ################################################################# */ -/* ### Win32 DLL COM Code ########################################## */ -/* ################################################################# */ - -/* this function is from this tutorial: - http://www.codeguru.com/Cpp/COM-Tech/activex/tutorials/article.php/c5567 */ -BOOL RegWriteKey(HKEY roothk, const char *lpSubKey, LPCTSTR val_name, - DWORD dwType, void *lpvData, DWORD dwDataSize) { - /* roothk: HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE, etc - lpSubKey: the key relative to 'roothk' - val_name: the key value name where the data will be written - dwType: REG_SZ,REG_BINARY, etc. - lpvData: a pointer to the data buffer - dwDataSize: the size of the data pointed to by lpvData */ - HKEY hk; - if (ERROR_SUCCESS != RegCreateKey(roothk,lpSubKey,&hk) ) return FALSE; - if (ERROR_SUCCESS != RegSetValueEx(hk,val_name,0,dwType,(CONST BYTE *)lpvData,dwDataSize)) return FALSE; - if (ERROR_SUCCESS != RegCloseKey(hk)) return FALSE; - return TRUE; -} - -/* this is used by the regsrv32 application */ -STDAPI DllRegisterServer(void) { - char key[MAX_PATH]; - char value[MAX_PATH]; - - wsprintf(key,"CLSID\\%s",dllGUID); - wsprintf(value,"%s",dlldesc); - RegWriteKey(HKEY_CLASSES_ROOT, key, NULL, REG_SZ, (void *)value, lstrlen(value)); - - wsprintf(key,"CLSID\\%s\\InprocServer32",dllGUID); - GetModuleFileName(glob_hModule,value,MAX_PATH); - RegWriteKey(HKEY_CLASSES_ROOT, key, NULL, REG_SZ, (void *)value, lstrlen(value)); - - wsprintf(key,"CLSID\\%s\\ProgId",dllGUID); - lstrcpy(value,dllid); - RegWriteKey(HKEY_CLASSES_ROOT, key, NULL, REG_SZ, (void *)value, lstrlen(value)); - - lstrcpy(key,dllid); - lstrcpy(value,dlldesc); - RegWriteKey(HKEY_CLASSES_ROOT, key, NULL, REG_SZ, (void *)value, lstrlen(value)); - - wsprintf(key,"%s\\CLSID",dllid); - RegWriteKey(HKEY_CLASSES_ROOT, key, NULL, REG_SZ, (void *)dllGUID, lstrlen(dllGUID)); - - return S_OK; -} - -/* this is used by the regsrv32 application */ -STDAPI DllUnregisterServer(void) { - char key[MAX_PATH]; - char value[MAX_PATH]; - - wsprintf(key,"%s\\CLSID",dllid); - RegDeleteKey(HKEY_CLASSES_ROOT,key); - - wsprintf(key,"%s",dllid); - RegDeleteKey(HKEY_CLASSES_ROOT,key); - - wsprintf(key,"CLSID\\%s\\InprocServer32",dllGUID); - RegDeleteKey(HKEY_CLASSES_ROOT,key); - - wsprintf(key,"CLSID\\%s\\ProgId",dllGUID); - RegDeleteKey(HKEY_CLASSES_ROOT,key); - - wsprintf(key,"CLSID\\%s",dllGUID); - RegDeleteKey(HKEY_CLASSES_ROOT,key); - - return S_OK; -} diff --git a/libs/thirdParty/libxbee/xsys/win32.h b/libs/thirdParty/libxbee/xsys/win32.h deleted file mode 100644 index 65f19f805..000000000 --- a/libs/thirdParty/libxbee/xsys/win32.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - libxbee - a C library to aid the use of Digi's Series 1 XBee modules - running in API mode (AP=2). - - Copyright (C) 2009 Attie Grande (attie@attie.co.uk) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -/* this file contains code that is used by Win32 ONLY */ -#ifndef _WIN32 -#error "This file should only be used on a Win32 system" -#endif - -/* ################################################################# */ -/* ### Win32 Code ################################################## */ -/* ################################################################# */ - -#pragma comment(lib, "Advapi32.lib") -#pragma comment(lib, "User32.lib") - -#define dllid "attie-co-uk.libxbee" -#define dlldesc "libxbee - XBee API Library" -/* libxbee's GUID is {7A6E25AA-ECB5-4370-87B5-A1D31840FE23} */ -#define dllGUID "{7A6E25AA-ECB5-4370-87B5-A1D31840FE23}" - -#define Win32Message() MessageBox(0,"Run xbee_setup() first!...","libxbee",MB_OK); - -HMODULE glob_hModule = NULL; - -/* this uses miliseconds not microseconds... */ -#define usleep(a) Sleep((a)/1000) - -#define xbee_thread_create(a,b,c) (((a) = CreateThread(NULL,0,(void *)(b),(void *)(c),0,NULL)) == NULL) -#define xbee_thread_cancel(a,b) TerminateThread((a),(b)) -#define xbee_thread_join(a) WaitForSingleObject((a),INFINITE) -#define xbee_thread_tryjoin(a) WaitForSingleObject((a),0) - -#define xbee_mutex_init(a) (((a) = CreateEvent(NULL,FALSE,TRUE,NULL)) == NULL) -#define xbee_mutex_destroy(a) CloseHandle((a)) -#define xbee_mutex_lock(a) WaitForSingleObject((a),INFINITE) -#define xbee_mutex_trylock(a) WaitForSingleObject((a),0) -#define xbee_mutex_unlock(a) SetEvent((a)) - -#define xbee_sem_init(a) (((a) = CreateEvent(NULL,FALSE,FALSE,NULL)) == NULL) -#define xbee_sem_destroy(a) CloseHandle((a)) -#define xbee_sem_wait(a) WaitForSingleObject((a),INFINITE) -#define xbee_sem_wait1sec(a) WaitForSingleObject((a),1000) -#define xbee_sem_post(a) SetEvent((a)) - -#define xbee_cond_init(a) InitializeConditionVariable(&(a)) -#define xbee_cond_destroy(a) -#define xbee_cond_wait(a,b) SleepConditionVariableCS(&(a),&(b),INFINITE) -#define xbee_cond_signal(a) WakeConditionVariable(&(a)) -#define xbee_cond_broadcast(a) WakeAllConditionVariable(&(a)) - -#define xbee_feof(a) (xbee->ttyeof) -#define xbee_ferror(a) (0) -#define xbee_close(a) (((a)==xbee->log)?fclose((a)):CloseHandle((a))) - -HWND win32_hWnd = 0; -UINT win32_MessageID = 0; diff --git a/libs/thirdParty/libxbee/xsys/win32.rc b/libs/thirdParty/libxbee/xsys/win32.rc deleted file mode 100644 index 80f7d33bc..000000000 --- a/libs/thirdParty/libxbee/xsys/win32.rc +++ /dev/null @@ -1,47 +0,0 @@ -/* more info: http://msdn.microsoft.com/en-us/library/aa381058(v=vs.85).aspx */ - -#define APSTUDIO_READONLY_SYMBOLS -#include "winresrc.h" -#undef APSTUDIO_READONLY_SYMBOLS - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) - -#ifdef _WIN32 -/* Englush (UK) */ -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK -#pragma code_page(1252) -#endif //_WIN32 - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,4,1,0 - FILEFLAGSMASK 0x00L - FILEFLAGS 0x00L - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "Provides a simple interface for XBee radios" - VALUE "CompanyName", "attie.co.uk" - VALUE "FileDescription", "Provides a simple interface for XBee radios" - VALUE "InternalName", "libxbee" - VALUE "LegalCopyright", "Copyright (C) 2009 onwards Attie Grande" - VALUE "LegalTrademarks", "" - VALUE "OriginalFilename", "libxbee.dll" - VALUE "PrivateBuild", "" - VALUE "ProductName", "libxbee - http://code.google.com/p/libxbee/" - VALUE "ProductVersion", "v1.4.1" - VALUE "SpecialBuild", "" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x809, 1200 - END -END - - -#endif diff --git a/src/comm/HexSpinBox.cpp b/src/comm/HexSpinBox.cpp deleted file mode 100644 index 878e9409e..000000000 --- a/src/comm/HexSpinBox.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "HexSpinBox.h" - -#include - -HexSpinBox::HexSpinBox(QWidget *parent) - : QSpinBox(parent), validator(NULL) -{ - setRange(0, 0x7fffffff); - validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this); -} - -HexSpinBox::~HexSpinBox(void) -{ - if(this->validator) - { - delete this->validator; - this->validator = NULL; - } -} - -QValidator::State HexSpinBox::validate(QString &text, int &pos) const -{ - return validator->validate(text, pos); -} - -QString HexSpinBox::textFromValue(int value) const -{ - return QString::number(value, 16).toUpper(); -} - -int HexSpinBox::valueFromText(const QString &text) const -{ - bool ok; - return text.toInt(&ok, 16); -} \ No newline at end of file diff --git a/src/comm/HexSpinBox.h b/src/comm/HexSpinBox.h deleted file mode 100644 index 84703c95c..000000000 --- a/src/comm/HexSpinBox.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef HEXSPINBOX_H_ -#define HEXSPINBOX_H_ - -#include - -class QRegExpValidator; - -class HexSpinBox : public QSpinBox -{ - Q_OBJECT -public: - HexSpinBox(QWidget *parent = 0); - ~HexSpinBox(void); - -protected: - QValidator::State validate(QString &text, int &pos) const; - int valueFromText(const QString &text) const; - QString textFromValue(int value) const; - -private: - QRegExpValidator *validator; -}; - -#endif // HEXSPINBOX_H_ - diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index af6b79b4b..42a181bf4 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -53,12 +53,6 @@ public: #ifdef QGC_ENABLE_BLUETOOTH TypeBluetooth, ///< Bluetooth Link #endif -#if 0 - // TODO Below is not yet implemented - TypeForwarding, ///< Forwarding Link - TypeXbee, ///< XBee Proprietary Link - TypeOpal, ///< Opal-RT Link -#endif #ifdef QT_DEBUG TypeMock, ///< Mock Link for Unitesting #endif diff --git a/src/comm/XbeeLink.cpp b/src/comm/XbeeLink.cpp deleted file mode 100644 index c3a7b1537..000000000 --- a/src/comm/XbeeLink.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include -#include -#include -#include -#include -#include "XbeeLink.h" - -XbeeLink::XbeeLink(QString portName, int baudRate) : - m_xbeeCon(NULL), - m_portName(NULL), - m_portNameLength(0), - m_baudRate(baudRate), - m_connected(false), - m_addrHigh(0), - m_addrLow(0) -{ - - /* setup the xbee */ - this->setPortName(portName); -} - -XbeeLink::~XbeeLink() -{ - if(m_portName) - { - delete m_portName; - m_portName = NULL; - } - _disconnect(); -} - -QString XbeeLink::getPortName() const -{ - QString portName; - for(unsigned int i = 0;im_portNameLength;i++) - { - portName.append(this->m_portName[i]); - } - return portName; -} - -int XbeeLink::getBaudRate() const -{ - return this->m_baudRate; -} - -bool XbeeLink::setPortName(QString portName) -{ - bool reconnect(false); - if(this->m_connected) - { - _disconnect(); - reconnect = true; - } - if(m_portName) - { - delete m_portName; - m_portName = NULL; - } - QStringList list = portName.split(QRegExp("\\s+"),QString::SkipEmptyParts); - if(list.size()>0) - { - this->m_portNameLength = list[0].size()+1; - m_portName = new char[this->m_portNameLength]; - for(int i=0;im_portName[i]=list[0][i].toLatin1(); - } - this->m_portName[list[0].size()] = '\0'; - } - else - { - this->m_portNameLength = 1; - m_portName = new char[this->m_portNameLength]; - this->m_portName[0] = '\0'; - } - - bool retVal(true); - if(reconnect) - { - retVal = _connect(); - } - - return retVal; -} - -bool XbeeLink::setBaudRate(int rate) -{ - bool reconnect(false); - if(this->m_connected) - { - _disconnect(); - reconnect = true; - } - bool retVal(true); - this->m_baudRate = rate; - if(reconnect) - { - retVal = _connect(); - } - return retVal; -} - -QString XbeeLink::getName() const -{ - return this->m_name; -} - -bool XbeeLink::isConnected() const -{ - return this->m_connected; -} - -qint64 XbeeLink::getConnectionSpeed() const -{ - return this->m_baudRate; -} - -qint64 XbeeLink::getCurrentInDataRate() const -{ - return 0; -} - -qint64 XbeeLink::getCurrentOutDataRate() const -{ - return 0; -} - -bool XbeeLink::hardwareConnect() -{ - emit tryConnectBegin(true); - if(this->isConnected()) - { - _disconnect(); - } - if (*this->m_portName == '\0') - { - return false; - } - if (xbee_setupAPI(this->m_portName,this->m_baudRate,0x2B,0x3E8) == -1) - { - /* oh no... it failed */ - qDebug() <<"xbee_setup() failed...\n"; - emit tryConnectEnd(true); - return false; - } - this->m_xbeeCon = xbee_newcon('A',xbee2_data,0x13A200,0x403D0935); - emit tryConnectEnd(true); - this->m_connected = true; - emit connected(); - return true; -} - -bool XbeeLink::_connect(void) -{ - if (this->isRunning()) _disconnect(); - this->start(LowPriority); - return true; -} - -void XbeeLink::_disconnect(void) -{ - if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect - - if(this->m_xbeeCon) - { - xbee_end(); - this->m_xbeeCon = NULL; - } - this->m_connected = false; - - emit disconnected(); -} - -void XbeeLink::_writeBytes(const QByteArray bytes) -{ - if(!xbee_nsenddata(this->m_xbeeCon,const_cast(bytes.data()),bytes.size())) // return value of 0 is successful written - { - _logOutputDataRate(bytes.size(), QDateTime::currentMSecsSinceEpoch()); - } - else - { - _disconnect(); - emit communicationError(tr("Link Error"), QString("Error on link: %1. Could not send data - link is disconnected!").arg(getName())); - } -} - -void XbeeLink::readBytes() -{ - xbee_pkt *xbeePkt; - xbeePkt = xbee_getpacketwait(this->m_xbeeCon); - if(!(NULL==xbeePkt)) - { - QByteArray data; - for(unsigned int i=0;i<=xbeePkt->datalen;i++) - { - data.push_back(xbeePkt->data[i]); - } - - _logInputDataRate(data.length(), QDateTime::currentMSecsSinceEpoch()); - emit bytesReceived(this, data); - } -} - -void XbeeLink::run() -{ - // Initialize the connection - if(this->hardwareConnect()) - { - // Qt way to make clear what a while(1) loop does - forever - { - this->readBytes(); - } - } -} - -bool XbeeLink::setRemoteAddressHigh(quint32 high) -{ - this->m_addrHigh = high; - return true; -} - -bool XbeeLink::setRemoteAddressLow(quint32 low) -{ - this->m_addrLow = low; - return true; -} - -/* -void CALLTYPE XbeeLink::portCallback(xbee_con *xbeeCon, xbee_pkt *XbeePkt) -{ - QByteArray buf; - for(quint8 i=0;idatalen;i++) - { - buf.push_back(XbeePkt->data[i]); - } - emit bytesReceived(this, buf); -}*/ diff --git a/src/comm/XbeeLink.h b/src/comm/XbeeLink.h deleted file mode 100644 index 02180dd25..000000000 --- a/src/comm/XbeeLink.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _XBEELINK_H_ -#define _XBEELINK_H_ - -#include -#include -#include -#include -#include -#include -#include "XbeeLinkInterface.h" -#include -#include "CallConv.h" - -class XbeeLink : public XbeeLinkInterface -{ - Q_OBJECT - -public: - XbeeLink(QString portName = "", int baudRate=57600); - ~XbeeLink(); - -public: // virtual functions from XbeeLinkInterface - QString getPortName() const; - void requestReset() { } - int getBaudRate() const; - - // These are left unimplemented in order to cause linker errors which indicate incorrect usage of - // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. - bool connect(void); - bool disconnect(void); - -public slots: // virtual functions from XbeeLinkInterface - bool setPortName(QString portName); - bool setBaudRate(int rate); - bool setRemoteAddressHigh(quint32 high); - bool setRemoteAddressLow(quint32 low); - -public: - // virtual functions from LinkInterface - QString getName() const; - bool isConnected() const; - - // Extensive statistics for scientific purposes - qint64 getConnectionSpeed() const; - qint64 getCurrentOutDataRate() const; - qint64 getCurrentInDataRate() const; - -private slots: // virtual functions from LinkInterface - void _writeBytes(const QByteArray bytes); - -protected slots: // virtual functions from LinkInterface - void readBytes(); - -public: - void run(); // initiating the thread - -protected: - xbee_con *m_xbeeCon; - char *m_portName; - unsigned int m_portNameLength; - int m_baudRate; - bool m_connected; - QString m_name; - quint32 m_addrHigh; - quint32 m_addrLow; - -private: - // From LinkInterface - virtual bool _connect(void); - virtual void _disconnect(void); - - bool hardwareConnect(); - //void CALLTYPE portCallback(xbee_con *XbeeCon, xbee_pkt *XbeePkt); -}; - - -#endif // _XBEELINK_H_ diff --git a/src/comm/XbeeLinkInterface.h b/src/comm/XbeeLinkInterface.h deleted file mode 100644 index d359f579d..000000000 --- a/src/comm/XbeeLinkInterface.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef XBEELINKINTERFACE_H_ -#define XBEELINKINTERFACE_H_ - -#include -#include -#include - -class XbeeLinkInterface : public LinkInterface -{ - Q_OBJECT - -public: - virtual QString getPortName() const = 0; - virtual int getBaudRate() const = 0; - -public slots: - virtual bool setPortName(QString portName) = 0; - virtual bool setBaudRate(int rate) = 0; - virtual bool setRemoteAddressHigh(quint32 high) = 0; - virtual bool setRemoteAddressLow(quint32 low) = 0; - -signals: - void tryConnectBegin(bool toTrue); - void tryConnectEnd(bool toTrue); -}; - -#endif // XBEELINKINTERFACE_H_ diff --git a/src/ui/XbeeConfigurationWindow.cpp b/src/ui/XbeeConfigurationWindow.cpp deleted file mode 100644 index 847ab8e93..000000000 --- a/src/ui/XbeeConfigurationWindow.cpp +++ /dev/null @@ -1,444 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -//#include -#include -#endif - -#if defined (__APPLE__) && defined (__MACH__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __MWERKS__ -#define __CF_USE_FRAMEWORK_INCLUDES__ -#endif - - -#include - -#include -#include -#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) -#include -#endif -#include - -// Apple internal modems default to local echo being on. If your modem has local echo disabled, -// undefine the following macro. -#define LOCAL_ECHO - -#define kATCommandString "AT\r" - -#ifdef LOCAL_ECHO -#define kOKResponseString "AT\r\r\nOK\r\n" -#else -#define kOKResponseString "\r\nOK\r\n" -#endif -#endif - - -// Some helper functions for serial port enumeration -#if defined (__APPLE__) && defined (__MACH__) - -enum { - kNumRetries = 3 -}; - -// Function prototypes -static kern_return_t FindModems(io_iterator_t *matchingServices); -static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize); - -// Returns an iterator across all known modems. Caller is responsible for -// releasing the iterator when iteration is complete. -static kern_return_t FindModems(io_iterator_t *matchingServices) -{ - kern_return_t kernResult; - CFMutableDictionaryRef classesToMatch; - - /*! @function IOServiceMatching - @abstract Create a matching dictionary that specifies an IOService class match. - @discussion A very common matching criteria for IOService is based on its class. IOServiceMatching will create a matching dictionary that specifies any IOService of a class, or its subclasses. The class is specified by C-string name. - @param name The class name, as a const C-string. Class matching is successful on IOService's of this class or any subclass. - @result The matching dictionary created, is returned on success, or zero on failure. The dictionary is commonly passed to IOServiceGetMatchingServices or IOServiceAddNotification which will consume a reference, otherwise it should be released with CFRelease by the caller. */ - - // Serial devices are instances of class IOSerialBSDClient - classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); - if (classesToMatch == NULL) { - printf("IOServiceMatching returned a NULL dictionary.\n"); - } else { - /*! - @function CFDictionarySetValue - Sets the value of the key in the dictionary. - @param theDict The dictionary to which the value is to be set. If this - parameter is not a valid mutable CFDictionary, the behavior is - undefined. If the dictionary is a fixed-capacity dictionary and - it is full before this operation, and the key does not exist in - the dictionary, the behavior is undefined. - @param key The key of the value to set into the dictionary. If a key - which matches this key is already present in the dictionary, only - the value is changed ("add if absent, replace if present"). If - no key matches the given key, the key-value pair is added to the - dictionary. If added, the key is retained by the dictionary, - using the retain callback provided - when the dictionary was created. If the key is not of the sort - expected by the key retain callback, the behavior is undefined. - @param value The value to add to or replace into the dictionary. The value - is retained by the dictionary using the retain callback provided - when the dictionary was created, and the previous value if any is - released. If the value is not of the sort expected by the - retain or release callbacks, the behavior is undefined. - */ - CFDictionarySetValue(classesToMatch, - CFSTR(kIOSerialBSDTypeKey), - CFSTR(kIOSerialBSDModemType)); - - // Each serial device object has a property with key - // kIOSerialBSDTypeKey and a value that is one of kIOSerialBSDAllTypes, - // kIOSerialBSDModemType, or kIOSerialBSDRS232Type. You can experiment with the - // matching by changing the last parameter in the above call to CFDictionarySetValue. - - // As shipped, this sample is only interested in modems, - // so add this property to the CFDictionary we're matching on. - // This will find devices that advertise themselves as modems, - // such as built-in and USB modems. However, this match won't find serial modems. - } - - /*! @function IOServiceGetMatchingServices - @abstract Look up registered IOService objects that match a matching dictionary. - @discussion This is the preferred method of finding IOService objects currently registered by IOKit. IOServiceAddNotification can also supply this information and install a notification of new IOServices. The matching information used in the matching dictionary may vary depending on the class of service being looked up. - @param masterPort The master port obtained from IOMasterPort(). - @param matching A CF dictionary containing matching information, of which one reference is consumed by this function. IOKitLib can contruct matching dictionaries for common criteria with helper functions such as IOServiceMatching, IOOpenFirmwarePathMatching. - @param existing An iterator handle is returned on success, and should be released by the caller when the iteration is finished. - @result A kern_return_t error code. */ - - kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); - if (KERN_SUCCESS != kernResult) { - printf("IOServiceGetMatchingServices returned %d\n", kernResult); - goto exit; - } - -exit: - return kernResult; -} - -/** Given an iterator across a set of modems, return the BSD path to the first one. - * If no modems are found the path name is set to an empty string. - */ -static kern_return_t GetModemPath(io_iterator_t serialPortIterator, char *bsdPath, CFIndex maxPathSize) -{ - io_object_t modemService; - kern_return_t kernResult = KERN_FAILURE; - Boolean modemFound = false; - - // Initialize the returned path - *bsdPath = '\0'; - - // Iterate across all modems found. In this example, we bail after finding the first modem. - - while ((modemService = IOIteratorNext(serialPortIterator)) && !modemFound) { - CFTypeRef bsdPathAsCFString; - - // Get the callout device's path (/dev/cu.xxxxx). The callout device should almost always be - // used: the dialin device (/dev/tty.xxxxx) would be used when monitoring a serial port for - // incoming calls, e.g. a fax listener. - - bsdPathAsCFString = IORegistryEntryCreateCFProperty(modemService, - CFSTR(kIOCalloutDeviceKey), - kCFAllocatorDefault, - 0); - if (bsdPathAsCFString) { - Boolean result; - - // Convert the path from a CFString to a C (NUL-terminated) string for use - // with the POSIX open() call. - - result = CFStringGetCString((CFStringRef)bsdPathAsCFString, - bsdPath, - maxPathSize, - kCFStringEncodingUTF8); - CFRelease(bsdPathAsCFString); - - if (result) { - //printf("Modem found with BSD path: %s", bsdPath); - modemFound = true; - kernResult = KERN_SUCCESS; - } - } - - printf("\n"); - - // Release the io_service_t now that we are done with it. - - (void) IOObjectRelease(modemService); - } - - return kernResult; -} -#endif - -XbeeConfigurationWindow::XbeeConfigurationWindow(LinkInterface* link, QWidget *parent, Qt::WindowFlags flags): QWidget(parent, flags), - userConfigured(false) -{ - XbeeLinkInterface *xbeeLink = dynamic_cast(link); - - if(xbeeLink != 0) - { - this->link = xbeeLink; - - baudLabel = new QLabel; - baudLabel->setText(tr("Baut Rate")); - baudBox = new QComboBox; - baudLabel->setBuddy(baudBox); - portLabel = new QLabel; - portLabel->setText(tr("SerialPort")); - portBox = new QComboBox; - portBox->setEditable(true); - portLabel->setBuddy(portBox); - highAddrLabel = new QLabel; - highAddrLabel->setText(tr("Remote hex Address &High")); - highAddr = new HexSpinBox(this); - highAddrLabel->setBuddy(highAddr); - lowAddrLabel = new QLabel; - lowAddrLabel->setText(tr("Remote hex Address &Low")); - lowAddr = new HexSpinBox(this); - lowAddrLabel->setBuddy(lowAddr); - actionLayout = new QGridLayout; - actionLayout->addWidget(baudLabel,1,1); - actionLayout->addWidget(baudBox,1,2); - actionLayout->addWidget(portLabel,2,1); - actionLayout->addWidget(portBox,2,2); - actionLayout->addWidget(highAddrLabel,3,1); - actionLayout->addWidget(highAddr,3,2); - actionLayout->addWidget(lowAddrLabel,4,1); - actionLayout->addWidget(lowAddr,4,2); - tmpLayout = new QVBoxLayout; - tmpLayout->addStretch(); - tmpLayout->addLayout(actionLayout); - xbeeLayout = new QHBoxLayout; - xbeeLayout->addStretch(); - xbeeLayout->addLayout(tmpLayout); - this->setLayout(xbeeLayout); - - //connect(portBox,SIGNAL(activated(QString)),this,SLOT(setPortName(QString))); - //connect(baudBox,SIGNAL(activated(QString)),this,SLOT(setBaudRateString(QString))); - connect(portBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(setPortName(QString))); - connect(portBox,SIGNAL(editTextChanged(QString)),this,SLOT(setPortName(QString))); - connect(baudBox,SIGNAL(currentIndexChanged(QString)),this,SLOT(setBaudRateString(QString))); - connect(highAddr,SIGNAL(valueChanged(int)),this,SLOT(addrChangedHigh(int))); - connect(lowAddr,SIGNAL(valueChanged(int)),this,SLOT(addrChangedLow(int))); - connect(this,SIGNAL(addrHighChanged(quint32)),xbeeLink,SLOT(setRemoteAddressHigh(quint32))); - connect(this,SIGNAL(addrLowChanged(quint32)),xbeeLink,SLOT(setRemoteAddressLow(quint32))); - - baudBox->addItem("1200",1200); - baudBox->addItem("2400",2400); - baudBox->addItem("4800",4800); - baudBox->addItem("9600",9600); - baudBox->addItem("19200",19200); - baudBox->addItem("38400",38400); - baudBox->addItem("57600",57600); - baudBox->setCurrentIndex(6); - - // try to open xbeeConf file for last remote address - QFile in("Xbeeconf.txt"); - if(in.open(QIODevice::ReadOnly)) - { - QDataStream inStr(&in); - int tmpaddrHigh; - int tmpaddrLow; - inStr >> tmpaddrHigh; - inStr >> tmpaddrLow; - highAddr->setValue(tmpaddrHigh); - lowAddr->setValue(tmpaddrLow); - } - else - { - highAddr->setValue(0x13A200); - lowAddr->setValue(0x40DDDDDD); - } - - - - this->setupPortList(); - - portCheckTimer = new QTimer(this); - portCheckTimer->setInterval(1000); - connect(portCheckTimer, SIGNAL(timeout()), this, SLOT(setupPortList())); - - // Display the widget - this->window()->setWindowTitle(tr("Xbee Communication Settings")); - } - else - { - qDebug() << "This is not a Xbee Link"; - } -} - -XbeeConfigurationWindow::~XbeeConfigurationWindow() -{ - -} - -void XbeeConfigurationWindow::setupPortList() -{ - #ifdef __linux - - // TODO Linux has no standard way of enumerating serial ports - // However the device files are only present when the physical - // device is connected, therefore listing the files should be - // sufficient. - - QString devdir = "/dev"; - QDir dir(devdir); - dir.setFilter(QDir::System); - - QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); ++i) { - QFileInfo fileInfo = list.at(i); - if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS"))) { - if (portBox->findText(fileInfo.canonicalFilePath()) == -1) { - portBox->addItem(fileInfo.canonicalFilePath()); - if (!userConfigured) portBox->setEditText(fileInfo.canonicalFilePath()); - } - } - } -#endif - -#if defined (__APPLE__) && defined (__MACH__) - - // Enumerate serial ports - //int fileDescriptor; - kern_return_t kernResult; // on PowerPC this is an int (4 bytes) - - io_iterator_t serialPortIterator; - char bsdPath[MAXPATHLEN]; - - kernResult = FindModems(&serialPortIterator); - - kernResult = GetModemPath(serialPortIterator, bsdPath, sizeof(bsdPath)); - - IOObjectRelease(serialPortIterator); // Release the iterator. - - // Add found modems - if (bsdPath[0]) { - if (portBox->findText(QString(bsdPath)) == -1) { - portBox->addItem(QString(bsdPath)); - if (!userConfigured) portBox->setEditText(QString(bsdPath)); - } - } - - // Add USB serial port adapters - // TODO Strangely usb serial port adapters are not enumerated, even when connected - QString devdir = "/dev"; - QDir dir(devdir); - dir.setFilter(QDir::System); - - QFileInfoList list = dir.entryInfoList(); - for (int i = list.size() - 1; i >= 0; i--) { - QFileInfo fileInfo = list.at(i); - if (fileInfo.fileName().contains(QString("ttyUSB")) || fileInfo.fileName().contains(QString("ttyS")) || fileInfo.fileName().contains(QString("tty.usbserial"))) { - if (portBox->findText(fileInfo.canonicalFilePath()) == -1) { - portBox->addItem(fileInfo.canonicalFilePath()); - if (!userConfigured) portBox->setEditText(fileInfo.canonicalFilePath()); - } - } - } - - -#endif - -#ifdef _WIN32 - // Get the ports available on this system - QList ports = QSerialPortInfo::availablePorts(); - //QList ports = QextSerialEnumerator::getPorts(); - - // Add the ports in reverse order, because we prepend them to the list - for (int i = ports.size() - 1; i >= 0; i--) { - QString portString = QString(ports.at(i).portName().toLocal8Bit().constData()); - // Prepend newly found port to the list - if (portBox->findText(portString) == -1) { - portBox->insertItem(0, portString); - if (!userConfigured) portBox->setEditText(portString); - } - } - - //printf("port name: %s\n", ports.at(i).portName.toLocal8Bit().constData()); - //printf("friendly name: %s\n", ports.at(i).friendName.toLocal8Bit().constData()); - //printf("physical name: %s\n", ports.at(i).physName.toLocal8Bit().constData()); - //printf("enumerator name: %s\n", ports.at(i).enumName.toLocal8Bit().constData()); - //printf("===================================\n\n"); -#endif -} - -void XbeeConfigurationWindow::showEvent(QShowEvent* event) -{ - Q_UNUSED(event); - portCheckTimer->start(); -} - -void XbeeConfigurationWindow::hideEvent(QHideEvent* event) -{ - Q_UNUSED(event); - portCheckTimer->stop(); -} - -void XbeeConfigurationWindow::configureCommunication() -{ - this->setupPortList(); - this->show(); -} - -void XbeeConfigurationWindow::setPortName(QString port) -{ - link->setPortName(port); -} - -void XbeeConfigurationWindow::setBaudRateString(QString baud) -{ - int rate = baud.toInt(); - this->link->setBaudRate(rate); -} - -void XbeeConfigurationWindow::addrChangedHigh(int addr) -{ - quint32 uaddr = static_cast(addr); - QFile out("Xbeeconf.txt"); - if(out.open(QIODevice::WriteOnly)) - { - QDataStream outStr(&out); - outStr << this->highAddr->value(); - outStr << this->lowAddr->value(); - } - emit addrHighChanged(uaddr); -} - -void XbeeConfigurationWindow::addrChangedLow(int addr) -{ - quint32 uaddr = static_cast(addr); - QFile out("Xbeeconf.txt"); - if(out.open(QIODevice::WriteOnly)) - { - QDataStream outStr(&out); - outStr << this->highAddr->value(); - outStr << this->lowAddr->value(); - } - emit addrLowChanged(uaddr); -} diff --git a/src/ui/XbeeConfigurationWindow.h b/src/ui/XbeeConfigurationWindow.h deleted file mode 100644 index 1a6f233fd..000000000 --- a/src/ui/XbeeConfigurationWindow.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _XBEECONFIGURATIONWINDOW_H_ -#define _XBEECONFIGURATIONWINDOW_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include"XbeeLinkInterface.h" -#include "HexSpinBox.h" - - -class XbeeConfigurationWindow : public QWidget -{ - Q_OBJECT - -public: - XbeeConfigurationWindow(LinkInterface* link, QWidget *parent = 0, Qt::WindowFlags flags = Qt::Sheet); - ~XbeeConfigurationWindow(); - -public slots: - void configureCommunication(); - void setPortName(QString port); - void setBaudRateString(QString baud); - void setupPortList(); - -private slots: - void addrChangedHigh(int addr); - void addrChangedLow(int addr); - -protected: - void showEvent(QShowEvent* event); - void hideEvent(QHideEvent* event); - bool userConfigured; ///< Switch to detect if current values are user-selected and shouldn't be overriden - -private: - QLabel *portLabel; - QLabel *baudLabel; - QComboBox *portBox; - QComboBox *baudBox; - QGridLayout *actionLayout; - QHBoxLayout *xbeeLayout; - QVBoxLayout *tmpLayout; - XbeeLinkInterface* link; - QTimer* portCheckTimer; - HexSpinBox* highAddr; - HexSpinBox* lowAddr; - QLabel* highAddrLabel; - QLabel* lowAddrLabel; - -signals: - void addrHighChanged(quint32 addrHigh); - void addrLowChanged(quint32 addrLow); -}; - - -#endif //_XBEECONFIGURATIONWINDOW_H_ -- GitLab From fd6a2f9064e7a56f5c3c11ca25c4d2bc1ddbc77b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 05:19:52 -0800 Subject: [PATCH 125/398] Remove xbee --- QGCSetup.pri | 1 - 1 file changed, 1 deletion(-) diff --git a/QGCSetup.pri b/QGCSetup.pri index 3cbfb7401..eabeca15f 100644 --- a/QGCSetup.pri +++ b/QGCSetup.pri @@ -63,7 +63,6 @@ WindowsBuild { ReleaseBuild: DLL_QT_DEBUGCHAR = "" COPY_FILE_LIST = \ $$BASEDIR\\libs\\lib\\sdl2\\msvc\\lib\\x86\\SDL2.dll \ - $$BASEDIR\\libs\\thirdParty\\libxbee\\lib\\libxbee.dll \ $$BASEDIR\\deploy\\libeay32.dll for(COPY_FILE, COPY_FILE_LIST) { -- GitLab From 3b8a69ad9ea37d43ebe015829230a00ed3acfce7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 05:20:05 -0800 Subject: [PATCH 126/398] Update android build to LinkReference changes --- src/comm/BluetoothLink.cc | 12 ++++-------- src/comm/BluetoothLink.h | 2 +- src/comm/LinkManager.cc | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc index 93bc9b8de..c5a019acd 100644 --- a/src/comm/BluetoothLink.cc +++ b/src/comm/BluetoothLink.cc @@ -29,24 +29,20 @@ #include "BluetoothLink.h" #include "QGC.h" -BluetoothLink::BluetoothLink(BluetoothConfiguration* config) - : _connectState(false) +BluetoothLink::BluetoothLink(SharedLinkConfigurationPointer& config) + : LinkInterface(config) + , _connectState(false) , _targetSocket(NULL) #ifdef __ios__ , _discoveryAgent(NULL) #endif , _shutDown(false) { - Q_ASSERT(config != NULL); - _config = config; - _config->setLink(this); - //moveToThread(this); + } BluetoothLink::~BluetoothLink() { - // Disconnect link from configuration - _config->setLink(NULL); _disconnect(); #ifdef __ios__ if(_discoveryAgent) { diff --git a/src/comm/BluetoothLink.h b/src/comm/BluetoothLink.h index 2a2b98144..9e9d318bb 100644 --- a/src/comm/BluetoothLink.h +++ b/src/comm/BluetoothLink.h @@ -169,7 +169,7 @@ protected: private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public - BluetoothLink(BluetoothConfiguration* config); + BluetoothLink(SharedLinkConfigurationPointer& config); ~BluetoothLink(); // From LinkInterface diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index c4482009d..e014464d3 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -493,7 +493,7 @@ void LinkManager::_updateAutoConnectLinks(void) // Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial // port is connected leaks file handles due to a bug somewhere in android serial code. In order to work around that // bug after we connect the first serial port we stop probing for additional ports. - if (!_autoconnectConfigurations.count()) { + if (!_sharedAutoconnectConfigurations.count()) { portList = QGCSerialPortInfo::availablePorts(); } #else -- GitLab From 9f8e053028dbddfcadfb5ac0ea593a52d25f61fe Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 07:20:04 -0800 Subject: [PATCH 127/398] Plugin resource restructuring --- apmresources.qrc | 40 ------------------- px4resources.qrc | 23 ----------- qgcresources.qrc | 27 ------------- qgroundcontrol.pro | 6 +-- qgroundcontrol.qrc | 10 +++++ src/FirmwarePlugin/APM/APMResources.qrc | 53 +++++++++++++++++++++++++ src/FirmwarePlugin/PX4/PX4Resources.qrc | 27 +++++++++++++ 7 files changed, 92 insertions(+), 94 deletions(-) delete mode 100644 apmresources.qrc delete mode 100644 px4resources.qrc create mode 100644 src/FirmwarePlugin/APM/APMResources.qrc create mode 100644 src/FirmwarePlugin/PX4/PX4Resources.qrc diff --git a/apmresources.qrc b/apmresources.qrc deleted file mode 100644 index 8c0ffc0f6..000000000 --- a/apmresources.qrc +++ /dev/null @@ -1,40 +0,0 @@ - - - src/comm/APMArduCopterMockLink.params - src/comm/APMArduPlaneMockLink.params - src/comm/APMArduSubMockLink.params - - - src/AutoPilotPlugins/APM/APMAirframeComponent.qml - src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml - src/AutoPilotPlugins/APM/APMCameraComponent.qml - src/AutoPilotPlugins/APM/APMCameraComponentSummary.qml - src/AutoPilotPlugins/APM/APMFlightModesComponent.qml - src/AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml - src/AutoPilotPlugins/APM/APMLightsComponent.qml - src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml - src/AutoPilotPlugins/APM/APMNotSupported.qml - src/AutoPilotPlugins/APM/APMPowerComponent.qml - src/AutoPilotPlugins/APM/APMPowerComponentSummary.qml - src/AutoPilotPlugins/APM/APMRadioComponentSummary.qml - src/AutoPilotPlugins/APM/APMSafetyComponentCopter.qml - src/AutoPilotPlugins/APM/APMSafetyComponentPlane.qml - src/AutoPilotPlugins/APM/APMSafetyComponentRover.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryCopter.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryPlane.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummaryRover.qml - src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml - src/AutoPilotPlugins/APM/APMSensorsComponent.qml - src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml - src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml - - - src/FirmwarePlugin/APM/MavCmdInfoCommon.json - src/FirmwarePlugin/APM/MavCmdInfoFixedWing.json - src/FirmwarePlugin/APM/MavCmdInfoMultiRotor.json - src/FirmwarePlugin/APM/MavCmdInfoRover.json - src/FirmwarePlugin/APM/MavCmdInfoSub.json - src/FirmwarePlugin/APM/MavCmdInfoVTOL.json - - diff --git a/px4resources.qrc b/px4resources.qrc deleted file mode 100644 index a25e79221..000000000 --- a/px4resources.qrc +++ /dev/null @@ -1,23 +0,0 @@ - - - src/comm/PX4MockLink.params - - - src/AutoPilotPlugins/PX4/PX4AdvancedFlightModes.qml - src/AutoPilotPlugins/PX4/PX4FlightModes.qml - src/VehicleSetup/PX4FlowSensor.qml - src/AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml - src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentPlane.qml - src/AutoPilotPlugins/PX4/PX4TuningComponentVTOL.qml - - - src/FirmwarePlugin/PX4/MavCmdInfoCommon.json - src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json - src/FirmwarePlugin/PX4/MavCmdInfoMultiRotor.json - src/FirmwarePlugin/PX4/MavCmdInfoRover.json - src/FirmwarePlugin/PX4/MavCmdInfoSub.json - src/FirmwarePlugin/PX4/MavCmdInfoVTOL.json - - diff --git a/qgcresources.qrc b/qgcresources.qrc index c8597ebdb..dcfc66ccf 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -268,33 +268,6 @@ resources/audio/alert.wav - - src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml - - - src/AutoPilotPlugins/APM/APMAirframeFactMetaData.xml - - - src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.3.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.5.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.4.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.0.xml - src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml - src/FirmwarePlugin/APM/CopterGeoFenceEditor.qml - src/FirmwarePlugin/APM/PlaneGeoFenceEditor.qml - src/FirmwarePlugin/APM/Copter3.4.OfflineEditing.params - src/FirmwarePlugin/APM/Plane3.7.OfflineEditing.params - - - src/FirmwarePlugin/GeoFenceEditor.qml - - - src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml - src/FirmwarePlugin/PX4/PX4GeoFenceEditor.qml - src/FirmwarePlugin/PX4/V1.4.OfflineEditing.params - resources/opengl/buglist.json diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index ce9cd426e..dd0d319fe 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -782,7 +782,7 @@ SOURCES += \ # ArduPilot FirmwarePlugin APMFirmwarePlugin { - RESOURCES *= apmresources.qrc + RESOURCES *= src/FirmwarePlugin/APM/APMResources.qrc INCLUDEPATH += \ src/AutoPilotPlugins/APM \ @@ -842,7 +842,6 @@ APMFirmwarePlugin { } APMFirmwarePluginFactory { - RESOURCES *= apmresources.qrc HEADERS += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h SOURCES += src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc } @@ -850,7 +849,7 @@ APMFirmwarePluginFactory { # PX4 FirmwarePlugin PX4FirmwarePlugin { - RESOURCES *= px4resources.qrc + RESOURCES *= src/FirmwarePlugin/PX4/PX4Resources.qrc INCLUDEPATH += \ src/AutoPilotPlugins/PX4 \ @@ -900,7 +899,6 @@ PX4FirmwarePlugin { } PX4FirmwarePluginFactory { - RESOURCES *= px4resources.qrc HEADERS += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h SOURCES += src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc } diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 4801189b0..67666880e 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -38,6 +38,7 @@ src/QtLocationPlugin/QMLControl/OfflineMap.qml src/AutoPilotPlugins/PX4/PowerComponent.qml src/AutoPilotPlugins/PX4/PowerComponentSummary.qml + src/VehicleSetup/PX4FlowSensor.qml src/AnalyzeView/AnalyzePage.qml src/QmlControls/AppMessages.qml src/QmlControls/ClickableColor.qml @@ -159,4 +160,13 @@ src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/Survey.FactMetaData.json + + src/comm/APMArduCopterMockLink.params + src/comm/APMArduPlaneMockLink.params + src/comm/APMArduSubMockLink.params + src/comm/PX4MockLink.params + + + src/FirmwarePlugin/GeoFenceEditor.qml + diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc new file mode 100644 index 000000000..9e0cb0c8b --- /dev/null +++ b/src/FirmwarePlugin/APM/APMResources.qrc @@ -0,0 +1,53 @@ + + + ../../AutoPilotPlugins/APM/APMAirframeComponent.qml + ../../AutoPilotPlugins/APM/APMAirframeComponentSummary.qml + ../../AutoPilotPlugins/APM/APMCameraComponent.qml + ../../AutoPilotPlugins/APM/APMCameraComponentSummary.qml + ../../AutoPilotPlugins/APM/APMFlightModesComponent.qml + ../../AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml + ../../AutoPilotPlugins/APM/APMLightsComponent.qml + ../../AutoPilotPlugins/APM/APMLightsComponentSummary.qml + ../../AutoPilotPlugins/APM/APMNotSupported.qml + ../../AutoPilotPlugins/APM/APMPowerComponent.qml + ../../AutoPilotPlugins/APM/APMPowerComponentSummary.qml + ../../AutoPilotPlugins/APM/APMRadioComponentSummary.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentCopter.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentPlane.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentRover.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentSub.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentSummaryCopter.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentSummaryPlane.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentSummaryRover.qml + ../../AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml + ../../AutoPilotPlugins/APM/APMSensorsComponent.qml + ../../AutoPilotPlugins/APM/APMSensorsComponentSummary.qml + ../../AutoPilotPlugins/APM/APMTuningComponentCopter.qml + APMSensorParams.qml + QGroundControl.ArduPilot.qmldir + + + MavCmdInfoCommon.json + MavCmdInfoFixedWing.json + MavCmdInfoMultiRotor.json + MavCmdInfoRover.json + MavCmdInfoSub.json + MavCmdInfoVTOL.json + + + ../../AutoPilotPlugins/APM/APMAirframeFactMetaData.xml + + + APMParameterFactMetaData.Plane.3.3.xml + APMParameterFactMetaData.Plane.3.5.xml + APMParameterFactMetaData.Plane.3.7.xml + APMParameterFactMetaData.Copter.3.3.xml + APMParameterFactMetaData.Copter.3.4.xml + APMParameterFactMetaData.Rover.3.0.xml + APMParameterFactMetaData.Sub.3.4.xml + CopterGeoFenceEditor.qml + PlaneGeoFenceEditor.qml + Copter3.4.OfflineEditing.params + Plane3.7.OfflineEditing.params + + diff --git a/src/FirmwarePlugin/PX4/PX4Resources.qrc b/src/FirmwarePlugin/PX4/PX4Resources.qrc new file mode 100644 index 000000000..de0775e1c --- /dev/null +++ b/src/FirmwarePlugin/PX4/PX4Resources.qrc @@ -0,0 +1,27 @@ + + + ../../AutoPilotPlugins/PX4/PX4AdvancedFlightModes.qml + ../../AutoPilotPlugins/PX4/PX4FlightModes.qml + ../../AutoPilotPlugins/PX4/PX4RadioComponentSummary.qml + ../../AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml + ../../AutoPilotPlugins/PX4/PX4TuningComponentCopter.qml + ../../AutoPilotPlugins/PX4/PX4TuningComponentPlane.qml + ../../AutoPilotPlugins/PX4/PX4TuningComponentVTOL.qml + + + MavCmdInfoCommon.json + MavCmdInfoFixedWing.json + MavCmdInfoMultiRotor.json + MavCmdInfoRover.json + MavCmdInfoSub.json + MavCmdInfoVTOL.json + + + ../../AutoPilotPlugins/PX4/AirframeFactMetaData.xml + + + PX4ParameterFactMetaData.xml + PX4GeoFenceEditor.qml + V1.4.OfflineEditing.params + + -- GitLab From b317b17cd355c91029d44249366b0642f724a5bc Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 07:20:15 -0800 Subject: [PATCH 128/398] Remove unused signal/slot --- src/Vehicle/Vehicle.cc | 9 --------- src/Vehicle/Vehicle.h | 5 ----- 2 files changed, 14 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 45cc6b6fd..fa5069f15 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -180,7 +180,6 @@ Vehicle::Vehicle(LinkInterface* link, // Now connect the new UAS connect(_mav, SIGNAL(attitudeChanged (UASInterface*,double,double,double,quint64)), this, SLOT(_updateAttitude(UASInterface*, double, double, double, quint64))); connect(_mav, SIGNAL(attitudeChanged (UASInterface*,int,double,double,double,quint64)), this, SLOT(_updateAttitude(UASInterface*,int,double, double, double, quint64))); - connect(_mav, SIGNAL(statusChanged (UASInterface*,QString,QString)), this, SLOT(_updateState(UASInterface*, QString,QString))); _loadSettings(); @@ -1124,14 +1123,6 @@ void Vehicle::_handletextMessageReceived(UASMessage* message) } } -void Vehicle::_updateState(UASInterface*, QString name, QString) -{ - if (_currentState != name) { - _currentState = name; - emit currentStateChanged(); - } -} - void Vehicle::_handleTextMessage(int newCount) { // Reset? diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 96f2f5e74..29e6477a4 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -230,7 +230,6 @@ public: Q_PROPERTY(QmlObjectListModel* trajectoryPoints READ trajectoryPoints CONSTANT) Q_PROPERTY(float latitude READ latitude NOTIFY coordinateChanged) Q_PROPERTY(float longitude READ longitude NOTIFY coordinateChanged) - Q_PROPERTY(QString currentState READ currentState NOTIFY currentStateChanged) Q_PROPERTY(bool messageTypeNone READ messageTypeNone NOTIFY messageTypeChanged) Q_PROPERTY(bool messageTypeNormal READ messageTypeNormal NOTIFY messageTypeChanged) Q_PROPERTY(bool messageTypeWarning READ messageTypeWarning NOTIFY messageTypeChanged) @@ -507,7 +506,6 @@ public: float latitude () { return _coordinate.latitude(); } float longitude () { return _coordinate.longitude(); } bool mavPresent () { return _mav != NULL; } - QString currentState () { return _currentState; } int rcRSSI () { return _rcRSSI; } bool px4Firmware () const { return _firmwareType == MAV_AUTOPILOT_PX4; } bool apmFirmware () const { return _firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA; } @@ -627,7 +625,6 @@ signals: void latestErrorChanged (); void longitudeChanged (); void currentConfigChanged (); - void currentStateChanged (); void flowImageIndexChanged (); void rcRSSIChanged (int rcRSSI); @@ -677,7 +674,6 @@ private slots: void _updateAttitude (UASInterface* uas, double roll, double pitch, double yaw, quint64 timestamp); /** @brief Attitude from one specific component / redundant autopilot */ void _updateAttitude (UASInterface* uas, int component, double roll, double pitch, double yaw, quint64 timestamp); - void _updateState (UASInterface* system, QString name, QString description); /** @brief A new camera image has arrived */ void _imageReady (UASInterface* uas); void _connectionLostTimeout(void); @@ -757,7 +753,6 @@ private: int _currentNormalCount; MessageType_t _currentMessageType; QString _latestError; - QString _currentState; int _updateCount; QString _formatedMessage; int _rcRSSI; -- GitLab From 465999bf2a0eb29368ecebc490e67485cb32bcc9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 07:20:26 -0800 Subject: [PATCH 129/398] Ardupilot onboard compass cal support --- .../APM/APMSensorsComponent.qml | 341 ++++++++--------- .../APM/APMSensorsComponentController.cc | 346 ++++++++++++++---- .../APM/APMSensorsComponentController.h | 65 +++- .../APM/APMSensorsComponentSummary.qml | 61 ++- src/FirmwarePlugin/APM/APMSensorParams.qml | 87 +++++ .../APM/QGroundControl.ArduPilot.qmldir | 3 + src/comm/MockLink.cc | 8 +- 7 files changed, 594 insertions(+), 317 deletions(-) create mode 100644 src/FirmwarePlugin/APM/APMSensorParams.qml create mode 100644 src/FirmwarePlugin/APM/QGroundControl.ArduPilot.qmldir diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index a0dcf688a..a0b553f52 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -20,6 +20,7 @@ import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Controllers 1.0 +import QGroundControl.ArduPilot 1.0 SetupPage { id: sensorsPage @@ -29,8 +30,8 @@ SetupPage { id: sensorsPageComponent RowLayout { - width: 1000//availableWidth - height: 1000//availableHeight + width: availableWidth + height: availableHeight spacing: ScreenTools.defaultFontPixelWidth / 2 // Help text which is shown both in the status text area prior to pressing a cal button and in the @@ -62,49 +63,12 @@ SetupPage { readonly property int rotationColumnWidth: 250 - property Fact compass1Id: controller.getParameterFact(-1, "COMPASS_DEV_ID") - property Fact compass2Id: controller.getParameterFact(-1, "COMPASS_DEV_ID2") - property Fact compass3Id: controller.getParameterFact(-1, "COMPASS_DEV_ID3") - property Fact compass1ExternalFact: controller.getParameterFact(-1, "COMPASS_EXTERNAL") - property Fact compass1Rot: controller.getParameterFact(-1, "COMPASS_ORIENT") - - property Fact boardRot: controller.getParameterFact(-1, "AHRS_ORIENTATION") - - property bool accelCalNeeded: controller.accelSetupNeeded - property bool compassCalNeeded: controller.compassSetupNeeded - - - // The following parameters are not available in olders firmwares + property Fact noFact: Fact { } - property bool compass2ExternalParamAvailable: controller.parameterExists(-1, "COMPASS_EXTERN2") - property bool compass3ExternalParamAvailable: controller.parameterExists(-1, "COMPASS_EXTERN3") - property bool compass2RotParamAvailable: controller.parameterExists(-1, "COMPASS_ORIENT2") - property bool compass3RotParamAvailable: controller.parameterExists(-1, "COMPASS_ORIENT3") - property bool compass1UseParamAvailable: controller.parameterExists(-1, "COMPASS_USE") - property bool compass2UseParamAvailable: controller.parameterExists(-1, "COMPASS_USE2") - property bool compass3UseParamAvailable: controller.parameterExists(-1, "COMPASS_USE3") + property bool accelCalNeeded: controller.accelSetupNeeded + property bool compassCalNeeded: controller.compassSetupNeeded - property Fact noFact: Fact { } - property Fact compass2ExternalFact: compass2ExternalParamAvailable ? controller.getParameterFact(-1, "COMPASS_EXTERN2") : noFact - property Fact compass3ExternalFact: compass3ExternalParamAvailable ? controller.getParameterFact(-1, "COMPASS_EXTERN3") : noFact - property Fact compass2Rot: compass2RotParamAvailable ? controller.getParameterFact(-1, "COMPASS_ORIENT2") : noFact - property Fact compass3Rot: compass3RotParamAvailable ? controller.getParameterFact(-1, "COMPASS_ORIENT3") : noFact - property Fact compass1UseFact: compass1UseParamAvailable ? controller.getParameterFact(-1, "COMPASS_USE") : noFact - property Fact compass2UseFact: compass2UseParamAvailable ? controller.getParameterFact(-1, "COMPASS_USE2") : noFact - property Fact compass3UseFact: compass3UseParamAvailable ? controller.getParameterFact(-1, "COMPASS_USE3") : noFact - - // We track these values by binding through a separate property so we can handle missing params - property bool compass1External: compass1ExternalFact.value - property bool compass2External: compass2ExternalParamAvailable ? compass2ExternalFact.value : false // false: Simulate internal so we don't show rotation combos - property bool compass3External: compass3ExternalParamAvailable ? compass3ExternalFact.value : false // false: Simulate internal so we don't show rotation combos - property bool compass1Use: compass1UseParamAvailable ? compass1UseFact.value : true - property bool compass2Use: compass2UseParamAvailable ? compass2UseFact.value : true - property bool compass3Use: compass3UseParamAvailable ? compass3UseFact.value : true - - // Id > = signals compass available, rot < 0 signals internal compass - property bool showCompass1: compass1Id.value > 0 - property bool showCompass2: compass2Id.value > 0 - property bool showCompass3: compass3Id.value > 0 + property Fact boardRot: controller.getParameterFact(-1, "AHRS_ORIENTATION") readonly property int _calTypeCompass: 1 ///< Calibrate compass readonly property int _calTypeAccel: 2 ///< Calibrate accel @@ -114,13 +78,6 @@ SetupPage { property string _orientationDialogHelp: orientationHelpSet property int _orientationDialogCalType - function validCompassOffsets(compassParamPrefix) { - var ofsX = controller.getParameterFact(-1, compassParamPrefix + "X") - var ofsY = controller.getParameterFact(-1, compassParamPrefix + "Y") - var ofsZ = controller.getParameterFact(-1, compassParamPrefix + "Z") - return Math.sqrt(ofsX.value^2 + ofsY.value^2 + ofsZ.value^2) < 600 - } - function showOrientationsDialog(calType) { var dialogTitle var buttons = StandardButton.Ok @@ -149,6 +106,11 @@ SetupPage { showDialog(orientationsDialogComponent, dialogTitle, qgcView.showDialogDefaultWidth, buttons) } + APMSensorParams { + id: sensorParams + factPanelController: controller + } + APMSensorsComponentController { id: controller factPanel: sensorsPage.viewPanel @@ -163,6 +125,8 @@ SetupPage { setOrientationsButton: setOrientationsButton orientationCalAreaHelpText: orientationCalAreaHelpText + property var rgCompassCalFitness: [ controller.compass1CalFitness, controller.compass2CalFitness, controller.compass3CalFitness ] + onResetStatusTextArea: statusLog.text = statusTextAreaDefaultText onWaitingForCancelChanged: { @@ -174,43 +138,16 @@ SetupPage { } onCalibrationComplete: { - if (_orientationDialogCalType == _calTypeAccel) { - _postCalibrationDialogText = qsTr("Accelerometer calibration complete.") - _postCalibrationDialogParams = [ "INS_ACCSCAL_X", "INS_ACCSCAL_Y", "INS_ACCSCAL_Z", - "INS_ACC2SCAL_X", "INS_ACC2SCAL_Y", "INS_ACC2SCAL_Z", - "INS_ACC3SCAL_X", "INS_ACC3SCAL_Y", "INS_ACC3SCAL_Z", - "INS_GYROFFS_X", "INS_GYROFFS_Y", "INS_GYROFFS_Z", - "INS_GYR2OFFS_X", "INS_GYR2OFFS_Y", "INS_GYR2OFFS_Z", - "INS_GYR3OFFS_X", "INS_GYR3OFFS_Y", "INS_GYR3OFFS_Z" ] - showDialog(postCalibrationDialogComponent, qsTr("Calibration complete"), qgcView.showDialogDefaultWidth, StandardButton.Ok) - } else if (_orientationDialogCalType == _calTypeCompass) { - _postCalibrationDialogText = qsTr("Compass calibration complete. ") - _postCalibrationDialogParams = []; - if (compass1Id.value > 0) { - if (!validCompassOffsets("COMPASS_OFS_")) { - _postCalibrationDialogText += _badCompassCalText.replace("%1", 1) - } - _postCalibrationDialogParams.push("COMPASS_OFS_X") - _postCalibrationDialogParams.push("COMPASS_OFS_Y") - _postCalibrationDialogParams.push("COMPASS_OFS_Z") - } - if (compass2Id.value > 0) { - if (!validCompassOffsets("COMPASS_OFS_")) { - _postCalibrationDialogText += _badCompassCalText.replace("%1", 2) - } - _postCalibrationDialogParams.push("COMPASS_OFS2_X") - _postCalibrationDialogParams.push("COMPASS_OFS2_Y") - _postCalibrationDialogParams.push("COMPASS_OFS2_Z") - } - if (compass3Id.value > 0) { - if (!validCompassOffsets("COMPASS_OFS_")) { - _postCalibrationDialogText += _badCompassCalText.replace("%1", 3) - } - _postCalibrationDialogParams.push("COMPASS_OFS3_X") - _postCalibrationDialogParams.push("COMPASS_OFS3_Y") - _postCalibrationDialogParams.push("COMPASS_OFS3_Z") - } - showDialog(postCalibrationDialogComponent, qsTr("Calibration complete"), qgcView.showDialogDefaultWidth, StandardButton.Ok) + switch (calType) { + case APMSensorsComponentController.CalTypeAccel: + showMessage(qsTr("Calibration complete"), qsTr("Accelerometer calibration complete."), StandardButton.Ok) + break + case APMSensorsComponentController.CalTypeOffboardCompass: + showMessage(qsTr("Calibration complete"), qsTr("Compass calibration complete."), StandardButton.Ok) + break + case APMSensorsComponentController.CalTypeOnboardCompass: + showDialog(postOnboardCompassCalibrationComponent, qsTr("Calibration complete"), qgcView.showDialogDefaultWidth, StandardButton.Ok) + break } } } @@ -218,7 +155,6 @@ SetupPage { Component.onCompleted: { var usingUDP = controller.usingUDPLink() if (usingUDP) { - console.log("onUsingUDPLink") showMessage("Sensor Calibration", "Performing sensor calibration over a WiFi connection is known to be unreliable. You should disconnect and perform calibration using a direct USB connection instead.", StandardButton.Ok) } } @@ -226,43 +162,144 @@ SetupPage { QGCPalette { id: qgcPal; colorGroupEnabled: true } Component { - id: postCalibrationDialogComponent + id: singleCompassOnboardResultsComponent - QGCViewDialog { - QGCLabel { - id: textLabel + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: Math.round(ScreenTools.defaultFontPixelHeight / 2) + visible: sensorParams.rgCompassAvailable[index] + + property real greenMaxThreshold: 8 * (sensorParams.rgCompassExternal[index] ? 1 : 2) + property real yellowMaxThreshold: 15 * (sensorParams.rgCompassExternal[index] ? 1 : 2) + property real fitnessRange: 25 * (sensorParams.rgCompassExternal[index] ? 1 : 2) + + Item { anchors.left: parent.left anchors.right: parent.right - wrapMode: Text.WordWrap - text: _postCalibrationDialogText + height: ScreenTools.defaultFontPixelHeight + + Row { + id: fitnessRow + anchors.fill: parent + + Rectangle { + width: parent.width * (greenMaxThreshold / fitnessRange) + height: parent.height + color: "green" + } + Rectangle { + width: parent.width * ((yellowMaxThreshold - greenMaxThreshold) / fitnessRange) + height: parent.height + color: "yellow" + } + Rectangle { + width: parent.width * ((fitnessRange - yellowMaxThreshold) / fitnessRange) + height: parent.height + color: "red" + } + } + + Rectangle { + height: fitnessRow.height * 0.66 + width: height + anchors.verticalCenter: fitnessRow.verticalCenter + x: (fitnessRow.width * (Math.min(Math.max(controller.rgCompassCalFitness[index], 0.0), fitnessRange) / fitnessRange)) - (width / 2) + radius: height / 2 + color: "white" + border.color: "black" + } } - QGCCheckBox { - id: showValues - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: textLabel.bottom - text: qsTr("Show values") + Column { + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 + anchors.left: parent.left + anchors.right: parent.right + spacing: Math.round(ScreenTools.defaultFontPixelHeight / 4) + + QGCLabel { + text: "Compass " + index + " " + + (sensorParams.rgCompassPrimary[index] ? "(primary" : "(secondary") + + (sensorParams.rgCompassExternalParamAvailable[index] ? + (sensorParams.rgCompassExternal[index] ? ", external" : ", internal" ) : + "") + + ")" + } + + FactCheckBox { + text: "Use Compass" + fact: sensorParams.rgCompassUseFact[index] + visible: sensorParams.rgCompassUseParamAvailable[index] && !sensorParams.rgCompassPrimary[index] + } } + } + } - QGCFlickable { - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: showValues.bottom - anchors.bottom: parent.bottom - contentHeight: valueColumn.height - flickableDirection: Flickable.VerticalFlick - visible: showValues.checked + Component { + id: postOnboardCompassCalibrationComponent - Column { - id: valueColumn + QGCViewDialog { + Column { + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight + + Repeater { + model: 3 + delegate: singleCompassOnboardResultsComponent + } - Repeater { - model: _postCalibrationDialogParams + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("Shown in the indicator bars is the quality of the calibration for each compass.\n\n") + + qsTr("- Green indicates a well functioning compass.\n") + + qsTr("- Yellow indicates a questionable compass or calibration.\n") + + qsTr("- Red indicates a compass which should not be used.\n\n") + + qsTr("YOU MUST REBOOT YOUR VEHICLE AFTER EACH CALIBRATION.") + } + } + } + } - QGCLabel { - text: fact.name +": " + fact.valueString + Component { + id: singleCompassSettingsComponent - property Fact fact: controller.getParameterFact(-1, modelData) - } + Column { + spacing: Math.round(ScreenTools.defaultFontPixelHeight / 2) + visible: sensorParams.rgCompassAvailable[index] + + QGCLabel { + text: "Compass " + index + " " + + (sensorParams.rgCompassPrimary[index] ? "(primary" : "(secondary") + + (sensorParams.rgCompassExternalParamAvailable[index] ? + (sensorParams.rgCompassExternal[index] ? ", external" : ", internal" ) : + "") + + ")" + } + + Column { + anchors.margins: ScreenTools.defaultFontPixelWidth * 2 + anchors.left: parent.left + spacing: Math.round(ScreenTools.defaultFontPixelHeight / 4) + + FactCheckBox { + text: "Use Compass" + fact: sensorParams.rgCompassUseFact[index] + visible: sensorParams.rgCompassUseParamAvailable[index] && !sensorParams.rgCompassPrimary[index] + } + + Column { + visible: sensorParams.rgCompassExternal[index] && sensorParams.rgCompassRotParamAvailable[index] + + QGCLabel { text: qsTr("Orientation:") } + + FactComboBox { + width: rotationColumnWidth + indexModel: false + fact: sensorParams.rgCompassRotFact[index] } } } @@ -304,9 +341,7 @@ SetupPage { } Column { - QGCLabel { - text: qsTr("Autopilot Orientation:") - } + QGCLabel { text: qsTr("Autopilot Orientation:") } FactComboBox { width: rotationColumnWidth @@ -315,73 +350,9 @@ SetupPage { } } - Column { - visible: _orientationsDialogShowCompass && showCompass1 - - FactCheckBox { - text: "Use Compass 1" - fact: compass1UseFact - } - - Column { - visible: showCompass1Rot - - QGCLabel { - text: qsTr("Compass 1 Orientation:") - } - - FactComboBox { - width: rotationColumnWidth - indexModel: false - fact: compass1Rot - } - } - } - - Column { - visible: _orientationsDialogShowCompass && showCompass2 - - FactCheckBox { - text: "Use Compass 2" - fact: compass2UseFact - } - - Column { - visible: showCompass1Rot - - QGCLabel { - text: qsTr("Compass 2 Orientation:") - } - - FactComboBox { - width: rotationColumnWidth - indexModel: false - fact: compass2Rot - } - } - } - - Column { - visible: _orientationsDialogShowCompass && showCompass3 - - FactCheckBox { - text: "Use Compass 3" - fact: compass3UseFact - } - - Column { - visible: showCompass3Rot - - QGCLabel { - text: qsTr("Compass 3 Orientation:") - } - - FactComboBox { - width: rotationColumnWidth - indexModel: false - fact: compass3Rot - } - } + Repeater { + model: _orientationsDialogShowCompass ? 3 : 0 + delegate: singleCompassSettingsComponent } } // Column } // QGCFlickable @@ -474,6 +445,7 @@ SetupPage { } // QGCViewDialog } // Component - levelHorizonDialogComponent + /// Left button column Column { spacing: ScreenTools.defaultFontPixelHeight / 2 Layout.alignment: Qt.AlignLeft | Qt.AlignTop @@ -551,6 +523,7 @@ SetupPage { } } // Column - Buttons + /// Right column - cal area Column { anchors.top: parent.top anchors.bottom: parent.bottom diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index b68c7f90a..6db800532 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -19,47 +19,48 @@ #include QGC_LOGGING_CATEGORY(APMSensorsComponentControllerLog, "APMSensorsComponentControllerLog") - -APMSensorsComponentController::APMSensorsComponentController(void) : - _statusLog(NULL), - _progressBar(NULL), - _compassButton(NULL), - _accelButton(NULL), - _compassMotButton(NULL), - _levelButton(NULL), - _nextButton(NULL), - _cancelButton(NULL), - _setOrientationsButton(NULL), - _showOrientationCalArea(false), - _magCalInProgress(false), - _accelCalInProgress(false), - _compassMotCalInProgress(false), - _levelInProgress(false), - _orientationCalDownSideDone(false), - _orientationCalUpsideDownSideDone(false), - _orientationCalLeftSideDone(false), - _orientationCalRightSideDone(false), - _orientationCalNoseDownSideDone(false), - _orientationCalTailDownSideDone(false), - _orientationCalDownSideVisible(false), - _orientationCalUpsideDownSideVisible(false), - _orientationCalLeftSideVisible(false), - _orientationCalRightSideVisible(false), - _orientationCalNoseDownSideVisible(false), - _orientationCalTailDownSideVisible(false), - _orientationCalDownSideInProgress(false), - _orientationCalUpsideDownSideInProgress(false), - _orientationCalLeftSideInProgress(false), - _orientationCalRightSideInProgress(false), - _orientationCalNoseDownSideInProgress(false), - _orientationCalTailDownSideInProgress(false), - _orientationCalDownSideRotate(false), - _orientationCalUpsideDownSideRotate(false), - _orientationCalLeftSideRotate(false), - _orientationCalRightSideRotate(false), - _orientationCalNoseDownSideRotate(false), - _orientationCalTailDownSideRotate(false), - _waitingForCancel(false) +QGC_LOGGING_CATEGORY(APMSensorsComponentControllerVerboseLog, "APMSensorsComponentControllerVerboseLog") + +const char* APMSensorsComponentController::_compassCalFitnessParam = "COMPASS_CAL_FIT"; + +APMSensorsComponentController::APMSensorsComponentController(void) + : _statusLog(NULL) + , _progressBar(NULL) + , _compassButton(NULL) + , _accelButton(NULL) + , _compassMotButton(NULL) + , _levelButton(NULL) + , _nextButton(NULL) + , _cancelButton(NULL) + , _setOrientationsButton(NULL) + , _showOrientationCalArea(false) + , _calTypeInProgress(CalTypeNone) + , _orientationCalDownSideDone(false) + , _orientationCalUpsideDownSideDone(false) + , _orientationCalLeftSideDone(false) + , _orientationCalRightSideDone(false) + , _orientationCalNoseDownSideDone(false) + , _orientationCalTailDownSideDone(false) + , _orientationCalDownSideVisible(false) + , _orientationCalUpsideDownSideVisible(false) + , _orientationCalLeftSideVisible(false) + , _orientationCalRightSideVisible(false) + , _orientationCalNoseDownSideVisible(false) + , _orientationCalTailDownSideVisible(false) + , _orientationCalDownSideInProgress(false) + , _orientationCalUpsideDownSideInProgress(false) + , _orientationCalLeftSideInProgress(false) + , _orientationCalRightSideInProgress(false) + , _orientationCalNoseDownSideInProgress(false) + , _orientationCalTailDownSideInProgress(false) + , _orientationCalDownSideRotate(false) + , _orientationCalUpsideDownSideRotate(false) + , _orientationCalLeftSideRotate(false) + , _orientationCalRightSideRotate(false) + , _orientationCalNoseDownSideRotate(false) + , _orientationCalTailDownSideRotate(false) + , _waitingForCancel(false) + , _restoreCompassCalFitness(false) { _compassCal.setVehicle(_vehicle); connect(&_compassCal, &APMCompassCal::vehicleTextMessage, this, &APMSensorsComponentController::_handleUASTextMessage); @@ -72,6 +73,11 @@ APMSensorsComponentController::APMSensorsComponentController(void) : connect(qgcApp()->toolbox()->mavlinkProtocol(), &MAVLinkProtocol::messageReceived, this, &APMSensorsComponentController::_mavlinkMessageReceived); } +APMSensorsComponentController::~APMSensorsComponentController() +{ + _restorePreviousCompassCalFitness(); +} + /// Appends the specified text to the status log area in the ui void APMSensorsComponentController::_appendStatusLog(const QString& text) { @@ -96,10 +102,10 @@ void APMSensorsComponentController::_startLogCalibration(void) _compassMotButton->setEnabled(false); _levelButton->setEnabled(false); _setOrientationsButton->setEnabled(false); - if (_accelCalInProgress || _compassMotCalInProgress) { + if (_calTypeInProgress == CalTypeAccel || _calTypeInProgress == CalTypeCompassMot) { _nextButton->setEnabled(true); } - _cancelButton->setEnabled(false); + _cancelButton->setEnabled(_calTypeInProgress == CalTypeOnboardCompass); } void APMSensorsComponentController::_startVisualCalibration(void) @@ -156,11 +162,15 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll _nextButton->setEnabled(false); _cancelButton->setEnabled(false); + if (_calTypeInProgress == CalTypeOnboardCompass) { + _restorePreviousCompassCalFitness(); + } + if (code == StopCalibrationSuccess) { _resetInternalState(); _progressBar->setProperty("value", 1); - if (_vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))) { - _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))->setRawValue(0); + if (parameterExists(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))) { + getParameterFact(FactSystem::defaultComponentId, QStringLiteral("COMPASS_LEARN"))->setRawValue(0); } } else { _progressBar->setProperty("value", 0); @@ -175,11 +185,11 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll case StopCalibrationSuccess: _orientationCalAreaHelpText->setProperty("text", "Calibration complete"); emit resetStatusTextArea(); - emit calibrationComplete(); + emit calibrationComplete(_calTypeInProgress); break; case StopCalibrationSuccessShowLog: - emit calibrationComplete(); + emit calibrationComplete(_calTypeInProgress); break; case StopCalibrationCancelled: @@ -194,21 +204,95 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll break; } - _magCalInProgress = false; - _accelCalInProgress = false; - _compassMotCalInProgress = false; - _levelInProgress = false; + _calTypeInProgress = CalTypeNone; +} + +void APMSensorsComponentController::_mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle) +{ + Q_UNUSED(component); + Q_UNUSED(noReponseFromVehicle); + + if (_vehicle->id() != vehicleId) { + return; + } + + if (command == MAV_CMD_DO_CANCEL_MAG_CAL) { + disconnect(_vehicle, &Vehicle::mavCommandResult, this, &APMSensorsComponentController::_mavCommandResult); + if (result == MAV_RESULT_ACCEPTED) { + // Onboard mag cal is supported + _calTypeInProgress = CalTypeOnboardCompass; + _rgCompassCalProgress[0] = 0; + _rgCompassCalProgress[1] = 0; + _rgCompassCalProgress[2] = 0; + _rgCompassCalComplete[0] = false; + _rgCompassCalComplete[1] = false; + _rgCompassCalComplete[2] = false; + + _startLogCalibration(); + uint8_t compassBits = 0; + if (getParameterFact(FactSystem::defaultComponentId, "COMPASS_DEV_ID")->rawValue().toInt() > 0) { + compassBits |= 1 << 0; + qCDebug(APMSensorsComponentControllerLog) << "Performing onboard compass cal for compass 1"; + } else { + _rgCompassCalComplete[0] = true; + _rgCompassCalSucceeded[0] = true; + _rgCompassCalFitness[0] = 0; + } + if (getParameterFact(FactSystem::defaultComponentId, "COMPASS_DEV_ID2")->rawValue().toInt() > 0) { + compassBits |= 1 << 1; + qCDebug(APMSensorsComponentControllerLog) << "Performing onboard compass cal for compass 2"; + } else { + _rgCompassCalComplete[1] = true; + _rgCompassCalSucceeded[1] = MAG_CAL_SUCCESS; + _rgCompassCalFitness[1] = 0; + } + if (getParameterFact(FactSystem::defaultComponentId, "COMPASS_DEV_ID3")->rawValue().toInt() > 0) { + compassBits |= 1 << 2; + qCDebug(APMSensorsComponentControllerLog) << "Performing onboard compass cal for compass 3"; + } else { + _rgCompassCalComplete[2] = true; + _rgCompassCalSucceeded[2] = MAG_CAL_SUCCESS; + _rgCompassCalFitness[2] = 0; + } + + // We bump up the fitness value so calibration will always succeed + Fact* compassCalFitness = getParameterFact(FactSystem::defaultComponentId, _compassCalFitnessParam); + _restoreCompassCalFitness = true; + _previousCompassCalFitness = compassCalFitness->rawValue().toFloat(); + getParameterFact(FactSystem::defaultComponentId, _compassCalFitnessParam)->setRawValue(100.0); + + _appendStatusLog(tr("Rotate the vehicle randomly around all axes until the progress bar fills all the way to the right .")); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), + MAV_CMD_DO_START_MAG_CAL, + true, // showError + compassBits, // which compass(es) to calibrate + 0, // no retry on failure + 1, // save values after complete + 0, // no delayed start + 0); // no auto-reboot + + } else { + // Onboard mag cal is not supported + _compassCal.startCalibration(); + } + } else if (command == MAV_CMD_DO_START_MAG_CAL && result != MAV_RESULT_ACCEPTED) { + _restorePreviousCompassCalFitness(); + } } void APMSensorsComponentController::calibrateCompass(void) { - _startLogCalibration(); - _compassCal.startCalibration(); + // First we need to determine if the vehicle support onboard compass cal. There isn't an easy way to + // do this. A hack is to send the mag cancel command and see if it is accepted. + connect(_vehicle, &Vehicle::mavCommandResult, this, &APMSensorsComponentController::_mavCommandResult); + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), MAV_CMD_DO_CANCEL_MAG_CAL, false /* showError */); + + // Now we wait for the result to come back } void APMSensorsComponentController::calibrateAccel(void) { - _accelCalInProgress = true; + _calTypeInProgress = CalTypeAccel; _vehicle->setConnectionLostEnabled(false); _startLogCalibration(); _uas->startCalibration(UASInterface::StartCalibrationAccel); @@ -216,7 +300,7 @@ void APMSensorsComponentController::calibrateAccel(void) void APMSensorsComponentController::calibrateMotorInterference(void) { - _compassMotCalInProgress = true; + _calTypeInProgress = CalTypeCompassMot; _vehicle->setConnectionLostEnabled(false); _startLogCalibration(); _appendStatusLog(tr("Raise the throttle slowly to between 50% ~ 75% (the props will spin!) for 5 ~ 10 seconds.")); @@ -227,7 +311,7 @@ void APMSensorsComponentController::calibrateMotorInterference(void) void APMSensorsComponentController::levelHorizon(void) { - _levelInProgress = true; + _calTypeInProgress = CalTypeLevelHorizon; _vehicle->setConnectionLostEnabled(false); _startLogCalibration(); _appendStatusLog(tr("Hold the vehicle in its level flight position.")); @@ -252,8 +336,7 @@ void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, QString percent = text.split("<").last().split(">").first(); bool ok; int p = percent.toInt(&ok); - if (ok) { - Q_ASSERT(_progressBar); + if (ok && _progressBar) { _progressBar->setProperty("value", (float)(p / 100.0)); } return; @@ -317,7 +400,7 @@ void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, _orientationCalAreaHelpText->setProperty("text", "Place your vehicle into one of the Incomplete orientations shown below and hold it still"); if (text == "accel") { - _accelCalInProgress = true; + _calTypeInProgress = CalTypeAccel; _orientationCalDownSideVisible = true; _orientationCalUpsideDownSideVisible = true; _orientationCalLeftSideVisible = true; @@ -325,7 +408,7 @@ void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, _orientationCalTailDownSideVisible = true; _orientationCalNoseDownSideVisible = true; } else if (text == "mag") { - _magCalInProgress = true; + _calTypeInProgress = CalTypeOffboardCompass; _orientationCalDownSideVisible = true; _orientationCalUpsideDownSideVisible = true; _orientationCalLeftSideVisible = true; @@ -349,37 +432,37 @@ void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, if (side == QLatin1Literal("down")) { _orientationCalDownSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalDownSideRotate = true; } } else if (side == QLatin1Literal("up")) { _orientationCalUpsideDownSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalUpsideDownSideRotate = true; } } else if (side == QLatin1Literal("left")) { _orientationCalLeftSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalLeftSideRotate = true; } } else if (side == QLatin1Literal("right")) { _orientationCalRightSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalRightSideRotate = true; } } else if (side == QLatin1Literal("front")) { _orientationCalNoseDownSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalNoseDownSideRotate = true; } } else if (side == QLatin1Literal("back")) { _orientationCalTailDownSideInProgress = true; - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalTailDownSideRotate = true; } } - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { _orientationCalAreaHelpText->setProperty("text", "Rotate the vehicle continuously as shown in the diagram until marked as Completed"); } else { _orientationCalAreaHelpText->setProperty("text", "Hold still in the current orientation"); @@ -472,17 +555,23 @@ void APMSensorsComponentController::_hideAllCalAreas(void) void APMSensorsComponentController::cancelCalibration(void) { - _waitingForCancel = true; - emit waitingForCancelChanged(); _cancelButton->setEnabled(false); - if (_magCalInProgress) { + if (_calTypeInProgress == CalTypeOffboardCompass) { + _waitingForCancel = true; + emit waitingForCancelChanged(); _compassCal.cancelCalibration(); + } else if (_calTypeInProgress == CalTypeOnboardCompass) { + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), MAV_CMD_DO_CANCEL_MAG_CAL, true /* showError */); + _stopCalibration(StopCalibrationCancelled); } else { + _waitingForCancel = true; + emit waitingForCancelChanged(); // The firmware doesn't always allow us to cancel calibration. The best we can do is wait // for it to timeout. _uas->stopCalibration(); } + } void APMSensorsComponentController::nextClicked(void) @@ -500,7 +589,7 @@ void APMSensorsComponentController::nextClicked(void) _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg); - if (_compassMotCalInProgress) { + if (_calTypeInProgress == CalTypeCompassMot) { _stopCalibration(StopCalibrationSuccess); } } @@ -520,15 +609,9 @@ bool APMSensorsComponentController::usingUDPLink(void) return _vehicle->priorityLink()->getLinkConfiguration()->type() == LinkConfiguration::TypeUdp; } -void APMSensorsComponentController::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) +void APMSensorsComponentController::_handleCommandAck(mavlink_message_t& message) { - Q_UNUSED(link); - - if (message.sysid != _vehicle->id()) { - return; - } - - if (message.msgid == MAVLINK_MSG_ID_COMMAND_ACK && _levelInProgress) { + if (_calTypeInProgress == CalTypeLevelHorizon) { mavlink_command_ack_t commandAck; mavlink_msg_command_ack_decode(&message, &commandAck); @@ -546,3 +629,108 @@ void APMSensorsComponentController::_mavlinkMessageReceived(LinkInterface* link, } } } + +void APMSensorsComponentController::_handleMagCalProgress(mavlink_message_t& message) +{ + if (_calTypeInProgress == CalTypeOnboardCompass) { + mavlink_mag_cal_progress_t magCalProgress; + mavlink_msg_mag_cal_progress_decode(&message, &magCalProgress); + + qCDebug(APMSensorsComponentControllerVerboseLog) << "_handleMagCalProgress id:mask:pct" + << magCalProgress.compass_id << magCalProgress.cal_mask << magCalProgress.completion_pct; + + // How many compasses are we calibrating? + int compassCalCount = 0; + for (int i=0; i<3; i++) { + if (magCalProgress.cal_mask & (1 << i)) { + compassCalCount++; + } + } + + if (magCalProgress.compass_id < 3) { + // Each compass gets a portion of the overall progress + _rgCompassCalProgress[magCalProgress.compass_id] = magCalProgress.completion_pct / compassCalCount; + } + + if (_progressBar) { + _progressBar->setProperty("value", (float)(_rgCompassCalProgress[0] + _rgCompassCalProgress[1] + _rgCompassCalProgress[2]) / 100.0); + } + } +} + +void APMSensorsComponentController::_handleMagCalReport(mavlink_message_t& message) +{ + if (_calTypeInProgress == CalTypeOnboardCompass) { + mavlink_mag_cal_report_t magCalReport; + mavlink_msg_mag_cal_report_decode(&message, &magCalReport); + + qCDebug(APMSensorsComponentControllerVerboseLog) << "_handleMagCalReport id:mask:status:fitness" + << magCalReport.compass_id << magCalReport.cal_mask << magCalReport.cal_status << magCalReport.fitness; + + bool additionalCompassCompleted = false; + if (magCalReport.compass_id < 3 && !_rgCompassCalComplete[magCalReport.compass_id]) { + if (magCalReport.cal_status == MAG_CAL_SUCCESS) { + _appendStatusLog(tr("Compass %1 calibration complete").arg(magCalReport.compass_id)); + } else { + _appendStatusLog(tr("Compass %1 calibration below quality threshold").arg(magCalReport.compass_id)); + } + _rgCompassCalComplete[magCalReport.compass_id] = true; + _rgCompassCalSucceeded[magCalReport.compass_id] = magCalReport.cal_status == MAG_CAL_SUCCESS; + _rgCompassCalFitness[magCalReport.compass_id] = magCalReport.fitness; + additionalCompassCompleted = true; + } + + if (_rgCompassCalComplete[0] && _rgCompassCalComplete[1] &&_rgCompassCalComplete[2]) { + for (int i=0; i<3; i++) { + qCDebug(APMSensorsComponentControllerLog) << QString("Onboard compass call report #%1: succeed:fitness %2:%3").arg(i).arg(_rgCompassCalSucceeded[i]).arg(_rgCompassCalFitness[i]); + } + emit compass1CalFitnessChanged(_rgCompassCalFitness[0]); + emit compass2CalFitnessChanged(_rgCompassCalFitness[1]); + emit compass3CalFitnessChanged(_rgCompassCalFitness[2]); + emit compass1CalSucceededChanged(_rgCompassCalSucceeded[0]); + emit compass2CalSucceededChanged(_rgCompassCalSucceeded[1]); + emit compass3CalSucceededChanged(_rgCompassCalSucceeded[2]); + if (_rgCompassCalSucceeded[0] && _rgCompassCalSucceeded[1] && _rgCompassCalSucceeded[2]) { + _appendStatusLog(tr("All compasses calibrated successfully")); + _appendStatusLog(tr("YOU MUST REBOOT YOUR VEHICLE NOW FOR NEW SETTINGS TO TAKE AFFECT")); + _stopCalibration(StopCalibrationSuccessShowLog); + } else { + _appendStatusLog(tr("Compass calibration failed")); + _appendStatusLog(tr("YOU MUST REBOOT YOUR VEHICLE NOW AND RETRY COMPASS CALIBRATION PRIOR TO FLIGHT")); + _stopCalibration(StopCalibrationFailed); + } + } else if (additionalCompassCompleted) { + _appendStatusLog(tr("Continue rotating...")); + } + + } +} + +void APMSensorsComponentController::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) +{ + Q_UNUSED(link); + + if (message.sysid != _vehicle->id()) { + return; + } + + switch (message.msgid) { + case MAVLINK_MSG_ID_COMMAND_ACK: + _handleCommandAck(message); + break; + case MAVLINK_MSG_ID_MAG_CAL_PROGRESS: + _handleMagCalProgress(message); + break; + case MAVLINK_MSG_ID_MAG_CAL_REPORT: + _handleMagCalReport(message); + break; + } +} + +void APMSensorsComponentController::_restorePreviousCompassCalFitness(void) +{ + if (_restoreCompassCalFitness) { + _restoreCompassCalFitness = false; + getParameterFact(FactSystem::defaultComponentId, _compassCalFitnessParam)->setRawValue(_previousCompassCalFitness); + } +} diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h index ca7e7a350..16129f6c9 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h @@ -20,6 +20,7 @@ #include "APMCompassCal.h" Q_DECLARE_LOGGING_CATEGORY(APMSensorsComponentControllerLog) +Q_DECLARE_LOGGING_CATEGORY(APMSensorsComponentControllerVerboseLog) /// Sensors Component MVC Controller for SensorsComponent.qml. class APMSensorsComponentController : public FactPanelController @@ -28,6 +29,7 @@ class APMSensorsComponentController : public FactPanelController public: APMSensorsComponentController(void); + ~APMSensorsComponentController(); Q_PROPERTY(QQuickItem* statusLog MEMBER _statusLog) Q_PROPERTY(QQuickItem* progressBar MEMBER _progressBar) @@ -75,7 +77,15 @@ public: Q_PROPERTY(bool orientationCalTailDownSideRotate MEMBER _orientationCalTailDownSideRotate NOTIFY orientationCalSidesRotateChanged) Q_PROPERTY(bool waitingForCancel MEMBER _waitingForCancel NOTIFY waitingForCancelChanged) - + + Q_PROPERTY(bool compass1CalSucceeded READ compass1CalSucceeded NOTIFY compass1CalSucceededChanged) + Q_PROPERTY(bool compass2CalSucceeded READ compass2CalSucceeded NOTIFY compass2CalSucceededChanged) + Q_PROPERTY(bool compass3CalSucceeded READ compass3CalSucceeded NOTIFY compass3CalSucceededChanged) + + Q_PROPERTY(double compass1CalFitness READ compass1CalFitness NOTIFY compass1CalFitnessChanged) + Q_PROPERTY(double compass2CalFitness READ compass2CalFitness NOTIFY compass2CalFitnessChanged) + Q_PROPERTY(double compass3CalFitness READ compass3CalFitness NOTIFY compass3CalFitnessChanged) + Q_INVOKABLE void calibrateCompass(void); Q_INVOKABLE void calibrateAccel(void); Q_INVOKABLE void calibrateMotorInterference(void); @@ -86,7 +96,25 @@ public: bool compassSetupNeeded(void) const; bool accelSetupNeeded(void) const; - + + typedef enum { + CalTypeAccel, + CalTypeOnboardCompass, + CalTypeOffboardCompass, + CalTypeLevelHorizon, + CalTypeCompassMot, + CalTypeNone + } CalType_t; + Q_ENUM(CalType_t) + + bool compass1CalSucceeded(void) const { return _rgCompassCalSucceeded[0]; } + bool compass2CalSucceeded(void) const { return _rgCompassCalSucceeded[1]; } + bool compass3CalSucceeded(void) const { return _rgCompassCalSucceeded[2]; } + + double compass1CalFitness(void) const { return _rgCompassCalFitness[0]; } + double compass2CalFitness(void) const { return _rgCompassCalFitness[1]; } + double compass3CalFitness(void) const { return _rgCompassCalFitness[2]; } + signals: void showGyroCalAreaChanged(void); void showOrientationCalAreaChanged(void); @@ -97,11 +125,18 @@ signals: void resetStatusTextArea(void); void waitingForCancelChanged(void); void setupNeededChanged(void); - void calibrationComplete(void); - + void calibrationComplete(CalType_t calType); + void compass1CalSucceededChanged(bool compass1CalSucceeded); + void compass2CalSucceededChanged(bool compass2CalSucceeded); + void compass3CalSucceededChanged(bool compass3CalSucceeded); + void compass1CalFitnessChanged(double compass1CalFitness); + void compass2CalFitnessChanged(double compass2CalFitness); + void compass3CalFitnessChanged(double compass3CalFitness); + private slots: void _handleUASTextMessage(int uasId, int compId, int severity, QString text); void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); + void _mavCommandResult(int vehicleId, int component, int command, int result, bool noReponseFromVehicle); private: void _startLogCalibration(void); @@ -110,7 +145,11 @@ private: void _refreshParams(void); void _hideAllCalAreas(void); void _resetInternalState(void); - + void _handleCommandAck(mavlink_message_t& message); + void _handleMagCalProgress(mavlink_message_t& message); + void _handleMagCalReport(mavlink_message_t& message); + void _restorePreviousCompassCalFitness(void); + enum StopCalibrationCode { StopCalibrationSuccess, StopCalibrationSuccessShowLog, @@ -137,11 +176,13 @@ private: bool _showOrientationCalArea; - bool _magCalInProgress; - bool _accelCalInProgress; - bool _compassMotCalInProgress; - bool _levelInProgress; - + CalType_t _calTypeInProgress; + + uint8_t _rgCompassCalProgress[3]; + bool _rgCompassCalComplete[3]; + bool _rgCompassCalSucceeded[3]; + float _rgCompassCalFitness[3]; + bool _orientationCalDownSideDone; bool _orientationCalUpsideDownSideDone; bool _orientationCalLeftSideDone; @@ -171,6 +212,10 @@ private: bool _orientationCalTailDownSideRotate; bool _waitingForCancel; + + bool _restoreCompassCalFitness; + float _previousCompassCalFitness; + static const char* _compassCalFitnessParam; static const int _supportedFirmwareCalVersion = 2; }; diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml b/src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml index 37d0738ff..85bd33730 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentSummary.qml @@ -7,6 +7,7 @@ import QGroundControl.FactControls 1.0 import QGroundControl.Controls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controllers 1.0 +import QGroundControl.ArduPilot 1.0 /* IMPORTANT NOTE: Any changes made here must also be made to SensorsComponentSummary.qml @@ -18,55 +19,35 @@ FactPanel { color: qgcPal.windowShadeDark QGCPalette { id: qgcPal; colorGroupEnabled: enabled } - APMSensorsComponentController { id: controller; factPanel: panel } - - property Fact compass1IdFact: controller.getParameterFact(-1, "COMPASS_DEV_ID") - property Fact compass2IdFact: controller.getParameterFact(-1, "COMPASS_DEV_ID2") - property Fact compass3IdFact: controller.getParameterFact(-1, "COMPASS_DEV_ID3") - - property Fact compass1OfsXFact: controller.getParameterFact(-1, "COMPASS_OFS_X") - property Fact compass1OfsYFact: controller.getParameterFact(-1, "COMPASS_OFS_Y") - property Fact compass1OfsZFact: controller.getParameterFact(-1, "COMPASS_OFS_Z") - property Fact compass2OfsXFact: controller.getParameterFact(-1, "COMPASS_OFS2_X") - property Fact compass2OfsYFact: controller.getParameterFact(-1, "COMPASS_OFS2_Y") - property Fact compass2OfsZFact: controller.getParameterFact(-1, "COMPASS_OFS2_Z") - property Fact compass3OfsXFact: controller.getParameterFact(-1, "COMPASS_OFS3_X") - property Fact compass3OfsYFact: controller.getParameterFact(-1, "COMPASS_OFS3_Y") - property Fact compass3OfsZFact: controller.getParameterFact(-1, "COMPASS_OFS3_Z") - - property bool compass1Available: compass1IdFact.value !== 0 - property bool compass2Available: compass2IdFact.value !== 0 - property bool compass3Available: compass3IdFact.value !== 0 - property bool compass1Calibrated: compass1Available ? compass1OfsXFact.value != 0.0 && compass1OfsYFact.value != 0.0 &&compass1OfsZFact.value != 0.0 : false - property bool compass2Calibrated: compass2Available ? compass2OfsXFact.value != 0.0 && compass2OfsYFact.value != 0.0 &&compass2OfsZFact.value != 0.0 : false - property bool compass3Calibrated: compass3Available ? compass3OfsXFact.value != 0.0 && compass3OfsYFact.value != 0.0 &&compass3OfsZFact.value != 0.0 : false + APMSensorsComponentController { id: controller; factPanel: panel } - property bool compassCalNeeded: controller.compassSetupNeeded + APMSensorParams { + id: sensorParams + factPanelController: controller + } Column { anchors.fill: parent - VehicleSummaryRow { - labelText: qsTr("Compass 1:") - visible: compass1Available - valueText: compass1Calibrated ? qsTr("Ready") : qsTr("Setup required") - } - - VehicleSummaryRow { - labelText: qsTr("Compass 2:") - visible: compass2Available - valueText: compass2Calibrated ? qsTr("Ready") : qsTr("Setup required") - } - - VehicleSummaryRow { - labelText: qsTr("Compass 3:") - visible: compass3Available - valueText: compass3Calibrated ? qsTr("Ready") : qsTr("Setup required") + Repeater { + model: 3 + + VehicleSummaryRow { + labelText: qsTr("Compass ") + (index + 1) + ":" + valueText: sensorParams.rgCompassAvailable[index] ? + (sensorParams.rgCompassCalibrated[index] ? + (sensorParams.rgCompassPrimary[index] ? "Primary" : "Secondary") + + (sensorParams.rgCompassExternalParamAvailable[index] ? + (sensorParams.rgCompassExternal[index] ? ", External" : ", Internal" ) : + "") : + qsTr("Setup required")) : + qsTr("Not installed") + } } VehicleSummaryRow { - labelText: qsTr("Accelerometer:") + labelText: qsTr("Accelerometer(s):") valueText: controller.accelSetupNeeded ? qsTr("Setup required") : qsTr("Ready") } } diff --git a/src/FirmwarePlugin/APM/APMSensorParams.qml b/src/FirmwarePlugin/APM/APMSensorParams.qml new file mode 100644 index 000000000..217ba564d --- /dev/null +++ b/src/FirmwarePlugin/APM/APMSensorParams.qml @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 + +import QGroundControl.FactSystem 1.0 + +/// This is used to handle the various differences between firmware versions and missing parameters in each in a standard way. +Item { + property var factPanelController ///< Must be specified by consumer of control + + property Fact _noFact: Fact { } + + property Fact compassPrimaryFact: factPanelController.getParameterFact(-1, "COMPASS_PRIMARY") + property bool compass1Primary: compassPrimaryFact.rawValue == 0 + property bool compass2Primary: compassPrimaryFact.rawValue == 1 + property bool compass3Primary: compassPrimaryFact.rawValue == 2 + property var rgCompassPrimary: [ compass1Primary, compass2Primary, compass3Primary ] + + property Fact compass1Id: factPanelController.getParameterFact(-1, "COMPASS_DEV_ID") + property Fact compass2Id: factPanelController.getParameterFact(-1, "COMPASS_DEV_ID2") + property Fact compass3Id: factPanelController.getParameterFact(-1, "COMPASS_DEV_ID3") + + property bool compass1Available: compass1Id.value > 0 + property bool compass2Available: compass2Id.value > 0 + property bool compass3Available: compass3Id.value > 0 + property var rgCompassAvailable: [ compass1Available, compass2Available, compass3Available ] + + property bool compass1RotParamAvailable: factPanelController.parameterExists(-1, "COMPASS_ORIENT") + property bool compass2RotParamAvailable: factPanelController.parameterExists(-1, "COMPASS_ORIENT2") + property bool compass3RotParamAvailable: factPanelController.parameterExists(-1, "COMPASS_ORIENT3") + property var rgCompassRotParamAvailable: [ compass1RotParamAvailable, compass2RotParamAvailable, compass3RotParamAvailable ] + + property Fact compass1RotFact: compass2RotParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_ORIENT") : _noFact + property Fact compass2RotFact: compass2RotParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_ORIENT2") : _noFact + property Fact compass3RotFact: compass3RotParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_ORIENT3") : _noFact + property var rgCompassRotFact: [ compass1RotFact, compass2RotFact, compass3RotFact ] + + property bool compass1UseParamAvailable: factPanelController.parameterExists(-1, "COMPASS_USE") + property bool compass2UseParamAvailable: factPanelController.parameterExists(-1, "COMPASS_USE2") + property bool compass3UseParamAvailable: factPanelController.parameterExists(-1, "COMPASS_USE3") + property var rgCompassUseParamAvailable: [ compass1UseParamAvailable, compass2UseParamAvailable, compass3UseParamAvailable ] + + property Fact compass1UseFact: compass1UseParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_USE") : _noFact + property Fact compass2UseFact: compass2UseParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_USE2") : _noFact + property Fact compass3UseFact: compass3UseParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_USE3") : _noFact + property var rgCompassUseFact: [ compass1UseFact, compass2UseFact, compass3UseFact ] + + property bool compass1Use: compass1UseParamAvailable ? compass1UseFact.value : true + property bool compass2Use: compass2UseParamAvailable ? compass2UseFact.value : true + property bool compass3Use: compass3UseParamAvailable ? compass3UseFact.value : true + + property bool compass1ExternalParamAvailable: factPanelController.parameterExists(-1, "COMPASS_EXTERNAL") + property bool compass2ExternalParamAvailable: factPanelController.parameterExists(-1, "COMPASS_EXTERN2") + property bool compass3ExternalParamAvailable: factPanelController.parameterExists(-1, "COMPASS_EXTERN3") + property var rgCompassExternalParamAvailable: [ compass1ExternalParamAvailable, compass2ExternalParamAvailable, compass3ExternalParamAvailable ] + + property Fact compass1ExternalFact: compass1ExternalParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_EXTERNAL") : _noFact + property Fact compass2ExternalFact: compass2ExternalParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_EXTERN2") : _noFact + property Fact compass3ExternalFact: compass3ExternalParamAvailable ? factPanelController.getParameterFact(-1, "COMPASS_EXTERN3") : _noFact + + property bool compass1External: !!compass1ExternalFact.rawValue + property bool compass2External: !!compass2ExternalFact.rawValue + property bool compass3External: !!compass3ExternalFact.rawValue + property var rgCompassExternal: [ compass1External, compass2External, compass3External ] + + property Fact compass1OfsXFact: factPanelController.getParameterFact(-1, "COMPASS_OFS_X") + property Fact compass1OfsYFact: factPanelController.getParameterFact(-1, "COMPASS_OFS_Y") + property Fact compass1OfsZFact: factPanelController.getParameterFact(-1, "COMPASS_OFS_Z") + property Fact compass2OfsXFact: factPanelController.getParameterFact(-1, "COMPASS_OFS2_X") + property Fact compass2OfsYFact: factPanelController.getParameterFact(-1, "COMPASS_OFS2_Y") + property Fact compass2OfsZFact: factPanelController.getParameterFact(-1, "COMPASS_OFS2_Z") + property Fact compass3OfsXFact: factPanelController.getParameterFact(-1, "COMPASS_OFS3_X") + property Fact compass3OfsYFact: factPanelController.getParameterFact(-1, "COMPASS_OFS3_Y") + property Fact compass3OfsZFact: factPanelController.getParameterFact(-1, "COMPASS_OFS3_Z") + + property bool compass1Calibrated: compass1Available ? compass1OfsXFact.value != 0.0 && compass1OfsYFact.value != 0.0 &&compass1OfsZFact.value != 0.0 : false + property bool compass2Calibrated: compass2Available ? compass2OfsXFact.value != 0.0 && compass2OfsYFact.value != 0.0 &&compass2OfsZFact.value != 0.0 : false + property bool compass3Calibrated: compass3Available ? compass3OfsXFact.value != 0.0 && compass3OfsYFact.value != 0.0 &&compass3OfsZFact.value != 0.0 : false + property var rgCompassCalibrated: [ compass1Calibrated, compass2Calibrated, compass3Calibrated ] +} diff --git a/src/FirmwarePlugin/APM/QGroundControl.ArduPilot.qmldir b/src/FirmwarePlugin/APM/QGroundControl.ArduPilot.qmldir new file mode 100644 index 000000000..54a0a5f36 --- /dev/null +++ b/src/FirmwarePlugin/APM/QGroundControl.ArduPilot.qmldir @@ -0,0 +1,3 @@ +Module QGroundControl.ArduPilot + +APMSensorParams 1.0 APMSensorParams.qml diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index 6b946f30f..24def6a8a 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -188,14 +188,14 @@ void MockLink::_loadParams(void) if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) { if (_vehicleType == MAV_TYPE_FIXED_WING) { - paramFile.setFileName(":/unittest/APMArduPlaneMockLink.params"); + paramFile.setFileName(":/MockLink/APMArduPlaneMockLink.params"); } else if (_vehicleType == MAV_TYPE_SUBMARINE ) { - paramFile.setFileName(":/unittest/APMArduSubMockLink.params"); + paramFile.setFileName(":/MockLink/APMArduSubMockLink.params"); } else { - paramFile.setFileName(":/unittest/APMArduCopterMockLink.params"); + paramFile.setFileName(":/MockLink/APMArduCopterMockLink.params"); } } else { - paramFile.setFileName(":/unittest/PX4MockLink.params"); + paramFile.setFileName(":/MockLink/PX4MockLink.params"); } -- GitLab From d3174b1c6dd3ae7ab71f0d1447eedacb4d6928d4 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 07:53:53 -0800 Subject: [PATCH 130/398] Bug fix --- src/AutoPilotPlugins/APM/APMSensorsComponentController.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index 6db800532..7cc06c590 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -243,7 +243,7 @@ void APMSensorsComponentController::_mavCommandResult(int vehicleId, int compone qCDebug(APMSensorsComponentControllerLog) << "Performing onboard compass cal for compass 2"; } else { _rgCompassCalComplete[1] = true; - _rgCompassCalSucceeded[1] = MAG_CAL_SUCCESS; + _rgCompassCalSucceeded[1] = true; _rgCompassCalFitness[1] = 0; } if (getParameterFact(FactSystem::defaultComponentId, "COMPASS_DEV_ID3")->rawValue().toInt() > 0) { @@ -251,7 +251,7 @@ void APMSensorsComponentController::_mavCommandResult(int vehicleId, int compone qCDebug(APMSensorsComponentControllerLog) << "Performing onboard compass cal for compass 3"; } else { _rgCompassCalComplete[2] = true; - _rgCompassCalSucceeded[2] = MAG_CAL_SUCCESS; + _rgCompassCalSucceeded[2] = true; _rgCompassCalFitness[2] = 0; } -- GitLab From e9f0e6fc19e5ddb0d8bf48367ca3abf30ef4b482 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 11:19:08 -0800 Subject: [PATCH 131/398] Be a little less picky about level horizon --- src/AutoPilotPlugins/PX4/SensorsComponent.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/PX4/SensorsComponent.qml b/src/AutoPilotPlugins/PX4/SensorsComponent.qml index e798565c4..7a732d786 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponent.qml +++ b/src/AutoPilotPlugins/PX4/SensorsComponent.qml @@ -97,6 +97,8 @@ SetupPage { property Fact sens_board_rot: controller.getParameterFact(-1, "SENS_BOARD_ROT") property Fact sens_board_x_off: controller.getParameterFact(-1, "SENS_BOARD_X_OFF") + property Fact sens_board_y_off: controller.getParameterFact(-1, "SENS_BOARD_Y_OFF") + property Fact sens_board_z_off: controller.getParameterFact(-1, "SENS_BOARD_Z_OFF") property Fact sens_dpres_off: controller.getParameterFact(-1, "SENS_DPRES_OFF") // Id > = signals compass available, rot < 0 signals internal compass @@ -372,7 +374,7 @@ SetupPage { id: levelButton width: parent.buttonWidth text: qsTr("Level Horizon") - indicatorGreen: sens_board_x_off.value != 0 + indicatorGreen: sens_board_x_off.value != 0 || sens_board_y_off != 0 | sens_board_z_off != 0 enabled: cal_acc0_id.value != 0 && cal_gyro0_id.value != 0 onClicked: { -- GitLab From 35603b4bdf09bb6412b4f992ed0f2f4dde3ed686 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 12:30:29 -0800 Subject: [PATCH 132/398] Correct handling of no GeoFence support --- qgroundcontrol.qrc | 2 +- src/FirmwarePlugin/APM/APMGeoFenceManager.cc | 15 ++++++++++----- .../{GeoFenceEditor.qml => NoGeoFenceEditor.qml} | 0 src/MissionManager/GeoFenceManager.h | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) rename src/FirmwarePlugin/{GeoFenceEditor.qml => NoGeoFenceEditor.qml} (100%) diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 67666880e..514604357 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -167,6 +167,6 @@ src/comm/PX4MockLink.params - src/FirmwarePlugin/GeoFenceEditor.qml + src/FirmwarePlugin/NoGeoFenceEditor.qml diff --git a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc index f65fb476c..f707bc32d 100644 --- a/src/FirmwarePlugin/APM/APMGeoFenceManager.cc +++ b/src/FirmwarePlugin/APM/APMGeoFenceManager.cc @@ -251,8 +251,9 @@ void APMGeoFenceManager::_parametersReady(void) if (!_firstParamLoadComplete) { _firstParamLoadComplete = true; - _fenceSupported = _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, QStringLiteral("FENCE_ACTION")) && - !qgcApp()->runningUnitTests(); + _fenceSupported = _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceTotalParam) && + _vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, _fenceActionParam) && + !qgcApp()->runningUnitTests(); if (_fenceSupported) { QStringList paramNames; @@ -319,7 +320,11 @@ void APMGeoFenceManager::_circleRadiusRawValueChanged(QVariant value) QString APMGeoFenceManager::editorQml(void) const { - return _vehicle->multiRotor() ? - QStringLiteral("qrc:/FirmwarePlugin/APM/CopterGeoFenceEditor.qml") : - QStringLiteral("qrc:/FirmwarePlugin/APM/PlaneGeoFenceEditor.qml"); + if (_fenceSupported) { + return _vehicle->multiRotor() ? + QStringLiteral("qrc:/FirmwarePlugin/APM/CopterGeoFenceEditor.qml") : + QStringLiteral("qrc:/FirmwarePlugin/APM/PlaneGeoFenceEditor.qml"); + } else { + return QStringLiteral("qrc:/FirmwarePlugin/NoGeoFenceEditor.qml"); + } } diff --git a/src/FirmwarePlugin/GeoFenceEditor.qml b/src/FirmwarePlugin/NoGeoFenceEditor.qml similarity index 100% rename from src/FirmwarePlugin/GeoFenceEditor.qml rename to src/FirmwarePlugin/NoGeoFenceEditor.qml diff --git a/src/MissionManager/GeoFenceManager.h b/src/MissionManager/GeoFenceManager.h index c88de86dd..f676e4201 100644 --- a/src/MissionManager/GeoFenceManager.h +++ b/src/MissionManager/GeoFenceManager.h @@ -49,7 +49,7 @@ public: virtual QVariantList params (void) const { return QVariantList(); } virtual QStringList paramLabels (void) const { return QStringList(); } - virtual QString editorQml(void) const { return QStringLiteral("qrc:/FirmwarePlugin/GeoFenceEditor.qml"); } + virtual QString editorQml(void) const { return QStringLiteral("qrc:/FirmwarePlugin/NoGeoFenceEditor.qml"); } /// Error codes returned in error signal typedef enum { -- GitLab From c7314f6dc64c2459eb9590f566b2f7edf8c467bd Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 13:15:07 -0800 Subject: [PATCH 133/398] Correct UAVCAN settings --- src/AutoPilotPlugins/PX4/PowerComponent.qml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.qml b/src/AutoPilotPlugins/PX4/PowerComponent.qml index 1cc89d81c..8ccdc9270 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -398,7 +398,7 @@ SetupPage { QGCCheckBox { id: showUAVCAN text: qsTr("Show UAVCAN Settings") - visible: uavcanEnable !== -1 + checked: uavcanEnable.rawValue != 0 } QGCLabel { @@ -409,24 +409,27 @@ SetupPage { Rectangle { width: parent.width - height: uavCanConfigColumn.height + ScreenTools.defaultFontPixelHeight + height: uavCanConfigRow.height + ScreenTools.defaultFontPixelHeight color: qgcPal.windowShade visible: showUAVCAN.checked - Column { - id: uavCanConfigColumn + Row { + id: uavCanConfigRow anchors.margins: ScreenTools.defaultFontPixelHeight / 2 anchors.left: parent.left anchors.top: parent.top spacing: ScreenTools.defaultFontPixelWidth - FactCheckBox { + FactComboBox { id: uavcanEnabledCheckBox width: ScreenTools.defaultFontPixelWidth * 20 fact: uavcanEnable - checkedValue: 3 - uncheckedValue: 0 - text: qsTr("Enable UAVCAN as the default MAIN output bus (requires autopilot restart)") + indexModel: false + } + + QGCLabel { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Change required restart") } } } -- GitLab From 35c61df64da37380a28091595d55f6461a43a02e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 14:26:31 -0800 Subject: [PATCH 134/398] MobileFileDialog bug fixes --- qgroundcontrol.qrc | 3 +- src/MissionEditor/MissionEditor.qml | 10 +- src/QGCMobileFileDialogController.cc | 13 +- src/QmlControls/ParameterEditor.qml | 7 +- src/QmlControls/QGCMobileFileDialog.qml | 121 ------------------ .../QGroundControl.Controls.qmldir | 3 +- 6 files changed, 21 insertions(+), 136 deletions(-) delete mode 100644 src/QmlControls/QGCMobileFileDialog.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 67666880e..7634f4901 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -67,7 +67,8 @@ src/QmlControls/QGCFlickableVerticalIndicator.qml src/QmlControls/QGCLabel.qml src/QmlControls/QGCListView.qml - src/QmlControls/QGCMobileFileDialog.qml + src/QmlControls/QGCMobileFileOpenDialog.qml + src/QmlControls/QGCMobileFileSaveDialog.qml src/QmlControls/QGCMovableItem.qml src/QmlControls/QGCPipable.qml src/QmlControls/QGCRadioButton.qml diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index c7b8bfd14..4ca41805d 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -207,7 +207,7 @@ QGCView { function loadFromSelectedFile() { if (ScreenTools.isMobile) { - qgcView.showDialog(mobileFilePicker, qsTr("Select Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel) + qgcView.showDialog(mobileFilePicker, qsTr("Select Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) } else { missionController.loadFromFilePicker() fitMapViewportToMissionItems() @@ -366,9 +366,8 @@ QGCView { Component { id: mobileFilePicker - QGCMobileFileDialog { - openDialog: true - fileExtension: _syncDropDownController.fileExtension + QGCMobileFileOpenDialog { + fileExtension: _syncDropDownController.fileExtension onFilenameReturned: { _syncDropDownController.loadFromFile(filename) _syncDropDownController.fitViewportToItems() @@ -379,8 +378,7 @@ QGCView { Component { id: mobileFileSaver - QGCMobileFileDialog { - openDialog: false + QGCMobileFileSaveDialog { fileExtension: _syncDropDownController.fileExtension onFilenameReturned: _syncDropDownController.saveToFile(filename) } diff --git a/src/QGCMobileFileDialogController.cc b/src/QGCMobileFileDialogController.cc index 42e09581b..f7e463015 100644 --- a/src/QGCMobileFileDialogController.cc +++ b/src/QGCMobileFileDialogController.cc @@ -33,6 +33,7 @@ QStringList QGCMobileFileDialogController::getFiles(const QString& fileExtension QString QGCMobileFileDialogController::fullPath(const QString& filename, const QString& fileExtension) { + qDebug() << "QGCMobileFileDialogController::fullPath" << filename << fileExtension; QString saveLocation(_getSaveLocation()); if (saveLocation.isEmpty()) { return filename; @@ -52,6 +53,7 @@ QString QGCMobileFileDialogController::fullPath(const QString& filename, const Q bool QGCMobileFileDialogController::fileExists(const QString& filename, const QString& fileExtension) { QFile file(fullPath(filename, fileExtension)); + qDebug() << "QGCMobileFileDialogController::fileExists" << file.fileName(); return file.exists(); } @@ -59,10 +61,15 @@ QString QGCMobileFileDialogController::_getSaveLocation(void) { QStringList docDirs = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); if (docDirs.count() <= 0) { - qCWarning(QGCMobileFileDialogControllerLog) << "No Documents location"; + qCWarning(QGCMobileFileDialogControllerLog) << "No save location"; return QString(); } - qCDebug(QGCMobileFileDialogControllerLog) << "Save directory" << docDirs.at(0); + + QString saveDirectory = docDirs[0]; + if (!QDir(saveDirectory).exists()) { + QDir().mkdir(saveDirectory); + } + qCDebug(QGCMobileFileDialogControllerLog) << "Save directory" << saveDirectory; - return docDirs.at(0); + return saveDirectory; } diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index 050aa21a5..50c8d837f 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -96,7 +96,7 @@ QGCView { text: qsTr("Load from file...") onTriggered: { if (ScreenTools.isMobile) { - qgcView.showDialog(mobileFilePicker, qsTr("Select Parameter File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel) + qgcView.showDialog(mobileFilePicker, qsTr("Select Parameter File"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) } else { controller.loadFromFilePicker() } @@ -263,7 +263,7 @@ QGCView { Component { id: mobileFilePicker - QGCMobileFileDialog { + QGCMobileFileOpenDialog { fileExtension: QGroundControl.parameterFileExtension onFilenameReturned: controller.loadFromFile(filename) } @@ -272,8 +272,7 @@ QGCView { Component { id: mobileFileSaver - QGCMobileFileDialog { - openDialog: false + QGCMobileFileSaveDialog { fileExtension: QGroundControl.parameterFileExtension onFilenameReturned: controller.saveToFile(filename) } diff --git a/src/QmlControls/QGCMobileFileDialog.qml b/src/QmlControls/QGCMobileFileDialog.qml deleted file mode 100644 index 99b48a41d..000000000 --- a/src/QmlControls/QGCMobileFileDialog.qml +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -import QtQuick 2.5 -import QtQuick.Controls 1.3 -import QtQuick.Dialogs 1.2 - -import QGroundControl 1.0 -import QGroundControl.ScreenTools 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.Controllers 1.0 -import QGroundControl.Palette 1.0 - -/// Simple file picker for mobile -QGCViewDialog { - property bool openDialog: true ///< true: Show file open dialog, false: show file save dialog - property string fileExtension ///< File extension for file listing - - signal filenameReturned(string filename) - - readonly property real _margins: ScreenTools.defaultFontPixelHeight / 2 - - function accept() { - if (!openDialog) { - console.log("filename", dialogLoader.item.filename) - if (!dialogLoader.item.replaceMessageShown) { - if (controller.fileExists(dialogLoader.item.filename, fileExtension)) { - dialogLoader.item.replaceMessageShown = true - return - } - } - filenameReturned(controller.fullPath(dialogLoader.item.filename, fileExtension)) - } - hideDialog() - } - - QGCMobileFileDialogController { id: controller } - QGCPalette { id: qgcPal; colorGroupEnabled: true } - - Loader { - id: dialogLoader - anchors.fill: parent - sourceComponent: openDialog ? openDialogComponent : saveDialogComponent - } - - Component { - id: saveDialogComponent - - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: ScreenTools.defaultFontPixelHeight - - property alias filename: filenameTextField.text - property alias replaceMessageShown: replaceMessage.visible - - QGCLabel { - text: qsTr("File name:") - } - - QGCTextField { - id: filenameTextField - onTextChanged: replaceMessage.visible = false - } - - QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - text: qsTr("File names must end with .%1 file extension. If missing it will be added.").arg(fileExtension) - } - - QGCLabel { - id: replaceMessage - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filename) - visible: false - color: qgcPal.warningText - } - } - } - - Component { - id: openDialogComponent - - Item { - anchors.margins: _margins - anchors.fill: parent - - QGCListView { - anchors.fill: parent - spacing: _margins / 2 - orientation: ListView.Vertical - model: controller.getFiles(fileExtension) - - delegate: QGCButton { - text: modelData - - onClicked: { - hideDialog() - filenameReturned(controller.fullPath(modelData, fileExtension)) - } - } - } - - QGCLabel { - text: qsTr("No files") - visible: controller.getFiles(fileExtension).length == 0 - } - } - } -} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 8392f8abe..1450309a1 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -28,7 +28,8 @@ QGCComboBox 1.0 QGCComboBox.qml QGCFlickable 1.0 QGCFlickable.qml QGCLabel 1.0 QGCLabel.qml QGCListView 1.0 QGCListView.qml -QGCMobileFileDialog 1.0 QGCMobileFileDialog.qml +QGCMobileFileOpenDialog 1.0 QGCMobileFileOpenDialog.qml +QGCMobileFileSaveDialog 1.0 QGCMobileFileSaveDialog.qml QGCMovableItem 1.0 QGCMovableItem.qml QGCPipable 1.0 QGCPipable.qml QGCRadioButton 1.0 QGCRadioButton.qml -- GitLab From a19e0d600708b976a1053a84166539d36afdc83c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 27 Dec 2016 14:37:27 -0800 Subject: [PATCH 135/398] Split QGCMobileFileDialog into two qml files --- src/QmlControls/QGCMobileFileOpenDialog.qml | 56 +++++++++++++++ src/QmlControls/QGCMobileFileSaveDialog.qml | 79 +++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/QmlControls/QGCMobileFileOpenDialog.qml create mode 100644 src/QmlControls/QGCMobileFileSaveDialog.qml diff --git a/src/QmlControls/QGCMobileFileOpenDialog.qml b/src/QmlControls/QGCMobileFileOpenDialog.qml new file mode 100644 index 000000000..657784042 --- /dev/null +++ b/src/QmlControls/QGCMobileFileOpenDialog.qml @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.3 +import QtQuick.Dialogs 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Palette 1.0 + +/// Simple file open dialog for mobile +QGCViewDialog { + property string fileExtension ///< File extension for file listing + + signal filenameReturned(string filename) + + readonly property real _margins: ScreenTools.defaultFontPixelHeight / 2 + + QGCMobileFileDialogController { id: controller } + QGCPalette { id: qgcPal; colorGroupEnabled: true } + + Item { + anchors.margins: _margins + anchors.fill: parent + + QGCListView { + anchors.fill: parent + spacing: _margins / 2 + orientation: ListView.Vertical + model: controller.getFiles(fileExtension) + + delegate: QGCButton { + text: modelData + + onClicked: { + hideDialog() + filenameReturned(controller.fullPath(modelData, fileExtension)) + } + } + } + + QGCLabel { + text: qsTr("No files") + visible: controller.getFiles(fileExtension).length == 0 + } + } +} diff --git a/src/QmlControls/QGCMobileFileSaveDialog.qml b/src/QmlControls/QGCMobileFileSaveDialog.qml new file mode 100644 index 000000000..06abfda2b --- /dev/null +++ b/src/QmlControls/QGCMobileFileSaveDialog.qml @@ -0,0 +1,79 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.3 +import QtQuick.Dialogs 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Palette 1.0 + +/// Simple file picker for mobile +QGCViewDialog { + property string fileExtension ///< File extension for file listing + + signal filenameReturned(string filename) + + readonly property real _margins: ScreenTools.defaultFontPixelHeight / 2 + + function accept() { + if (filenameTextField.text == "") { + return + } + if (!replaceMessage.visible) { + if (controller.fileExists(filenameTextField.text, fileExtension)) { + console.log("File exists") + replaceMessage.visible = true + return + } + } + filenameReturned(controller.fullPath(filenameTextField.text, fileExtension)) + hideDialog() + } + + QGCMobileFileDialogController { id: controller } + QGCPalette { id: qgcPal; colorGroupEnabled: true } + + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight + + QGCLabel { + text: qsTr("File name:") + } + + QGCTextField { + id: filenameTextField + onTextChanged: replaceMessage.visible = false + } + + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("File names must end with .%1 file extension. If missing it will be added.").arg(fileExtension) + } + + QGCLabel { + id: replaceMessage + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("The file %1 exists. Click Save again to replace it.").arg(filenameTextField.text) + visible: false + color: qgcPal.warningText + } + } +} + -- GitLab From 9d4833a3a7e1f03fec09bbeca0c6956cffab4daf Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 28 Dec 2016 09:35:48 -0800 Subject: [PATCH 136/398] New CenterMapDropButton control --- qgroundcontrol.qrc | 1 + src/FlightDisplay/FlightDisplayView.qml | 17 +- src/FlightDisplay/FlightDisplayViewMap.qml | 141 +++++++++++ .../FlightDisplayViewWidgets.qml | 163 ------------ src/FlightDisplay/qmldir | 7 +- src/FlightMap/FlightMap.qml | 8 + src/FlightMap/Widgets/CenterMapDropButton.qml | 235 ++++++++++++++++++ src/FlightMap/qmldir | 1 + src/MissionEditor/MissionEditor.qml | 193 ++------------ src/QmlControls/OfflineMapButton.qml | 82 +++--- .../QMLControl/OfflineMap.qml | 181 +++++++------- 11 files changed, 559 insertions(+), 470 deletions(-) create mode 100644 src/FlightMap/Widgets/CenterMapDropButton.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index e0fc578cf..c74abab6e 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -105,6 +105,7 @@ src/FlightDisplay/FlightDisplayViewVideo.qml src/FlightDisplay/FlightDisplayViewWidgets.qml src/FlightDisplay/qmldir + src/FlightMap/Widgets/CenterMapDropButton.qml src/FlightMap/FlightMap.qml src/FlightMap/Widgets/InstrumentSwipeView.qml src/FlightMap/MapScale.qml diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 85faa00b0..bcc4ee330 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -116,7 +116,6 @@ QGCView { onActiveVehicleJoystickEnabledChanged: px4JoystickCheck() Component.onCompleted: { - widgetsLoader.source = "FlightDisplayViewWidgets.qml" setStates() px4JoystickCheck() } @@ -153,9 +152,10 @@ QGCView { } ] FlightDisplayViewMap { - id: _flightMap - anchors.fill: parent - flightWidgets: widgetsLoader.item + id: _flightMap + anchors.fill: parent + flightWidgets: flightDisplayViewWidgets + rightPanelWidth: ScreenTools.defaultFontPixelHeight * 9 } } @@ -218,16 +218,13 @@ QGCView { } } - //-- Widgets - Loader { - id: widgetsLoader + FlightDisplayViewWidgets { + id: flightDisplayViewWidgets z: _panel.z + 4 height: ScreenTools.availableHeight anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - asynchronous: true - visible: status == Loader.Ready property bool isBackgroundDark: root.isBackgroundDark property var qgcView: root @@ -242,7 +239,7 @@ QGCView { visible: QGroundControl.virtualTabletJoystick anchors.bottom: _flightVideoPipControl.top anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 2 - anchors.horizontalCenter: widgetsLoader.horizontalCenter + anchors.horizontalCenter: flightDisplayViewWidgets.horizontalCenter source: "qrc:/qml/VirtualJoystick.qml" active: QGroundControl.virtualTabletJoystick diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index cce975b99..f02f022de 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -29,6 +29,7 @@ FlightMap { property alias missionController: missionController property var flightWidgets + property var rightPanelWidth property bool _followVehicle: true property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle @@ -36,6 +37,7 @@ FlightMap { property var activeVehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : QtPositioning.coordinate() property var _gotoHereCoordinate: QtPositioning.coordinate() property int _retaskSequence: 0 + property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) Component.onCompleted: { QGroundControl.flightMapPosition = center @@ -52,6 +54,7 @@ FlightMap { } QGCPalette { id: qgcPal; colorGroupEnabled: true } + QGCMapPalette { id: mapPal; lightColors: isSatelliteMap } MissionController { id: missionController @@ -68,6 +71,144 @@ FlightMap { Component.onCompleted: start(false /* editMode */) } + QGCLabel { + id: flyLabel + text: qsTr("Fly") + color: mapPal.text + visible: !ScreenTools.isShortScreen + anchors.topMargin: _toolButtonTopMargin + anchors.horizontalCenter: toolColumn.horizontalCenter + anchors.top: parent.top + } + + //-- Vertical Tool Buttons + + ExclusiveGroup { + id: dropButtonsExclusiveGroup + } + + ExclusiveGroup { + id: mapTypeButtonsExclusiveGroup + } + + //-- Dismiss Drop Down (if any) + MouseArea { + anchors.fill: parent + enabled: dropButtonsExclusiveGroup.current != null + onClicked: { + if (dropButtonsExclusiveGroup.current) { + dropButtonsExclusiveGroup.current.checked = false + } + dropButtonsExclusiveGroup.current = null + } + } + + Column { + id: toolColumn + anchors.topMargin: ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2 + anchors.leftMargin: ScreenTools.defaultFontPixelHeight + anchors.left: parent.left + anchors.top: ScreenTools.isShortScreen ? parent.top : flyLabel.bottom + spacing: ScreenTools.defaultFontPixelHeight + z: QGroundControl.zOrderWidgets + + //-- Map Center Control + CenterMapDropButton { + id: centerMapDropButton + exclusiveGroup: dropButtonsExclusiveGroup + map: _flightMap + mapFitViewport: Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin) + usePlannedHomePosition: false + geoFenceController: geoFenceController + missionController: missionController + rallyPointController: rallyPointController + showFollowVehicle: true + followVehicle: _followVehicle + onFollowVehicleChanged: _followVehicle = followVehicle + + property real leftToolWidth: centerMapDropButton.x + centerMapDropButton.width + } + + //-- Map Type Control + DropButton { + id: mapTypeButton + dropDirection: dropRight + buttonImage: "/qmlimages/MapType.svg" + viewportMargins: ScreenTools.defaultFontPixelWidth / 2 + exclusiveGroup: dropButtonsExclusiveGroup + z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap + + dropDownComponent: Component { + Column { + spacing: ScreenTools.defaultFontPixelWidth + + Row { + spacing: ScreenTools.defaultFontPixelWidth + + Repeater { + model: QGroundControl.flightMapSettings.mapTypes + + QGCButton { + checkable: true + checked: QGroundControl.flightMapSettings.mapType === text + text: modelData + width: clearButton.width + exclusiveGroup: mapTypeButtonsExclusiveGroup + + onClicked: { + QGroundControl.flightMapSettings.mapType = text + checked = true + dropButtonsExclusiveGroup.current = null + } + } + } + } + + QGCButton { + id: clearButton + text: qsTr("Clear Flight Trails") + enabled: QGroundControl.multiVehicleManager.activeVehicle + onClicked: { + QGroundControl.multiVehicleManager.activeVehicle.clearTrajectoryPoints() + dropButtonsExclusiveGroup.current = null + } + } + } + } + } + + //-- Zoom Map In + RoundButton { + id: mapZoomPlus + visible: !ScreenTools.isTinyScreen && _mainIsMap + buttonImage: "/qmlimages/ZoomPlus.svg" + exclusiveGroup: dropButtonsExclusiveGroup + z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap + onClicked: { + if(_flightMap) + _flightMap.zoomLevel += 0.5 + checked = false + } + } + + //-- Zoom Map Out + RoundButton { + id: mapZoomMinus + visible: !ScreenTools.isTinyScreen && _mainIsMap + buttonImage: "/qmlimages/ZoomMinus.svg" + exclusiveGroup: dropButtonsExclusiveGroup + z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap + onClicked: { + if(_flightMap) + _flightMap.zoomLevel -= 0.5 + checked = false + } + } + } + // Add trajectory points to the map MapItemView { model: _mainIsMap ? _activeVehicle ? _activeVehicle.trajectoryPoints : 0 : 0 diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 7492de0d0..cd460922f 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -34,7 +34,6 @@ Item { property bool _useAlternateInstruments: QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen readonly property real _margins: ScreenTools.defaultFontPixelHeight / 2 - readonly property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) QGCMapPalette { id: mapPal; lightColors: isBackgroundDark } QGCPalette { id: qgcPal } @@ -47,14 +46,6 @@ Item { return Math.min(w, 200) } - ExclusiveGroup { - id: _dropButtonsExclusiveGroup - } - - ExclusiveGroup { - id: _mapTypeButtonsExclusiveGroup - } - //-- Map warnings Column { anchors.horizontalCenter: parent.horizontalCenter @@ -80,17 +71,6 @@ Item { } } - //-- Dismiss Drop Down (if any) - MouseArea { - anchors.fill: parent - enabled: _dropButtonsExclusiveGroup.current != null - onClicked: { - if(_dropButtonsExclusiveGroup.current) - _dropButtonsExclusiveGroup.current.checked = false - _dropButtonsExclusiveGroup.current = null - } - } - //-- Instrument Panel QGCInstrumentWidget { id: instrumentGadget @@ -139,149 +119,6 @@ Item { maxHeight: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.y - y : parent.height - anchors.margins - y } - QGCLabel { - id: flyLabel - text: qsTr("Fly") - color: mapPal.text - visible: !ScreenTools.isShortScreen && _mainIsMap - anchors.topMargin: _toolButtonTopMargin - anchors.horizontalCenter: toolColumn.horizontalCenter - anchors.top: parent.top - } - - //-- Vertical Tool Buttons - Column { - id: toolColumn - anchors.topMargin: ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2 - anchors.leftMargin: ScreenTools.defaultFontPixelHeight - anchors.left: parent.left - anchors.top: ScreenTools.isShortScreen ? parent.top : flyLabel.bottom - spacing: ScreenTools.defaultFontPixelHeight - visible: _mainIsMap - - //-- Map Center Control - DropButton { - id: centerMapDropButton - dropDirection: dropRight - buttonImage: "/qmlimages/MapCenter.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: _dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: _lightWidgetBorders - - dropDownComponent: Component { - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCCheckBox { - id: followVehicleCheckBox - text: qsTr("Follow Vehicle") - checked: _flightMap ? _flightMap._followVehicle : false - anchors.verticalCenter: parent.verticalCenter - //anchors.baseline: centerMapButton.baseline - This doesn't work correctly on mobile for some strange reason, so we center instead - - onClicked: { - _dropButtonsExclusiveGroup.current = null - _flightMap._followVehicle = !_flightMap._followVehicle - } - } - - QGCButton { - id: centerMapButton - text: qsTr("Center map on Vehicle") - enabled: _activeVehicle && !followVehicleCheckBox.checked - - property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - - onClicked: { - _dropButtonsExclusiveGroup.current = null - _flightMap.center = activeVehicle.coordinate - } - } - } - } - } - - //-- Map Type Control - DropButton { - id: mapTypeButton - dropDirection: dropRight - buttonImage: "/qmlimages/MapType.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: _dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: _lightWidgetBorders - - dropDownComponent: Component { - Column { - spacing: ScreenTools.defaultFontPixelWidth - - Row { - spacing: ScreenTools.defaultFontPixelWidth - - Repeater { - model: QGroundControl.flightMapSettings.mapTypes - - QGCButton { - checkable: true - checked: QGroundControl.flightMapSettings.mapType === text - text: modelData - width: clearButton.width - exclusiveGroup: _mapTypeButtonsExclusiveGroup - - onClicked: { - QGroundControl.flightMapSettings.mapType = text - checked = true - _dropButtonsExclusiveGroup.current = null - } - } - } - } - - QGCButton { - id: clearButton - text: qsTr("Clear Flight Trails") - enabled: QGroundControl.multiVehicleManager.activeVehicle - onClicked: { - QGroundControl.multiVehicleManager.activeVehicle.clearTrajectoryPoints() - _dropButtonsExclusiveGroup.current = null - } - } - } - } - } - - //-- Zoom Map In - RoundButton { - id: mapZoomPlus - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomPlus.svg" - exclusiveGroup: _dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: _lightWidgetBorders - onClicked: { - if(_flightMap) - _flightMap.zoomLevel += 0.5 - checked = false - } - } - - //-- Zoom Map Out - RoundButton { - id: mapZoomMinus - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomMinus.svg" - exclusiveGroup: _dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: _lightWidgetBorders - onClicked: { - if(_flightMap) - _flightMap.zoomLevel -= 0.5 - checked = false - } - } - } - //-- Guided mode buttons Rectangle { id: _guidedModeBar diff --git a/src/FlightDisplay/qmldir b/src/FlightDisplay/qmldir index b1bf782af..ccd5c34b8 100644 --- a/src/FlightDisplay/qmldir +++ b/src/FlightDisplay/qmldir @@ -1,6 +1,7 @@ Module QGroundControl.FlightDisplay -FlightDisplayView 1.0 FlightDisplayView.qml -FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml -FlightDisplayViewVideo 1.0 FlightDisplayViewVideo.qml +FlightDisplayView 1.0 FlightDisplayView.qml +FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml +FlightDisplayViewVideo 1.0 FlightDisplayViewVideo.qml +FlightDisplayViewWidgets 1.0 FlightDisplayViewWidgets.qml diff --git a/src/FlightMap/FlightMap.qml b/src/FlightMap/FlightMap.qml index 96018f1ad..8d9830e43 100644 --- a/src/FlightMap/FlightMap.qml +++ b/src/FlightMap/FlightMap.qml @@ -82,6 +82,14 @@ Map { scaleText.text = text } + function setVisibleRegion(region) { + // This works around a bug on Qt where if you set a visibleRegion and then the user moves or zooms the map + // and then you set the same visibleRegion the map will not move/scale appropriately since it thinks there + // is nothing to do. + _map.visibleRegion = QtPositioning.rectangle(QtPositioning.coordinate(0, 0), QtPositioning.coordinate(0, 0)) + _map.visibleRegion = region + } + zoomLevel: 18 center: QGroundControl.lastKnownHomePosition gesture.flickDeceleration: 3000 diff --git a/src/FlightMap/Widgets/CenterMapDropButton.qml b/src/FlightMap/Widgets/CenterMapDropButton.qml new file mode 100644 index 000000000..9fb58839e --- /dev/null +++ b/src/FlightMap/Widgets/CenterMapDropButton.qml @@ -0,0 +1,235 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.2 +import QtPositioning 5.3 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 + +DropButton { + id: dropButton + dropDirection: dropRight + buttonImage: "/qmlimages/MapCenter.svg" + viewportMargins: ScreenTools.defaultFontPixelWidth / 2 + lightBorders: map.isSatelliteMap + + property var map + property rect mapFitViewport + property bool usePlannedHomePosition ///< true: planned home position used for calculations, false: vehicle home position use for calculations + property var geoFenceController + property var missionController + property var rallyPointController + property bool showMission: true + property bool showAllItems: true + property bool showFollowVehicle: false + property bool followVehicle: false + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + function fitHomePosition() { + var homePosition = QtPositioning.coordinate() + var activeVehicle = QGroundControl.multiVehicleManager.activeVehicle + if (usePlannedHomePosition) { + homePosition = missionController.visualItems.get(0).coordinate + } else if (activeVehicle) { + homePosition = activeVehicle.homePosition + } + return homePosition + } + + /// Normalize latitude to range: 0 to 180, S to N + function normalizeLat(lat) { + return lat + 90.0 + } + + /// Normalize longitude to range: 0 to 360, W to E + function normalizeLon(lon) { + return lon + 180.0 + } + + /// Fits the visible region of the map to inclues all of the specified coordinates. If no coordinates + /// are specified the map will center to fitHomePosition() + function fitMapViewportToAllCoordinates(coordList) { + if (coordList.length == 0) { + map.center = fitHomePosition() + return + } + + // Create the normalized lat/lon corners for the coordinate bounding rect from the list of coordinates + var north = normalizeLat(coordList[0].latitude) + var south = north + var east = normalizeLon(coordList[0].longitude) + var west = east + for (var i=1; i 2) { + for (var i=0; i 2) { - for (var i=0; i + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.2 import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 -Rectangle -{ - id: __mapButton - - property var __qgcPal: QGCPalette { colorGroupEnabled: enabled } - property bool __showHighlight: (__pressed | __hovered | checked) && !__forceHoverOff - - property bool __forceHoverOff: false - property int __lastGlobalMouseX: 0 - property int __lastGlobalMouseY: 0 - property bool __pressed: false - property bool __hovered: false +Rectangle { + id: mapButton + anchors.margins: ScreenTools.defaultFontPixelWidth + color: _showHighlight ? qgcPal.buttonHighlight : qgcPal.button + border.width: _showBorder ? 1: 0 + border.color: qgcPal.buttonText property bool checked: false property bool complete: false @@ -23,25 +26,34 @@ Rectangle property int tiles: 0 property string size: "" + property bool _showHighlight: (_pressed | _hovered | checked) && !_forceHoverOff + property bool _showBorder: qgcPal.globalTheme === QGCPalette.Light + + property bool _forceHoverOff: false + property int _lastGlobalMouseX: 0 + property int _lastGlobalMouseY: 0 + property bool _pressed: false + property bool _hovered: false + signal clicked() - color: __showHighlight ? __qgcPal.buttonHighlight : __qgcPal.button - anchors.margins: ScreenTools.defaultFontPixelWidth + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + Row { anchors.centerIn: parent QGCLabel { id: nameLabel - width: __mapButton.width * 0.4 - color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText + width: mapButton.width * 0.4 + color: _showHighlight ? qgcPal.buttonHighlightText : qgcPal.buttonText anchors.verticalCenter: parent.verticalCenter } QGCLabel { id: sizeLabel - width: __mapButton.width * 0.4 + width: mapButton.width * 0.4 horizontalAlignment: Text.AlignRight anchors.verticalCenter: parent.verticalCenter - color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText - text: __mapButton.size + (tiles > 0 ? " (" + tiles + " tiles)" : "") + color: _showHighlight ? qgcPal.buttonHighlightText : qgcPal.buttonText + text: mapButton.size + (tiles > 0 ? " (" + tiles + " tiles)" : "") } Item { width: ScreenTools.defaultFontPixelWidth * 2 @@ -66,7 +78,7 @@ Rectangle source: "/res/buttonRight.svg" mipmap: true fillMode: Image.PreserveAspectFit - color: __showHighlight ? __qgcPal.buttonHighlightText : __qgcPal.buttonText + color: _showHighlight ? qgcPal.buttonHighlightText : qgcPal.buttonText anchors.verticalCenter: parent.verticalCenter } } @@ -75,20 +87,18 @@ Rectangle anchors.fill: parent hoverEnabled: true onMouseXChanged: { - __lastGlobalMouseX = ScreenTools.mouseX() - __lastGlobalMouseY = ScreenTools.mouseY() + _lastGlobalMouseX = ScreenTools.mouseX() + _lastGlobalMouseY = ScreenTools.mouseY() } onMouseYChanged: { - __lastGlobalMouseX = ScreenTools.mouseX() - __lastGlobalMouseY = ScreenTools.mouseY() - } - onEntered: { __hovered = true; __forceHoverOff = false; hoverTimer.start() } - onExited: { __hovered = false; __forceHoverOff = false; hoverTimer.stop() } - onPressed: { __pressed = true; } - onReleased: { __pressed = false; } - onClicked: { - __mapButton.clicked() + _lastGlobalMouseX = ScreenTools.mouseX() + _lastGlobalMouseY = ScreenTools.mouseY() } + onEntered: { _hovered = true; _forceHoverOff = false; hoverTimer.start() } + onExited: { _hovered = false; _forceHoverOff = false; hoverTimer.stop() } + onPressed: { _pressed = true; } + onReleased: { _pressed = false; } + onClicked: mapButton.clicked() } Timer { @@ -96,10 +106,10 @@ Rectangle interval: 250 repeat: true onTriggered: { - if (__lastGlobalMouseX !== ScreenTools.mouseX() || __lastGlobalMouseY !== ScreenTools.mouseY()) { - __forceHoverOff = true + if (_lastGlobalMouseX !== ScreenTools.mouseX() || _lastGlobalMouseY !== ScreenTools.mouseY()) { + _forceHoverOff = true } else { - __forceHoverOff = false + _forceHoverOff = false } } } diff --git a/src/QtLocationPlugin/QMLControl/OfflineMap.qml b/src/QtLocationPlugin/QMLControl/OfflineMap.qml index a80510eba..450a51058 100644 --- a/src/QtLocationPlugin/QMLControl/OfflineMap.qml +++ b/src/QtLocationPlugin/QMLControl/OfflineMap.qml @@ -7,7 +7,6 @@ * ****************************************************************************/ - import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 @@ -38,6 +37,7 @@ QGCView { property string savedMapType: "" property bool _showPreview: true property bool _defaultSet: offlineMapView && offlineMapView._currentSelection && offlineMapView._currentSelection.defaultSet + property real _margins: ScreenTools.defaultFontPixelWidth /2 property bool _saveRealEstate: ScreenTools.isTinyScreen || ScreenTools.isShortScreen property real _adjustableFontPointSize: _saveRealEstate ? ScreenTools.smallFontPointSize : ScreenTools.defaultFontPointSize @@ -327,6 +327,17 @@ QGCView { anchors.fill: parent } + CenterMapDropButton { + anchors.margins: _margins + anchors.left: parent.left + anchors.top: parent.top + map: _map + z: QGroundControl.zOrderTopMost + showMission: false + showAllItems: false + visible: addNewSetView.visible + } + MapScale { anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 anchors.bottomMargin: anchors.leftMargin @@ -475,97 +486,91 @@ QGCView { anchors.fill: parent visible: false - Map { - id: minZoomPreview - anchors.leftMargin: ScreenTools.defaultFontPixelWidth /2 - anchors.topMargin: anchors.leftMargin - anchors.top: parent.top - anchors.left: parent.left - width: parent.width / 4 - height: parent.height / 4 - center: _map.center - activeMapType: _map.activeMapType - zoomLevel: sliderMinZoom.value - gesture.enabled: false - visible: _showPreview - - property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1 - - plugin: Plugin { name: "QGroundControl" } - - MapScale { - anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 - anchors.bottomMargin: anchors.leftMargin - anchors.left: parent.left - anchors.bottom: parent.bottom - mapControl: parent + Column { + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: _margins + anchors.left: parent.left + spacing: _margins + + QGCButton { + text: "Show zoom previews" + visible: !_showPreview + onClicked: _showPreview = !_showPreview } - } - Map { - id: maxZoomPreview - anchors.topMargin: minZoomPreview.anchors.topMargin - anchors.left: minZoomPreview.left - anchors.top: minZoomPreview.bottom - width: minZoomPreview.width - height: minZoomPreview.height - center: _map.center - activeMapType: _map.activeMapType - zoomLevel: sliderMaxZoom.value - gesture.enabled: false - visible: _showPreview - - property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1 - - plugin: Plugin { name: "QGroundControl" } - - MapScale { - anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 - anchors.bottomMargin: anchors.leftMargin - anchors.left: parent.left - anchors.bottom: parent.bottom - mapControl: parent - } - } + Map { + id: minZoomPreview + width: addNewSetView.width / 4 + height: addNewSetView.height / 4 + center: _map.center + activeMapType: _map.activeMapType + zoomLevel: sliderMinZoom.value + gesture.enabled: false + visible: _showPreview + + plugin: Plugin { name: "QGroundControl" } + + MapScale { + anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 + anchors.bottomMargin: anchors.leftMargin + anchors.left: parent.left + anchors.bottom: parent.bottom + mapControl: parent + } - Rectangle { - anchors.fill: minZoomPreview - border.color: _mapAdjustedColor - color: "transparent" - visible: _showPreview - QGCLabel { - anchors.centerIn: parent - color: _mapAdjustedColor - text: qsTr("Min Zoom: %1").arg(sliderMinZoom.value) - } - MouseArea { - anchors.fill: parent - onClicked: _showPreview = false - } - } + Rectangle { + anchors.fill: parent + border.color: _mapAdjustedColor + color: "transparent" - Rectangle { - anchors.fill: maxZoomPreview - border.color: _mapAdjustedColor - color: "transparent" - visible: _showPreview - QGCLabel { - anchors.centerIn: parent - color: _mapAdjustedColor - text: qsTr("Max Zoom: %1").arg(sliderMaxZoom.value) - } - MouseArea { - anchors.fill: parent - onClicked: _showPreview = false - } - } + QGCLabel { + anchors.centerIn: parent + color: _mapAdjustedColor + text: qsTr("Min Zoom: %1").arg(sliderMinZoom.value) + } + MouseArea { + anchors.fill: parent + onClicked: _showPreview = false + } + } + } // Map + + Map { + id: maxZoomPreview + width: minZoomPreview.width + height: minZoomPreview.height + center: _map.center + activeMapType: _map.activeMapType + zoomLevel: sliderMaxZoom.value + gesture.enabled: false + visible: _showPreview + + plugin: Plugin { name: "QGroundControl" } + + MapScale { + anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 + anchors.bottomMargin: anchors.leftMargin + anchors.left: parent.left + anchors.bottom: parent.bottom + mapControl: parent + } - QGCButton { - anchors.left: minZoomPreview.left - anchors.top: minZoomPreview.top - text: "Show zoom previews" - visible: !_showPreview - onClicked: _showPreview = !_showPreview + Rectangle { + anchors.fill: parent + border.color: _mapAdjustedColor + color: "transparent" + + QGCLabel { + anchors.centerIn: parent + color: _mapAdjustedColor + text: qsTr("Max Zoom: %1").arg(sliderMaxZoom.value) + } + MouseArea { + anchors.fill: parent + onClicked: _showPreview = false + } + } + } // Map } //-- Add new set dialog @@ -574,7 +579,7 @@ QGCView { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right width: ScreenTools.defaultFontPixelWidth * 24 - height: Math.min(parent.height - (anchors.margins * 2), addNewSetFlickable.y + addNewSetColumn.height + ScreenTools.defaultFontPixelHeight) + height: Math.min(parent.height - (anchors.margins * 2), addNewSetFlickable.y + addNewSetColumn.height + addNewSetLabel.anchors.margins) color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.85) radius: ScreenTools.defaultFontPixelWidth * 0.5 -- GitLab From b7a7384a9c2fef9d9ba48d81df55d8d322a9caaa Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 28 Dec 2016 19:31:28 -0800 Subject: [PATCH 137/398] Combo uses full width --- src/QmlControls/ParameterEditorDialog.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/QmlControls/ParameterEditorDialog.qml b/src/QmlControls/ParameterEditorDialog.qml index d43190b06..7270ede46 100644 --- a/src/QmlControls/ParameterEditorDialog.qml +++ b/src/QmlControls/ParameterEditorDialog.qml @@ -126,7 +126,8 @@ QGCViewDialog { QGCComboBox { id: factCombo - width: valueField.width + anchors.left: parent.left + anchors.right: parent.right visible: _showCombo model: fact.enumStrings -- GitLab From 4211b7e971cd2b0e0295c18dc2751e66b0222046 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 28 Dec 2016 19:31:44 -0800 Subject: [PATCH 138/398] Link reference bug fixes --- src/comm/LinkManager.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index e014464d3..e2fad94e2 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -642,16 +642,15 @@ void LinkManager::_updateAutoConnectLinks(void) // Now remove all configs that are gone foreach (LinkConfiguration* pDeleteConfig, _confToDelete) { qCDebug(LinkManagerLog) << "Removing unused autoconnect config" << pDeleteConfig->name(); + if (pDeleteConfig->link()) { + disconnectLink(pDeleteConfig->link()); + } for (int i=0; i<_sharedAutoconnectConfigurations.count(); i++) { if (_sharedAutoconnectConfigurations[i].data() == pDeleteConfig) { _sharedAutoconnectConfigurations.removeAt(i); break; } } - if (pDeleteConfig->link()) { - disconnectLink(pDeleteConfig->link()); - } - delete pDeleteConfig; } #endif #endif // NO_SERIAL_LINK -- GitLab From 3ccf2e54f4a11b223ea722c10f65bc1a7e203935 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 28 Dec 2016 20:13:39 -0800 Subject: [PATCH 139/398] Update MAV 2 switching semantics --- src/Vehicle/Vehicle.cc | 8 ++------ src/comm/LinkInterface.cc | 1 + src/comm/LinkInterface.h | 6 +++++- src/comm/LinkManager.cc | 2 +- src/comm/MAVLinkProtocol.cc | 26 ++++---------------------- 5 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index fa5069f15..51f59ee92 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -563,15 +563,11 @@ void Vehicle::_handleAltitude(mavlink_message_t& message) void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& message) { + Q_UNUSED(link); + mavlink_autopilot_version_t autopilotVersion; mavlink_msg_autopilot_version_decode(&message, &autopilotVersion); - bool isMavlink2 = (autopilotVersion.capabilities & MAV_PROTOCOL_CAPABILITY_MAVLINK2) != 0; - if(isMavlink2) { - mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(link->mavlinkChannel()); - mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; - } - if (autopilotVersion.flight_sw_version != 0) { int majorVersion, minorVersion, patchVersion; FIRMWARE_VERSION_TYPE versionType; diff --git a/src/comm/LinkInterface.cc b/src/comm/LinkInterface.cc index a65d3fb31..9361fb2b7 100644 --- a/src/comm/LinkInterface.cc +++ b/src/comm/LinkInterface.cc @@ -26,6 +26,7 @@ LinkInterface::LinkInterface(SharedLinkConfigurationPointer& config) , _mavlinkChannelSet(false) , _active(false) , _enableRateCollection(false) + , _decodedFirstMavlinkPacket(false) { _config->setLink(this); diff --git a/src/comm/LinkInterface.h b/src/comm/LinkInterface.h index 63ce68257..477b8a9e4 100644 --- a/src/comm/LinkInterface.h +++ b/src/comm/LinkInterface.h @@ -116,6 +116,9 @@ public: /// set into the link when it is added to LinkManager uint8_t mavlinkChannel(void) const; + bool decodedFirstMavlinkPacket(void) const { return _decodedFirstMavlinkPacket; } + bool setDecodedFirstMavlinkPacket(bool decodedFirstMavlinkPacket) { return _decodedFirstMavlinkPacket = decodedFirstMavlinkPacket; } + // These are left unimplemented in order to cause linker errors which indicate incorrect usage of // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. bool connect(void); @@ -263,8 +266,9 @@ private: mutable QMutex _dataRateMutex; // Mutex for accessing the data rate member variables - bool _active; ///< true: link is actively receiving mavlink messages + bool _active; ///< true: link is actively receiving mavlink messages bool _enableRateCollection; + bool _decodedFirstMavlinkPacket; ///< true: link has correctly decoded it's first mavlink packet }; typedef QSharedPointer SharedLinkInterfacePointer; diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index e014464d3..da028d086 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -191,7 +191,7 @@ void LinkManager::_addLink(LinkInterface* link) // Start the channel on Mav 1 protocol mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(i); mavlinkStatus->flags = mavlink_get_channel_status(i)->flags | MAVLINK_STATUS_FLAG_OUT_MAVLINK1; - qDebug() << "LinkManager mavlinkStatus" << mavlinkStatus << i << mavlinkStatus->flags; + qDebug() << "LinkManager mavlinkStatus:channel:flags" << mavlinkStatus << i << mavlinkStatus->flags; _mavlinkChannelsUsedBitMask |= 1 << i; channelSet = true; break; diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 4fee75482..17f1e853c 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -169,29 +169,14 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) int mavlinkChannel = link->mavlinkChannel(); - static int mavlink09Count = 0; static int nonmavlinkCount = 0; - static bool decodedFirstPacket = false; - static bool warnedUser = false; static bool checkedUserNonMavlink = false; static bool warnedUserNonMavlink = false; for (int position = 0; position < b.size(); position++) { unsigned int decodeState = mavlink_parse_char(mavlinkChannel, (uint8_t)(b[position]), &message, &status); - if ((uint8_t)b[position] == 0x55) mavlink09Count++; - if ((mavlink09Count > 100) && !decodedFirstPacket && !warnedUser) - { - warnedUser = true; - // Obviously the user tries to use a 0.9 autopilot - // with QGroundControl built for version 1.0 - emit protocolStatusMessage(tr("MAVLink Protocol"), tr("There is a MAVLink Version or Baud Rate Mismatch. " - "Your MAVLink device seems to use the deprecated version 0.9, while QGroundControl only supports version 1.0+. " - "Please upgrade the MAVLink version of your autopilot. " - "If your autopilot is using version 1.0, check if the baud rates of QGroundControl and your autopilot are the same.")); - } - - if (decodeState == 0 && !decodedFirstPacket) + if (decodeState == 0 && !link->decodedFirstMavlinkPacket()) { nonmavlinkCount++; if (nonmavlinkCount > 2000 && !warnedUserNonMavlink) @@ -212,16 +197,13 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) } if (decodeState == 1) { - if(!decodedFirstPacket) { + if (!link->decodedFirstMavlinkPacket()) { mavlink_status_t* mavlinkStatus = mavlink_get_channel_status(mavlinkChannel); if (!(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && (mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { - qDebug() << "switch to mavlink 2.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; + qDebug() << "Switching outbound to mavlink 2.0 due to incoming mavlink 2.0 packet:" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; mavlinkStatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1; - } else if ((mavlinkStatus->flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) && !(mavlinkStatus->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) { - qDebug() << "switch to mavlink 1.0" << mavlinkStatus << mavlinkChannel << mavlinkStatus->flags; - mavlinkStatus->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1; } - decodedFirstPacket = true; + link->setDecodedFirstMavlinkPacket(true); } if(message.msgid == MAVLINK_MSG_ID_RADIO_STATUS) -- GitLab From d53bb78e268749af67658e1480c0dfb8c370a42e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 29 Dec 2016 09:26:38 -0800 Subject: [PATCH 140/398] Fix DropButton parenting --- src/FlightDisplay/FlightDisplayViewMap.qml | 179 ++++++++-------- src/MissionEditor/MissionEditor.qml | 234 +++++++++++---------- src/QmlControls/DropButton.qml | 15 +- 3 files changed, 221 insertions(+), 207 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index f02f022de..3ccdeb548 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -77,7 +77,7 @@ FlightMap { color: mapPal.text visible: !ScreenTools.isShortScreen anchors.topMargin: _toolButtonTopMargin - anchors.horizontalCenter: toolColumn.horizontalCenter + anchors.horizontalCenter: centerMapDropButton.horizontalCenter anchors.top: parent.top } @@ -103,109 +103,116 @@ FlightMap { } } - Column { - id: toolColumn - anchors.topMargin: ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2 - anchors.leftMargin: ScreenTools.defaultFontPixelHeight - anchors.left: parent.left - anchors.top: ScreenTools.isShortScreen ? parent.top : flyLabel.bottom - spacing: ScreenTools.defaultFontPixelHeight + // IMPORTANT NOTE: Drop Buttons must be parented directly to the map. If they are placed in a Column for example the drop control positioning + // will not work correctly. + + //-- Map Center Control + CenterMapDropButton { + id: centerMapDropButton + anchors.topMargin: flyLabel.visible ? ScreenTools.defaultFontPixelHeight / 2 : _toolButtonTopMargin + anchors.leftMargin: ScreenTools.defaultFontPixelHeight + anchors.left: parent.left + anchors.top: flyLabel.visible ? flyLabel.bottom : parent.top + z: QGroundControl.zOrderWidgets + exclusiveGroup: dropButtonsExclusiveGroup + map: _flightMap + mapFitViewport: Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin) + usePlannedHomePosition: false + geoFenceController: geoFenceController + missionController: missionController + rallyPointController: rallyPointController + showFollowVehicle: true + followVehicle: _followVehicle + onFollowVehicleChanged: _followVehicle = followVehicle + + property real leftToolWidth: centerMapDropButton.x + centerMapDropButton.width + } + + //-- Map Type Control + DropButton { + id: mapTypeButton + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: centerMapDropButton.bottom + anchors.left: centerMapDropButton.left + dropDirection: dropRight + buttonImage: "/qmlimages/MapType.svg" + viewportMargins: ScreenTools.defaultFontPixelWidth / 2 + exclusiveGroup: dropButtonsExclusiveGroup z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap - //-- Map Center Control - CenterMapDropButton { - id: centerMapDropButton - exclusiveGroup: dropButtonsExclusiveGroup - map: _flightMap - mapFitViewport: Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin) - usePlannedHomePosition: false - geoFenceController: geoFenceController - missionController: missionController - rallyPointController: rallyPointController - showFollowVehicle: true - followVehicle: _followVehicle - onFollowVehicleChanged: _followVehicle = followVehicle - - property real leftToolWidth: centerMapDropButton.x + centerMapDropButton.width - } + dropDownComponent: Component { + Column { + spacing: ScreenTools.defaultFontPixelWidth - //-- Map Type Control - DropButton { - id: mapTypeButton - dropDirection: dropRight - buttonImage: "/qmlimages/MapType.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap - - dropDownComponent: Component { - Column { + Row { spacing: ScreenTools.defaultFontPixelWidth - Row { - spacing: ScreenTools.defaultFontPixelWidth - - Repeater { - model: QGroundControl.flightMapSettings.mapTypes + Repeater { + model: QGroundControl.flightMapSettings.mapTypes - QGCButton { - checkable: true - checked: QGroundControl.flightMapSettings.mapType === text - text: modelData - width: clearButton.width - exclusiveGroup: mapTypeButtonsExclusiveGroup + QGCButton { + checkable: true + checked: QGroundControl.flightMapSettings.mapType === text + text: modelData + width: clearButton.width + exclusiveGroup: mapTypeButtonsExclusiveGroup - onClicked: { - QGroundControl.flightMapSettings.mapType = text - checked = true - dropButtonsExclusiveGroup.current = null - } + onClicked: { + QGroundControl.flightMapSettings.mapType = text + checked = true + dropButtonsExclusiveGroup.current = null } } } + } - QGCButton { - id: clearButton - text: qsTr("Clear Flight Trails") - enabled: QGroundControl.multiVehicleManager.activeVehicle - onClicked: { - QGroundControl.multiVehicleManager.activeVehicle.clearTrajectoryPoints() - dropButtonsExclusiveGroup.current = null - } + QGCButton { + id: clearButton + text: qsTr("Clear Flight Trails") + enabled: QGroundControl.multiVehicleManager.activeVehicle + onClicked: { + QGroundControl.multiVehicleManager.activeVehicle.clearTrajectoryPoints() + dropButtonsExclusiveGroup.current = null } } } } + } - //-- Zoom Map In - RoundButton { - id: mapZoomPlus - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomPlus.svg" - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap - onClicked: { - if(_flightMap) - _flightMap.zoomLevel += 0.5 - checked = false - } + //-- Zoom Map In + RoundButton { + id: mapZoomPlus + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: mapTypeButton.bottom + anchors.left: mapTypeButton.left + visible: !ScreenTools.isTinyScreen && _mainIsMap + buttonImage: "/qmlimages/ZoomPlus.svg" + exclusiveGroup: dropButtonsExclusiveGroup + z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap + onClicked: { + if(_flightMap) + _flightMap.zoomLevel += 0.5 + checked = false } + } - //-- Zoom Map Out - RoundButton { - id: mapZoomMinus - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomMinus.svg" - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap - onClicked: { - if(_flightMap) - _flightMap.zoomLevel -= 0.5 - checked = false - } + //-- Zoom Map Out + RoundButton { + id: mapZoomMinus + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: mapZoomPlus.bottom + anchors.left: mapZoomPlus.left + visible: !ScreenTools.isTinyScreen && _mainIsMap + buttonImage: "/qmlimages/ZoomMinus.svg" + exclusiveGroup: dropButtonsExclusiveGroup + z: QGroundControl.zOrderWidgets + lightBorders: isSatelliteMap + onClicked: { + if(_flightMap) + _flightMap.zoomLevel -= 0.5 + checked = false } } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 002b1d61d..a349bf947 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -768,136 +768,152 @@ QGCView { } QGCLabel { - id: planLabel - text: qsTr("Plan") - color: mapPal.text - visible: !ScreenTools.isShortScreen + id: planLabel + text: qsTr("Plan") + color: mapPal.text + visible: !ScreenTools.isShortScreen anchors.topMargin: _toolButtonTopMargin - anchors.horizontalCenter: toolColumn.horizontalCenter + anchors.horizontalCenter: addMissionItemsButton.horizontalCenter anchors.top: parent.top } + // IMPORTANT NOTE: Drop Buttons must be parented directly to the map. If they are placed in a Column for example the drop control positioning + // will not work correctly. + //-- Vertical Tool Buttons - Column { - id: toolColumn - anchors.topMargin: ScreenTools.isShortScreen ? _toolButtonTopMargin : ScreenTools.defaultFontPixelHeight / 2 + + RoundButton { + id: addMissionItemsButton + anchors.topMargin: planLabel.visible ? ScreenTools.defaultFontPixelHeight / 2 : _toolButtonTopMargin anchors.leftMargin: ScreenTools.defaultFontPixelHeight anchors.left: parent.left - anchors.top: ScreenTools.isShortScreen ? parent.top : planLabel.bottom - spacing: ScreenTools.defaultFontPixelHeight - z: QGroundControl.zOrderWidgets - - RoundButton { - id: addMissionItemsButton - buttonImage: "/qmlimages/MapAddMission.svg" - lightBorders: _lightWidgetBorders - visible: _editingLayer == _layerMission - } + anchors.top: planLabel.visible ? planLabel.bottom : parent.top + buttonImage: "/qmlimages/MapAddMission.svg" + lightBorders: _lightWidgetBorders + visible: _editingLayer == _layerMission + } - RoundButton { - id: addShapeButton - buttonImage: "/qmlimages/MapDrawShape.svg" - lightBorders: _lightWidgetBorders - visible: _editingLayer == _layerMission - - onClicked: { - var coordinate = editorMap.center - coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces) - coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces) - coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces) - var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count) - setCurrentItem(sequenceNumber) - checked = false - addMissionItemsButton.checked = false - } - } + RoundButton { + id: addShapeButton + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: addMissionItemsButton.bottom + anchors.left: addMissionItemsButton.left + buttonImage: "/qmlimages/MapDrawShape.svg" + lightBorders: _lightWidgetBorders + visible: _editingLayer == _layerMission - DropButton { - id: syncButton - dropDirection: dropRight - buttonImage: _syncDropDownController.dirty ? "/qmlimages/MapSyncChanged.svg" : "/qmlimages/MapSync.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: _dropButtonsExclusiveGroup - dropDownComponent: syncDropDownComponent - enabled: !_syncDropDownController.syncInProgress - rotateImage: _syncDropDownController.syncInProgress - lightBorders: _lightWidgetBorders + onClicked: { + var coordinate = editorMap.center + coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces) + coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces) + coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces) + var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count) + setCurrentItem(sequenceNumber) + checked = false + addMissionItemsButton.checked = false } + } - CenterMapDropButton { - id: centerMapButton - exclusiveGroup: _dropButtonsExclusiveGroup - map: editorMap - mapFitViewport: Qt.rect(leftToolWidth, toolbarHeight, editorMap.width - leftToolWidth - rightPanelWidth, editorMap.height - toolbarHeight) - usePlannedHomePosition: true - geoFenceController: geoFenceController - missionController: missionController - rallyPointController: rallyPointController - - property real toolbarHeight: qgcView.height - ScreenTools.availableHeight - property real rightPanelWidth: _rightPanelWidth - property real leftToolWidth: centerMapButton.x + centerMapButton.width - } + DropButton { + id: syncButton + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: addShapeButton.bottom + anchors.left: addShapeButton.left + dropDirection: dropRight + buttonImage: _syncDropDownController.dirty ? "/qmlimages/MapSyncChanged.svg" : "/qmlimages/MapSync.svg" + viewportMargins: ScreenTools.defaultFontPixelWidth / 2 + exclusiveGroup: _dropButtonsExclusiveGroup + dropDownComponent: syncDropDownComponent + enabled: !_syncDropDownController.syncInProgress + rotateImage: _syncDropDownController.syncInProgress + lightBorders: _lightWidgetBorders + } - DropButton { - id: mapTypeButton - dropDirection: dropRight - buttonImage: "/qmlimages/MapType.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: _dropButtonsExclusiveGroup - lightBorders: _lightWidgetBorders - - dropDownComponent: Component { - Column { - spacing: _margin - QGCLabel { text: qsTr("Map type:") } - Row { - spacing: ScreenTools.defaultFontPixelWidth - Repeater { - model: QGroundControl.flightMapSettings.mapTypes - - QGCButton { - checkable: true - checked: QGroundControl.flightMapSettings.mapType === text - text: modelData - exclusiveGroup: _mapTypeButtonsExclusiveGroup - onClicked: { - QGroundControl.flightMapSettings.mapType = text - checked = true - mapTypeButton.hideDropDown() - } + CenterMapDropButton { + id: centerMapButton + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: syncButton.bottom + anchors.left: syncButton.left + exclusiveGroup: _dropButtonsExclusiveGroup + map: editorMap + mapFitViewport: Qt.rect(leftToolWidth, toolbarHeight, editorMap.width - leftToolWidth - rightPanelWidth, editorMap.height - toolbarHeight) + usePlannedHomePosition: true + geoFenceController: geoFenceController + missionController: missionController + rallyPointController: rallyPointController + + property real toolbarHeight: qgcView.height - ScreenTools.availableHeight + property real rightPanelWidth: _rightPanelWidth + property real leftToolWidth: centerMapButton.x + centerMapButton.width + } + + DropButton { + id: mapTypeButton + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: centerMapButton.bottom + anchors.left: centerMapButton.left + dropDirection: dropRight + buttonImage: "/qmlimages/MapType.svg" + viewportMargins: ScreenTools.defaultFontPixelWidth / 2 + exclusiveGroup: _dropButtonsExclusiveGroup + lightBorders: _lightWidgetBorders + + dropDownComponent: Component { + Column { + spacing: _margin + QGCLabel { text: qsTr("Map type:") } + Row { + spacing: ScreenTools.defaultFontPixelWidth + Repeater { + model: QGroundControl.flightMapSettings.mapTypes + + QGCButton { + checkable: true + checked: QGroundControl.flightMapSettings.mapType === text + text: modelData + exclusiveGroup: _mapTypeButtonsExclusiveGroup + onClicked: { + QGroundControl.flightMapSettings.mapType = text + checked = true + mapTypeButton.hideDropDown() } } } } } } + } - //-- Zoom Map In - RoundButton { - id: mapZoomPlus - visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen - buttonImage: "/qmlimages/ZoomPlus.svg" - lightBorders: _lightWidgetBorders - - onClicked: { - if(editorMap) - editorMap.zoomLevel += 0.5 - checked = false - } + //-- Zoom Map In + RoundButton { + id: mapZoomPlus + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: mapTypeButton.bottom + anchors.left: mapTypeButton.left + visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen + buttonImage: "/qmlimages/ZoomPlus.svg" + lightBorders: _lightWidgetBorders + + onClicked: { + if(editorMap) + editorMap.zoomLevel += 0.5 + checked = false } + } - //-- Zoom Map Out - RoundButton { - id: mapZoomMinus - visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen - buttonImage: "/qmlimages/ZoomMinus.svg" - lightBorders: _lightWidgetBorders - onClicked: { - if(editorMap) - editorMap.zoomLevel -= 0.5 - checked = false - } + //-- Zoom Map Out + RoundButton { + id: mapZoomMinus + anchors.topMargin: ScreenTools.defaultFontPixelHeight + anchors.top: mapZoomPlus.bottom + anchors.left: mapZoomPlus.left + visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen + buttonImage: "/qmlimages/ZoomMinus.svg" + lightBorders: _lightWidgetBorders + onClicked: { + if(editorMap) + editorMap.zoomLevel -= 0.5 + checked = false } } diff --git a/src/QmlControls/DropButton.qml b/src/QmlControls/DropButton.qml index 956115e60..6482a9062 100644 --- a/src/QmlControls/DropButton.qml +++ b/src/QmlControls/DropButton.qml @@ -15,6 +15,7 @@ Item { property int dropDirection: dropDown property alias dropDownComponent: dropDownLoader.sourceComponent property real viewportMargins: 0 + property real topMargin: parent.height - ScreenTools.availableHeight property alias lightBorders: roundButton.lightBorders width: radius * 2 @@ -35,7 +36,7 @@ Item { property real _viewportMaxLeft: -x + viewportMargins property real _viewportMaxRight: parent.width - (viewportMargins * 2) - x - property real _viewportMaxTop: -y + viewportMargins + property real _viewportMaxTop: -y + viewportMargins + topMargin property real _viewportMaxBottom: parent.height - (viewportMargins * 2) - y // Set up ExclusiveGroup support. We use the checked property to drive visibility of drop down. @@ -53,8 +54,6 @@ Item { checked = false } - Component.onCompleted: _calcPositions() - function _calcPositions() { var dropComponentWidth = dropDownLoader.item.width var dropComponentHeight = dropDownLoader.item.height @@ -161,6 +160,7 @@ Item { id: roundButton radius: parent.width / 2 onClicked: { + _calcPositions() _root.clicked() } } @@ -216,20 +216,11 @@ Item { Item { id: dropItemHolderRect - //color: qgcPal.button - //radius: _dropCornerRadius Loader { id: dropDownLoader x: _dropMargin y: _dropMargin - - Connections { - target: dropDownLoader.item - - onWidthChanged: _calcPositions() - onHeightChanged: _calcPositions() - } } } } // Item - dropDownItem -- GitLab From 8cdc7f9f591a5339b7f9c1a54d03e57169495714 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 29 Dec 2016 09:39:52 -0800 Subject: [PATCH 141/398] Fix Qml API --- src/comm/LinkManager.cc | 10 ++++++++++ src/comm/LinkManager.h | 5 ++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 9e999ced5..dc6c49652 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -99,6 +99,16 @@ void LinkManager::setToolbox(QGCToolbox *toolbox) } +// This should only be used by Qml code +void LinkManager::createConnectedLink(LinkConfiguration* config) +{ + for(int i = 0; i < _sharedConfigurations.count(); i++) { + SharedLinkConfigurationPointer& sharedConf = _sharedConfigurations[i]; + if (sharedConf->name() == config->name()) + createConnectedLink(sharedConf); + } +} + LinkInterface* LinkManager::createConnectedLink(SharedLinkConfigurationPointer& config) { if (!config) { diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index f75fc50f1..9383c38a0 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -124,7 +124,10 @@ public: /// Creates, connects (and adds) a link based on the given configuration instance. /// Link takes ownership of config. - Q_INVOKABLE LinkInterface* createConnectedLink(SharedLinkConfigurationPointer& config); + LinkInterface* createConnectedLink(SharedLinkConfigurationPointer& config); + + // This should only be used by Qml code + Q_INVOKABLE void createConnectedLink(LinkConfiguration* config); /// Creates, connects (and adds) a link based on the given configuration name. LinkInterface* createConnectedLink(const QString& name); -- GitLab From 9d413108f8116a5ba049f41d94929789f799a509 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 29 Dec 2016 13:00:11 -0500 Subject: [PATCH 142/398] Disabling MAVLink 2.0 logging for APM. --- src/Vehicle/MAVLinkLogManager.cc | 26 +++++++++++++------------- src/ui/preferences/MavlinkSettings.qml | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index 94e9b3467..c792e0724 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -243,7 +243,7 @@ MAVLinkLogProcessor::processStreamData(uint16_t sequence, uint8_t first_message, if(!_gotHeader) { if(data.size() < 16) { //-- Shouldn't happen but if it does, we might as well close shop. - qCCritical(MAVLinkLogManagerLog) << "Corrupt log header. Canceling log download."; + qCWarning(MAVLinkLogManagerLog) << "Corrupt log header. Canceling log download."; return false; } //-- Write header @@ -327,7 +327,7 @@ MAVLinkLogManager::MAVLinkLogManager(QGCApplication* app) _logPath += "/MAVLinkLogs"; if(!QDir(_logPath).exists()) { if(!QDir().mkpath(_logPath)) { - qCCritical(MAVLinkLogManagerLog) << "Could not create MAVLink log download path:" << _logPath; + qCWarning(MAVLinkLogManagerLog) << "Could not create MAVLink log download path:" << _logPath; _loggingDisabled = true; } } @@ -604,7 +604,7 @@ MAVLinkLogManager::cancelUpload() void MAVLinkLogManager::startLogging() { - if(_vehicle) { + if(_vehicle && _vehicle->px4Firmware()) { if(_createNewLog()) { _vehicle->startMavlinkLog(); _logRunning = true; @@ -617,7 +617,7 @@ MAVLinkLogManager::startLogging() void MAVLinkLogManager::stopLogging() { - if(_vehicle) { + if(_vehicle && _vehicle->px4Firmware()) { //-- Tell vehicle to stop sending logs _vehicle->stopMavlinkLog(); } @@ -660,16 +660,16 @@ MAVLinkLogManager::_sendLog(const QString& logFile) defaultDescription = kDefaultDescr; } if(_emailAddress.isEmpty()) { - qCCritical(MAVLinkLogManagerLog) << "User email missing."; + qCWarning(MAVLinkLogManagerLog) << "User email missing."; return false; } if(_uploadURL.isEmpty()) { - qCCritical(MAVLinkLogManagerLog) << "Upload URL missing."; + qCWarning(MAVLinkLogManagerLog) << "Upload URL missing."; return false; } QFileInfo fi(logFile); if(!fi.exists()) { - qCCritical(MAVLinkLogManagerLog) << "Log file missing:" << logFile; + qCWarning(MAVLinkLogManagerLog) << "Log file missing:" << logFile; return false; } QFile* file = new QFile(logFile); @@ -677,7 +677,7 @@ MAVLinkLogManager::_sendLog(const QString& logFile) if(file) { delete file; } - qCCritical(MAVLinkLogManagerLog) << "Could not open log file:" << logFile; + qCWarning(MAVLinkLogManagerLog) << "Could not open log file:" << logFile; return false; } if(!_nam) { @@ -826,7 +826,7 @@ MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) // far, I'm working on the assumption that multiple vehicles is a rare exception. // For now, we only handle one log download at a time. // Disconnect the previous one (if any) - if(_vehicle) { + if(_vehicle && _vehicle->px4Firmware()) { disconnect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); disconnect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); disconnect(_vehicle, &Vehicle::mavCommandResult, this, &MAVLinkLogManager::_mavCommandResult); @@ -836,7 +836,7 @@ MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) emit canStartLogChanged(); } // Connect new system - if(vehicle) { + if(_vehicle && _vehicle->px4Firmware()) { _vehicle = vehicle; connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); @@ -851,7 +851,7 @@ MAVLinkLogManager::_mavlinkLogData(Vehicle* /*vehicle*/, uint8_t /*target_system { if(_logProcessor && _logProcessor->valid()) { if(!_logProcessor->processStreamData(sequence, first_message, data)) { - qCCritical(MAVLinkLogManagerLog) << "Error writing MAVLink log file:" << _logProcessor->fileName(); + qCWarning(MAVLinkLogManagerLog) << "Error writing MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = NULL; _logRunning = false; @@ -916,7 +916,7 @@ MAVLinkLogManager::_createNewLog() _insertNewLog(_logProcessor->record()); emit logFilesChanged(); } else { - qCCritical(MAVLinkLogManagerLog) << "Could not create MAVLink log file:" << _logProcessor->fileName(); + qCWarning(MAVLinkLogManagerLog) << "Could not create MAVLink log file:" << _logProcessor->fileName(); delete _logProcessor; _logProcessor = NULL; } @@ -927,7 +927,7 @@ MAVLinkLogManager::_createNewLog() void MAVLinkLogManager::_armedChanged(bool armed) { - if(_vehicle) { + if(_vehicle && _vehicle->px4Firmware()) { if(armed) { if(_enableAutoStart) { startLogging(); diff --git a/src/ui/preferences/MavlinkSettings.qml b/src/ui/preferences/MavlinkSettings.qml index 4de149c26..83ac9ac8c 100644 --- a/src/ui/preferences/MavlinkSettings.qml +++ b/src/ui/preferences/MavlinkSettings.qml @@ -160,7 +160,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: mavlogLabel - text: qsTr("Vehicle MAVLink Logging") + text: qsTr("MAVLink 2.0 Logging (PX4 Firmware Only)") font.family: ScreenTools.demiboldFontFamily } } @@ -220,7 +220,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: logLabel - text: qsTr("MAVLink Log Uploads") + text: qsTr("MAVLink 2.0 Log Uploads (PX4 Firmware Only)") font.family: ScreenTools.demiboldFontFamily } } -- GitLab From 1b89b4c18b74a8e22086813030cf06dc0a0a0a83 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 08:39:52 -0800 Subject: [PATCH 143/398] Remove dead code --- qgroundcontrol.pro | 3 - src/ui/MainWindow.cc | 1 - src/ui/MainWindow.h | 1 - src/ui/QGCDataPlot2D.cc | 734 ---------------------------------------- src/ui/QGCDataPlot2D.h | 77 ----- src/ui/QGCDataPlot2D.ui | 309 ----------------- 6 files changed, 1125 deletions(-) delete mode 100644 src/ui/QGCDataPlot2D.cc delete mode 100644 src/ui/QGCDataPlot2D.h delete mode 100644 src/ui/QGCDataPlot2D.ui diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index dd0d319fe..4fa119de4 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -314,7 +314,6 @@ FORMS += \ FORMS += \ src/ui/Linechart.ui \ src/ui/MultiVehicleDockWidget.ui \ - src/ui/QGCDataPlot2D.ui \ src/ui/QGCHilConfiguration.ui \ src/ui/QGCHilFlightGearConfiguration.ui \ src/ui/QGCHilJSBSimConfiguration.ui \ @@ -545,7 +544,6 @@ HEADERS += \ src/ui/MAVLinkDecoder.h \ src/ui/MainWindow.h \ src/ui/MultiVehicleDockWidget.h \ - src/ui/QGCDataPlot2D.h \ src/ui/QGCHilConfiguration.h \ src/ui/QGCHilFlightGearConfiguration.h \ src/ui/QGCHilJSBSimConfiguration.h \ @@ -694,7 +692,6 @@ SOURCES += \ src/ui/MAVLinkDecoder.cc \ src/ui/MainWindow.cc \ src/ui/MultiVehicleDockWidget.cc \ - src/ui/QGCDataPlot2D.cc \ src/ui/QGCHilConfiguration.cc \ src/ui/QGCHilFlightGearConfiguration.cc \ src/ui/QGCHilJSBSimConfiguration.cc \ diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index cd40b1927..d2bce2a37 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -43,7 +43,6 @@ #include "QGCImageProvider.h" #ifndef __mobile__ -#include "QGCDataPlot2D.h" #include "Linecharts.h" #include "QGCUASFileViewMulti.h" #include "UASQuickView.h" diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index a8851c7e2..dc1706a42 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -47,7 +47,6 @@ class QGCStatusBar; class Linecharts; -class QGCDataPlot2D; /** * @brief Main Application Window diff --git a/src/ui/QGCDataPlot2D.cc b/src/ui/QGCDataPlot2D.cc deleted file mode 100644 index 472b91e1f..000000000 --- a/src/ui/QGCDataPlot2D.cc +++ /dev/null @@ -1,734 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/** - * @file - * @brief Implementation of QGCDataPlot2D - * @author Lorenz Meier - * - */ - -#include -#ifndef __mobile__ -#include -#include -#endif -#include -#include -#include -#include -#include - -#include - -#include "QGCDataPlot2D.h" -#include "ui_QGCDataPlot2D.h" -#include "MG.h" -#include "QGCFileDialog.h" -#include "QGCMessageBox.h" - -QGCDataPlot2D::QGCDataPlot2D(QWidget *parent) : - QWidget(parent), - plot(new IncrementalPlot(parent)), - logFile(NULL), - ui(new Ui::QGCDataPlot2D) -{ - ui->setupUi(this); - - // Add plot to ui - QHBoxLayout* layout = new QHBoxLayout(ui->plotFrame); - layout->addWidget(plot); - ui->plotFrame->setLayout(layout); - ui->gridCheckBox->setChecked(plot->gridEnabled()); - - // Connect user actions - connect(ui->selectFileButton, &QPushButton::clicked, this, &QGCDataPlot2D::selectFile); - connect(ui->saveCsvButton, &QPushButton::clicked, this, &QGCDataPlot2D::saveCsvLog); - connect(ui->reloadButton, &QPushButton::clicked, this, &QGCDataPlot2D::reloadFile); - connect(ui->savePlotButton, &QPushButton::clicked, this, &QGCDataPlot2D::savePlot); - connect(ui->printButton, &QPushButton::clicked, this, &QGCDataPlot2D::print); - connect(ui->legendCheckBox, &QCheckBox::clicked, plot, &IncrementalPlot::showLegend); - connect(ui->symmetricCheckBox,&QCheckBox::clicked, plot, &IncrementalPlot::setSymmetric); - connect(ui->gridCheckBox, &QCheckBox::clicked, plot, &IncrementalPlot::showGrid); - - connect(ui->style, static_cast(&QComboBox::currentIndexChanged), - plot, &IncrementalPlot::setStyleText); - - //TODO: calculateRegression returns bool, slots are expected to return void, this makes - // converting to new style way too hard. - connect(ui->regressionButton, SIGNAL(clicked()), this, SLOT(calculateRegression())); - - // Allow style changes to propagate through this widget - connect(qgcApp(), &QGCApplication::styleChanged, plot, &IncrementalPlot::styleChanged); -} - -void QGCDataPlot2D::reloadFile() -{ - if (QFileInfo(fileName).isReadable()) { - if (ui->inputFileType->currentText().contains("pxIMU") || ui->inputFileType->currentText().contains("RAW")) { - loadRawLog(fileName, ui->xAxis->currentText(), ui->yAxis->text()); - } else if (ui->inputFileType->currentText().contains("CSV")) { - loadCsvLog(fileName, ui->xAxis->currentText(), ui->yAxis->text()); - } - } -} - -void QGCDataPlot2D::loadFile() -{ - qDebug() << "DATA PLOT: Loading file:" << fileName; - if (QFileInfo(fileName).isReadable()) { - if (ui->inputFileType->currentText().contains("pxIMU") || ui->inputFileType->currentText().contains("RAW")) { - loadRawLog(fileName); - } else if (ui->inputFileType->currentText().contains("CSV")) { - loadCsvLog(fileName); - } - } -} - -void QGCDataPlot2D::loadFile(QString file) -{ - // TODO This "filename" is a private/protected member variable. It should be named in such way - // it indicates so. This same name is used in several places within this file in local scopes. - fileName = file; - QFileInfo fi(fileName); - if (fi.isReadable()) { - if (fi.suffix() == QString("raw") || fi.suffix() == QString("imu")) { - loadRawLog(fileName); - } else if (fi.suffix() == QString("txt") || fi.suffix() == QString("csv")) { - loadCsvLog(fileName); - } - // TODO Else, tell the user it doesn't know what to do with the file... - } -} - -/** - * This function brings up a file name dialog and asks the user to enter a file to save to - */ -QString QGCDataPlot2D::getSavePlotFilename() -{ - QString fileName = QGCFileDialog::getSaveFileName( - this, "Save Plot File", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), - "PDF Documents (*.pdf);;SVG Images (*.svg)", - "pdf"); - return fileName; -} - -/** - * This function aks the user for a filename and exports to either PDF or SVG, depending on the filename - */ -void QGCDataPlot2D::savePlot() -{ - QString fileName = getSavePlotFilename(); - if (fileName.isEmpty()) - return; - - while(!(fileName.endsWith(".svg") || fileName.endsWith(".pdf"))) { - QMessageBox::StandardButton button = QGCMessageBox::warning( - tr("Unsuitable file extension for Plot document type."), - tr("Please choose .pdf or .svg as file extension. Click OK to change the file extension, cancel to not save the file."), - QMessageBox::Ok | QMessageBox::Cancel, - QMessageBox::Ok); - // Abort if cancelled - if (button == QMessageBox::Cancel) { - return; - } - - fileName = getSavePlotFilename(); - if (fileName.isEmpty()) - return; //Abort if cancelled - } - - if (fileName.endsWith(".pdf")) { - exportPDF(fileName); - } else if (fileName.endsWith(".svg")) { - exportSVG(fileName); - } -} - - -void QGCDataPlot2D::print() -{ -#ifndef __mobile__ - QPrinter printer(QPrinter::HighResolution); - // printer.setOutputFormat(QPrinter::PdfFormat); - // //QPrinter printer(QPrinter::HighResolution); - // printer.setOutputFileName(fileName); - - QString docName = plot->title().text(); - if ( !docName.isEmpty() ) { - docName.replace (QRegExp (QString::fromLatin1 ("\n")), tr (" -- ")); - printer.setDocName (docName); - } - - printer.setCreator("QGroundControl"); - printer.setOrientation(QPrinter::Landscape); - - QPrintDialog dialog(&printer); - if ( dialog.exec() ) { - plot->setStyleSheet("QWidget { background-color: #FFFFFF; color: #000000; background-clip: border; font-size: 10pt;}"); - plot->setCanvasBackground(Qt::white); - // FIXME: QwtPlotPrintFilter no longer exists in Qwt 6.1 - //QwtPlotPrintFilter filter; - //filter.color(Qt::white, QwtPlotPrintFilter::CanvasBackground); - //filter.color(Qt::black, QwtPlotPrintFilter::AxisScale); - //filter.color(Qt::black, QwtPlotPrintFilter::AxisTitle); - //filter.color(Qt::black, QwtPlotPrintFilter::MajorGrid); - //filter.color(Qt::black, QwtPlotPrintFilter::MinorGrid); - //if ( printer.colorMode() == QPrinter::GrayScale ) { - // int options = QwtPlotPrintFilter::PrintAll; - // options &= ~QwtPlotPrintFilter::PrintBackground; - // options |= QwtPlotPrintFilter::PrintFrameWithScales; - // filter.setOptions(options); - //} - //plot->print(printer); - plot->setStyleSheet("QWidget { background-color: #050508; color: #DDDDDF; background-clip: border; font-size: 11pt;}"); - //plot->setCanvasBackground(QColor(5, 5, 8)); - } -#endif -} - -void QGCDataPlot2D::exportPDF(QString fileName) -{ -#ifdef __mobile__ - Q_UNUSED(fileName) -#else - QPrinter printer; - printer.setOutputFormat(QPrinter::PdfFormat); - printer.setOutputFileName(fileName); - //printer.setFullPage(true); - printer.setPageMargins(10.0, 10.0, 10.0, 10.0, QPrinter::Millimeter); - printer.setPageSize(QPrinter::A4); - - QString docName = plot->title().text(); - if ( !docName.isEmpty() ) { - docName.replace (QRegExp (QString::fromLatin1 ("\n")), tr (" -- ")); - printer.setDocName (docName); - } - - printer.setCreator("QGroundControl"); - printer.setOrientation(QPrinter::Landscape); - - plot->setStyleSheet("QWidget { background-color: #FFFFFF; color: #000000; background-clip: border; font-size: 10pt;}"); - // plot->setCanvasBackground(Qt::white); - // FIXME: QwtPlotPrintFilter no longer exists in Qwt 6.1 - // QwtPlotPrintFilter filter; - // filter.color(Qt::white, QwtPlotPrintFilter::CanvasBackground); - // filter.color(Qt::black, QwtPlotPrintFilter::AxisScale); - // filter.color(Qt::black, QwtPlotPrintFilter::AxisTitle); - // filter.color(Qt::black, QwtPlotPrintFilter::MajorGrid); - // filter.color(Qt::black, QwtPlotPrintFilter::MinorGrid); - // if ( printer.colorMode() == QPrinter::GrayScale ) - // { - // int options = QwtPlotPrintFilter::PrintAll; - // options &= ~QwtPlotPrintFilter::PrintBackground; - // options |= QwtPlotPrintFilter::PrintFrameWithScales; - // filter.setOptions(options); - // } - //plot->print(printer); - plot->setStyleSheet("QWidget { background-color: #050508; color: #DDDDDF; background-clip: border; font-size: 11pt;}"); - //plot->setCanvasBackground(QColor(5, 5, 8)); -#endif -} - -void QGCDataPlot2D::exportSVG(QString fileName) -{ -#ifdef __mobile__ - Q_UNUSED(fileName) -#else - if ( !fileName.isEmpty() ) { - plot->setStyleSheet("QWidget { background-color: #FFFFFF; color: #000000; background-clip: border; font-size: 10pt;}"); - //plot->setCanvasBackground(Qt::white); - QSvgGenerator generator; - generator.setFileName(fileName); - generator.setSize(QSize(800, 600)); - - // FIXME: QwtPlotPrintFilter no longer exists in Qwt 6.1 - //QwtPlotPrintFilter filter; - //filter.color(Qt::white, QwtPlotPrintFilter::CanvasBackground); - //filter.color(Qt::black, QwtPlotPrintFilter::AxisScale); - //filter.color(Qt::black, QwtPlotPrintFilter::AxisTitle); - //filter.color(Qt::black, QwtPlotPrintFilter::MajorGrid); - //filter.color(Qt::black, QwtPlotPrintFilter::MinorGrid); - - //plot->print(generator); - plot->setStyleSheet("QWidget { background-color: #050508; color: #DDDDDF; background-clip: border; font-size: 11pt;}"); - } -#endif -} - -/** - * Selects a filename and attempts immediately to load it. - */ -void QGCDataPlot2D::selectFile() -{ - // Open a file dialog prompting the user for the file to load. - // Note the special case for the Pixhawk. - if (ui->inputFileType->currentText().contains("pxIMU") || ui->inputFileType->currentText().contains("RAW")) { - fileName = QGCFileDialog::getOpenFileName(this, tr("Load Log File"), QString(), "Log Files (*.imu *.raw)"); - } - else - { - fileName = QGCFileDialog::getOpenFileName(this, tr("Load Log File"), QString(), "Log Files (*.csv);;All Files (*)"); - } - - // Check if the user hit cancel, which results in an empty string. - // If this is the case, we just stop. - if (fileName.isEmpty()) - { - return; - } - - // Now attempt to open the file - QFileInfo fileInfo(fileName); - if (!fileInfo.isReadable()) - { - // TODO This needs some TLC. File used by another program sounds like a Windows only issue. - QGCMessageBox::critical( - tr("Could not open file"), - tr("The file is owned by user %1. Is the file currently used by another program?").arg(fileInfo.owner())); - ui->filenameLabel->setText(tr("Could not open %1").arg(fileInfo.fileName())); - } - else - { - ui->filenameLabel->setText(tr("Opened %1").arg(fileInfo.completeBaseName()+"."+fileInfo.completeSuffix())); - // Open and import the file - loadFile(); - } - -} - -void QGCDataPlot2D::loadRawLog(QString file, QString xAxisName, QString yAxisFilter) -{ - Q_UNUSED(xAxisName); - Q_UNUSED(yAxisFilter); - - if (logFile != NULL) { - logFile->close(); - delete logFile; - } - // Postprocess log file - logFile = new QTemporaryFile("qt_qgc_temp_log.XXXXXX.csv"); - compressor = new LogCompressor(file, logFile->fileName()); - connect(compressor, &LogCompressor::finishedFile, this, static_cast(&QGCDataPlot2D::loadFile)); - compressor->startCompression(); -} - -/** - * This function loads a CSV file into the plot. It tries to assign the dimension names - * based on the first data row and tries to guess the separator char. - * - * @param file Name of the file to open - * @param xAxisName Optional paramater. If given, the x axis dimension will be selected to match this string - * @param yAxisFilter Optional parameter. If given, only data dimension names present in the filter string will be - * plotted - * - * @code - * - * QString file = "/home/user/datalog.txt"; // With header: xyz - * QString xAxis = "x"; - * QString yAxis = "z"; - * - * // Plotted result will be x vs z with y ignored. - * @endcode - */ -void QGCDataPlot2D::loadCsvLog(QString file, QString xAxisName, QString yAxisFilter) -{ - if (logFile != NULL) { - logFile->close(); - delete logFile; - curveNames.clear(); - } - logFile = new QFile(file); - - // Load CSV data - if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) - return; - - // Set plot title - if (ui->plotTitle->text() != "") plot->setTitle(ui->plotTitle->text()); - if (ui->plotXAxisLabel->text() != "") plot->setAxisTitle(QwtPlot::xBottom, ui->plotXAxisLabel->text()); - if (ui->plotYAxisLabel->text() != "") plot->setAxisTitle(QwtPlot::yLeft, ui->plotYAxisLabel->text()); - - // Extract header - - // Read in values - // Find all keys - QTextStream in(logFile); - - // First line is header - QString header = in.readLine(); - - bool charRead = false; - QString separator = ""; - QList sepCandidates; - sepCandidates << '\t'; - sepCandidates << ','; - sepCandidates << ';'; - sepCandidates << ' '; - sepCandidates << '~'; - sepCandidates << '|'; - - // Iterate until separator is found - // or full header is parsed - for (int i = 0; i < header.length(); i++) { - if (sepCandidates.contains(header.at(i))) { - // Separator found - if (charRead) { - separator += header[i]; - } - } else { - // Char found - charRead = true; - // If the separator is not empty, this char - // has been read after a separator, so detection - // is now complete - if (separator != "") break; - } - } - - QString out = separator; - out.replace("\t", ""); - ui->filenameLabel->setText(file.split("/").last().split("\\").last()+" Separator: \""+out+"\""); - //qDebug() << "READING CSV:" << header; - - // Clear plot - plot->removeData(); - - QMap* > xValues; - QMap* > yValues; - - curveNames.append(header.split(separator, QString::SkipEmptyParts)); - - // Eliminate any non-string curve names - for (int i = 0; i < curveNames.count(); ++i) - { - if (curveNames.at(i).length() == 0 || - curveNames.at(i) == " " || - curveNames.at(i) == "\n" || - curveNames.at(i) == "\t" || - curveNames.at(i) == "\r") - { - // Remove bogus curve name - curveNames.removeAt(i); - } - } - - QString curveName; - - // Clear UI elements - ui->xAxis->clear(); - ui->yAxis->clear(); - ui->xRegressionComboBox->clear(); - ui->yRegressionComboBox->clear(); - ui->regressionOutput->clear(); - - int curveNameIndex = 0; - - QString xAxisFilter; - if (xAxisName == "") { - xAxisFilter = curveNames.first(); - } else { - xAxisFilter = xAxisName; - } - - // Fill y-axis renaming lookup table - // Allow the user to rename data dimensions in the plot - QMap renaming; - - QStringList yCurves = yAxisFilter.split("|", QString::SkipEmptyParts); - - // Figure out the correct renaming - for (int i = 0; i < yCurves.count(); ++i) - { - if (yCurves.at(i).contains(":")) - { - QStringList parts = yCurves.at(i).split(":", QString::SkipEmptyParts); - if (parts.count() > 1) - { - // Insert renaming map - renaming.insert(parts.first(), parts.last()); - // Replace curve value with first part only - yCurves.replace(i, parts.first()); - } - } -// else -// { -// // Insert same value, not renaming anything -// renaming.insert(yCurves.at(i), yCurves.at(i)); -// } - } - - - foreach(curveName, curveNames) { - // Add to plot x axis selection - ui->xAxis->addItem(curveName); - // Add to regression selection - ui->xRegressionComboBox->addItem(curveName); - ui->yRegressionComboBox->addItem(curveName); - if (curveName != xAxisFilter) { - if ((yAxisFilter == "") || yCurves.contains(curveName)) { - yValues.insert(curveName, new QVector()); - xValues.insert(curveName, new QVector()); - // Add separator starting with second item - if (curveNameIndex > 0 && curveNameIndex < curveNames.count()) { - ui->yAxis->setText(ui->yAxis->text()+"|"); - } - // If this curve was renamed, re-add the renaming to the text field - QString renamingText = ""; - if (renaming.contains(curveName)) renamingText = QString(":%1").arg(renaming.value(curveName)); - ui->yAxis->setText(ui->yAxis->text()+curveName+renamingText); - // Insert same value, not renaming anything - if (!renaming.contains(curveName)) renaming.insert(curveName, curveName); - curveNameIndex++; - } - } - } - - // Select current axis in UI - ui->xAxis->setCurrentIndex(curveNames.indexOf(xAxisFilter)); - - // Read data - - double x = 0; - double y = 0; - - while (!in.atEnd()) - { - QString line = in.readLine(); - - // Keep empty parts here - we still have to act on them - QStringList values = line.split(separator, QString::KeepEmptyParts); - - bool headerfound = false; - - // First get header - ORDER MATTERS HERE! - foreach(curveName, curveNames) - { - if (curveName == xAxisFilter) - { - // X AXIS HANDLING - - // Take this value as x if it is selected - QString text = values.at(curveNames.indexOf(curveName)); - text = text.trimmed(); - if (text.length() > 0 && text != " " && text != "\n" && text != "\r" && text != "\t") - { - bool okx = true; - x = text.toDouble(&okx); - if (okx && !qIsNaN(x) && !qIsInf(x)) - { - headerfound = true; - } - } - } - } - - if (headerfound) - { - // Search again from start for values - ORDER MATTERS HERE! - foreach(curveName, curveNames) - { - // Y AXIS HANDLING - // Only plot non-x curver and those selected in the yAxisFilter (or all if the filter is not set) - if(curveName != xAxisFilter && (yAxisFilter == "" || yCurves.contains(curveName))) - { - bool oky; - int curveNameIndex = curveNames.indexOf(curveName); - if (values.count() > curveNameIndex) - { - QString text(values.at(curveNameIndex)); - text = text.trimmed(); - y = text.toDouble(&oky); - // Only INF is really an issue for the plot - // NaN is fine - if (oky && !qIsNaN(y) && !qIsInf(y) && text.length() > 0 && text != " " && text != "\n" && text != "\r" && text != "\t") - { - // Only append definitely valid values - xValues.value(curveName)->append(x); - yValues.value(curveName)->append(y); - } - } - } - } - } - } - - // Add data array of each curve to the plot at once (fast) - // Iterates through all x-y curve combinations - for (int i = 0; i < yValues.count(); i++) { - if (renaming.contains(yValues.keys().at(i))) - { - plot->appendData(renaming.value(yValues.keys().at(i)), xValues.values().at(i)->data(), yValues.values().at(i)->data(), xValues.values().at(i)->count()); - } - else - { - plot->appendData(yValues.keys().at(i), xValues.values().at(i)->data(), yValues.values().at(i)->data(), xValues.values().at(i)->count()); - } - } - plot->updateScale(); - plot->setStyleText(ui->style->currentText()); -} - -bool QGCDataPlot2D::calculateRegression() -{ - // TODO: Add support for quadratic / cubic curve fitting - return calculateRegression(ui->xRegressionComboBox->currentText(), ui->yRegressionComboBox->currentText(), "linear"); -} - -/** - * @param xName Name of the x dimension - * @param yName Name of the y dimension - * @param method Regression method, either "linear", "quadratic" or "cubic". Only linear is supported at this point - */ -bool QGCDataPlot2D::calculateRegression(QString xName, QString yName, QString method) -{ - bool result = false; - QString function; - if (xName != yName) { - if (QFileInfo(fileName).isReadable()) { - loadCsvLog(fileName, xName, yName); - ui->xRegressionComboBox->setCurrentIndex(curveNames.indexOf(xName)); - ui->yRegressionComboBox->setCurrentIndex(curveNames.indexOf(yName)); - } - - // Create a couple of arrays for us to use to temporarily store some of the data from the plot. - // These arrays are allocated on the heap as they are far too big to go in the stack and will - // cause an overflow. - // TODO: Look into if this would be better done by having a getter return const double pointers instead - // of using memcpy(). - const int size = 100000; - double *x = new double[size]; - double *y = new double[size]; - int copied = plot->data(yName, x, y, size); - - if (method == "linear") { - double a; // Y-axis crossing - double b; // Slope - double r; // Regression coefficient - if (linearRegression(x, y, copied, &a, &b, &r)) { - function = tr("%1 = %2 * %3 + %4 | R-coefficient: %5").arg(yName, QString::number(b), xName, QString::number(a), QString::number(r)); - - // Plot curve - // y-axis crossing (x = 0) - // Set plotting to lines only - plot->appendData(tr("regression %1-%2").arg(xName, yName), 0.0, a); - plot->setStyleText("lines"); - // x-value of the current rightmost x position in the plot - plot->appendData(tr("regression %1-%2").arg(xName, yName), plot->invTransform(QwtPlot::xBottom, plot->width() - plot->width()*0.08f), (a + b*plot->invTransform(QwtPlot::xBottom, plot->width() - plot->width() * 0.08f))); - - result = true; - } else { - function = tr("Linear regression failed. (Limit: %1 data points. Try with less)").arg(size); - } - } else { - function = tr("Regression method %1 not found").arg(method); - } - - delete[] x; - delete[] y; - } else { - // xName == yName - function = tr("Please select different X and Y dimensions, not %1 = %2").arg(xName, yName); - } - ui->regressionOutput->setText(function); - return result; -} - -/** - * Linear regression (least squares) for n data points. - * Computes: - * - * y = a * x + b - * - * @param x values on x axis - * @param y corresponding values on y axis - * @param n Number of values - * @param a returned slope of line - * @param b y-axis intersection - * @param r regression coefficient. The larger the coefficient is, the better is - * the match of the regression. - * @return 1 on success, 0 on failure (e.g. because of infinite slope) - */ -bool QGCDataPlot2D::linearRegression(double *x, double *y, int n, double *a, double *b, double *r) -{ - int i; - double sumx=0,sumy=0,sumx2=0,sumy2=0,sumxy=0; - double sxx,syy,sxy; - - *a = 0; - *b = 0; - *r = 0; - if (n < 2) - return true; - - /* Conpute some things we need */ - for (i=0; icopy(fileName); - - qDebug() << "Saved CSV log (" << fileName << "). Success: " << success; - - //qDebug() << "READE TO SAVE CSV LOG TO " << fileName; -} - -QGCDataPlot2D::~QGCDataPlot2D() -{ - delete ui; -} - -void QGCDataPlot2D::changeEvent(QEvent *e) -{ - QWidget::changeEvent(e); - switch (e->type()) { - case QEvent::LanguageChange: - ui->retranslateUi(this); - break; - default: - break; - } -} diff --git a/src/ui/QGCDataPlot2D.h b/src/ui/QGCDataPlot2D.h deleted file mode 100644 index 2ff6cacab..000000000 --- a/src/ui/QGCDataPlot2D.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef QGCDATAPLOT2D_H -#define QGCDATAPLOT2D_H - -#include -#include -#include "IncrementalPlot.h" -#include "LogCompressor.h" - -namespace Ui -{ -class QGCDataPlot2D; -} - -class QGCDataPlot2D : public QWidget -{ - Q_OBJECT -public: - QGCDataPlot2D(QWidget *parent = 0); - ~QGCDataPlot2D(); - - /** @brief Calculate and display regression function*/ - bool calculateRegression(QString xName, QString yName, QString method="linear"); - - /** @brief Linear regression over data points */ - bool linearRegression(double *x, double *y, int n, double *a, double *b, double *r); - -public slots: - /** @brief Load previously selected file */ - void loadFile(); - /** @brief Load file with this name */ - void loadFile(QString file); - /** @brief Reload a file, with filtering enabled */ - void reloadFile(); - void selectFile(); - void loadCsvLog(QString file, QString xAxisName="", QString yAxisFilter=""); - void loadRawLog(QString file, QString xAxisName="", QString yAxisFilter=""); - void saveCsvLog(); - /** @brief Save plot to PDF or SVG */ - void savePlot(); - /** @brief Export PDF file */ - void exportPDF(QString fileName); - /** @brief Export SVG file */ - void exportSVG(QString file); - /** @brief Print or save PDF file (MacOS/Linux) */ - void print(); - /** @brief Calculate and display regression function*/ - bool calculateRegression(); - -signals: - void visibilityChanged(bool visible); - -protected: - void showEvent(QShowEvent* event) - { - QWidget::showEvent(event); - emit visibilityChanged(true); - } - - void hideEvent(QHideEvent* event) - { - QWidget::hideEvent(event); - emit visibilityChanged(false); - } - - void changeEvent(QEvent *e); - QString getSavePlotFilename(); - IncrementalPlot* plot; - LogCompressor* compressor; - QFile* logFile; - QString fileName; - QStringList curveNames; - -private: - Ui::QGCDataPlot2D *ui; -}; - -#endif // QGCDATAPLOT2D_H diff --git a/src/ui/QGCDataPlot2D.ui b/src/ui/QGCDataPlot2D.ui deleted file mode 100644 index dba180727..000000000 --- a/src/ui/QGCDataPlot2D.ui +++ /dev/null @@ -1,309 +0,0 @@ - - - QGCDataPlot2D - - - - 0 - 0 - 1463 - 311 - - - - Form - - - - - - X - - - - - - - - - - Y - - - - - - - - - - Set line style - - - - Only lines - - - - - Only crosses - - - - - Only circles - - - - - Only rectangles - - - - - Lines and crosses - - - - - Lines and circles - - - - - Lines and rects - - - - - Dotted lines and crosses - - - - - Dotted lines and circles - - - - - Dotted lines and rects - - - - - Dashed lines and crosses - - - - - Dashed lines and circles - - - - - Dashed lines and rects - - - - - - - - Replot - - - - - - - Qt::Vertical - - - - - - - Save Image - - - - - - - Print - - - - - - - Title - - - - - - - - - - X label - - - - - - - - - - Y label - - - - - - - - - - Symmetric - - - - - - - Legend - - - - - - - Grid - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - File - - - - - - - - CSV - - - - - pxIMU - - - - - RAW - - - - - - - - Please select input file.. - - - - - - - Select file - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical - - - - - - - Regression - - - - - - - - - - - - - Calculate - - - - - - - true - - - - - - - Save Data - - - - - - - Qt::Horizontal - - - - 5 - 20 - - - - - - - - - -- GitLab From 73e607b020db703aab38d6dc1aecc8c159c215b3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 11:28:15 -0800 Subject: [PATCH 144/398] Add version check to command retry --- src/Vehicle/Vehicle.cc | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 51f59ee92..ab67ab2f0 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1877,13 +1877,25 @@ void Vehicle::_sendMavCommandAgain(void) } if (_mavCommandRetryCount > 1) { - if (queuedCommand.command == MAV_CMD_START_RX_PAIR) { - // Implementation of this command in all firmwares is incorrect. It does not send Ack so we can't retry. - return; - } - if (px4Firmware()) { - // PX4 stack is inconsistent with repect to sending back an Ack from a COMMAND_LONG, hence we can't support retry logic for it. - return; + // We always let AUTOPILOT_CAPABILITIES go through multiple times even if we don't get acks. This is because + // we really need to get capabilities and version info back over a lossy link. + if (queuedCommand.command != MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES) { + if (px4Firmware()) { + // Older PX4 firmwares are inconsistent with repect to sending back an Ack from a COMMAND_LONG, hence we can't support retry logic for it. + if (_firmwareMajorVersion != versionNotSetValue) { + // If no version set assume lastest master dev build, so acks are suppored + if (_firmwareMajorVersion <= 1 && _firmwareMinorVersion <= 5 && _firmwarePatchVersion <= 3) { + // Acks not supported in this version + return; + } + } + } else { + if (queuedCommand.command == MAV_CMD_START_RX_PAIR) { + // The implementation of this command comes from the IO layer and is shared across stacks. So for other firmwares + // we aren't really sure whether they are correct or not. + return; + } + } } qDebug() << "Vehicle::_sendMavCommandAgain retrying command:_mavCommandRetryCount" << queuedCommand.command << _mavCommandRetryCount; } -- GitLab From 3499594fe93b50ddcb7c772c23900c4dbb78b059 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 13:20:04 -0800 Subject: [PATCH 145/398] Update PX4 MockLink params --- src/FactSystem/FactSystemTest.qml | 2 +- src/FactSystem/FactSystemTestBase.cc | 4 +- src/comm/PX4MockLink.params | 910 +++++++++++---------------- 3 files changed, 358 insertions(+), 558 deletions(-) diff --git a/src/FactSystem/FactSystemTest.qml b/src/FactSystem/FactSystemTest.qml index 127c2470c..a9155de82 100644 --- a/src/FactSystem/FactSystemTest.qml +++ b/src/FactSystem/FactSystemTest.qml @@ -32,7 +32,7 @@ FactPanel { TextInput { text: fact2.value - property Fact fact2: controller.getParameterFact(50, "RC_MAP_THROTTLE") + property Fact fact2: controller.getParameterFact(1, "RC_MAP_THROTTLE") onAccepted: fact2.value = text } diff --git a/src/FactSystem/FactSystemTestBase.cc b/src/FactSystem/FactSystemTestBase.cc index 41cb1195c..58fbe6ea8 100644 --- a/src/FactSystem/FactSystemTestBase.cc +++ b/src/FactSystem/FactSystemTestBase.cc @@ -58,8 +58,8 @@ void FactSystemTestBase::_parameter_default_component_id_test(void) void FactSystemTestBase::_parameter_specific_component_id_test(void) { - QVERIFY(_vehicle->parameterManager()->parameterExists(50, "RC_MAP_THROTTLE")); - Fact* fact = _vehicle->parameterManager()->getParameter(50, "RC_MAP_THROTTLE"); + QVERIFY(_vehicle->parameterManager()->parameterExists(1, "RC_MAP_THROTTLE")); + Fact* fact = _vehicle->parameterManager()->getParameter(1, "RC_MAP_THROTTLE"); QVERIFY(fact != NULL); QVariant factValue = fact->rawValue(); QCOMPARE(factValue.isValid(), true); diff --git a/src/comm/PX4MockLink.params b/src/comm/PX4MockLink.params index f975f257b..3ae08c462 100644 --- a/src/comm/PX4MockLink.params +++ b/src/comm/PX4MockLink.params @@ -1,556 +1,356 @@ -# Onboard parameters for system MAV 001 +# Onboard parameters for vehicle 1 # -# MAV ID COMPONENT ID PARAM NAME VALUE (FLOAT) -1 50 ATT_ACC_COMP 2 6 -1 50 ATT_J11 0.0018 9 -1 50 ATT_J22 0.0018 9 -1 50 ATT_J33 0.0037 9 -1 50 ATT_J_EN 0 6 -1 50 ATT_MAG_DECL 0 9 -1 50 BAT_CRIT_THR 0.05 9 -1 50 BAT_CAPACITY -1 9 -1 50 BAT_C_SCALING 0.0124 9 -1 50 BAT_LOW_THR 0.15 9 -1 50 BAT_N_CELLS 3 6 -1 50 BAT_V_CHARGED 4.2 9 -1 50 BAT_V_EMPTY 3.4 9 -1 50 BAT_V_LOAD_DROP 0.07 9 -1 50 BAT_V_SCALE_IO 10000 6 -1 50 BAT_V_SCALING 0.00989 9 -1 50 BD_GPROPERTIES 0.03 9 -1 50 BD_OBJ_CD 0.1 9 -1 50 BD_OBJ_MASS 0.6 9 -1 50 BD_OBJ_SURFACE 0.00311725 9 -1 50 BD_PRECISION 30 9 -1 50 BD_TURNRADIUS 120 9 -1 50 CAL_ACC0_ID 1246218 6 -1 50 CAL_ACC0_XOFF 0.0657825 9 -1 50 CAL_ACC0_XSCALE 0.99657 9 -1 50 CAL_ACC0_YOFF -0.0470138 9 -1 50 CAL_ACC0_YSCALE 1.00066 9 -1 50 CAL_ACC0_ZOFF 0.2244 9 -1 50 CAL_ACC0_ZSCALE 0.985475 9 -1 50 CAL_ACC1_ID 1114634 6 -1 50 CAL_ACC1_XOFF 0.976983 9 -1 50 CAL_ACC1_XSCALE 0.978487 9 -1 50 CAL_ACC1_YOFF 0.92215 9 -1 50 CAL_ACC1_YSCALE 1.01181 9 -1 50 CAL_ACC1_ZOFF 1.07291 9 -1 50 CAL_ACC1_ZSCALE 1.00673 9 -1 50 CAL_ACC2_ID 0 6 -1 50 CAL_ACC2_XOFF 0 9 -1 50 CAL_ACC2_XSCALE 1 9 -1 50 CAL_ACC2_YOFF 0 9 -1 50 CAL_ACC2_YSCALE 1 9 -1 50 CAL_ACC2_ZOFF 0 9 -1 50 CAL_ACC2_ZSCALE 1 9 -1 50 CAL_BOARD_ID 825374003 6 -1 50 CAL_GYRO0_ID 2163722 6 -1 50 CAL_GYRO0_XOFF 0.0102227 9 -1 50 CAL_GYRO0_XSCALE 1 9 -1 50 CAL_GYRO0_YOFF 0.0485427 9 -1 50 CAL_GYRO0_YSCALE 1 9 -1 50 CAL_GYRO0_ZOFF -0.00950837 9 -1 50 CAL_GYRO0_ZSCALE 1 9 -1 50 CAL_GYRO1_ID 2228490 6 -1 50 CAL_GYRO1_XOFF 0.012822 9 -1 50 CAL_GYRO1_XSCALE 1 9 -1 50 CAL_GYRO1_YOFF 0.00126972 9 -1 50 CAL_GYRO1_YSCALE 1 9 -1 50 CAL_GYRO1_ZOFF -0.0285923 9 -1 50 CAL_GYRO1_ZSCALE 1 9 -1 50 CAL_GYRO2_ID 0 6 -1 50 CAL_GYRO2_XOFF 0 9 -1 50 CAL_GYRO2_XSCALE 1 9 -1 50 CAL_GYRO2_YOFF 0 9 -1 50 CAL_GYRO2_YSCALE 1 9 -1 50 CAL_GYRO2_ZOFF 0 9 -1 50 CAL_GYRO2_ZSCALE 1 9 -1 50 CAL_MAG0_ID 73225 6 -1 50 CAL_MAG0_ROT 0 6 -1 50 CAL_MAG0_XOFF 0.0546612 9 -1 50 CAL_MAG0_XSCALE 1.03474 9 -1 50 CAL_MAG0_YOFF -0.0299049 9 -1 50 CAL_MAG0_YSCALE 0.934338 9 -1 50 CAL_MAG0_ZOFF -0.100793 9 -1 50 CAL_MAG0_ZSCALE 0.981021 9 -1 50 CAL_MAG1_ID 131594 6 -1 50 CAL_MAG1_ROT -1 6 -1 50 CAL_MAG1_XOFF -0.0239289 9 -1 50 CAL_MAG1_XSCALE 1 9 -1 50 CAL_MAG1_YOFF 0.0811809 9 -1 50 CAL_MAG1_YSCALE 1 9 -1 50 CAL_MAG1_ZOFF 0.0455695 9 -1 50 CAL_MAG1_ZSCALE 1 9 -1 50 CAL_MAG2_ID 0 6 -1 50 CAL_MAG2_ROT -1 6 -1 50 CAL_MAG2_XOFF 0 9 -1 50 CAL_MAG2_XSCALE 1 9 -1 50 CAL_MAG2_YOFF 0 9 -1 50 CAL_MAG2_YSCALE 1 9 -1 50 CAL_MAG2_ZOFF 0 9 -1 50 CAL_MAG2_ZSCALE 1 9 -1 50 CBRK_AIRSPD_CHK 0 6 -1 50 CBRK_ENGINEFAIL 284953 6 -1 50 CBRK_FLIGHTTERM 121212 6 -1 50 CBRK_IO_SAFETY 0 6 -1 50 CBRK_NO_VISION 0 6 -1 50 CBRK_RATE_CTRL 0 6 -1 50 CBRK_SUPPLY_CHK 0 6 -1 50 COM_DISARM_LAND 0 3 -1 50 COM_DL_LOSS_T 10 6 -1 50 COM_DL_REG_T 0 6 -1 50 COM_EF_C2T 5 9 -1 50 COM_EF_THROT 0.5 9 -1 50 COM_EF_TIME 10 9 -1 50 COM_FLTMODE1 -1 6 -1 50 COM_FLTMODE2 -1 6 -1 50 COM_FLTMODE3 -1 6 -1 50 COM_FLTMODE4 -1 6 -1 50 COM_FLTMODE5 -1 6 -1 50 COM_FLTMODE6 -1 6 -1 50 COM_LOW_BAT_ACT 0 5 -1 50 COM_RC_IN_MODE 0 6 -1 50 COM_RC_LOSS_T 0.5 9 -1 50 EKF_ATT_V3_Q0 0.0001 9 -1 50 EKF_ATT_V3_Q1 0.08 9 -1 50 EKF_ATT_V3_Q2 0.009 9 -1 50 EKF_ATT_V3_Q3 0.005 9 -1 50 EKF_ATT_V4_R0 0.0008 9 -1 50 EKF_ATT_V4_R1 10000 9 -1 50 EKF_ATT_V4_R2 100 9 -1 50 FPE_DEBUG 0 6 -1 50 FPE_LO_THRUST 0.4 9 -1 50 FPE_SONAR_LP_L 0.2 9 -1 50 FPE_SONAR_LP_U 0.5 9 -1 50 FW_AIRSPD_MAX 20 9 -1 50 FW_AIRSPD_MIN 10 9 -1 50 FW_AIRSPD_TRIM 15 9 -1 50 FW_ATT_TC 0.5 9 -1 50 FW_CLMBOUT_DIFF 25 9 -1 50 FW_FLARE_PMAX 15 9 -1 50 FW_FLARE_PMIN 2.5 9 -1 50 FW_L1_DAMPING 0.75 9 -1 50 FW_L1_PERIOD 25 9 -1 50 FW_LND_ANG 5 9 -1 50 FW_LND_FLALT 8 9 -1 50 FW_LND_HHDIST 15 9 -1 50 FW_LND_HVIRT 10 9 -1 50 FW_LND_TLALT -1 9 -1 50 FW_LND_USETER 0 6 -1 50 FW_MAN_P_MAX 45 9 -1 50 FW_MAN_R_MAX 45 9 -1 50 FW_PR_FF 0.4 9 -1 50 FW_PR_I 0 9 -1 50 FW_PR_IMAX 0.2 9 -1 50 FW_PR_P 0.05 9 -1 50 FW_PSP_OFF 0 9 -1 50 FW_P_LIM_MAX 45 9 -1 50 FW_P_LIM_MIN -45 9 -1 50 FW_P_RMAX_NEG 0 9 -1 50 FW_P_RMAX_POS 0 9 -1 50 FW_P_ROLLFF 0 9 -1 50 FW_RR_FF 0.3 9 -1 50 FW_RR_I 0 9 -1 50 FW_RR_IMAX 0.2 9 -1 50 FW_RR_P 0.05 9 -1 50 FW_RSP_OFF 0 9 -1 50 FW_R_LIM 45 9 -1 50 FW_R_RMAX 0 9 -1 50 FW_THR_CRUISE 0.7 9 -1 50 FW_THR_LND_MAX 1 9 -1 50 FW_THR_MAX 1 9 -1 50 FW_THR_MIN 0 9 -1 50 FW_THR_SLEW_MAX 0 9 -1 50 FW_T_CLMB_MAX 5 9 -1 50 FW_T_HGT_OMEGA 3 9 -1 50 FW_T_HRATE_FF 0 9 -1 50 FW_T_HRATE_P 0.05 9 -1 50 FW_T_INTEG_GAIN 0.1 9 -1 50 FW_T_PTCH_DAMP 0 9 -1 50 FW_T_RLL2THR 10 9 -1 50 FW_T_SINK_MAX 5 9 -1 50 FW_T_SINK_MIN 2 9 -1 50 FW_T_SPDWEIGHT 1 9 -1 50 FW_T_SPD_OMEGA 2 9 -1 50 FW_T_SRATE_P 0.05 9 -1 50 FW_T_THRO_CONST 8 9 -1 50 FW_T_THR_DAMP 0.5 9 -1 50 FW_T_TIME_CONST 5 9 -1 50 FW_T_VERT_ACC 7 9 -1 50 FW_YCO_METHOD 0 6 -1 50 FW_YCO_VMIN 1000 9 -1 50 FW_YR_FF 0.3 9 -1 50 FW_YR_I 0 9 -1 50 FW_YR_IMAX 0.2 9 -1 50 FW_YR_P 0.05 9 -1 50 FW_Y_RMAX 0 9 -1 50 GF_ACTION 1 6 -1 50 GF_ALTMODE 0 6 -1 50 GF_COUNT -1 6 -1 50 GF_MAX_HOR_DIST -1 6 -1 50 GF_MAX_VER_DIST -1 6 -1 50 GF_SOURCE 0 6 -1 50 INAV_DELAY_GPS 0.2 9 -1 50 INAV_ENABLED 0 6 -1 50 INAV_FLOW_K 0.15 9 -1 50 INAV_FLOW_Q_MIN 0.5 9 -1 50 INAV_LAND_DISP 0.7 9 -1 50 INAV_LAND_T 3 9 -1 50 INAV_LAND_THR 0.2 9 -1 50 INAV_SONAR_ERR 0.5 9 -1 50 INAV_SONAR_FILT 0.05 9 -1 50 INAV_W_ACC_BIAS 0.05 9 -1 50 INAV_W_GPS_FLOW 0.1 9 -1 50 INAV_W_XY_FLOW 5 9 -1 50 INAV_W_XY_GPS_P 1 9 -1 50 INAV_W_XY_GPS_V 2 9 -1 50 INAV_W_XY_RES_V 0.5 9 -1 50 INAV_W_XY_VIS_P 7 9 -1 50 INAV_W_XY_VIS_V 0 9 -1 50 INAV_W_Z_BARO 0.5 9 -1 50 INAV_W_Z_GPS_P 0.005 9 -1 50 INAV_W_Z_GPS_V 0 9 -1 50 INAV_W_Z_SONAR 3 9 -1 50 INAV_W_Z_VIS_P 0.5 9 -1 50 INAV_W_Z_VIS_V 0.5 9 -1 50 LPE_ENABLED 0 6 -1 50 LPE_INTEGRATE 1 6 -1 50 LPE_FLW_XY 0.015 9 -1 50 LPE_SNR_Z 0.2 9 -1 50 LPE_LDR_Z 0.1 9 -1 50 LPE_ACC_XY 1 9 -1 50 LPE_ACC_Z 0.10 9 -1 50 LPE_BAR_Z 3 9 -1 50 LPE_GPS_XY 2 9 -1 50 LPE_GPS_Z 3 9 -1 50 LPE_GPS_VXY 1 9 -1 50 LPE_GPS_VZ 1 9 -1 50 LPE_VIS_XY 0.5 9 -1 50 LPE_VIS_Z 0.5 9 -1 50 LPE_VIS_VXY 1 9 -1 50 LPE_VIS_VZ 1 9 -1 50 LPE_NO_VISION 0 6 -1 50 LPE_VIC_P 0.05 9 -1 50 LPE_PN_P 1 9 -1 50 LPE_PN_V 0.1 9 -1 50 LAUN_ALL_ON 0 6 -1 50 LAUN_CAT_A 30 9 -1 50 LAUN_CAT_MDEL 0 9 -1 50 LAUN_CAT_PMAX 30 9 -1 50 LAUN_CAT_T 0.05 9 -1 50 LAUN_THR_PRE 0 9 -1 50 LNDFW_AIRSPD_MAX 10 9 -1 50 LNDFW_VEL_XY_MAX 0.2 9 -1 50 LNDFW_VEL_Z_MAX 10 9 -1 50 LNDMC_ROT_MAX 20 9 -1 50 LNDMC_THR_MAX 0.2 9 -1 50 LNDMC_XY_VEL_MAX 1 9 -1 50 LNDMC_Z_VEL_MAX 0.3 9 -1 50 MAV_COMP_ID 50 6 -1 50 MAV_FWDEXTSP 1 6 -1 50 MAV_SYS_ID 76 6 -1 50 MAV_TYPE 2 6 -1 50 MAV_USEHILGPS 0 6 -1 50 MC_ACRO_P_MAX 90 9 -1 50 MC_ACRO_R_MAX 90 9 -1 50 MC_ACRO_Y_MAX 120 9 -1 50 MC_PITCHRATE_D 0.004 9 -1 50 MC_PITCHRATE_I 0 9 -1 50 MC_PITCHRATE_P 0.13 9 -1 50 MC_PITCH_P 7 9 -1 50 MC_PITCH_TC 0.200 9 -1 50 MC_ROLLRATE_D 0.004 9 -1 50 MC_ROLLRATE_I 0 9 -1 50 MC_ROLLRATE_P 0.13 9 -1 50 MC_ROLL_P 7 9 -1 50 MC_ROLL_TC 0.200 9 -1 50 MC_YAWRATE_D 0 9 -1 50 MC_YAWRATE_I 0.25 9 -1 50 MC_YAWRATE_MAX 120 9 -1 50 MC_YAWRATE_P 0.25 9 -1 50 MC_YAW_FF 0.5 9 -1 50 MC_YAW_P 2.5 9 -1 50 MIS_ALTMODE 0 6 -1 50 MIS_DIST_1WP 500 9 -1 50 MIS_ONBOARD_EN 1 6 -1 50 MIS_TAKEOFF_ALT 10 9 -1 50 MIS_YAWMODE 0 6 -1 50 MKBLCTRL_TEST 0 6 -1 50 MPC_LAND_SPEED 1 9 -1 50 MPC_MAN_P_MAX 35 9 -1 50 MPC_MAN_R_MAX 35 9 -1 50 MPC_MAN_Y_MAX 120 9 -1 50 MPC_MANTHR_MAX 0.900 9 -1 50 MPC_MANTHR_MIN 0.080 9 -1 50 MPC_THR_MAX 1 9 -1 50 MPC_THR_MIN 0.1 9 -1 50 MPC_THR_HOVER 0.5 9 -1 50 MPC_TILTMAX_AIR 45 9 -1 50 MPC_TILTMAX_LND 15 9 -1 50 MPC_XY_FF 0.5 9 -1 50 MPC_XY_P 1 9 -1 50 MPC_XY_VEL_D 0.01 9 -1 50 MPC_XY_VEL_I 0.02 9 -1 50 MPC_XY_VEL_MAX 5 9 -1 50 MPC_XY_VEL_P 0.1 9 -1 50 MPC_Z_FF 0.5 9 -1 50 MPC_Z_P 1 9 -1 50 MPC_Z_VEL_D 0 9 -1 50 MPC_Z_VEL_I 0.02 9 -1 50 MPC_Z_VEL_MAX 3 9 -1 50 MPC_Z_VEL_P 0.1 9 -1 50 MT_ACC_D 0 9 -1 50 MT_ACC_D_LP 0.5 9 -1 50 MT_ACC_MAX 40 9 -1 50 MT_ACC_MIN -40 9 -1 50 MT_ACC_P 0.3 9 -1 50 MT_AD_LP 0.5 9 -1 50 MT_ALT_LP 1 9 -1 50 MT_A_LP 0.5 9 -1 50 MT_ENABLED 0 6 -1 50 MT_FPA_D 0 9 -1 50 MT_FPA_D_LP 1 9 -1 50 MT_FPA_LP 1 9 -1 50 MT_FPA_MAX 30 9 -1 50 MT_FPA_MIN -20 9 -1 50 MT_FPA_P 0.3 9 -1 50 MT_LND_PIT_MAX 15 9 -1 50 MT_LND_PIT_MIN -5 9 -1 50 MT_LND_THR_MAX 0 9 -1 50 MT_LND_THR_MIN 0 9 -1 50 MT_PIT_FF 0.4 9 -1 50 MT_PIT_I 0.03 9 -1 50 MT_PIT_I_MAX 10 9 -1 50 MT_PIT_MAX 20 9 -1 50 MT_PIT_MIN -45 9 -1 50 MT_PIT_OFF 0 9 -1 50 MT_PIT_P 0.03 9 -1 50 MT_THR_FF 0.7 9 -1 50 MT_THR_I 0.25 9 -1 50 MT_THR_I_MAX 10 9 -1 50 MT_THR_MAX 1 9 -1 50 MT_THR_MIN 0 9 -1 50 MT_THR_OFF 0.7 9 -1 50 MT_THR_P 0.1 9 -1 50 MT_TKF_PIT_MAX 45 9 -1 50 MT_TKF_PIT_MIN 0 9 -1 50 MT_TKF_THR_MAX 1 9 -1 50 MT_TKF_THR_MIN 1 9 -1 50 MT_USP_PIT_MAX 0 9 -1 50 MT_USP_PIT_MIN -45 9 -1 50 MT_USP_THR_MAX 1 9 -1 50 MT_USP_THR_MIN 1 9 -1 50 NAV_ACC_RAD 2 9 -1 50 NAV_AH_ALT 600 9 -1 50 NAV_AH_LAT -265847810 6 -1 50 NAV_AH_LON 1518423250 6 -1 50 NAV_DLL_ACT 0 2 -1 50 NAV_DLL_AH_T 120 9 -1 50 NAV_DLL_CHSK 0 6 -1 50 NAV_DLL_CH_ALT 600 9 -1 50 NAV_DLL_CH_LAT -266072120 6 -1 50 NAV_DLL_CH_LON 1518453890 6 -1 50 NAV_DLL_CH_T 120 9 -1 50 NAV_DLL_N 2 6 -1 50 NAV_DLL_OBC 0 6 -1 50 NAV_GPSF_LT 30 9 -1 50 NAV_GPSF_P 0 9 -1 50 NAV_GPSF_R 15 9 -1 50 NAV_GPSF_TR 0.7 9 -1 50 NAV_LOITER_RAD 50 9 -1 50 NAV_RCL_ACT 0 2 -1 50 NAV_RCL_LT 120 9 -1 50 NAV_RCL_OBC 0 6 -1 50 PE_ABIAS_PNOISE 5e-05 9 -1 50 PE_ACC_PNOISE 0.25 9 -1 50 PE_EAS_NOISE 1.4 9 -1 50 PE_GBIAS_PNOISE 1e-07 9 -1 50 PE_GPS_ALT_WGT 0.9 9 -1 50 PE_GYRO_PNOISE 0.015 9 -1 50 PE_HGT_DELAY_MS 350 6 -1 50 PE_MAGB_PNOISE 0.0003 9 -1 50 PE_MAGE_PNOISE 0.0003 9 -1 50 PE_MAG_DELAY_MS 30 6 -1 50 PE_MAG_NOISE 0.05 9 -1 50 PE_POSDEV_INIT 5 9 -1 50 PE_POSD_NOISE 1 9 -1 50 PE_POSNE_NOISE 0.5 9 -1 50 PE_POS_DELAY_MS 210 6 -1 50 PE_TAS_DELAY_MS 210 6 -1 50 PE_VELD_NOISE 0.7 9 -1 50 PE_VELNE_NOISE 0.5 9 -1 50 PE_VEL_DELAY_MS 230 6 -1 50 RC10_DZ 0 9 -1 50 RC10_MAX 2000 9 -1 50 RC10_MIN 1000 9 -1 50 RC10_REV 1 9 -1 50 RC10_TRIM 1500 9 -1 50 RC11_DZ 0 9 -1 50 RC11_MAX 2000 9 -1 50 RC11_MIN 1000 9 -1 50 RC11_REV 1 9 -1 50 RC11_TRIM 1500 9 -1 50 RC12_DZ 0 9 -1 50 RC12_MAX 2000 9 -1 50 RC12_MIN 1000 9 -1 50 RC12_REV 1 9 -1 50 RC12_TRIM 1500 9 -1 50 RC13_DZ 0 9 -1 50 RC13_MAX 2000 9 -1 50 RC13_MIN 1000 9 -1 50 RC13_REV 1 9 -1 50 RC13_TRIM 1500 9 -1 50 RC14_DZ 0 9 -1 50 RC14_MAX 2000 9 -1 50 RC14_MIN 1000 9 -1 50 RC14_REV 1 9 -1 50 RC14_TRIM 1500 9 -1 50 RC15_DZ 0 9 -1 50 RC15_MAX 2000 9 -1 50 RC15_MIN 1000 9 -1 50 RC15_REV 1 9 -1 50 RC15_TRIM 1500 9 -1 50 RC16_DZ 0 9 -1 50 RC16_MAX 2000 9 -1 50 RC16_MIN 1000 9 -1 50 RC16_REV 1 9 -1 50 RC16_TRIM 1500 9 -1 50 RC17_DZ 0 9 -1 50 RC17_MAX 2000 9 -1 50 RC17_MIN 1000 9 -1 50 RC17_REV 1 9 -1 50 RC17_TRIM 1500 9 -1 50 RC18_DZ 0 9 -1 50 RC18_MAX 2000 9 -1 50 RC18_MIN 1000 9 -1 50 RC18_REV 1 9 -1 50 RC18_TRIM 1500 9 -1 50 RC1_DZ 10 9 -1 50 RC1_MAX 2000 9 -1 50 RC1_MIN 1000 9 -1 50 RC1_REV 1 9 -1 50 RC1_TRIM 1501 9 -1 50 RC2_DZ 10 9 -1 50 RC2_MAX 2000 9 -1 50 RC2_MIN 1000 9 -1 50 RC2_REV 1 9 -1 50 RC2_TRIM 1501 9 -1 50 RC3_DZ 10 9 -1 50 RC3_MAX 2000 9 -1 50 RC3_MIN 1000 9 -1 50 RC3_REV 1 9 -1 50 RC3_TRIM 1501 9 -1 50 RC4_DZ 10 9 -1 50 RC4_MAX 2000 9 -1 50 RC4_MIN 1000 9 -1 50 RC4_REV 1 9 -1 50 RC4_TRIM 1501 9 -1 50 RC5_DZ 10 9 -1 50 RC5_MAX 2000 9 -1 50 RC5_MIN 1000 9 -1 50 RC5_REV 1 9 -1 50 RC5_TRIM 1500 9 -1 50 RC6_DZ 10 9 -1 50 RC6_MAX 2000 9 -1 50 RC6_MIN 1000 9 -1 50 RC6_REV 1 9 -1 50 RC6_TRIM 1500 9 -1 50 RC7_DZ 10 9 -1 50 RC7_MAX 2000 9 -1 50 RC7_MIN 1000 9 -1 50 RC7_REV 1 9 -1 50 RC7_TRIM 1500 9 -1 50 RC8_DZ 10 9 -1 50 RC8_MAX 2000 9 -1 50 RC8_MIN 1000 9 -1 50 RC8_REV 1 9 -1 50 RC8_TRIM 1500 9 -1 50 RC9_DZ 0 9 -1 50 RC9_MAX 2000 9 -1 50 RC9_MIN 1000 9 -1 50 RC9_REV 1 9 -1 50 RC9_TRIM 1500 9 -1 50 RC_ACRO_TH 0.5 9 -1 50 RC_ASSIST_TH 0.25 9 -1 50 RC_AUTO_TH 0.75 9 -1 50 RC_CHAN_CNT 0 6 -1 50 RC_DSM_BIND -1 6 -1 50 RC_FAILS_THR 0 6 -1 50 RC_LOITER_TH 0.5 9 -1 50 RC_MAP_ACRO_SW 0 6 -1 50 RC_MAP_AUX1 11 6 -1 50 RC_MAP_AUX2 12 6 -1 50 RC_MAP_AUX3 0 6 -1 50 RC_MAP_FAILSAFE 0 6 -1 50 RC_MAP_FLAPS 10 6 -1 50 RC_MAP_FLTMODE 0 6 -1 50 RC_MAP_KILL_SW 0 6 -1 50 RC_MAP_LOITER_SW 9 6 -1 50 RC_MAP_MODE_SW 6 6 -1 50 RC_MAP_OFFB_SW 0 6 -1 50 RC_MAP_PARAM1 0 6 -1 50 RC_MAP_PARAM2 0 6 -1 50 RC_MAP_PARAM3 0 6 -1 50 RC_MAP_PITCH 2 6 -1 50 RC_MAP_POSCTL_SW 7 6 -1 50 RC_MAP_RETURN_SW 8 6 -1 50 RC_MAP_ROLL 1 6 -1 50 RC_MAP_THROTTLE 3 6 -1 50 RC_MAP_YAW 4 6 -1 50 RC_OFFB_TH 0.5 9 -1 50 RC_POSCTL_TH 0.5 9 -1 50 RC_RETURN_TH 0.5 9 -1 50 RC_TH_USER 1 6 -1 50 RTL_DESCEND_ALT 10 9 -1 50 RTL_LAND_DELAY -1 9 -1 50 RTL_LOITER_RAD 50 9 -1 50 RTL_RETURN_ALT 30 9 -1 50 RV_YAW_P 0.1 9 -1 50 SDLOG_EXT -1 6 -1 50 SDLOG_RATE -1 6 -1 50 SENS_BARO_QNH 1013.25 9 -1 50 SENS_BOARD_ROT 0 6 -1 50 SENS_BOARD_X_OFF 0 9 -1 50 SENS_BOARD_Y_OFF 0 9 -1 50 SENS_BOARD_Z_OFF 0 9 -1 50 SENS_DPRES_ANSC 0 9 -1 50 SENS_DPRES_OFF 0 9 -1 50 SENS_EXT_MAG 0 6 -1 50 SENS_EXT_MAG_ROT 0 6 -1 50 SENS_FLOW_ROT 0 6 -1 50 SYS_AUTOCONFIG 0 6 -1 50 SYS_AUTOSTART 10016 6 -1 50 SYS_COMPANION 157600 6 -1 50 SYS_PARAM_VER 1 6 -1 50 SYS_RESTART_TYPE 0 6 -1 50 SYS_USE_IO 1 6 -1 50 TEST_D 0.01 9 -1 50 TEST_DEV 2 9 -1 50 TEST_D_LP 10 9 -1 50 TEST_HP 10 9 -1 50 TEST_I 0.1 9 -1 50 TEST_I_MAX 1 9 -1 50 TEST_LP 10 9 -1 50 TEST_MAX 1 9 -1 50 TEST_MEAN 1 9 -1 50 TEST_MIN -1 9 -1 50 TEST_P 0.2 9 -1 50 TEST_TRIM 0.5 9 -1 50 TRIM_PITCH 0 9 -1 50 TRIM_ROLL 0 9 -1 50 TRIM_YAW 0 9 -1 50 UAVCAN_BITRATE 1000000 6 -1 50 UAVCAN_ENABLE 0 6 -1 50 UAVCAN_NODE_ID 1 6 -1 50 VT_ARSP_LP_GAIN 0.3 9 -1 50 VT_FW_PERM_STAB 0 6 -1 50 VT_FW_PITCH_TRIM 0 9 -1 50 VT_IDLE_PWM_MC 900 6 -1 50 VT_MC_ARSPD_MAX 30 9 -1 50 VT_MC_ARSPD_MIN 10 9 -1 50 VT_MC_ARSPD_TRIM 10 9 -1 50 VT_MOT_COUNT 0 6 -1 50 VT_POWER_MAX 120 9 -1 50 VT_PROP_EFF 0 9 -1 51 COMPONENT_51 51 6 +# MAV ID COMPONENT ID PARAM NAME VALUE (FLOAT) +1 1 BAT_A_PER_V 15.391030311584472656 9 +1 1 BAT_CAPACITY -1.000000000000000000 9 +1 1 BAT_CNT_V_CURR 0.000805664050858468 9 +1 1 BAT_CNT_V_VOLT 0.000805664050858468 9 +1 1 BAT_CRIT_THR 0.070000000298023224 9 +1 1 BAT_LOW_THR 0.150000005960464478 9 +1 1 BAT_N_CELLS 3 6 +1 1 BAT_R_INTERNAL -1.000000000000000000 9 +1 1 BAT_SOURCE 0 6 +1 1 BAT_V_CHARGED 4.050000190734863281 9 +1 1 BAT_V_DIV 10.177939414978027344 9 +1 1 BAT_V_EMPTY 3.400000095367431641 9 +1 1 BAT_V_LOAD_DROP 0.300000011920928955 9 +1 1 BAT_V_OFFS_CURR 0.000000000000000000 9 +1 1 BAT_V_SCALE_IO 10000 6 +1 1 CAL_ACC0_ID 1246218 6 +1 1 CAL_ACC1_ID 1114634 6 +1 1 CAL_ACC1_XOFF 0.982475280761718750 9 +1 1 CAL_ACC1_XSCALE 0.977573156356811523 9 +1 1 CAL_ACC1_YOFF 0.922321319580078125 9 +1 1 CAL_ACC1_YSCALE 1.008152961730957031 9 +1 1 CAL_ACC1_ZOFF 1.137038230895996094 9 +1 1 CAL_ACC1_ZSCALE 1.007824659347534180 9 +1 1 CAL_ACC2_ID 0 6 +1 1 CAL_ACC2_XOFF 0.000000000000000000 9 +1 1 CAL_ACC2_XSCALE 1.000000000000000000 9 +1 1 CAL_ACC2_YOFF 0.000000000000000000 9 +1 1 CAL_ACC2_YSCALE 1.000000000000000000 9 +1 1 CAL_ACC2_ZOFF 0.000000000000000000 9 +1 1 CAL_ACC2_ZSCALE 1.000000000000000000 9 +1 1 CAL_ACC_PRIME 1246218 6 +1 1 CAL_BARO_PRIME 0 6 +1 1 CAL_GYRO0_ID 2162688 6 +1 1 CAL_GYRO1_ID 2228490 6 +1 1 CAL_GYRO1_XOFF 0.015412596054375172 9 +1 1 CAL_GYRO1_XSCALE 1.000000000000000000 9 +1 1 CAL_GYRO1_YOFF 0.004993732087314129 9 +1 1 CAL_GYRO1_YSCALE 1.000000000000000000 9 +1 1 CAL_GYRO1_ZOFF -0.027027824893593788 9 +1 1 CAL_GYRO1_ZSCALE 1.000000000000000000 9 +1 1 CAL_GYRO2_ID 0 6 +1 1 CAL_GYRO2_XOFF 0.000000000000000000 9 +1 1 CAL_GYRO2_XSCALE 1.000000000000000000 9 +1 1 CAL_GYRO2_YOFF 0.000000000000000000 9 +1 1 CAL_GYRO2_YSCALE 1.000000000000000000 9 +1 1 CAL_GYRO2_ZOFF 0.000000000000000000 9 +1 1 CAL_GYRO2_ZSCALE 1.000000000000000000 9 +1 1 CAL_GYRO_PRIME 2162688 6 +1 1 CAL_MAG0_ID 73225 6 +1 1 CAL_MAG0_ROT 0 6 +1 1 CAL_MAG1_ID 131594 6 +1 1 CAL_MAG1_ROT -1 6 +1 1 CAL_MAG1_XOFF 0.010499725118279457 9 +1 1 CAL_MAG1_XSCALE 1.000000000000000000 9 +1 1 CAL_MAG1_YOFF 0.041090622544288635 9 +1 1 CAL_MAG1_YSCALE 1.000000000000000000 9 +1 1 CAL_MAG1_ZOFF 0.046924047172069550 9 +1 1 CAL_MAG1_ZSCALE 1.000000000000000000 9 +1 1 CAL_MAG2_ID 0 6 +1 1 CAL_MAG2_ROT -1 6 +1 1 CAL_MAG2_XOFF 0.000000000000000000 9 +1 1 CAL_MAG2_XSCALE 1.000000000000000000 9 +1 1 CAL_MAG2_YOFF 0.000000000000000000 9 +1 1 CAL_MAG2_YSCALE 1.000000000000000000 9 +1 1 CAL_MAG2_ZOFF 0.000000000000000000 9 +1 1 CAL_MAG2_ZSCALE 1.000000000000000000 9 +1 1 CAL_MAG_PRIME 73225 6 +1 1 CAL_MAG_SIDES 63 6 +1 1 CBRK_AIRSPD_CHK 0 6 +1 1 CBRK_BUZZER 0 6 +1 1 CBRK_ENGINEFAIL 284953 6 +1 1 CBRK_FLIGHTTERM 121212 6 +1 1 CBRK_GPSFAIL 0 6 +1 1 CBRK_IO_SAFETY 0 6 +1 1 CBRK_SUPPLY_CHK 0 6 +1 1 CBRK_USB_CHK 0 6 +1 1 COM_ARM_EKF_AB 0.004999999888241291 9 +1 1 COM_ARM_EKF_GB 0.000869999988935888 9 +1 1 COM_ARM_EKF_HGT 1.000000000000000000 9 +1 1 COM_ARM_EKF_POS 0.500000000000000000 9 +1 1 COM_ARM_EKF_VEL 0.500000000000000000 9 +1 1 COM_ARM_EKF_YAW 0.500000000000000000 9 +1 1 COM_ARM_IMU_ACC 0.699999988079071045 9 +1 1 COM_ARM_IMU_GYR 0.090000003576278687 9 +1 1 COM_ARM_WO_GPS 1 6 +1 1 COM_AUTOS_PAR 1 6 +1 1 COM_DISARM_LAND 0 6 +1 1 COM_DL_LOSS_T 10 6 +1 1 COM_DL_REG_T 0 6 +1 1 COM_EF_C2T 5.000000000000000000 9 +1 1 COM_EF_THROT 0.500000000000000000 9 +1 1 COM_EF_TIME 10.000000000000000000 9 +1 1 COM_FLTMODE1 -1 6 +1 1 COM_FLTMODE2 -1 6 +1 1 COM_FLTMODE3 -1 6 +1 1 COM_FLTMODE4 -1 6 +1 1 COM_FLTMODE5 -1 6 +1 1 COM_FLTMODE6 -1 6 +1 1 COM_HOME_H_T 5.000000000000000000 9 +1 1 COM_HOME_V_T 10.000000000000000000 9 +1 1 COM_LOW_BAT_ACT 0 6 +1 1 COM_OBL_ACT 0 6 +1 1 COM_OBL_RC_ACT 0 6 +1 1 COM_OF_LOSS_T 0.000000000000000000 9 +1 1 COM_RC_ARM_HYST 1000 6 +1 1 COM_RC_IN_MODE 0 6 +1 1 COM_RC_LOSS_T 0.500000000000000000 9 +1 1 EKF2_REC_RPL 0 6 +1 1 FW_AIRSPD_TRIM 15.000000000000000000 9 +1 1 FW_CLMBOUT_DIFF 10.000000000000000000 9 +1 1 FW_MAN_P_SC 1.000000000000000000 9 +1 1 FW_MAN_R_SC 1.000000000000000000 9 +1 1 FW_MAN_Y_SC 1.000000000000000000 9 +1 1 FW_THR_CRUISE 0.600000023841857910 9 +1 1 GF_ACTION 1 6 +1 1 GF_ALTMODE 0 6 +1 1 GF_COUNT -1 6 +1 1 GF_MAX_HOR_DIST 0.000000000000000000 9 +1 1 GF_MAX_VER_DIST 0.000000000000000000 9 +1 1 GF_SOURCE 0 6 +1 1 GPS_DUMP_COMM 0 6 +1 1 LED_RGB_MAXBRT 15 6 +1 1 MAV_BROADCAST 0 6 +1 1 MAV_COMP_ID 1 6 +1 1 MAV_FWDEXTSP 1 6 +1 1 MAV_PROTO_VER 0 6 +1 1 MAV_RADIO_ID 0 6 +1 1 MAV_SYS_ID 1 6 +1 1 MAV_TEST_PAR 1 6 +1 1 MAV_TYPE 2 6 +1 1 MAV_USEHILGPS 0 6 +1 1 MC_YAWRAUTO_MAX 45.000000000000000000 9 +1 1 MIS_ALTMODE 1 6 +1 1 MIS_DIST_1WP 900.000000000000000000 9 +1 1 MIS_LTRMIN_ALT 1.200000047683715820 9 +1 1 MIS_ONBOARD_EN 1 6 +1 1 MIS_TAKEOFF_ALT 2.500000000000000000 9 +1 1 MIS_YAWMODE 1 6 +1 1 MIS_YAW_ERR 12.000000000000000000 9 +1 1 MIS_YAW_TMT -1.000000000000000000 9 +1 1 MOT_SLEW_MAX 0.000000000000000000 9 +1 1 MPC_XY_CRUISE 5.000000000000000000 9 +1 1 NAV_ACC_RAD 10.000000000000000000 9 +1 1 NAV_AH_ALT 600.000000000000000000 9 +1 1 NAV_AH_LAT -265847810 6 +1 1 NAV_AH_LON 1518423250 6 +1 1 NAV_DLL_ACT 0 6 +1 1 NAV_DLL_AH_T 120.000000000000000000 9 +1 1 NAV_DLL_CHSK 0 6 +1 1 NAV_DLL_CH_ALT 600.000000000000000000 9 +1 1 NAV_DLL_CH_LAT -266072120 6 +1 1 NAV_DLL_CH_LON 1518453890 6 +1 1 NAV_DLL_CH_T 120.000000000000000000 9 +1 1 NAV_DLL_N 2 6 +1 1 NAV_FT_DST 8.000000000000000000 9 +1 1 NAV_FT_FS 1 6 +1 1 NAV_FT_RS 0.500000000000000000 9 +1 1 NAV_FW_ALT_RAD 10.000000000000000000 9 +1 1 NAV_GPSF_LT 30.000000000000000000 9 +1 1 NAV_GPSF_P 0.000000000000000000 9 +1 1 NAV_GPSF_R 15.000000000000000000 9 +1 1 NAV_GPSF_TR 0.699999988079071045 9 +1 1 NAV_LOITER_RAD 50.000000000000000000 9 +1 1 NAV_MC_ALT_RAD 3.000000000000000000 9 +1 1 NAV_MIN_FT_HT 8.000000000000000000 9 +1 1 NAV_RCL_ACT 2 6 +1 1 NAV_RCL_LT 120.000000000000000000 9 +1 1 PWM_AUX_DISARMED 1000 6 +1 1 PWM_AUX_MAX 2000 6 +1 1 PWM_AUX_MIN 1000 6 +1 1 PWM_DISARMED 0 6 +1 1 PWM_MAIN_REV1 0 6 +1 1 PWM_MAIN_REV2 0 6 +1 1 PWM_MAIN_REV3 0 6 +1 1 PWM_MAIN_REV4 0 6 +1 1 PWM_MAIN_REV5 0 6 +1 1 PWM_MAIN_REV6 0 6 +1 1 PWM_MAIN_REV7 0 6 +1 1 PWM_MAIN_REV8 0 6 +1 1 PWM_MAX 2000 6 +1 1 PWM_MIN 1000 6 +1 1 PWM_RATE 400 6 +1 1 PWM_SBUS_MODE 0 6 +1 1 RC10_DZ 0.000000000000000000 9 +1 1 RC10_MAX 2000.000000000000000000 9 +1 1 RC10_MIN 1000.000000000000000000 9 +1 1 RC10_REV 1.000000000000000000 9 +1 1 RC10_TRIM 1500.000000000000000000 9 +1 1 RC11_DZ 0.000000000000000000 9 +1 1 RC11_MAX 2000.000000000000000000 9 +1 1 RC11_MIN 1000.000000000000000000 9 +1 1 RC11_REV 1.000000000000000000 9 +1 1 RC11_TRIM 1500.000000000000000000 9 +1 1 RC12_DZ 0.000000000000000000 9 +1 1 RC12_MAX 2000.000000000000000000 9 +1 1 RC12_MIN 1000.000000000000000000 9 +1 1 RC12_REV 1.000000000000000000 9 +1 1 RC12_TRIM 1500.000000000000000000 9 +1 1 RC13_DZ 0.000000000000000000 9 +1 1 RC13_MAX 2000.000000000000000000 9 +1 1 RC13_MIN 1000.000000000000000000 9 +1 1 RC13_REV 1.000000000000000000 9 +1 1 RC13_TRIM 1500.000000000000000000 9 +1 1 RC14_DZ 0.000000000000000000 9 +1 1 RC14_MAX 2000.000000000000000000 9 +1 1 RC14_MIN 1000.000000000000000000 9 +1 1 RC14_REV 1.000000000000000000 9 +1 1 RC14_TRIM 1500.000000000000000000 9 +1 1 RC15_DZ 0.000000000000000000 9 +1 1 RC15_MAX 2000.000000000000000000 9 +1 1 RC15_MIN 1000.000000000000000000 9 +1 1 RC15_REV 1.000000000000000000 9 +1 1 RC15_TRIM 1500.000000000000000000 9 +1 1 RC16_DZ 0.000000000000000000 9 +1 1 RC16_MAX 2000.000000000000000000 9 +1 1 RC16_MIN 1000.000000000000000000 9 +1 1 RC16_REV 1.000000000000000000 9 +1 1 RC16_TRIM 1500.000000000000000000 9 +1 1 RC17_DZ 0.000000000000000000 9 +1 1 RC17_MAX 2000.000000000000000000 9 +1 1 RC17_MIN 1000.000000000000000000 9 +1 1 RC17_REV 1.000000000000000000 9 +1 1 RC17_TRIM 1500.000000000000000000 9 +1 1 RC18_DZ 0.000000000000000000 9 +1 1 RC18_MAX 2000.000000000000000000 9 +1 1 RC18_MIN 1000.000000000000000000 9 +1 1 RC18_REV 1.000000000000000000 9 +1 1 RC18_TRIM 1500.000000000000000000 9 +1 1 RC1_DZ 10.000000000000000000 9 +1 1 RC1_MAX 1900.000000000000000000 9 +1 1 RC1_MIN 1100.000000000000000000 9 +1 1 RC1_REV -1.000000000000000000 9 +1 1 RC1_TRIM 1492.000000000000000000 9 +1 1 RC2_DZ 10.000000000000000000 9 +1 1 RC2_MAX 1900.000000000000000000 9 +1 1 RC2_MIN 1100.000000000000000000 9 +1 1 RC2_REV 1.000000000000000000 9 +1 1 RC2_TRIM 1483.000000000000000000 9 +1 1 RC3_DZ 10.000000000000000000 9 +1 1 RC3_MAX 1901.000000000000000000 9 +1 1 RC3_MIN 1099.000000000000000000 9 +1 1 RC3_REV 1.000000000000000000 9 +1 1 RC3_TRIM 1099.000000000000000000 9 +1 1 RC4_DZ 10.000000000000000000 9 +1 1 RC4_MAX 1900.000000000000000000 9 +1 1 RC4_MIN 1100.000000000000000000 9 +1 1 RC4_REV -1.000000000000000000 9 +1 1 RC4_TRIM 1500.000000000000000000 9 +1 1 RC5_DZ 10.000000000000000000 9 +1 1 RC5_MAX 2000.000000000000000000 9 +1 1 RC5_MIN 1000.000000000000000000 9 +1 1 RC5_REV 1.000000000000000000 9 +1 1 RC5_TRIM 1500.000000000000000000 9 +1 1 RC6_DZ 10.000000000000000000 9 +1 1 RC6_MAX 1901.000000000000000000 9 +1 1 RC6_MIN 1099.000000000000000000 9 +1 1 RC6_REV 1.000000000000000000 9 +1 1 RC6_TRIM 1500.000000000000000000 9 +1 1 RC7_DZ 10.000000000000000000 9 +1 1 RC7_MAX 2000.000000000000000000 9 +1 1 RC7_MIN 1000.000000000000000000 9 +1 1 RC7_REV 1.000000000000000000 9 +1 1 RC7_TRIM 1500.000000000000000000 9 +1 1 RC8_DZ 10.000000000000000000 9 +1 1 RC8_MAX 2000.000000000000000000 9 +1 1 RC8_MIN 1000.000000000000000000 9 +1 1 RC8_REV 1.000000000000000000 9 +1 1 RC8_TRIM 1500.000000000000000000 9 +1 1 RC9_DZ 0.000000000000000000 9 +1 1 RC9_MAX 2000.000000000000000000 9 +1 1 RC9_MIN 1000.000000000000000000 9 +1 1 RC9_REV 1.000000000000000000 9 +1 1 RC9_TRIM 1500.000000000000000000 9 +1 1 RC_ACRO_TH 0.500000000000000000 9 +1 1 RC_ASSIST_TH 0.250000000000000000 9 +1 1 RC_AUTO_TH 0.750000000000000000 9 +1 1 RC_CHAN_CNT 8 6 +1 1 RC_DSM_BIND -1 6 +1 1 RC_FAILS_THR 0 6 +1 1 RC_GEAR_TH 0.250000000000000000 9 +1 1 RC_KILLSWITCH_TH 0.250000000000000000 9 +1 1 RC_LOITER_TH 0.500000000000000000 9 +1 1 RC_MAP_ACRO_SW 0 6 +1 1 RC_MAP_AUX1 0 6 +1 1 RC_MAP_AUX2 0 6 +1 1 RC_MAP_AUX3 0 6 +1 1 RC_MAP_AUX4 0 6 +1 1 RC_MAP_AUX5 0 6 +1 1 RC_MAP_FAILSAFE 0 6 +1 1 RC_MAP_FLAPS 0 6 +1 1 RC_MAP_FLTMODE 5 6 +1 1 RC_MAP_GEAR_SW 0 6 +1 1 RC_MAP_KILL_SW 0 6 +1 1 RC_MAP_LOITER_SW 0 6 +1 1 RC_MAP_MODE_SW 0 6 +1 1 RC_MAP_OFFB_SW 0 6 +1 1 RC_MAP_PARAM1 0 6 +1 1 RC_MAP_PARAM2 0 6 +1 1 RC_MAP_PARAM3 0 6 +1 1 RC_MAP_PITCH 2 6 +1 1 RC_MAP_POSCTL_SW 0 6 +1 1 RC_MAP_RATT_SW 0 6 +1 1 RC_MAP_RETURN_SW 0 6 +1 1 RC_MAP_ROLL 1 6 +1 1 RC_MAP_THROTTLE 3 6 +1 1 RC_MAP_TRANS_SW 0 6 +1 1 RC_MAP_YAW 4 6 +1 1 RC_OFFB_TH 0.500000000000000000 9 +1 1 RC_POSCTL_TH 0.500000000000000000 9 +1 1 RC_RATT_TH 0.500000000000000000 9 +1 1 RC_RETURN_TH 0.500000000000000000 9 +1 1 RC_RSSI_PWM_CHAN 0 6 +1 1 RC_RSSI_PWM_MAX 1000 6 +1 1 RC_RSSI_PWM_MIN 2000 6 +1 1 RC_TH_USER 1 6 +1 1 RC_TRANS_TH 0.250000000000000000 9 +1 1 RTL_DESCEND_ALT 30.000000000000000000 9 +1 1 RTL_LAND_DELAY -1.000000000000000000 9 +1 1 RTL_MIN_DIST 5.000000000000000000 9 +1 1 RTL_RETURN_ALT 60.000000000000000000 9 +1 1 SDLOG_EXT -1 6 +1 1 SDLOG_GPSTIME 1 6 +1 1 SDLOG_PRIO_BOOST 2 6 +1 1 SDLOG_RATE -1 6 +1 1 SENS_BARO_QNH 1013.250000000000000000 9 +1 1 SENS_BOARD_ROT 0 6 +1 1 SENS_BOARD_X_OFF 0.000000000000000000 9 +1 1 SENS_BOARD_Y_OFF 0.000000000000000000 9 +1 1 SENS_BOARD_Z_OFF 0.000000000000000000 9 +1 1 SENS_DPRES_ANSC 0.000000000000000000 9 +1 1 SENS_DPRES_OFF 0.000000000000000000 9 +1 1 SENS_EN_LL40LS 0 6 +1 1 SENS_EN_MB12XX 0 6 +1 1 SENS_EN_SF0X 0 6 +1 1 SENS_EN_SF1XX 0 6 +1 1 SENS_EN_THERMAL -1 6 +1 1 SENS_EN_TRONE 0 6 +1 1 SYS_AUTOCONFIG 1 6 +1 1 SYS_AUTOSTART 10018 6 +1 1 SYS_COMPANION 157600 6 +1 1 SYS_LOGGER 0 6 +1 1 SYS_MC_EST_GROUP 2 6 +1 1 SYS_PARAM_VER 1 6 +1 1 SYS_RESTART_TYPE 0 6 +1 1 SYS_USE_IO 1 6 +1 1 TRIG_MODE 0 6 +1 1 TRIM_PITCH 0.000000000000000000 9 +1 1 TRIM_ROLL 0.000000000000000000 9 +1 1 TRIM_YAW 0.000000000000000000 9 +1 1 UAVCAN_ENABLE 0 6 +1 1 VT_NAV_FORCE_VT 1 6 +1 1 VT_WV_LND_EN 0 6 +1 1 VT_WV_LTR_EN 0 6 -- GitLab From fe0a7e90a2ec7cb88d39972c7bd08bc9483ff03f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 14:13:15 -0800 Subject: [PATCH 146/398] Better error output --- src/comm/UDPLink.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index 51faba4ad..4828c6f9b 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -257,7 +257,7 @@ bool UDPLink::_hardwareConnect() QObject::connect(_socket, &QUdpSocket::readyRead, this, &UDPLink::readBytes); emit connected(); } else { - emit communicationError("UDP Link Error", "Error binding UDP port"); + emit communicationError(tr("UDP Link Error"), tr("Error binding UDP port: %1").arg(_socket->errorString())); } return _connectState; } -- GitLab From 36d0044b7089a0199464f9d9c1238b6dd5c99416 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 15:00:39 -0800 Subject: [PATCH 147/398] All reverse setting for PX4 --- src/AutoPilotPlugins/Common/RadioComponentController.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index 46a27dbef..2af400c22 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -743,8 +743,8 @@ void RadioComponentController::_writeCalibration(void) } // For multi-rotor we can determine reverse setting during radio cal. For anything other than multi-rotor, servo installation - // may affect channel reversing so we can't automatically determine it. - if (_vehicle->multiRotor()) { + // may affect channel reversing so we can't automatically determine it. This is ok for PX4 given how it uses mixers, but not for ArduPilot. + if (_vehicle->px4Firmware() || _vehicle->multiRotor()) { // APM multi-rotor has a backwards interpretation of "reversed" on the Pitch control. So be careful. float reversedParamValue; if (_px4Vehicle() || info->function != rcCalFunctionPitch) { -- GitLab From 68280d32aec3dcf914c5567f4bc3dc80f456839c Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 30 Dec 2016 17:34:00 -0500 Subject: [PATCH 148/398] Add frame configuration page for ArduSub --- qgcresources.qrc | 6 + qgroundcontrol.pro | 2 + .../APM/APMAutoPilotPlugin.cc | 9 ++ src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h | 3 + .../APM/APMSubFrameComponent.cc | 78 +++++++++ .../APM/APMSubFrameComponent.h | 40 +++++ .../APM/APMSubFrameComponent.qml | 151 ++++++++++++++++++ .../APM/APMSubFrameComponentSummary.qml | 57 +++++++ .../APM/Images/SubFrameComponentIcon.png | Bin 0 -> 5847 bytes .../APM/Images/bluerov-frame.png | Bin 0 -> 42591 bytes .../APM/Images/simple3-frame.png | Bin 0 -> 20583 bytes .../APM/Images/simple4-frame.png | Bin 0 -> 27835 bytes .../APM/Images/vectored-frame.png | Bin 0 -> 42944 bytes .../APM/Images/vectored6dof-frame.png | Bin 0 -> 60605 bytes src/FirmwarePlugin/APM/APMResources.qrc | 2 + 15 files changed, 348 insertions(+) create mode 100644 src/AutoPilotPlugins/APM/APMSubFrameComponent.cc create mode 100644 src/AutoPilotPlugins/APM/APMSubFrameComponent.h create mode 100644 src/AutoPilotPlugins/APM/APMSubFrameComponent.qml create mode 100644 src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml create mode 100644 src/AutoPilotPlugins/APM/Images/SubFrameComponentIcon.png create mode 100644 src/AutoPilotPlugins/APM/Images/bluerov-frame.png create mode 100644 src/AutoPilotPlugins/APM/Images/simple3-frame.png create mode 100644 src/AutoPilotPlugins/APM/Images/simple4-frame.png create mode 100644 src/AutoPilotPlugins/APM/Images/vectored-frame.png create mode 100644 src/AutoPilotPlugins/APM/Images/vectored6dof-frame.png diff --git a/qgcresources.qrc b/qgcresources.qrc index dcfc66ccf..5c714bbed 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -58,6 +58,12 @@ src/AutoPilotPlugins/PX4/Images/LandMode.svg src/AutoPilotPlugins/PX4/Images/LandModeCopter.svg src/AutoPilotPlugins/APM/Images/LightsComponentIcon.png + src/AutoPilotPlugins/APM/Images/SubFrameComponentIcon.png + src/AutoPilotPlugins/APM/Images/bluerov-frame.png + src/AutoPilotPlugins/APM/Images/vectored-frame.png + src/AutoPilotPlugins/APM/Images/vectored6dof-frame.png + src/AutoPilotPlugins/APM/Images/simple3-frame.png + src/AutoPilotPlugins/APM/Images/simple4-frame.png src/AnalyzeView/LogDownloadIcon.svg src/AutoPilotPlugins/PX4/Images/LowBattery.svg src/AutoPilotPlugins/PX4/Images/LowBatteryLight.svg diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 4fa119de4..356059415 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -796,6 +796,7 @@ APMFirmwarePlugin { src/AutoPilotPlugins/APM/APMFlightModesComponent.h \ src/AutoPilotPlugins/APM/APMFlightModesComponentController.h \ src/AutoPilotPlugins/APM/APMLightsComponent.h \ + src/AutoPilotPlugins/APM/APMSubFrameComponent.h \ src/AutoPilotPlugins/APM/APMPowerComponent.h \ src/AutoPilotPlugins/APM/APMRadioComponent.h \ src/AutoPilotPlugins/APM/APMSafetyComponent.h \ @@ -822,6 +823,7 @@ APMFirmwarePlugin { src/AutoPilotPlugins/APM/APMFlightModesComponent.cc \ src/AutoPilotPlugins/APM/APMFlightModesComponentController.cc \ src/AutoPilotPlugins/APM/APMLightsComponent.cc \ + src/AutoPilotPlugins/APM/APMSubFrameComponent.cc \ src/AutoPilotPlugins/APM/APMPowerComponent.cc \ src/AutoPilotPlugins/APM/APMRadioComponent.cc \ src/AutoPilotPlugins/APM/APMSafetyComponent.cc \ diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 618e2a7ab..4edebf5de 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -27,6 +27,7 @@ #include "MotorComponent.h" #include "APMCameraComponent.h" #include "APMLightsComponent.h" +#include "APMSubFrameComponent.h" #include "ESP8266Component.h" /// This is the AutoPilotPlugin implementatin for the MAV_AUTOPILOT_ARDUPILOT type. @@ -36,6 +37,7 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent) , _airframeComponent(NULL) , _cameraComponent(NULL) , _lightsComponent(NULL) + , _subFrameComponent(NULL) , _flightModesComponent(NULL) , _powerComponent(NULL) #if 0 @@ -109,6 +111,13 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void) _lightsComponent = new APMLightsComponent(_vehicle, this); _lightsComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_lightsComponent)); + + qWarning() << "ArduSub Version Detected:" << _vehicle->firmwareMajorVersion() << _vehicle->firmwareMinorVersion(); + if(_vehicle->firmwareMajorVersion() > 3 || (_vehicle->firmwareMajorVersion() == 3 && _vehicle->firmwareMinorVersion() >= 5)) { + _subFrameComponent = new APMSubFrameComponent(_vehicle, this); + _subFrameComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_subFrameComponent)); + } } //-- Is there an ESP8266 Connected? diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h index 6bd603152..8ac267b42 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h @@ -25,6 +25,7 @@ class APMPowerComponent; class MotorComponent; class APMCameraComponent; class APMLightsComponent; +class APMSubFrameComponent; class ESP8266Component; /// This is the APM specific implementation of the AutoPilot class. @@ -42,6 +43,7 @@ public: APMAirframeComponent* airframeComponent (void) const { return _airframeComponent; } APMCameraComponent* cameraComponent (void) const { return _cameraComponent; } APMLightsComponent* lightsComponent (void) const { return _lightsComponent; } + APMSubFrameComponent* subFrameComponent (void) const { return _subFrameComponent; } APMFlightModesComponent* flightModesComponent(void) const { return _flightModesComponent; } APMPowerComponent* powerComponent (void) const { return _powerComponent; } #if 0 @@ -61,6 +63,7 @@ private: APMAirframeComponent* _airframeComponent; APMCameraComponent* _cameraComponent; APMLightsComponent* _lightsComponent; + APMSubFrameComponent* _subFrameComponent; APMFlightModesComponent* _flightModesComponent; APMPowerComponent* _powerComponent; #if 0 diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc b/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc new file mode 100644 index 000000000..95506a14c --- /dev/null +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc @@ -0,0 +1,78 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +/// @file +/// @author Don Gagne +/// @author Jacob Walser + +#include "APMSubFrameComponent.h" +#include "QGCQmlWidgetHolder.h" +#include "APMAutoPilotPlugin.h" +#include "APMAirframeComponent.h" + +APMSubFrameComponent::APMSubFrameComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) + : VehicleComponent(vehicle, autopilot, parent) + , _name(tr("Frame")) +{ +} + +QString APMSubFrameComponent::name(void) const +{ + return _name; +} + +QString APMSubFrameComponent::description(void) const +{ + return tr("Frame setup allows you to choose your vehicle's motor configuration. Install clockwise" \ + "\npropellers on the green thrusters and counter-clockwise propellers on the blue thrusters" \ + "\n(or vice-versa). The flight controller will need to be rebooted to apply changes."); +} + +QString APMSubFrameComponent::iconResource(void) const +{ + return QStringLiteral("/qmlimages/SubFrameComponentIcon.png"); +} + +bool APMSubFrameComponent::requiresSetup(void) const +{ + return false; +} + +bool APMSubFrameComponent::setupComplete(void) const +{ + return true; +} + +QStringList APMSubFrameComponent::setupCompleteChangedTriggerList(void) const +{ + return QStringList(); +} + +QUrl APMSubFrameComponent::setupSource(void) const +{ + return QUrl::fromUserInput(QStringLiteral("qrc:/qml/APMSubFrameComponent.qml")); +} + +QUrl APMSubFrameComponent::summaryQmlSource(void) const +{ + return QUrl::fromUserInput(QStringLiteral("qrc:/qml/APMSubFrameComponentSummary.qml")); +} + +QString APMSubFrameComponent::prerequisiteSetup(void) const +{ + APMAutoPilotPlugin* plugin = dynamic_cast(_autopilot); + Q_ASSERT(plugin); + + if (!plugin->airframeComponent()->setupComplete()) { + return plugin->airframeComponent()->name(); + } + + return QString(); +} diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponent.h b/src/AutoPilotPlugins/APM/APMSubFrameComponent.h new file mode 100644 index 000000000..68b3eabb1 --- /dev/null +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponent.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * + * (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 APMSubFrameComponent_H +#define APMSubFrameComponent_H + +#include "VehicleComponent.h" + +class APMSubFrameComponent : public VehicleComponent +{ + Q_OBJECT + +public: + APMSubFrameComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = NULL); + + // Virtuals from VehicleComponent + QStringList setupCompleteChangedTriggerList(void) const final; + + // Virtuals from VehicleComponent + QString name(void) const final; + QString description(void) const final; + QString iconResource(void) const final; + bool requiresSetup(void) const final; + bool setupComplete(void) const final; + QUrl setupSource(void) const final; + QUrl summaryQmlSource(void) const final; + QString prerequisiteSetup(void) const final; + +private: + const QString _name; +}; + +#endif diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml b/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml new file mode 100644 index 000000000..df06fec69 --- /dev/null +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml @@ -0,0 +1,151 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 + +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +SetupPage { + id: subFramePage + pageComponent: subFramePageComponent + + Component { + id: subFramePageComponent + + Column { + id: mainColumn + width: availableWidth + + FactPanelController { id: controller; factPanel: subFramePage.viewPanel } + + QGCPalette { id: palette; colorGroupEnabled: true } + + property Fact _frameConfig: controller.getParameterFact(-1, "FRAME_CONFIG") + + function setFrameConfig(frame) { + _frameConfig.value = frame; + } + + property real _minW: ScreenTools.defaultFontPixelWidth * 30 + property real _boxWidth: _minW + property real _boxSpace: ScreenTools.defaultFontPixelWidth + + onWidthChanged: { + computeDimensions() + } + + Component.onCompleted: computeDimensions() + + function computeDimensions() { + var sw = 0 + var rw = 0 + var idx = Math.floor(mainColumn.width / (_minW + ScreenTools.defaultFontPixelWidth)) + if(idx < 1) { + _boxWidth = mainColumn.width + _boxSpace = 0 + } else { + _boxSpace = 0 + if(idx > 1) { + _boxSpace = ScreenTools.defaultFontPixelWidth + sw = _boxSpace * (idx - 1) + } + rw = mainColumn.width - sw + _boxWidth = rw / idx + } + } + + ListModel { + id: subFrameModel + + ListElement { + name: "BlueROV1" + resource: "qrc:///qmlimages/Frames/BlueROV1.png" + paramValue: 0 + } + + ListElement { + name: "BlueROV2/Vectored" + resource: "qrc:///qmlimages/Frames/Vectored.png" + paramValue: 1 + } + + ListElement { + name: "Vectored-6DOF" + resource: "qrc:///qmlimages/Frames/Vectored6DOF.png" + paramValue: 2 + } + + ListElement { + name: "SimpleROV-3" + resource: "qrc:///qmlimages/Frames/SimpleROV-3.png" + paramValue: 4 + } + + ListElement { + name: "SimpleROV-4" + resource: "qrc:///qmlimages/Frames/SimpleROV-4.png" + paramValue: 5 + } + } + + Flow { + id: flowView + width: parent.width + spacing: _boxSpace + + Repeater { + model: subFrameModel + + Rectangle { + width: _boxWidth + height: ScreenTools.defaultFontPixelHeight * 14 + color: qgcPal.window + + QGCLabel { + id: title + text: subFrameModel.get(index).name + } + + Rectangle { + anchors.topMargin: ScreenTools.defaultFontPixelHeight / 2 + anchors.top: title.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + color: subFrameModel.get(index).paramValue == _frameConfig.value ? qgcPal.buttonHighlight: qgcPal.windowShade + + Image { + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + fillMode: Image.PreserveAspectFit + smooth: true + mipmap: true + source: subFrameModel.get(index).resource + } + + MouseArea { + anchors.fill: parent + onClicked: setFrameConfig(subFrameModel.get(index).paramValue) + } + } + } + }// Repeater + }// Flow + } // Column + } // Component +} // SetupPage diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml b/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml new file mode 100644 index 000000000..6dcf659fd --- /dev/null +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml @@ -0,0 +1,57 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 + +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Palette 1.0 + +FactPanel { + id: panel + anchors.fill: parent + color: qgcPal.windowShadeDark + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + FactPanelController { id: controller; factPanel: panel } + + property Fact frameFact: controller.getParameterFact(-1, "FRAME_CONFIG") + + function frameName() { + switch(frameFact.value) { + case 0: + return "BlueROV1" + case 1: + return "Vectored/BlueROV2" + case 2: + return "Vectored 6DOF" + case 3: + return "Vectored 6DOF 90Degree" + case 4: + return "SimpleROV-3" + case 5: + return "SimpleROV-4" + case 6: + return "SimpleROV-5" + case 7: + return "Custom" + default: + return "Unknown" + } + } + + Column { + anchors.fill: parent + VehicleSummaryRow { + id: nameRow; + labelText: qsTr("Frame Type:") + valueText: frameName() + } + + VehicleSummaryRow { + labelText: qsTr("Firmware Version:") + valueText: activeVehicle.firmwareMajorVersion == -1 ? qsTr("Unknown") : activeVehicle.firmwareMajorVersion + "." + activeVehicle.firmwareMinorVersion + "." + activeVehicle.firmwarePatchVersion + "-" + activeVehicle.firmwareVersionTypeString + } + } +} diff --git a/src/AutoPilotPlugins/APM/Images/SubFrameComponentIcon.png b/src/AutoPilotPlugins/APM/Images/SubFrameComponentIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..6401e093c8591eb554f2dccb99a717d12124ae20 GIT binary patch literal 5847 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 SPvG}&v)(l$t#VDf2mb|(Gzc63 literal 0 HcmV?d00001 diff --git a/src/AutoPilotPlugins/APM/Images/bluerov-frame.png b/src/AutoPilotPlugins/APM/Images/bluerov-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..5a5edaf360c11b6fa76cf2b54351f66f62ac0faf GIT binary patch literal 42591 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 Jl_G`#{{tGR&u;(# literal 0 HcmV?d00001 diff --git a/src/AutoPilotPlugins/APM/Images/simple3-frame.png b/src/AutoPilotPlugins/APM/Images/simple3-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..c7368a3e228044513282810f12bc45f1d13e60d0 GIT binary patch literal 20583 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�aH1f7Ox-Rn(-gWY*rJzI)N+u-dJuZpZaQPl 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 q53j<8Z>Et*jd?yT literal 0 HcmV?d00001 diff --git a/src/AutoPilotPlugins/APM/Images/simple4-frame.png b/src/AutoPilotPlugins/APM/Images/simple4-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..793b36521c292297e622bae17728d69a83780a4f GIT binary patch literal 27835 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 literal 0 HcmV?d00001 diff --git a/src/AutoPilotPlugins/APM/Images/vectored-frame.png b/src/AutoPilotPlugins/APM/Images/vectored-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..aa1c3cf9edf75fbf4b1c18df39ae4a3aa8301fad GIT binary patch literal 42944 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 RMxX!y002ovPDHLkV1n>2Z|ML4 literal 0 HcmV?d00001 diff --git a/src/AutoPilotPlugins/APM/Images/vectored6dof-frame.png b/src/AutoPilotPlugins/APM/Images/vectored6dof-frame.png new file mode 100644 index 0000000000000000000000000000000000000000..c690be96964d9ac4f8b2930f6b88cd7a69d5c2bd GIT binary patch literal 60605 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

D)Aapo~Bh?&T{_P2*fJ&kP>dMgI|5>?EJ3~ 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 GQU3=gIQ<*| literal 0 HcmV?d00001 diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc index 9e0cb0c8b..4ca53d7fc 100644 --- a/src/FirmwarePlugin/APM/APMResources.qrc +++ b/src/FirmwarePlugin/APM/APMResources.qrc @@ -8,6 +8,8 @@ ../../AutoPilotPlugins/APM/APMFlightModesComponentSummary.qml ../../AutoPilotPlugins/APM/APMLightsComponent.qml ../../AutoPilotPlugins/APM/APMLightsComponentSummary.qml + ../../AutoPilotPlugins/APM/APMSubFrameComponent.qml + ../../AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml ../../AutoPilotPlugins/APM/APMNotSupported.qml ../../AutoPilotPlugins/APM/APMPowerComponent.qml ../../AutoPilotPlugins/APM/APMPowerComponentSummary.qml -- GitLab From 128c1be95399760d9cf693ae411bd1728bc507a3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 30 Dec 2016 17:34:03 -0800 Subject: [PATCH 149/398] Fix compass numbering --- src/AutoPilotPlugins/APM/APMSensorsComponent.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index a0b553f52..c538a576a 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -218,7 +218,7 @@ SetupPage { spacing: Math.round(ScreenTools.defaultFontPixelHeight / 4) QGCLabel { - text: "Compass " + index + " " + + text: "Compass " + (index+1) + " " + (sensorParams.rgCompassPrimary[index] ? "(primary" : "(secondary") + (sensorParams.rgCompassExternalParamAvailable[index] ? (sensorParams.rgCompassExternal[index] ? ", external" : ", internal" ) : @@ -272,7 +272,7 @@ SetupPage { visible: sensorParams.rgCompassAvailable[index] QGCLabel { - text: "Compass " + index + " " + + text: "Compass " + (index+1) + " " + (sensorParams.rgCompassPrimary[index] ? "(primary" : "(secondary") + (sensorParams.rgCompassExternalParamAvailable[index] ? (sensorParams.rgCompassExternal[index] ? ", external" : ", internal" ) : -- GitLab From 0878a1893fd7befddba5179b3abd85ec7eaa7670 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 31 Dec 2016 10:22:59 -0800 Subject: [PATCH 150/398] Remove console output --- src/ui/MainWindowInner.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index 9f54ee812..b11b073ab 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -67,7 +67,6 @@ Item { } settingsViewLoader.visible = true toolBar.checkSettingsButton() - console.log("showSettingsView") } function showSetupView() { -- GitLab From 01806fae6128f0bf2dad67a5e9b10ef27e293e27 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 31 Dec 2016 10:23:07 -0800 Subject: [PATCH 151/398] Fix text coloring --- src/MissionEditor/MissionEditor.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index a349bf947..738a9864c 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -583,6 +583,7 @@ QGCView { exclusiveGroup: planElementSelectorGroup text: qsTr("Mission") checked: true + color: mapPal.text } Item { height: 1; width: 1 } @@ -591,6 +592,7 @@ QGCView { id: planElementGeoFence exclusiveGroup: planElementSelectorGroup text: qsTr("Fence") + color: mapPal.text } Item { height: 1; width: 1 } @@ -599,6 +601,7 @@ QGCView { id: planElementRallyPoints exclusiveGroup: planElementSelectorGroup text: qsTr("Rally") + color: mapPal.text } } // Row - Plan Element Selector -- GitLab From 68886a1aeb7eabeea27d8aa63e6f0733c5e2feec Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 31 Dec 2016 10:23:41 -0800 Subject: [PATCH 152/398] Fix passed in property mechanism qgcView was coming through null causing widget dialog to not pop up --- src/FlightDisplay/FlightDisplayView.qml | 17 ++++++++--------- src/FlightDisplay/FlightDisplayViewWidgets.qml | 10 ++++++---- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index bcc4ee330..ed01d1c6f 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -219,15 +219,14 @@ QGCView { } FlightDisplayViewWidgets { - id: flightDisplayViewWidgets - z: _panel.z + 4 - height: ScreenTools.availableHeight - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - property bool isBackgroundDark: root.isBackgroundDark - property var qgcView: root + id: flightDisplayViewWidgets + z: _panel.z + 4 + height: ScreenTools.availableHeight + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + qgcView: root + isBackgroundDark: root.isBackgroundDark } //-- Virtual Joystick diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index cd460922f..e295ef9d8 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -25,8 +25,10 @@ import QGroundControl.FlightMap 1.0 Item { id: _root - property alias guidedModeBar: _guidedModeBar - property bool gotoEnabled: _activeVehicle && _activeVehicle.guidedMode && _activeVehicle.flying + property alias guidedModeBar: _guidedModeBar + property bool gotoEnabled: _activeVehicle && _activeVehicle.guidedMode && _activeVehicle.flying + property var qgcView + property bool isBackgroundDark property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property bool _isSatellite: _mainIsMap ? (_flightMap ? _flightMap.isSatelliteMap : true) : true @@ -87,7 +89,7 @@ Item { airSpeedFact: _airSpeedFact lightBorders: _lightWidgetBorders z: QGroundControl.zOrderWidgets - qgcView: parent.parent.qgcView + qgcView: _root.qgcView maxHeight: parent.height - (anchors.margins * 2) } @@ -113,7 +115,7 @@ Item { anchors.top: instrumentGadgetAlternate.bottom anchors.horizontalCenter: instrumentGadgetAlternate.horizontalCenter width: getGadgetWidth() - qgcView: parent.parent.qgcView + qgcView: _root.qgcView textColor: _isSatellite ? "white" : "black" visible: _useAlternateInstruments maxHeight: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.y - y : parent.height - anchors.margins - y -- GitLab From 056d2015c45d1a1cea1dcabdb585d047f4a9ca3e Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 31 Dec 2016 13:45:55 -0500 Subject: [PATCH 153/398] Fix issue where artificial horizon was not working right when pointing straight up or down. --- src/FlightMap/Widgets/QGCArtificialHorizon.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightMap/Widgets/QGCArtificialHorizon.qml b/src/FlightMap/Widgets/QGCArtificialHorizon.qml index 935a56422..73f876536 100644 --- a/src/FlightMap/Widgets/QGCArtificialHorizon.qml +++ b/src/FlightMap/Widgets/QGCArtificialHorizon.qml @@ -28,7 +28,7 @@ Item { Item { id: artificialHorizon width: root.width * 4 - height: root.height * 4 + height: root.height * 8 anchors.centerIn: parent Rectangle { id: sky -- GitLab From e44cd3243c9431620dba0a54077d174bb9ab6fab Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 31 Dec 2016 14:07:02 -0500 Subject: [PATCH 154/398] Testing Android issues with WiFi and no Internet. --- android/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 24bdd8a1b..f5009b3f4 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -48,7 +48,7 @@ - + -- GitLab From f694a15798d96ba24ade51dec0eef19f701a541d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 31 Dec 2016 14:11:53 -0800 Subject: [PATCH 155/398] Reword warning --- src/AutoPilotPlugins/APM/APMSensorsComponent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index a0b553f52..75dde2db4 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -155,7 +155,7 @@ SetupPage { Component.onCompleted: { var usingUDP = controller.usingUDPLink() if (usingUDP) { - showMessage("Sensor Calibration", "Performing sensor calibration over a WiFi connection is known to be unreliable. You should disconnect and perform calibration using a direct USB connection instead.", StandardButton.Ok) + showMessage("Sensor Calibration", "Performing sensor calibration over a WiFi connection can be unreliable. If you run into problems try using a direct USB connection instead.", StandardButton.Ok) } } -- GitLab From 01acf4925ddce9a06622840dc3f77fa6699f15ab Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Sun, 1 Jan 2017 13:06:32 -0500 Subject: [PATCH 156/398] Remove uneccessary qWarning --- src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 4edebf5de..23e5daa84 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -112,7 +112,6 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void) _lightsComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_lightsComponent)); - qWarning() << "ArduSub Version Detected:" << _vehicle->firmwareMajorVersion() << _vehicle->firmwareMinorVersion(); if(_vehicle->firmwareMajorVersion() > 3 || (_vehicle->firmwareMajorVersion() == 3 && _vehicle->firmwareMinorVersion() >= 5)) { _subFrameComponent = new APMSubFrameComponent(_vehicle, this); _subFrameComponent->setupTriggerSignals(); -- GitLab From a5ed9bbb069e09e229769e17e884b9482ec7eb8d Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Sun, 1 Jan 2017 13:12:48 -0500 Subject: [PATCH 157/398] Remove prerequisiteSetup checks from APMLightsComponent and APMSubFrameComponent --- src/AutoPilotPlugins/APM/APMLightsComponent.cc | 7 ------- src/AutoPilotPlugins/APM/APMSubFrameComponent.cc | 7 ------- 2 files changed, 14 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMLightsComponent.cc b/src/AutoPilotPlugins/APM/APMLightsComponent.cc index 6c6d62219..46d7bdbd7 100644 --- a/src/AutoPilotPlugins/APM/APMLightsComponent.cc +++ b/src/AutoPilotPlugins/APM/APMLightsComponent.cc @@ -65,12 +65,5 @@ QUrl APMLightsComponent::summaryQmlSource(void) const QString APMLightsComponent::prerequisiteSetup(void) const { - APMAutoPilotPlugin* plugin = dynamic_cast(_autopilot); - Q_ASSERT(plugin); - - if (!plugin->airframeComponent()->setupComplete()) { - return plugin->airframeComponent()->name(); - } - return QString(); } diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc b/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc index 95506a14c..9d8827444 100644 --- a/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponent.cc @@ -67,12 +67,5 @@ QUrl APMSubFrameComponent::summaryQmlSource(void) const QString APMSubFrameComponent::prerequisiteSetup(void) const { - APMAutoPilotPlugin* plugin = dynamic_cast(_autopilot); - Q_ASSERT(plugin); - - if (!plugin->airframeComponent()->setupComplete()) { - return plugin->airframeComponent()->name(); - } - return QString(); } -- GitLab From 3a33aaa6cdae2e92c59da2d60d0ef37df83b45ac Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 09:18:56 -0800 Subject: [PATCH 158/398] Fixed text header --- src/FactSystem/ParameterManager.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 925621b65..a678407b8 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -875,9 +875,9 @@ QString ParameterManager::readParametersFromStream(QTextStream& stream) void ParameterManager::writeParametersToStream(QTextStream &stream) { - stream << "# Onboard parameters for vehicle " << _vehicle->id() << "\n"; + stream << "# Onboard parameters for Vehicle " << _vehicle->id() << "\n"; stream << "#\n"; - stream << "# MAV ID COMPONENT ID PARAM NAME VALUE (FLOAT)\n"; + stream << "# Vehicle-Id Component-Id Name Value Type\n"; foreach (int componentId, _mapParameterName2Variant.keys()) { foreach (const QString ¶mName, _mapParameterName2Variant[componentId].keys()) { -- GitLab From 5027a358cd474c08a94bb2df1225ba9451db83cd Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 09:19:02 -0800 Subject: [PATCH 159/398] Remove debug output --- src/MissionEditor/SurveyItemEditor.qml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/MissionEditor/SurveyItemEditor.qml b/src/MissionEditor/SurveyItemEditor.qml index 0192ed38c..dca4a2b8f 100644 --- a/src/MissionEditor/SurveyItemEditor.qml +++ b/src/MissionEditor/SurveyItemEditor.qml @@ -29,28 +29,6 @@ Rectangle { readonly property int _gridTypeCustomCamera: 1 readonly property int _gridTypeCamera: 2 - Component.onCompleted: { - console.log("gridAltitude", missionItem.gridAltitude.value) - console.log("gridAltitudeRelative", missionItem.gridAltitudeRelative) - console.log("gridAngle", missionItem.gridAngle.value) - console.log("gridSpacing", missionItem.gridSpacing.value) - console.log("turnaroundDist", missionItem.turnaroundDist.value) - console.log("cameraTrigger", missionItem.cameraTrigger) - console.log("cameraTriggerDistance", missionItem.cameraTriggerDistance.value) - console.log("groundResolution", missionItem.groundResolution.value) - console.log("frontalOverlap", missionItem.frontalOverlap.value) - console.log("sideOverlap", missionItem.sideOverlap.value) - console.log("cameraSensorWidth", missionItem.cameraSensorWidth.value) - console.log("cameraSensorHeight", missionItem.cameraSensorHeight.value) - console.log("cameraResolutionWidth", missionItem.cameraResolutionWidth.value) - console.log("cameraResolutionHeight", missionItem.cameraResolutionHeight.value) - console.log("cameraFocalLength", missionItem.cameraFocalLength.value) - console.log("fixedValueIsAltitude", missionItem.fixedValueIsAltitude) - console.log("cameraOrientationLandscape", missionItem.cameraOrientationLandscape) - console.log("manualGrid", missionItem.manualGrid) - console.log("camera", missionItem.camera) - } - ListModel { id: cameraModelList -- GitLab From 658d767f623878745d78d4a22204ad3d7088470b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 09:19:10 -0800 Subject: [PATCH 160/398] Fix drop control z-order --- src/QmlControls/DropButton.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/QmlControls/DropButton.qml b/src/QmlControls/DropButton.qml index 6482a9062..b8deec86f 100644 --- a/src/QmlControls/DropButton.qml +++ b/src/QmlControls/DropButton.qml @@ -2,11 +2,13 @@ import QtQuick 2.4 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 +import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 Item { id: _root + z: QGroundControl.zOrderWidgets signal clicked() property alias buttonImage: roundButton.buttonImage -- GitLab From b15eba775cbf7f6fc80bd52a09d43874fc8ab811 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 10:18:01 -0800 Subject: [PATCH 161/398] New V2 mission file format --- src/JsonHelper.cc | 72 ++-- src/JsonHelper.h | 12 +- src/MissionManager/ComplexMissionItem.cc | 3 +- src/MissionManager/ComplexMissionItem.h | 6 +- src/MissionManager/GeoFenceController.cc | 30 +- src/MissionManager/MissionController.cc | 208 ++++++++--- src/MissionManager/MissionController.h | 13 +- src/MissionManager/MissionItem.cc | 125 +++++-- src/MissionManager/MissionItem.h | 21 +- src/MissionManager/MissionItemTest.cc | 406 ++++----------------- src/MissionManager/MissionItemTest.h | 5 +- src/MissionManager/RallyPointController.cc | 11 +- src/MissionManager/SimpleMissionItem.cc | 18 +- src/MissionManager/SimpleMissionItem.h | 2 +- src/MissionManager/SurveyMissionItem.cc | 147 ++++---- src/MissionManager/SurveyMissionItem.h | 8 +- src/MissionManager/VisualMissionItem.cc | 4 + src/MissionManager/VisualMissionItem.h | 4 + 18 files changed, 540 insertions(+), 555 deletions(-) diff --git a/src/JsonHelper.cc b/src/JsonHelper.cc index b15220ca9..e29a94a89 100644 --- a/src/JsonHelper.cc +++ b/src/JsonHelper.cc @@ -97,10 +97,12 @@ void JsonHelper::saveGeoCoordinate(const QGeoCoordinate& coordinate, bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList& types, QString& errorString) { - for (int i=0; i supportedMajorVersion || (fileMajorVersion == supportedMajorVersion && fileMinorVersion > supportedMinorVersion)) { - errorString = QObject::tr("File version (%1.%2) is larger than current supported version (%3.%4)").arg(fileMajorVersion).arg(fileMinorVersion).arg(supportedMajorVersion).arg(supportedMinorVersion); + if (version > maxSupportedVersion) { + errorString = QObject::tr("File version %1 is newer than current supported version %2").arg(version).arg(maxSupportedVersion); return false; } @@ -284,3 +284,27 @@ bool JsonHelper::validateKeys(const QJsonObject& jsonObject, const QList typeList; - keyList << jsonSimpleItemsKey << _jsonVersionKey << _jsonGroundStationKey << _jsonMavAutopilotKey << _jsonComplexItemsKey << _jsonPlannedHomePositionKey; - typeList << QJsonValue::Array << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Object; - if (!JsonHelper::validateKeyTypes(json, keyList, typeList, errorString)) { - return false; - } -#endif - - // Version check - if (json[JsonHelper::jsonVersionKey].toString() != "1.0") { - errorString = QStringLiteral("QGroundControl does not support this file version"); + int fileVersion; + if (!JsonHelper::validateQGCJsonFile(json, + _jsonFileTypeValue, // expected file type + 1, // minimum supported version + 1, // maximum supported version + fileVersion, + errorString)) { return false; } @@ -269,7 +255,7 @@ void GeoFenceController::saveToFile(const QString& filename) QJsonObject fenceFileObject; // top level json object fenceFileObject[JsonHelper::jsonFileTypeKey] = _jsonFileTypeValue; - fenceFileObject[JsonHelper::jsonVersionKey] = QStringLiteral("1.0"); + fenceFileObject[JsonHelper::jsonVersionKey] = 1; fenceFileObject[JsonHelper::jsonGroundStationKey] = JsonHelper::jsonGroundStationValue; QStringList paramNames; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 03bf70e4e..760bbc248 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -27,12 +27,19 @@ QGC_LOGGING_CATEGORY(MissionControllerLog, "MissionControllerLog") -const char* MissionController::jsonSimpleItemsKey = "items"; const char* MissionController::_settingsGroup = "MissionController"; -const char* MissionController::_jsonMavAutopilotKey = "MAV_AUTOPILOT"; -const char* MissionController::_jsonComplexItemsKey = "complexItems"; +const char* MissionController::_jsonFileTypeValue = "Mission"; +const char* MissionController::_jsonItemsKey = "items"; const char* MissionController::_jsonPlannedHomePositionKey = "plannedHomePosition"; +const char* MissionController::_jsonFirmwareTypeKey = "firmwareType"; +const char* MissionController::_jsonParamsKey = "params"; + +// Deprecated V1 format keys +const char* MissionController::_jsonComplexItemsKey = "complexItems"; +const char* MissionController::_jsonMavAutopilotKey = "MAV_AUTOPILOT"; + +const int MissionController::_missionFileVersion = 2; MissionController::MissionController(QObject *parent) : PlanElementController(parent) @@ -252,28 +259,40 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL errorString = jsonParseError.errorString(); return false; } - QJsonObject json = jsonDoc.object(); - // Check for required keys - QStringList requiredKeys; - requiredKeys << JsonHelper::jsonVersionKey << _jsonPlannedHomePositionKey; - if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) { - return false; + // V1 file format has no file type key and version key is string. Convert to new format. + if (!json.contains(JsonHelper::jsonFileTypeKey)) { + json[JsonHelper::jsonFileTypeKey] = _jsonFileTypeValue; } - // Validate base key types - QStringList keyList; - QList typeList; - keyList << jsonSimpleItemsKey << JsonHelper::jsonVersionKey << JsonHelper::jsonGroundStationKey << _jsonMavAutopilotKey << _jsonComplexItemsKey << _jsonPlannedHomePositionKey; - typeList << QJsonValue::Array << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Object; - if (!JsonHelper::validateKeyTypes(json, keyList, typeList, errorString)) { + int fileVersion; + if (!JsonHelper::validateQGCJsonFile(json, + _jsonFileTypeValue, // expected file type + 1, // minimum supported version + 2, // maximum supported version + fileVersion, + errorString)) { return false; } - // Version check - if (json[JsonHelper::jsonVersionKey].toString() != "1.0") { - errorString = QStringLiteral("QGroundControl does not support this file version"); + if (fileVersion == 1) { + return _loadJsonMissionFileV1(json, visualItems, complexItems, errorString); + } else { + return _loadJsonMissionFileV2(json, visualItems, complexItems, errorString); + } +} + +bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) +{ + // Validate root object keys + QList rootKeyInfoList = { + { _jsonPlannedHomePositionKey, QJsonValue::Object, true }, + { _jsonItemsKey, QJsonValue::Array, true }, + { _jsonMavAutopilotKey, QJsonValue::Double, true }, + { _jsonComplexItemsKey, QJsonValue::Array, true }, + }; + if (!JsonHelper::validateKeys(json, rootKeyInfoList, errorString)) { return false; } @@ -289,8 +308,8 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL } SurveyMissionItem* item = new SurveyMissionItem(_activeVehicle, this); - if (item->load(itemValue.toObject(), errorString)) { - qCDebug(MissionControllerLog) << "Json load: complex item start:stop" << item->sequenceNumber() << item->lastSequenceNumber(); + const QJsonObject itemObject = itemValue.toObject(); + if (item->load(itemObject, itemObject["id"].toInt(), errorString)) { complexItems->append(item); } else { return false; @@ -302,7 +321,7 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL int nextSimpleItemIndex= 0; int nextComplexItemIndex= 0; int nextSequenceNumber = 1; // Start with 1 since home is in 0 - QJsonArray itemArray(json[jsonSimpleItemsKey].toArray()); + QJsonArray itemArray(json[_jsonItemsKey].toArray()); qCDebug(MissionControllerLog) << "Json load: simple item loop start simpleItemCount:ComplexItemCount" << itemArray.count() << complexItems->count(); do { @@ -330,8 +349,9 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL return false; } + const QJsonObject itemObject = itemValue.toObject(); SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this); - if (item->load(itemValue.toObject(), errorString)) { + if (item->load(itemObject, itemObject["id"].toInt(), errorString)) { qCDebug(MissionControllerLog) << "Json load: adding simple item expectedSequence:actualSequence" << nextSequenceNumber << item->sequenceNumber(); visualItems->append(item); } else { @@ -345,7 +365,7 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL if (json.contains(_jsonPlannedHomePositionKey)) { SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this); - if (item->load(json[_jsonPlannedHomePositionKey].toObject(), errorString)) { + if (item->load(json[_jsonPlannedHomePositionKey].toObject(), 0, errorString)) { visualItems->insert(0, item); } else { return false; @@ -357,6 +377,117 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL return true; } +bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) +{ + // Validate root object keys + QList rootKeyInfoList = { + { _jsonPlannedHomePositionKey, QJsonValue::Array, true }, + { _jsonItemsKey, QJsonValue::Array, true }, + { _jsonFirmwareTypeKey, QJsonValue::Double, true }, + }; + if (!JsonHelper::validateKeys(json, rootKeyInfoList, errorString)) { + return false; + } + + qCDebug(MissionControllerLog) << "MissionController::_loadJsonMissionFileV2 itemCount:" << json[_jsonItemsKey].toArray().count(); + + // Planned home position + QGeoCoordinate homeCoordinate; + if (!JsonHelper::loadGeoCoordinate(json[_jsonPlannedHomePositionKey], true /* altitudeRequired */, homeCoordinate, errorString)) { + return false; + } + SimpleMissionItem* homeItem = new SimpleMissionItem(_activeVehicle, this); + homeItem->setCoordinate(homeCoordinate); + visualItems->insert(0, homeItem); + qCDebug(MissionControllerLog) << "plannedHomePosition" << homeCoordinate; + + // Read mission items + + int nextSequenceNumber = 1; // Start with 1 since home is in 0 + const QJsonArray rgMissionItems(json[_jsonItemsKey].toArray()); + for (int i=0; i itemKeyInfoList = { + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + }; + if (!JsonHelper::validateKeys(itemObject, itemKeyInfoList, errorString)) { + return false; + } + QString itemType = itemObject[VisualMissionItem::jsonTypeKey].toString(); + + if (itemType == VisualMissionItem::jsonTypeSimpleItemValue) { + qCDebug(MissionControllerLog) << "Loading MISSION_ITEM: nextSequenceNumber" << nextSequenceNumber; + SimpleMissionItem* simpleItem = new SimpleMissionItem(_activeVehicle, this); + if (simpleItem->load(itemObject, nextSequenceNumber++, errorString)) { + visualItems->append(simpleItem); + } else { + return false; + } + } else if (itemType == VisualMissionItem::jsonTypeComplexItemValue) { + QList complexItemKeyInfoList = { + { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, + }; + if (!JsonHelper::validateKeys(itemObject, complexItemKeyInfoList, errorString)) { + return false; + } + QString complexItemType = itemObject[ComplexMissionItem::jsonComplexItemTypeKey].toString(); + + if (complexItemType == SurveyMissionItem::jsonComplexItemTypeValue) { + qCDebug(MissionControllerLog) << "Loading Survey: nextSequenceNumber" << nextSequenceNumber; + SurveyMissionItem* surveyItem = new SurveyMissionItem(_activeVehicle, this); + if (!surveyItem->load(itemObject, nextSequenceNumber++, errorString)) { + return false; + } + nextSequenceNumber = surveyItem->lastSequenceNumber() + 1; + qCDebug(MissionControllerLog) << "Survey load complete: nextSequenceNumber" << nextSequenceNumber; + visualItems->append(surveyItem); + complexItems->append(surveyItem); + } else { + errorString = tr("Unsupported complex item type: %1").arg(complexItemType); + } + } else { + errorString = tr("Unknown item type: %1").arg(itemType); + return false; + } + } + + // Fix up the DO_JUMP commands jump sequence number by finding the item with the matching doJumpId + for (int i=0; icount(); i++) { + if (visualItems->value(i)->isSimpleItem()) { + SimpleMissionItem* doJumpItem = visualItems->value(i); + if (doJumpItem->command() == MAV_CMD_DO_JUMP) { + bool found = false; + int findDoJumpId = doJumpItem->missionItem().param1(); + for (int j=0; jcount(); j++) { + if (visualItems->value(j)->isSimpleItem()) { + SimpleMissionItem* targetItem = visualItems->value(j); + if (targetItem->missionItem().doJumpId() == findDoJumpId) { + doJumpItem->missionItem().setParam1(targetItem->sequenceNumber()); + found = true; + break; + } + } + } + if (!found) { + errorString = tr("Could not find doJumpId: %1").arg(findDoJumpId); + return false; + } + } + } + } + + return true; +} + bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString) { bool addPlannedHomePosition = false; @@ -498,10 +629,8 @@ void MissionController::saveToFile(const QString& filename) qgcApp()->showMessage(file.errorString()); } else { QJsonObject missionFileObject; // top level json object - QJsonArray simpleItemsObject; - QJsonArray complexItemsObject; - missionFileObject[JsonHelper::jsonVersionKey] = "1.0"; + missionFileObject[JsonHelper::jsonVersionKey] = _missionFileVersion; missionFileObject[JsonHelper::jsonGroundStationKey] = JsonHelper::jsonGroundStationValue; MAV_AUTOPILOT firmwareType = MAV_AUTOPILOT_GENERIC; @@ -514,35 +643,28 @@ void MissionController::saveToFile(const QString& filename) QSettings settings; firmwareType = (MAV_AUTOPILOT)settings.value("OfflineEditingFirmwareType", MAV_AUTOPILOT_ARDUPILOTMEGA).toInt(); } - missionFileObject[_jsonMavAutopilotKey] = firmwareType; + missionFileObject[_jsonFirmwareTypeKey] = firmwareType; // Save planned home position - QJsonObject homePositionObject; SimpleMissionItem* homeItem = qobject_cast(_visualItems->get(0)); - if (homeItem) { - homeItem->missionItem().save(homePositionObject); - } else { + if (!homeItem) { qgcApp()->showMessage(QStringLiteral("Internal error: VisualMissionItem at index 0 not SimpleMissionItem")); return; } - missionFileObject[_jsonPlannedHomePositionKey] = homePositionObject; + QJsonValue coordinateValue; + JsonHelper::saveGeoCoordinate(homeItem->coordinate(), true /* writeAltitude */, coordinateValue); + missionFileObject[_jsonPlannedHomePositionKey] = coordinateValue; // Save the visual items + QJsonArray rgMissionItems; for (int i=1; i<_visualItems->count(); i++) { QJsonObject itemObject; VisualMissionItem* visualItem = qobject_cast(_visualItems->get(i)); visualItem->save(itemObject); - - if (visualItem->isSimpleItem()) { - simpleItemsObject.append(itemObject); - } else { - complexItemsObject.append(itemObject); - } + rgMissionItems.append(itemObject); } - - missionFileObject[jsonSimpleItemsKey] = simpleItemsObject; - missionFileObject[_jsonComplexItemsKey] = complexItemsObject; + missionFileObject[_jsonItemsKey] = rgMissionItems; QJsonDocument saveDoc(missionFileObject); file.write(saveDoc.toJson()); @@ -657,7 +779,7 @@ void MissionController::_recalcWaypointLines(void) // Create a new segment and wire update notifiers auto linevect = new CoordinateVector(lastCoordinateItem->isSimpleItem() ? lastCoordinateItem->coordinate() : lastCoordinateItem->exitCoordinate(), item->coordinate(), this); auto originNotifier = lastCoordinateItem->isSimpleItem() ? &VisualMissionItem::coordinateChanged : &VisualMissionItem::exitCoordinateChanged, - endNotifier = &VisualMissionItem::coordinateChanged; + endNotifier = &VisualMissionItem::coordinateChanged; // Use signals/slots to update the coordinate endpoints connect(lastCoordinateItem, originNotifier, linevect, &CoordinateVector::setCoordinate1); connect(item, endNotifier, linevect, &CoordinateVector::setCoordinate2); @@ -883,7 +1005,7 @@ void MissionController::_recalcSequence(void) ComplexMissionItem* complexItem = qobject_cast(item); if (complexItem) { - sequenceNumber = complexItem->lastSequenceNumber() + 1; + sequenceNumber = complexItem->lastSequenceNumber() + 1; } else { qWarning() << "isSimpleItem == false, yet not ComplexMissionItem"; } diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index fc1f288d3..42e495e48 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -88,7 +88,6 @@ public: void setCruiseDistance (double cruiseDistance ); void setHoverDistance (double hoverDistance ); - static const char* jsonSimpleItemsKey; ///< Key for simple items in a json file signals: void plannedHomePositionChanged(QGeoCoordinate plannedHomePosition); @@ -129,6 +128,8 @@ private: double _normalizeLat(double lat); double _normalizeLon(double lon); bool _loadJsonMissionFile(const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + bool _loadJsonMissionFileV1(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + bool _loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); bool _loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); int _nextSequenceNumber(void); @@ -150,9 +151,17 @@ private: double _hoverDistance; static const char* _settingsGroup; + static const char* _jsonFileTypeValue; + static const char* _jsonFirmwareTypeKey; + static const char* _jsonItemsKey; + static const char* _jsonPlannedHomePositionKey; + static const char* _jsonParamsKey; + + // Deprecated V1 format keys static const char* _jsonMavAutopilotKey; static const char* _jsonComplexItemsKey; - static const char* _jsonPlannedHomePositionKey; + + static const int _missionFileVersion; }; #endif diff --git a/src/MissionManager/MissionItem.cc b/src/MissionManager/MissionItem.cc index 8b7df76ce..0a50253a9 100644 --- a/src/MissionManager/MissionItem.cc +++ b/src/MissionManager/MissionItem.cc @@ -15,22 +15,25 @@ #include "FirmwarePluginManager.h" #include "QGCApplication.h" #include "JsonHelper.h" +#include "VisualMissionItem.h" -const char* MissionItem::_itemType = "missionItem"; -const char* MissionItem::_jsonTypeKey = "type"; -const char* MissionItem::_jsonIdKey = "id"; const char* MissionItem::_jsonFrameKey = "frame"; const char* MissionItem::_jsonCommandKey = "command"; +const char* MissionItem::_jsonAutoContinueKey = "autoContinue"; +const char* MissionItem::_jsonCoordinateKey = "coordinate"; +const char* MissionItem::_jsonParamsKey = "params"; +const char* MissionItem::_jsonDoJumpIdKey = "doJumpId"; + +// Deprecated V1 format keys const char* MissionItem::_jsonParam1Key = "param1"; const char* MissionItem::_jsonParam2Key = "param2"; const char* MissionItem::_jsonParam3Key = "param3"; const char* MissionItem::_jsonParam4Key = "param4"; -const char* MissionItem::_jsonAutoContinueKey = "autoContinue"; -const char* MissionItem::_jsonCoordinateKey = "coordinate"; MissionItem::MissionItem(QObject* parent) : QObject(parent) , _sequenceNumber(0) + , _doJumpId(-1) , _isCurrentItem(false) , _autoContinueFact (0, "AutoContinue", FactMetaData::valueTypeUint32) , _commandFact (0, "", FactMetaData::valueTypeUint32) @@ -65,6 +68,7 @@ MissionItem::MissionItem(int sequenceNumber, QObject* parent) : QObject(parent) , _sequenceNumber(sequenceNumber) + , _doJumpId(-1) , _isCurrentItem(isCurrentItem) , _commandFact (0, "", FactMetaData::valueTypeUint32) , _frameFact (0, "", FactMetaData::valueTypeUint32) @@ -96,6 +100,7 @@ MissionItem::MissionItem(int sequenceNumber, MissionItem::MissionItem(const MissionItem& other, QObject* parent) : QObject(parent) , _sequenceNumber(0) + , _doJumpId(-1) , _isCurrentItem(false) , _commandFact (0, "", FactMetaData::valueTypeUint32) , _frameFact (0, "", FactMetaData::valueTypeUint32) @@ -116,6 +121,8 @@ MissionItem::MissionItem(const MissionItem& other, QObject* parent) const MissionItem& MissionItem::operator=(const MissionItem& other) { + _doJumpId = other._doJumpId; + setCommand(other.command()); setFrame(other.frame()); setSequenceNumber(other._sequenceNumber); @@ -138,19 +145,18 @@ MissionItem::~MissionItem() void MissionItem::save(QJsonObject& json) const { - json[_jsonTypeKey] = _itemType; - json[_jsonIdKey] = sequenceNumber(); + json[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeSimpleItemValue; json[_jsonFrameKey] = frame(); json[_jsonCommandKey] = command(); - json[_jsonParam1Key] = param1(); - json[_jsonParam2Key] = param2(); - json[_jsonParam3Key] = param3(); - json[_jsonParam4Key] = param4(); json[_jsonAutoContinueKey] = autoContinue(); + json[_jsonDoJumpIdKey] = _sequenceNumber; + + QJsonArray rgParams = { param1(), param2(), param3(), param4() }; + json[_jsonParamsKey] = rgParams; - QJsonArray coordinateArray; - coordinateArray << param5() << param6() << param7(); - json[_jsonCoordinateKey] = coordinateArray; + QJsonValue coordinateValue; + JsonHelper::saveGeoCoordinate(QGeoCoordinate(param5(), param6(), param7()), true /* writeAltitude */, coordinateValue); + json[_jsonCoordinateKey] = coordinateValue; } bool MissionItem::load(QTextStream &loadStream) @@ -175,41 +181,98 @@ bool MissionItem::load(QTextStream &loadStream) return false; } -bool MissionItem::load(const QJsonObject& json, QString& errorString) +bool MissionItem::_convertJsonV1ToV2(const QJsonObject& json, QJsonObject& v2Json, QString& errorString) +{ + // V1 format type = "missionItem", V2 format type = "MissionItem" + // V1 format has params in separate param[1-n] keys + // V2 format has params in params array + v2Json = json; + + if (json.contains(_jsonParamsKey)) { + // Already V2 format + return true; + } + + QList keyInfoList = { + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + { _jsonParam1Key, QJsonValue::Double, true }, + { _jsonParam2Key, QJsonValue::Double, true }, + { _jsonParam3Key, QJsonValue::Double, true }, + { _jsonParam4Key, QJsonValue::Double, true }, + }; + if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) { + return false; + } + + if (v2Json[VisualMissionItem::jsonTypeKey].toString() == QStringLiteral("missionItem")) { + v2Json[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeSimpleItemValue; + } + + QJsonArray rgParams = { json[_jsonParam1Key].toDouble(), json[_jsonParam2Key].toDouble(), json[_jsonParam3Key].toDouble(), json[_jsonParam4Key].toDouble() }; + v2Json[_jsonParamsKey] = rgParams; + v2Json.remove(_jsonParam1Key); + v2Json.remove(_jsonParam2Key); + v2Json.remove(_jsonParam3Key); + v2Json.remove(_jsonParam4Key); + + return true; +} + +bool MissionItem::load(const QJsonObject& json, int sequenceNumber, QString& errorString) { - QStringList requiredKeys; + QJsonObject v2Json; + if (!_convertJsonV1ToV2(json, v2Json, errorString)) { + return false; + } + + QList keyInfoList = { + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + { _jsonFrameKey, QJsonValue::Double, true }, + { _jsonCommandKey, QJsonValue::Double, true }, + { _jsonParamsKey, QJsonValue::Array, true }, + { _jsonAutoContinueKey, QJsonValue::Bool, true }, + { _jsonCoordinateKey, QJsonValue::Array, true }, + { _jsonDoJumpIdKey, QJsonValue::Double, false }, + }; + if (!JsonHelper::validateKeys(v2Json, keyInfoList, errorString)) { + return false; + } - requiredKeys << _jsonTypeKey << _jsonIdKey << _jsonFrameKey << _jsonCommandKey << - _jsonParam1Key << _jsonParam2Key << _jsonParam3Key << _jsonParam4Key << - _jsonAutoContinueKey << _jsonCoordinateKey; - if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) { + if (v2Json[VisualMissionItem::jsonTypeKey] != VisualMissionItem::jsonTypeSimpleItemValue) { + errorString = tr("Type found: %1 must be: %2").arg(v2Json[VisualMissionItem::jsonTypeKey].toString()).arg(VisualMissionItem::jsonTypeSimpleItemValue); return false; } - if (json[_jsonTypeKey] != _itemType) { - errorString = QString("type found: %1 must be: %2").arg(json[_jsonTypeKey].toString()).arg(_itemType); + QJsonArray rgParams = v2Json[_jsonParamsKey].toArray(); + if (rgParams.count() != 4) { + errorString = tr("%1 key must contains 4 values").arg(_jsonParamsKey); return false; } // Make sure to set these first since they can signal other changes - setFrame((MAV_FRAME)json[_jsonFrameKey].toInt()); - setCommand((MAV_CMD)json[_jsonCommandKey].toInt()); + setFrame((MAV_FRAME)v2Json[_jsonFrameKey].toInt()); + setCommand((MAV_CMD)v2Json[_jsonCommandKey].toInt()); QGeoCoordinate coordinate; - if (!JsonHelper::loadGeoCoordinate(json[_jsonCoordinateKey], true /* altitudeRequired */, coordinate, errorString)) { + if (!JsonHelper::loadGeoCoordinate(v2Json[_jsonCoordinateKey], true /* altitudeRequired */, coordinate, errorString)) { return false; } setParam5(coordinate.latitude()); setParam6(coordinate.longitude()); setParam7(coordinate.altitude()); + _doJumpId = -1; + if (v2Json.contains(_jsonDoJumpIdKey)) { + _doJumpId = v2Json[_jsonDoJumpIdKey].toInt(); + } setIsCurrentItem(false); - setSequenceNumber(json[_jsonIdKey].toInt()); - setParam1(json[_jsonParam1Key].toDouble()); - setParam2(json[_jsonParam2Key].toDouble()); - setParam3(json[_jsonParam3Key].toDouble()); - setParam4(json[_jsonParam4Key].toDouble()); - setAutoContinue(json[_jsonAutoContinueKey].toBool()); + setSequenceNumber(sequenceNumber); + setAutoContinue(v2Json[_jsonAutoContinueKey].toBool()); + + setParam1(rgParams[0].toDouble()); + setParam2(rgParams[1].toDouble()); + setParam3(rgParams[2].toDouble()); + setParam4(rgParams[3].toDouble()); return true; } diff --git a/src/MissionManager/MissionItem.h b/src/MissionManager/MissionItem.h index ee52892ae..5d4b0f2dc 100644 --- a/src/MissionManager/MissionItem.h +++ b/src/MissionManager/MissionItem.h @@ -74,6 +74,7 @@ public: double param6 (void) const { return _param6Fact.rawValue().toDouble(); } double param7 (void) const { return _param7Fact.rawValue().toDouble(); } QGeoCoordinate coordinate (void) const; + int doJumpId (void) const { return _doJumpId; } void setCommand (MAV_CMD command); void setSequenceNumber (int sequenceNumber); @@ -91,7 +92,7 @@ public: void save(QJsonObject& json) const; bool load(QTextStream &loadStream); - bool load(const QJsonObject& json, QString& errorString); + bool load(const QJsonObject& json, int sequenceNumber, QString& errorString); bool relativeAltitude(void) const { return frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; } @@ -100,8 +101,11 @@ signals: void sequenceNumberChanged (int sequenceNumber); private: - int _sequenceNumber; - bool _isCurrentItem; + bool _convertJsonV1ToV2(const QJsonObject& json, QJsonObject& v2Json, QString& errorString); + + int _sequenceNumber; + int _doJumpId; + bool _isCurrentItem; Fact _autoContinueFact; Fact _commandFact; @@ -115,17 +119,18 @@ private: Fact _param7Fact; // Keys for Json save - static const char* _itemType; - static const char* _jsonTypeKey; - static const char* _jsonIdKey; static const char* _jsonFrameKey; static const char* _jsonCommandKey; + static const char* _jsonAutoContinueKey; + static const char* _jsonCoordinateKey; + static const char* _jsonParamsKey; + static const char* _jsonDoJumpIdKey; + + // Deprecated V1 format keys static const char* _jsonParam1Key; static const char* _jsonParam2Key; static const char* _jsonParam3Key; static const char* _jsonParam4Key; - static const char* _jsonAutoContinueKey; - static const char* _jsonCoordinateKey; friend class SurveyMissionItem; friend class SimpleMissionItem; diff --git a/src/MissionManager/MissionItemTest.cc b/src/MissionManager/MissionItemTest.cc index a408bf911..bb7d95e52 100644 --- a/src/MissionManager/MissionItemTest.cc +++ b/src/MissionManager/MissionItemTest.cc @@ -226,7 +226,7 @@ void MissionItemTest::_testFactSignals(void) void MissionItemTest::_checkExpectedMissionItem(const MissionItem& missionItem) { - QCOMPARE(missionItem.sequenceNumber(), 10); + QCOMPARE(missionItem.sequenceNumber(), _seq); QCOMPARE(missionItem.isCurrentItem(), false); QCOMPARE(missionItem.frame(), (MAV_FRAME)3); QCOMPARE(missionItem.command(), (MAV_CMD)80); @@ -264,33 +264,71 @@ void MissionItemTest::_testSimpleLoadFromStream(void) _checkExpectedMissionItem(simpleMissionItem.missionItem()); } -void MissionItemTest::_testLoadFromJson(void) +void MissionItemTest::_testLoadFromJsonV1(void) { MissionItem missionItem; QString errorString; QJsonArray coordinateArray; coordinateArray << -10.0 << -20.0 <<-30.0; QJsonObject jsonObject; - jsonObject.insert("autoContinue", true); - jsonObject.insert("command", 80); - jsonObject.insert("frame", 3); - jsonObject.insert("id", 10); - jsonObject.insert("param1", 10); - jsonObject.insert("param2", 20); - jsonObject.insert("param3", 30); - jsonObject.insert("param4", 40); - jsonObject.insert("type", "missionItem"); - jsonObject.insert("coordinate", coordinateArray); + jsonObject.insert(MissionItem::_jsonAutoContinueKey, true); + jsonObject.insert(MissionItem::_jsonCommandKey, 80); + jsonObject.insert(MissionItem::_jsonFrameKey, 3); + jsonObject.insert(MissionItem::_jsonParam1Key, 10); + jsonObject.insert(MissionItem::_jsonParam2Key, 20); + jsonObject.insert(MissionItem::_jsonParam3Key, 30); + jsonObject.insert(MissionItem::_jsonParam4Key, 40); + jsonObject.insert(VisualMissionItem::jsonTypeKey, VisualMissionItem::jsonTypeSimpleItemValue); + jsonObject.insert(MissionItem::_jsonCoordinateKey, coordinateArray); + // We only need to test the differences between V1 and V2 + + QStringList removeKeys; + removeKeys << MissionItem::_jsonParam1Key << MissionItem::_jsonParam2Key << MissionItem::_jsonParam3Key << MissionItem::_jsonParam4Key; + foreach (const QString& removeKey, removeKeys) { + QJsonObject badObject = jsonObject; + badObject.remove(removeKey); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); + QVERIFY(!errorString.isEmpty()); + qDebug() << errorString; + } + + // Test good load + + QVERIFY(missionItem.load(jsonObject, _seq, errorString)); + _checkExpectedMissionItem(missionItem); +} + +void MissionItemTest::_testLoadFromJsonV2(void) +{ + MissionItem missionItem; + QString errorString; + QJsonArray coordinateArray; + coordinateArray << -10.0 << -20.0 <<-30.0; + QJsonObject jsonObject; + jsonObject.insert(MissionItem::_jsonAutoContinueKey, true); + jsonObject.insert(MissionItem::_jsonCommandKey, 80); + jsonObject.insert(MissionItem::_jsonFrameKey, 3); + jsonObject.insert(VisualMissionItem::jsonTypeKey, VisualMissionItem::jsonTypeSimpleItemValue); + jsonObject.insert(MissionItem::_jsonCoordinateKey, coordinateArray); + + QJsonArray rgParams = { 10, 20, 30, 40 }; + jsonObject.insert(MissionItem::_jsonParamsKey, rgParams); + // Test missing key detection QStringList removeKeys; - removeKeys << "autoContinue" << "command" << "frame" << "id" << "param1" << "param2" << "param3" << "param4" << "type" << "coordinate"; + removeKeys << MissionItem::_jsonAutoContinueKey << + MissionItem::_jsonCommandKey << + MissionItem::_jsonFrameKey << + MissionItem::_jsonParamsKey << + VisualMissionItem::jsonTypeKey << + MissionItem::_jsonCoordinateKey; foreach(const QString& removeKey, removeKeys) { QJsonObject badObject = jsonObject; badObject.remove(removeKey); - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; } @@ -300,7 +338,7 @@ void MissionItemTest::_testLoadFromJson(void) QJsonObject badObject = jsonObject; badObject.remove("coordinate"); badObject["coordinate"] = 10; - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; @@ -309,7 +347,7 @@ void MissionItemTest::_testLoadFromJson(void) badObject = jsonObject; badObject.remove("coordinate"); badObject["coordinate"] = badCoordinateArray; - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; @@ -318,7 +356,7 @@ void MissionItemTest::_testLoadFromJson(void) badObject = jsonObject; badObject.remove("coordinate"); badObject["coordinate"] = badCoordinateArray_second; - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; @@ -329,7 +367,7 @@ void MissionItemTest::_testLoadFromJson(void) badObject = jsonObject; badObject.remove("coordinate"); badObject["coordinate"] = badCoordinateArray_third; - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; @@ -338,13 +376,17 @@ void MissionItemTest::_testLoadFromJson(void) badObject = jsonObject; badObject.remove("type"); badObject["type"] = "foo"; - QCOMPARE(missionItem.load(badObject, errorString), false); + QCOMPARE(missionItem.load(badObject, _seq, errorString), false); QVERIFY(!errorString.isEmpty()); qDebug() << errorString; // Test good load - QVERIFY(missionItem.load(jsonObject, errorString)); + bool result = missionItem.load(jsonObject, _seq, errorString); + if (!result) { + qDebug() << errorString; + QVERIFY(result); + } _checkExpectedMissionItem(missionItem); } @@ -356,21 +398,19 @@ void MissionItemTest::_testSimpleLoadFromJson(void) SimpleMissionItem simpleMissionItem(NULL); QString errorString; QJsonArray coordinateArray; - coordinateArray << -10.0 << -20.0 << -30.0; QJsonObject jsonObject; - jsonObject.insert("autoContinue", true); - jsonObject.insert("command", 80); - jsonObject.insert("frame", 3); - jsonObject.insert("id", 10); - jsonObject.insert("param1", 10); - jsonObject.insert("param2", 20); - jsonObject.insert("param3", 30); - jsonObject.insert("param4", 40); - jsonObject.insert("type", "missionItem"); - jsonObject.insert("coordinate", coordinateArray); - - - QVERIFY(simpleMissionItem.load(jsonObject, errorString)); + + coordinateArray << -10.0 << -20.0 <<-30.0; + jsonObject.insert(MissionItem::_jsonAutoContinueKey, true); + jsonObject.insert(MissionItem::_jsonCommandKey, 80); + jsonObject.insert(MissionItem::_jsonFrameKey, 3); + jsonObject.insert(VisualMissionItem::jsonTypeKey, VisualMissionItem::jsonTypeSimpleItemValue); + jsonObject.insert(MissionItem::_jsonCoordinateKey, coordinateArray); + + QJsonArray rgParams = { 10, 20, 30, 40 }; + jsonObject.insert(MissionItem::_jsonParamsKey, rgParams); + + QVERIFY(simpleMissionItem.load(jsonObject, _seq, errorString)); _checkExpectedMissionItem(simpleMissionItem.missionItem()); } @@ -378,7 +418,7 @@ void MissionItemTest::_testSaveToJson(void) { MissionItem missionItem; - missionItem.setSequenceNumber(10); + missionItem.setSequenceNumber(_seq); missionItem.setIsCurrentItem(true); missionItem.setFrame((MAV_FRAME)3); missionItem.setCommand((MAV_CMD)80); @@ -395,9 +435,9 @@ void MissionItemTest::_testSaveToJson(void) QJsonObject jsonObject; QString errorString; missionItem.save(jsonObject); - QVERIFY(missionItem.load(jsonObject, errorString)); + QVERIFY(missionItem.load(jsonObject, _seq, errorString)); - QCOMPARE(missionItem.sequenceNumber(), 10); + QCOMPARE(missionItem.sequenceNumber(), _seq); QCOMPARE(missionItem.isCurrentItem(), false); QCOMPARE(missionItem.frame(), (MAV_FRAME)3); QCOMPARE(missionItem.command(), (MAV_CMD)80); @@ -410,295 +450,3 @@ void MissionItemTest::_testSaveToJson(void) QCOMPARE(missionItem.param7(), -30.1234567); QCOMPARE(missionItem.autoContinue(), true); } - -#if 0 -void MissionItemTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t failureMode) -{ - _mockLink->setMissionItemFailureMode(failureMode); - - // Setup our test case data - QList missionItems; - - // Editor has a home position item on the front, so we do the same - MissionItem* homeItem = new MissionItem(NULL /* Vehicle */, this); - homeItem->setCommand(MAV_CMD_NAV_WAYPOINT); - homeItem->setCoordinate(QGeoCoordinate(47.3769, 8.549444, 0)); - homeItem->setSequenceNumber(0); - missionItems.append(homeItem); - - for (size_t i=0; i<_cTestCases; i++) { - const TestCase_t* testCase = &_rgTestCases[i]; - - MissionItem* missionItem = new MissionItem(this); - - QTextStream loadStream(testCase->itemStream, QIODevice::ReadOnly); - QVERIFY(missionItem->load(loadStream)); - - // Mission Manager expects to get 1-base sequence numbers for write - missionItem->setSequenceNumber(missionItem->sequenceNumber() + 1); - - missionItems.append(missionItem); - } - - // Send the items to the vehicle - _missionManager->writeMissionItems(missionItems); - - // writeMissionItems should emit these signals before returning: - // inProgressChanged - // newMissionItemsAvailable - QVERIFY(_missionManager->inProgress()); - QCOMPARE(_multiSpyMissionManager->checkSignalByMask(inProgressChangedSignalMask | newMissionItemsAvailableSignalMask), true); - _checkInProgressValues(true); - - _multiSpyMissionManager->clearAllSignals(); - - if (failureMode == MockLinkMissionItemHandler::FailNone) { - // This should be clean run - - // Wait for write sequence to complete. We should get: - // inProgressChanged(false) signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkOnlySignalByMask(inProgressChangedSignalMask), true); - - // Validate inProgressChanged signal value - _checkInProgressValues(false); - - // Validate item count in mission manager - - int expectedCount = (int)_cTestCases; - if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // Home position at position 0 comes from vehicle - expectedCount++; - } - - QCOMPARE(_missionManager->missionItems().count(), expectedCount); - } else { - // This should be a failed run - - setExpectedMessageBox(QMessageBox::Ok); - - // Wait for write sequence to complete. We should get: - // inProgressChanged(false) signal - // error(errorCode, QString) signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkSignalByMask(inProgressChangedSignalMask | errorSignalMask), true); - - // Validate inProgressChanged signal value - _checkInProgressValues(false); - - // Validate error signal values - QSignalSpy* spy = _multiSpyMissionManager->getSpyByIndex(errorSignalIndex); - QList signalArgs = spy->takeFirst(); - QCOMPARE(signalArgs.count(), 2); - qDebug() << signalArgs[1].toString(); - - checkExpectedMessageBox(); - } - - _multiSpyMissionManager->clearAllSignals(); -} - -void MissionItemTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode_t failureMode) -{ - _writeItems(MockLinkMissionItemHandler::FailNone); - - _mockLink->setMissionItemFailureMode(failureMode); - - // Read the items back from the vehicle - _missionManager->requestMissionItems(); - - // requestMissionItems should emit inProgressChanged signal before returning so no need to wait for it - QVERIFY(_missionManager->inProgress()); - QCOMPARE(_multiSpyMissionManager->checkOnlySignalByMask(inProgressChangedSignalMask), true); - _checkInProgressValues(true); - - _multiSpyMissionManager->clearAllSignals(); - - if (failureMode == MockLinkMissionItemHandler::FailNone) { - // This should be clean run - - // Now wait for read sequence to complete. We should get: - // inProgressChanged(false) signal to signal completion - // newMissionItemsAvailable signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true); - _checkInProgressValues(false); - - } else { - // This should be a failed run - - setExpectedMessageBox(QMessageBox::Ok); - - // Wait for read sequence to complete. We should get: - // inProgressChanged(false) signal to signal completion - // error(errorCode, QString) signal - // newMissionItemsAvailable signal - _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); - QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask | errorSignalMask), true); - - // Validate inProgressChanged signal value - _checkInProgressValues(false); - - // Validate error signal values - QSignalSpy* spy = _multiSpyMissionManager->getSpyByIndex(errorSignalIndex); - QList signalArgs = spy->takeFirst(); - QCOMPARE(signalArgs.count(), 2); - qDebug() << signalArgs[1].toString(); - - checkExpectedMessageBox(); - } - - _multiSpyMissionManager->clearAllSignals(); - - // Validate returned items - - size_t cMissionItemsExpected; - - if (failureMode == MockLinkMissionItemHandler::FailNone) { - cMissionItemsExpected = (int)_cTestCases; - if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // Home position at position 0 comes from vehicle - cMissionItemsExpected++; - } - } else { - cMissionItemsExpected = 0; - } - - QCOMPARE(_missionManager->missionItems().count(), (int)cMissionItemsExpected); - - size_t firstActualItem = 0; - if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // First item is home position, don't validate it - firstActualItem++; - } - - int testCaseIndex = 0; - for (size_t actualItemIndex=firstActualItem; actualItemIndexexpectedItem.sequenceNumber; - if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { - // Account for home position in first item - expectedSequenceNumber++; - } - - MissionItem* actual = _missionManager->missionItems()[actualItemIndex]; - - qDebug() << "Test case" << testCaseIndex; - QCOMPARE(actual->sequenceNumber(), expectedSequenceNumber); - QCOMPARE(actual->coordinate().latitude(), testCase->expectedItem.coordinate.latitude()); - QCOMPARE(actual->coordinate().longitude(), testCase->expectedItem.coordinate.longitude()); - QCOMPARE(actual->coordinate().altitude(), testCase->expectedItem.coordinate.altitude()); - QCOMPARE((int)actual->command(), (int)testCase->expectedItem.command); - QCOMPARE(actual->param1(), testCase->expectedItem.param1); - QCOMPARE(actual->param2(), testCase->expectedItem.param2); - QCOMPARE(actual->param3(), testCase->expectedItem.param3); - QCOMPARE(actual->param4(), testCase->expectedItem.param4); - QCOMPARE(actual->autoContinue(), testCase->expectedItem.autocontinue); - QCOMPARE(actual->frame(), testCase->expectedItem.frame); - - testCaseIndex++; - } - -} - -void MissionItemTest::_testWriteFailureHandlingWorker(void) -{ - /* - /// Called to send a MISSION_ACK message while the MissionManager is in idle state - void sendUnexpectedMissionAck(MAV_MISSION_RESULT ackType) { _missionItemHandler.sendUnexpectedMissionAck(ackType); } - - /// Called to send a MISSION_ITEM message while the MissionManager is in idle state - void sendUnexpectedMissionItem(void) { _missionItemHandler.sendUnexpectedMissionItem(); } - - /// Called to send a MISSION_REQUEST message while the MissionManager is in idle state - void sendUnexpectedMissionRequest(void) { _missionItemHandler.sendUnexpectedMissionRequest(); } - */ - - typedef struct { - const char* failureText; - MockLinkMissionItemHandler::FailureMode_t failureMode; - } TestCase_t; - - static const TestCase_t rgTestCases[] = { - { "No Failure", MockLinkMissionItemHandler::FailNone }, - { "FailWriteRequest0NoResponse", MockLinkMissionItemHandler::FailWriteRequest0NoResponse }, - { "FailWriteRequest1NoResponse", MockLinkMissionItemHandler::FailWriteRequest1NoResponse }, - { "FailWriteRequest0IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest0IncorrectSequence }, - { "FailWriteRequest1IncorrectSequence", MockLinkMissionItemHandler::FailWriteRequest1IncorrectSequence }, - { "FailWriteRequest0ErrorAck", MockLinkMissionItemHandler::FailWriteRequest0ErrorAck }, - { "FailWriteRequest1ErrorAck", MockLinkMissionItemHandler::FailWriteRequest1ErrorAck }, - { "FailWriteFinalAckNoResponse", MockLinkMissionItemHandler::FailWriteFinalAckNoResponse }, - { "FailWriteFinalAckErrorAck", MockLinkMissionItemHandler::FailWriteFinalAckErrorAck }, - { "FailWriteFinalAckMissingRequests", MockLinkMissionItemHandler::FailWriteFinalAckMissingRequests }, - }; - - for (size_t i=0; iresetMissionItemHandler(); - } -} - -void MissionItemTest::_testReadFailureHandlingWorker(void) -{ - /* - /// Called to send a MISSION_ACK message while the MissionManager is in idle state - void sendUnexpectedMissionAck(MAV_MISSION_RESULT ackType) { _missionItemHandler.sendUnexpectedMissionAck(ackType); } - - /// Called to send a MISSION_ITEM message while the MissionManager is in idle state - void sendUnexpectedMissionItem(void) { _missionItemHandler.sendUnexpectedMissionItem(); } - - /// Called to send a MISSION_REQUEST message while the MissionManager is in idle state - void sendUnexpectedMissionRequest(void) { _missionItemHandler.sendUnexpectedMissionRequest(); } - */ - - typedef struct { - const char* failureText; - MockLinkMissionItemHandler::FailureMode_t failureMode; - } TestCase_t; - - static const TestCase_t rgTestCases[] = { - { "No Failure", MockLinkMissionItemHandler::FailNone }, - { "FailReadRequestListNoResponse", MockLinkMissionItemHandler::FailReadRequestListNoResponse }, - { "FailReadRequest0NoResponse", MockLinkMissionItemHandler::FailReadRequest0NoResponse }, - { "FailReadRequest1NoResponse", MockLinkMissionItemHandler::FailReadRequest1NoResponse }, - { "FailReadRequest0IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest0IncorrectSequence }, - { "FailReadRequest1IncorrectSequence", MockLinkMissionItemHandler::FailReadRequest1IncorrectSequence }, - { "FailReadRequest0ErrorAck", MockLinkMissionItemHandler::FailReadRequest0ErrorAck }, - { "FailReadRequest1ErrorAck", MockLinkMissionItemHandler::FailReadRequest1ErrorAck }, - }; - - for (size_t i=0; iresetMissionItemHandler(); - _multiSpyMissionManager->clearAllSignals(); - } -} - -void MissionItemTest::_testWriteFailureHandlingAPM(void) -{ - _initForFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); - _testWriteFailureHandlingWorker(); -} - -void MissionItemTest::_testReadFailureHandlingAPM(void) -{ - _initForFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); - _testReadFailureHandlingWorker(); -} - - -void MissionItemTest::_testWriteFailureHandlingPX4(void) -{ - _initForFirmwareType(MAV_AUTOPILOT_PX4); - _testWriteFailureHandlingWorker(); -} - -void MissionItemTest::_testReadFailureHandlingPX4(void) -{ - _initForFirmwareType(MAV_AUTOPILOT_PX4); - _testReadFailureHandlingWorker(); -} -#endif diff --git a/src/MissionManager/MissionItemTest.h b/src/MissionManager/MissionItemTest.h index 857235606..4dd1f6b35 100644 --- a/src/MissionManager/MissionItemTest.h +++ b/src/MissionManager/MissionItemTest.h @@ -29,12 +29,15 @@ private slots: void _testFactSignals(void); void _testLoadFromStream(void); void _testSimpleLoadFromStream(void); - void _testLoadFromJson(void); + void _testLoadFromJsonV1(void); + void _testLoadFromJsonV2(void); void _testSimpleLoadFromJson(void); void _testSaveToJson(void); private: void _checkExpectedMissionItem(const MissionItem& missionItem); + + int _seq = 10; }; #endif diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 0b3bfeb01..3a653e776 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -75,8 +75,13 @@ bool RallyPointController::_loadJsonFile(QJsonDocument& jsonDoc, QString& errorS { QJsonObject json = jsonDoc.object(); - int fileMajorVersion, fileMinorVersion; - if (!JsonHelper::validateQGCJsonFile(json, _jsonFileTypeValue, 1 /* supportedMajorVersion */, 0 /* supportedMinorVersion */, fileMajorVersion, fileMinorVersion, errorString)) { + int fileVersion; + if (!JsonHelper::validateQGCJsonFile(json, + _jsonFileTypeValue, // expected file type + 1, // minimum supported version + 1, // maximum supported version + fileVersion, + errorString)) { return false; } @@ -166,7 +171,7 @@ void RallyPointController::saveToFile(const QString& filename) QJsonObject jsonObject; jsonObject[JsonHelper::jsonFileTypeKey] = _jsonFileTypeValue; - jsonObject[JsonHelper::jsonVersionKey] = QStringLiteral("1.0"); + jsonObject[JsonHelper::jsonVersionKey] = 1; jsonObject[JsonHelper::jsonGroundStationKey] = JsonHelper::jsonGroundStationValue; QJsonArray rgPoints; diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 35614a1eb..22ecd6252 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -18,14 +18,14 @@ #include "MissionCommandTree.h" #include "MissionCommandUIInfo.h" -const double SimpleMissionItem::defaultAltitude = 50.0; +const double SimpleMissionItem::defaultAltitude = 50.0; -FactMetaData* SimpleMissionItem::_altitudeMetaData = NULL; -FactMetaData* SimpleMissionItem::_commandMetaData = NULL; -FactMetaData* SimpleMissionItem::_defaultParamMetaData = NULL; -FactMetaData* SimpleMissionItem::_frameMetaData = NULL; -FactMetaData* SimpleMissionItem::_latitudeMetaData = NULL; -FactMetaData* SimpleMissionItem::_longitudeMetaData = NULL; +FactMetaData* SimpleMissionItem::_altitudeMetaData = NULL; +FactMetaData* SimpleMissionItem::_commandMetaData = NULL; +FactMetaData* SimpleMissionItem::_defaultParamMetaData = NULL; +FactMetaData* SimpleMissionItem::_frameMetaData = NULL; +FactMetaData* SimpleMissionItem::_latitudeMetaData = NULL; +FactMetaData* SimpleMissionItem::_longitudeMetaData = NULL; struct EnumInfo_s { const char * label; @@ -248,9 +248,9 @@ bool SimpleMissionItem::load(QTextStream &loadStream) return _missionItem.load(loadStream); } -bool SimpleMissionItem::load(const QJsonObject& json, QString& errorString) +bool SimpleMissionItem::load(const QJsonObject& json, int sequenceNumber, QString& errorString) { - return _missionItem.load(json, errorString); + return _missionItem.load(json, sequenceNumber, errorString); } bool SimpleMissionItem::isStandaloneCoordinate(void) const diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index a079478b2..9d3650c0c 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -71,7 +71,7 @@ public: void setDistance (double distance); bool load(QTextStream &loadStream); - bool load(const QJsonObject& json, QString& errorString); + bool load(const QJsonObject& json, int sequenceNumber, QString& errorString); bool relativeAltitude(void) { return _missionItem.frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; } diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index aae975f13..fd10f9aeb 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -17,9 +17,9 @@ QGC_LOGGING_CATEGORY(SurveyMissionItemLog, "SurveyMissionItemLog") -const char* SurveyMissionItem::_jsonTypeKey = "type"; +const char* SurveyMissionItem::jsonComplexItemTypeValue = "survey"; + const char* SurveyMissionItem::_jsonPolygonObjectKey = "polygon"; -const char* SurveyMissionItem::_jsonIdKey = "id"; const char* SurveyMissionItem::_jsonGridObjectKey = "grid"; const char* SurveyMissionItem::_jsonGridAltitudeKey = "altitude"; const char* SurveyMissionItem::_jsonGridAltitudeRelativeKey = "relativeAltitude"; @@ -56,8 +56,6 @@ const char* SurveyMissionItem::_cameraResolutionWidthFactName = "Camera reso const char* SurveyMissionItem::_cameraResolutionHeightFactName = "Camera resolution height"; const char* SurveyMissionItem::_cameraFocalLengthFactName = "Focal length"; -const char* SurveyMissionItem::_complexType = "survey"; - QMap SurveyMissionItem::_metaDataMap; SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) @@ -239,12 +237,12 @@ void SurveyMissionItem::setDirty(bool dirty) void SurveyMissionItem::save(QJsonObject& saveObject) const { - saveObject[JsonHelper::jsonVersionKey] = 2; - saveObject[_jsonTypeKey] = _complexType; - saveObject[_jsonIdKey] = sequenceNumber(); - saveObject[_jsonCameraTriggerKey] = _cameraTrigger; - saveObject[_jsonManualGridKey] = _manualGrid; - saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitude; + saveObject[JsonHelper::jsonVersionKey] = 3; + saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; + saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; + saveObject[_jsonCameraTriggerKey] = _cameraTrigger; + saveObject[_jsonManualGridKey] = _manualGrid; + saveObject[_jsonFixedValueIsAltitudeKey] = _fixedValueIsAltitude; if (_cameraTrigger) { saveObject[_jsonCameraTriggerDistanceKey] = _cameraTriggerDistanceFact.rawValue().toDouble(); @@ -306,98 +304,107 @@ void SurveyMissionItem::_clear(void) } -bool SurveyMissionItem::load(const QJsonObject& complexObject, QString& errorString) +bool SurveyMissionItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) { - struct jsonKeyInfo_s { - const char* key; - QJsonValue::Type type; - bool required; - }; + QJsonObject v2Object = complexObject; - QList mainKeyInfoList = { - { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, - { _jsonTypeKey, QJsonValue::String, true }, - { _jsonPolygonObjectKey, QJsonValue::Array, true }, - { _jsonIdKey, QJsonValue::Double, true }, - { _jsonGridObjectKey, QJsonValue::Object, true }, - { _jsonCameraObjectKey, QJsonValue::Object, false }, - { _jsonCameraTriggerKey, QJsonValue::Bool, true }, - { _jsonCameraTriggerDistanceKey, QJsonValue::Double, false }, - { _jsonManualGridKey, QJsonValue::Bool, true }, - { _jsonFixedValueIsAltitudeKey, QJsonValue::Bool, true }, + // We need to pull version first to determine what validation/conversion needs to be performed. + QList versionKeyInfoList = { + { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, }; - - QList gridKeyInfoList = { - { _jsonGridAltitudeKey, QJsonValue::Double, true }, - { _jsonGridAltitudeRelativeKey, QJsonValue::Bool, true }, - { _jsonGridAngleKey, QJsonValue::Double, true }, - { _jsonGridSpacingKey, QJsonValue::Double, true }, - { _jsonTurnaroundDistKey, QJsonValue::Double, true }, - }; - - QList cameraKeyInfoList = { - { _jsonGroundResolutionKey, QJsonValue::Double, true }, - { _jsonFrontalOverlapKey, QJsonValue::Double, true }, - { _jsonSideOverlapKey, QJsonValue::Double, true }, - { _jsonCameraSensorWidthKey, QJsonValue::Double, true }, - { _jsonCameraSensorHeightKey, QJsonValue::Double, true }, - { _jsonCameraResolutionWidthKey, QJsonValue::Double, true }, - { _jsonCameraResolutionHeightKey, QJsonValue::Double, true }, - { _jsonCameraFocalLengthKey, QJsonValue::Double, true }, - { _jsonCameraNameKey, QJsonValue::String, true }, - { _jsonCameraOrientationLandscapeKey, QJsonValue::Bool, true }, - }; - - if (!JsonHelper::validateKeys(complexObject, mainKeyInfoList, errorString)) { + if (!JsonHelper::validateKeys(v2Object, versionKeyInfoList, errorString)) { return false; } - if (!JsonHelper::validateKeys(complexObject[_jsonGridObjectKey].toObject(), gridKeyInfoList, errorString)) { + + int version = v2Object[JsonHelper::jsonVersionKey].toInt(); + if (version != 2 && version != 3) { + errorString = tr("QGroundControl does not support this version of survey items"); return false; } + if (version == 2) { + // Convert to v3 + if (v2Object.contains(VisualMissionItem::jsonTypeKey) && v2Object[VisualMissionItem::jsonTypeKey].toString() == QStringLiteral("survey")) { + v2Object[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; + v2Object[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; + } + } - // Version check - if (complexObject[JsonHelper::jsonVersionKey].toInt() != 2) { - errorString = tr("QGroundControl does not support this version of survey items"); + QList mainKeyInfoList = { + { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, + { _jsonPolygonObjectKey, QJsonValue::Array, true }, + { _jsonGridObjectKey, QJsonValue::Object, true }, + { _jsonCameraObjectKey, QJsonValue::Object, false }, + { _jsonCameraTriggerKey, QJsonValue::Bool, true }, + { _jsonCameraTriggerDistanceKey, QJsonValue::Double, false }, + { _jsonManualGridKey, QJsonValue::Bool, true }, + { _jsonFixedValueIsAltitudeKey, QJsonValue::Bool, true }, + }; + if (!JsonHelper::validateKeys(v2Object, mainKeyInfoList, errorString)) { return false; } - QString complexType = complexObject[_jsonTypeKey].toString(); - if (complexType != _complexType) { - errorString = tr("QGroundControl does not support loading this complex mission item type: %1").arg(complexType); + + QString itemType = v2Object[VisualMissionItem::jsonTypeKey].toString(); + QString complexType = v2Object[ComplexMissionItem::jsonComplexItemTypeKey].toString(); + if (itemType != VisualMissionItem::jsonTypeComplexItemValue || complexType != jsonComplexItemTypeValue) { + errorString = tr("QGroundControl does not support loading this complex mission item type: %1:2").arg(itemType).arg(complexType); return false; } _clear(); - setSequenceNumber(complexObject[_jsonIdKey].toInt()); - - _manualGrid = complexObject[_jsonManualGridKey].toBool(true); - _cameraTrigger = complexObject[_jsonCameraTriggerKey].toBool(false); - _fixedValueIsAltitude = complexObject[_jsonFixedValueIsAltitudeKey].toBool(true); - _gridAltitudeRelative = complexObject[_jsonGridAltitudeRelativeKey].toBool(true); + setSequenceNumber(sequenceNumber); - QJsonObject gridObject = complexObject[_jsonGridObjectKey].toObject(); + _manualGrid = v2Object[_jsonManualGridKey].toBool(true); + _cameraTrigger = v2Object[_jsonCameraTriggerKey].toBool(false); + _fixedValueIsAltitude = v2Object[_jsonFixedValueIsAltitudeKey].toBool(true); + _gridAltitudeRelative = v2Object[_jsonGridAltitudeRelativeKey].toBool(true); + QList gridKeyInfoList = { + { _jsonGridAltitudeKey, QJsonValue::Double, true }, + { _jsonGridAltitudeRelativeKey, QJsonValue::Bool, true }, + { _jsonGridAngleKey, QJsonValue::Double, true }, + { _jsonGridSpacingKey, QJsonValue::Double, true }, + { _jsonTurnaroundDistKey, QJsonValue::Double, true }, + }; + QJsonObject gridObject = v2Object[_jsonGridObjectKey].toObject(); + if (!JsonHelper::validateKeys(gridObject, gridKeyInfoList, errorString)) { + return false; + } _gridAltitudeFact.setRawValue (gridObject[_jsonGridAltitudeKey].toDouble()); _gridAngleFact.setRawValue (gridObject[_jsonGridAngleKey].toDouble()); _gridSpacingFact.setRawValue (gridObject[_jsonGridSpacingKey].toDouble()); _turnaroundDistFact.setRawValue (gridObject[_jsonTurnaroundDistKey].toDouble()); if (_cameraTrigger) { - if (!complexObject.contains(_jsonCameraTriggerDistanceKey)) { + if (!v2Object.contains(_jsonCameraTriggerDistanceKey)) { errorString = tr("%1 but %2 is missing").arg("cameraTrigger = true").arg("cameraTriggerDistance"); return false; } - _cameraTriggerDistanceFact.setRawValue(complexObject[_jsonCameraTriggerDistanceKey].toDouble()); + _cameraTriggerDistanceFact.setRawValue(v2Object[_jsonCameraTriggerDistanceKey].toDouble()); } if (!_manualGrid) { - if (!complexObject.contains(_jsonCameraObjectKey)) { + if (!v2Object.contains(_jsonCameraObjectKey)) { errorString = tr("%1 but %2 object is missing").arg("manualGrid = false").arg("camera"); return false; } - QJsonObject cameraObject = complexObject[_jsonCameraObjectKey].toObject(); - + QJsonObject cameraObject = v2Object[_jsonCameraObjectKey].toObject(); + + QList cameraKeyInfoList = { + { _jsonGroundResolutionKey, QJsonValue::Double, true }, + { _jsonFrontalOverlapKey, QJsonValue::Double, true }, + { _jsonSideOverlapKey, QJsonValue::Double, true }, + { _jsonCameraSensorWidthKey, QJsonValue::Double, true }, + { _jsonCameraSensorHeightKey, QJsonValue::Double, true }, + { _jsonCameraResolutionWidthKey, QJsonValue::Double, true }, + { _jsonCameraResolutionHeightKey, QJsonValue::Double, true }, + { _jsonCameraFocalLengthKey, QJsonValue::Double, true }, + { _jsonCameraNameKey, QJsonValue::String, true }, + { _jsonCameraOrientationLandscapeKey, QJsonValue::Bool, true }, + }; if (!JsonHelper::validateKeys(cameraObject, cameraKeyInfoList, errorString)) { return false; } @@ -416,7 +423,7 @@ bool SurveyMissionItem::load(const QJsonObject& complexObject, QString& errorStr } // Polygon shape - QJsonArray polygonArray(complexObject[_jsonPolygonObjectKey].toArray()); + QJsonArray polygonArray(v2Object[_jsonPolygonObjectKey].toArray()); for (int i=0; i _metaDataMap; - static const char* _jsonTypeKey; static const char* _jsonPolygonObjectKey; - static const char* _jsonIdKey; static const char* _jsonGridObjectKey; static const char* _jsonGridAltitudeKey; static const char* _jsonGridAltitudeRelativeKey; @@ -212,8 +212,6 @@ private: static const char* _cameraResolutionWidthFactName; static const char* _cameraResolutionHeightFactName; static const char* _cameraFocalLengthFactName; - - static const char* _complexType; }; #endif diff --git a/src/MissionManager/VisualMissionItem.cc b/src/MissionManager/VisualMissionItem.cc index e0b8faf16..db8b1ba78 100644 --- a/src/MissionManager/VisualMissionItem.cc +++ b/src/MissionManager/VisualMissionItem.cc @@ -16,6 +16,10 @@ #include "QGCApplication.h" #include "JsonHelper.h" +const char* VisualMissionItem::jsonTypeKey = "type"; +const char* VisualMissionItem::jsonTypeSimpleItemValue = "SimpleItem"; +const char* VisualMissionItem::jsonTypeComplexItemValue = "ComplexItem"; + VisualMissionItem::VisualMissionItem(Vehicle* vehicle, QObject* parent) : QObject(parent) , _vehicle(vehicle) diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index 6e1fa8daf..32bc65aad 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -123,6 +123,10 @@ public: /// @param saveObject Save the item to this json object virtual void save(QJsonObject& saveObject) const = 0; + static const char* jsonTypeKey; ///< Json file attribute which specifies the item type + static const char* jsonTypeSimpleItemValue; ///< Item type is MISSION_ITEM + static const char* jsonTypeComplexItemValue; ///< Item type is Complex Item + signals: void altDifferenceChanged (double altDifference); void altPercentChanged (double altPercent); -- GitLab From 2bf3657141592e1a507b739a6237ed07c19288ec Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 11:48:22 -0800 Subject: [PATCH 162/398] Fix compiler warning --- src/MissionManager/MissionController.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 760bbc248..bb2d1c15e 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -464,7 +464,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec for (int i=0; icount(); i++) { if (visualItems->value(i)->isSimpleItem()) { SimpleMissionItem* doJumpItem = visualItems->value(i); - if (doJumpItem->command() == MAV_CMD_DO_JUMP) { + if (doJumpItem->command() == (MAV_CMD)MAV_CMD_DO_JUMP) { bool found = false; int findDoJumpId = doJumpItem->missionItem().param1(); for (int j=0; jcount(); j++) { -- GitLab From c84d76e334a337d27535997e43d500ca9b6ca585 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 11:48:41 -0800 Subject: [PATCH 163/398] Fix typo in camera.imageSideOverlap --- src/MissionManager/SurveyMissionItem.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index fd10f9aeb..0c0a4ff18 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -30,7 +30,7 @@ const char* SurveyMissionItem::_jsonCameraTriggerKey = "cameraTrigg const char* SurveyMissionItem::_jsonCameraTriggerDistanceKey = "cameraTriggerDistance"; const char* SurveyMissionItem::_jsonGroundResolutionKey = "groundResolution"; const char* SurveyMissionItem::_jsonFrontalOverlapKey = "imageFrontalOverlap"; -const char* SurveyMissionItem::_jsonSideOverlapKey = "imageSizeOverlap"; +const char* SurveyMissionItem::_jsonSideOverlapKey = "imageSideOverlap"; const char* SurveyMissionItem::_jsonCameraSensorWidthKey = "sensorWidth"; const char* SurveyMissionItem::_jsonCameraSensorHeightKey = "sensorHeight"; const char* SurveyMissionItem::_jsonCameraResolutionWidthKey = "resolutionWidth"; @@ -393,6 +393,13 @@ bool SurveyMissionItem::load(const QJsonObject& complexObject, int sequenceNumbe QJsonObject cameraObject = v2Object[_jsonCameraObjectKey].toObject(); + // Older code had typo on "imageSideOverlap" incorrectly being "imageSizeOverlap" + QString incorrectImageSideOverlap = "imageSizeOverlap"; + if (cameraObject.contains(incorrectImageSideOverlap)) { + cameraObject[_jsonSideOverlapKey] = cameraObject[incorrectImageSideOverlap]; + cameraObject.remove(incorrectImageSideOverlap); + } + QList cameraKeyInfoList = { { _jsonGroundResolutionKey, QJsonValue::Double, true }, { _jsonFrontalOverlapKey, QJsonValue::Double, true }, -- GitLab From 388a95516dedda90d3f6d0d2c89fe0eade0b4736 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 2 Jan 2017 13:20:50 -0800 Subject: [PATCH 164/398] Fix compiler warning --- src/MissionManager/MissionController.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index bb2d1c15e..2f51f25c5 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -464,7 +464,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec for (int i=0; icount(); i++) { if (visualItems->value(i)->isSimpleItem()) { SimpleMissionItem* doJumpItem = visualItems->value(i); - if (doJumpItem->command() == (MAV_CMD)MAV_CMD_DO_JUMP) { + if ((MAV_CMD)doJumpItem->command() == MAV_CMD_DO_JUMP) { bool found = false; int findDoJumpId = doJumpItem->missionItem().param1(); for (int j=0; jcount(); j++) { -- GitLab From 58629dbe9998abef69df5bb9422cfc22555e93a3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 3 Jan 2017 10:32:29 -0800 Subject: [PATCH 165/398] Fix current fact type --- src/Vehicle/Vehicle.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index ab67ab2f0..f0caf147a 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2179,7 +2179,7 @@ VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) , _voltageFact (0, _voltageFactName, FactMetaData::valueTypeDouble) , _percentRemainingFact (0, _percentRemainingFactName, FactMetaData::valueTypeInt32) , _mahConsumedFact (0, _mahConsumedFactName, FactMetaData::valueTypeInt32) - , _currentFact (0, _currentFactName, FactMetaData::valueTypeInt32) + , _currentFact (0, _currentFactName, FactMetaData::valueTypeFloat) , _temperatureFact (0, _temperatureFactName, FactMetaData::valueTypeDouble) , _cellCountFact (0, _cellCountFactName, FactMetaData::valueTypeInt32) { -- GitLab From 17cb4c3be6cd0fdbf667890f5e46112ea3dc4f6b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 3 Jan 2017 11:01:31 -0800 Subject: [PATCH 166/398] Alternate instrument fixes --- .../FlightDisplayViewWidgets.qml | 7 ++- .../Widgets/QGCInstrumentWidgetAlternate.qml | 62 +++++++++++++++++-- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index e295ef9d8..fa01ef116 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -106,10 +106,13 @@ Item { pitchAngle: _pitch groundSpeedFact: _groundSpeedFact airSpeedFact: _airSpeedFact - isSatellite: _isSatellite + lightBorders: _lightWidgetBorders + qgcView: _root.qgcView + maxHeight: parent.height - (anchors.margins * 2) z: QGroundControl.zOrderWidgets } + /* ValuesWidget { anchors.topMargin: ScreenTools.defaultFontPixelHeight anchors.top: instrumentGadgetAlternate.bottom @@ -119,7 +122,7 @@ Item { textColor: _isSatellite ? "white" : "black" visible: _useAlternateInstruments maxHeight: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.y - y : parent.height - anchors.margins - y - } + }*/ //-- Guided mode buttons Rectangle { diff --git a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml index de61685db..ad11cf65b 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml @@ -14,20 +14,25 @@ import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.FactSystem 1.0 import QGroundControl.FlightMap 1.0 +import QGroundControl.Palette 1.0 /// Instrument panel shown when virtual thumbsticks are visible Rectangle { - id: root - height: _outerRadius * 2 - radius: _outerRadius - color: isSatellite ? Qt.rgba(1,1,1,0.75) : Qt.rgba(0,0,0,0.75) + id: root + height: _outerRadius * 2 + radius: _outerRadius + color: qgcPal.window + border.width: 1 + border.color: lightBorders ? qgcPal.mapWidgetBorderLight : qgcPal.mapWidgetBorderDark property alias heading: compass.heading property alias rollAngle: attitude.rollAngle property alias pitchAngle: attitude.pitchAngle property real size: _defaultSize - property bool isSatellite: false property bool active: false + property bool lightBorders: true + property var qgcView + property real maxHeight property Fact _emptyFact: Fact { } property Fact groundSpeedFact: _emptyFact @@ -45,6 +50,9 @@ Rectangle { property real _labelFontSize: ScreenTools.defaultFontPointSize * 0.75 * _sizeRatio property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33 property real _topBottomMargin: (size * 0.05) / 2 + property real _availableValueHeight: maxHeight - (root.height + _valuesItem.anchors.topMargin) + + QGCPalette { id: qgcPal } QGCAttitudeWidget { id: attitude @@ -63,4 +71,48 @@ Rectangle { active: root.active anchors.verticalCenter: parent.verticalCenter } + + Image { + id: gearThingy + anchors.bottomMargin: _topBottomMargin + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + source: qgcPal.globalTheme == QGCPalette.Light ? "/res/gear-black.svg" : "/res/gear-white.svg" + mipmap: true + opacity: 0.5 + width: root.height * 0.15 + sourceSize.width: width + fillMode: Image.PreserveAspectFit + } + + MouseArea { + anchors.fill: parent + onClicked: _valuesWidget.showPicker() + } + + Item { + id: _valuesItem + anchors.topMargin: ScreenTools.defaultFontPixelHeight / 4 + anchors.top: parent.bottom + width: parent.width + height: _valuesWidget.height + + Rectangle { + anchors.fill: _valuesWidget + color: qgcPal.window + } + + InstrumentSwipeView { + id: _valuesWidget + anchors.margins: 1 + anchors.left: parent.left + anchors.right: parent.right + qgcView: root.qgcView + textColor: qgcPal.text + backgroundColor: qgcPal.window + maxHeight: _availableValueHeight + } + } + + } -- GitLab From 1b623edd697589341e499235a37aaa5365598d38 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 4 Jan 2017 10:29:11 -0800 Subject: [PATCH 167/398] Remove check marks --- src/ui/toolbar/MainToolBar.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 5054eb8e7..b1b8ed899 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -172,8 +172,7 @@ Rectangle { id: vehicleMenuItemComponent MenuItem { - checkable: true - onTriggered: QGroundControl.multiVehicleManager.activeVehicle = vehicle + onTriggered: QGroundControl.multiVehicleManager.activeVehicle = vehicle property int vehicleId: Number(text.split(" ")[1]) property var vehicle: QGroundControl.multiVehicleManager.getVehicleById(vehicleId) -- GitLab From 00b8e33ab1b623a23de9f059c3528442d9916ed9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 4 Jan 2017 14:55:43 -0800 Subject: [PATCH 168/398] Update goole version --- src/QtLocationPlugin/QGCMapUrlEngine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QtLocationPlugin/QGCMapUrlEngine.cpp b/src/QtLocationPlugin/QGCMapUrlEngine.cpp index 9d1adec9e..43eebe1e3 100644 --- a/src/QtLocationPlugin/QGCMapUrlEngine.cpp +++ b/src/QtLocationPlugin/QGCMapUrlEngine.cpp @@ -45,10 +45,10 @@ UrlFactory::UrlFactory() #ifndef QGC_NO_GOOGLE_MAPS // Google version strings - _versionGoogleMap = "m@338000000"; - _versionGoogleSatellite = "198"; + _versionGoogleMap = "m@354000000"; + _versionGoogleSatellite = "692"; _versionGoogleLabels = "h@336"; - _versionGoogleTerrain = "t@132,r@338000000"; + _versionGoogleTerrain = "t@354,r@354000000"; _secGoogleWord = "Galileo"; #endif // BingMaps -- GitLab From 088c2d94f4456d04826d38f641a5408859e6e2f4 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 5 Jan 2017 09:41:56 -0800 Subject: [PATCH 169/398] Starting point for new MultiVehicle view --- qgroundcontrol.qrc | 4 + src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 10 + src/FirmwarePlugin/APM/APMFirmwarePlugin.h | 46 +-- .../APM/ArduCopterFirmwarePlugin.cc | 5 + .../APM/ArduCopterFirmwarePlugin.h | 1 + .../APM/ArduPlaneFirmwarePlugin.cc | 5 + .../APM/ArduPlaneFirmwarePlugin.h | 1 + src/FirmwarePlugin/FirmwarePlugin.cc | 15 + src/FirmwarePlugin/FirmwarePlugin.h | 9 + src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 89 +++--- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 35 +-- src/FlightDisplay/FlightDisplayView.qml | 46 ++- src/FlightDisplay/MultiVehicleList.qml | 138 +++++++++ src/FlightDisplay/qmldir | 1 + src/MissionManager/GeoFenceController.cc | 12 + src/MissionManager/GeoFenceController.h | 24 +- src/MissionManager/MissionController.cc | 12 + src/MissionManager/MissionController.h | 24 +- src/MissionManager/PlanElementController.cc | 6 + src/MissionManager/PlanElementController.h | 4 + src/MissionManager/RallyPointController.cc | 7 - src/MissionManager/RallyPointController.h | 1 - src/MultiVehicle/MultiVehicleView.qml | 268 +++++++++++++++++ src/QmlControls/FlightModeDropdown.qml | 65 +++++ src/QmlControls/GuidedBar.qml | 269 ++++++++++++++++++ .../QGroundControl.Controls.qmldir | 2 + src/QmlControls/ScreenTools.qml | 1 + src/Vehicle/Vehicle.cc | 17 ++ src/Vehicle/Vehicle.h | 6 + src/ui/MainWindowInner.qml | 2 +- 30 files changed, 1016 insertions(+), 109 deletions(-) create mode 100644 src/FlightDisplay/MultiVehicleList.qml create mode 100644 src/MultiVehicle/MultiVehicleView.qml create mode 100644 src/QmlControls/FlightModeDropdown.qml create mode 100644 src/QmlControls/GuidedBar.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index c74abab6e..e5bc99415 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -34,6 +34,7 @@ src/MissionEditor/MissionEditor.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml + src/MultiVehicle/MultiVehicleView.qml src/AutoPilotPlugins/Common/MotorComponent.qml src/QtLocationPlugin/QMLControl/OfflineMap.qml src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -45,6 +46,8 @@ src/QmlControls/DropButton.qml src/QmlControls/ExclusiveGroupItem.qml src/QmlControls/FactSliderPanel.qml + src/QmlControls/FlightModeDropdown.qml + src/QmlControls/GuidedBar.qml src/QmlControls/IndicatorButton.qml src/QmlControls/JoystickThumbPad.qml src/ui/toolbar/MainToolBar.qml @@ -104,6 +107,7 @@ src/FlightDisplay/FlightDisplayViewMap.qml src/FlightDisplay/FlightDisplayViewVideo.qml src/FlightDisplay/FlightDisplayViewWidgets.qml + src/FlightDisplay/MultiVehicleList.qml src/FlightDisplay/qmldir src/FlightMap/Widgets/CenterMapDropButton.qml src/FlightMap/FlightMap.qml diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 7b3d2ced1..9d6f40fe2 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -762,3 +762,13 @@ QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) return QString(); } } + +QString APMFirmwarePlugin::missionFlightMode(void) +{ + return QStringLiteral("Auto"); +} + +QString APMFirmwarePlugin::rtlFlightMode(void) +{ + return QStringLiteral("RTL"); +} diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index 7306599c4..6dcfac972 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -75,28 +75,30 @@ public: QList componentsForVehicle(AutoPilotPlugin* vehicle) final; QList supportedMissionCommands(void) final; - AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) final; - bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities); - QStringList flightModes (Vehicle* vehicle) final; - QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; - bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) final; - bool isGuidedMode (const Vehicle* vehicle) const final; - void pauseVehicle (Vehicle* vehicle); - int manualControlReservedButtonCount(void); - bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) final; - void adjustOutgoingMavlinkMessage (Vehicle* vehicle, LinkInterface* outgoingLink, mavlink_message_t* message) final; - void initializeVehicle (Vehicle* vehicle) final; - bool sendHomePositionToVehicle (void) final; - void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; - QString getDefaultComponentIdParam (void) const final { return QString("SYSID_SW_TYPE"); } - QString missionCommandOverrides (MAV_TYPE vehicleType) const; - QString getVersionParam (void) final { return QStringLiteral("SYSID_SW_MREV"); } - QString internalParameterMetaDataFile (Vehicle* vehicle) final; - void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { APMParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } - QObject* loadParameterMetaData (const QString& metaDataFile); - GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new APMGeoFenceManager(vehicle); } - RallyPointManager* newRallyPointManager (Vehicle* vehicle) { return new APMRallyPointManager(vehicle); } - QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } + AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) final; + bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities); + QStringList flightModes (Vehicle* vehicle) final; + QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; + bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) final; + bool isGuidedMode (const Vehicle* vehicle) const final; + void pauseVehicle (Vehicle* vehicle); + int manualControlReservedButtonCount(void); + bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) final; + void adjustOutgoingMavlinkMessage (Vehicle* vehicle, LinkInterface* outgoingLink, mavlink_message_t* message) final; + void initializeVehicle (Vehicle* vehicle) final; + bool sendHomePositionToVehicle (void) final; + void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; + QString getDefaultComponentIdParam (void) const final { return QString("SYSID_SW_TYPE"); } + QString missionCommandOverrides (MAV_TYPE vehicleType) const; + QString getVersionParam (void) final { return QStringLiteral("SYSID_SW_MREV"); } + QString internalParameterMetaDataFile (Vehicle* vehicle) final; + void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { APMParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } + QObject* loadParameterMetaData (const QString& metaDataFile); + GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new APMGeoFenceManager(vehicle); } + RallyPointManager* newRallyPointManager (Vehicle* vehicle) { return new APMRallyPointManager(vehicle); } + QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImage"); } + QString missionFlightMode (void) final; + QString rtlFlightMode (void) final; protected: /// All access to singleton is through stack specific implementation diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index dee9bc789..33fb7fb02 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -204,3 +204,8 @@ QString ArduCopterFirmwarePlugin::geoFenceRadiusParam(Vehicle* vehicle) Q_UNUSED(vehicle); return QStringLiteral("FENCE_RADIUS"); } + +QString ArduCopterFirmwarePlugin::takeControlFlightMode(void) +{ + return QStringLiteral("Stabilize"); +} diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h index 3d43481a1..9eb8e0d73 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.h @@ -68,6 +68,7 @@ public: bool multiRotorXConfig(Vehicle* vehicle) final; QString geoFenceRadiusParam(Vehicle* vehicle) final; QString offlineEditingParamFile(Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Copter.OfflineEditing.params"); } + QString takeControlFlightMode(void) final; private: static bool _remapParamNameIntialized; diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc index 9fd499eee..da2380d4f 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc @@ -64,3 +64,8 @@ ArduPlaneFirmwarePlugin::ArduPlaneFirmwarePlugin(void) supportedFlightModes << APMPlaneMode(APMPlaneMode::QRTL ,true); setSupportedModes(supportedFlightModes); } + +QString ArduPlaneFirmwarePlugin::takeControlFlightMode(void) +{ + return QStringLiteral("Manual"); +} diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h index 111abf4fa..8067354b9 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h @@ -57,6 +57,7 @@ public: // Overrides from FirmwarePlugin QString offlineEditingParamFile(Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Plane.OfflineEditing.params"); } + QString takeControlFlightMode(void) final; }; #endif diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index 5a19fc777..eb69d9a71 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -282,3 +282,18 @@ int FirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumbe Q_UNUSED(majorVersionNumber); return 0; } + +QString FirmwarePlugin::missionFlightMode(void) +{ + return QString(); +} + +QString FirmwarePlugin::rtlFlightMode(void) +{ + return QString(); +} + +QString FirmwarePlugin::takeControlFlightMode(void) +{ + return QString(); +} diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 640b491c8..096ae7151 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -125,6 +125,15 @@ public: /// Command vehicle to change to the specified relatice altitude virtual void guidedModeChangeAltitude(Vehicle* vehicle, double altitudeRel); + /// Returns the flight mode for running missions + virtual QString missionFlightMode(void); + + /// Returns the flight mode for RTL + virtual QString rtlFlightMode(void); + + /// Returns the flight mode to use when the operator wants to take back control from autonomouse flight. + virtual QString takeControlFlightMode(void); + /// FIXME: This isn't quite correct being here. All code for Joystick suvehicleTypepport is currently firmware specific /// not just this. I'm going to try to change that. If not, this will need to be removed. /// Returns the number of buttons which are reserved for firmware use in the MANUAL_CONTROL mavlink diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 16d04d301..3aac9dfa0 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -35,44 +35,42 @@ struct Modes2Name { bool multiRotor; /// multi rotor compatible }; -const char* PX4FirmwarePlugin::manualFlightMode = "Manual"; -const char* PX4FirmwarePlugin::altCtlFlightMode = "Altitude"; -const char* PX4FirmwarePlugin::posCtlFlightMode = "Position"; -const char* PX4FirmwarePlugin::missionFlightMode = "Mission"; -const char* PX4FirmwarePlugin::holdFlightMode = "Hold"; -const char* PX4FirmwarePlugin::takeoffFlightMode = "Takeoff"; -const char* PX4FirmwarePlugin::landingFlightMode = "Land"; -const char* PX4FirmwarePlugin::rtlFlightMode = "Return"; -const char* PX4FirmwarePlugin::acroFlightMode = "Acro"; -const char* PX4FirmwarePlugin::offboardFlightMode = "Offboard"; -const char* PX4FirmwarePlugin::stabilizedFlightMode = "Stabilized"; -const char* PX4FirmwarePlugin::rattitudeFlightMode = "Rattitude"; -const char* PX4FirmwarePlugin::followMeFlightMode = "Follow Me"; - -const char* PX4FirmwarePlugin::rtgsFlightMode = "Return to Groundstation"; - -const char* PX4FirmwarePlugin::readyFlightMode = "Ready"; // unused +const char* PX4FirmwarePlugin::_manualFlightMode = "Manual"; +const char* PX4FirmwarePlugin::_altCtlFlightMode = "Altitude"; +const char* PX4FirmwarePlugin::_posCtlFlightMode = "Position"; +const char* PX4FirmwarePlugin::_missionFlightMode = "Mission"; +const char* PX4FirmwarePlugin::_holdFlightMode = "Hold"; +const char* PX4FirmwarePlugin::_takeoffFlightMode = "Takeoff"; +const char* PX4FirmwarePlugin::_landingFlightMode = "Land"; +const char* PX4FirmwarePlugin::_rtlFlightMode = "Return"; +const char* PX4FirmwarePlugin::_acroFlightMode = "Acro"; +const char* PX4FirmwarePlugin::_offboardFlightMode = "Offboard"; +const char* PX4FirmwarePlugin::_stabilizedFlightMode = "Stabilized"; +const char* PX4FirmwarePlugin::_rattitudeFlightMode = "Rattitude"; +const char* PX4FirmwarePlugin::_followMeFlightMode = "Follow Me"; +const char* PX4FirmwarePlugin::_rtgsFlightMode = "Return to Groundstation"; +const char* PX4FirmwarePlugin::_readyFlightMode = "Ready"; /// Tranlates from PX4 custom modes to flight mode names static const struct Modes2Name rgModes2Name[] = { - //main_mode sub_mode name canBeSet FW MC - { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::manualFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::stabilizedFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::acroFlightMode, true, false, true }, - { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::rattitudeFlightMode, true, false, true }, - { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::altCtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::posCtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::holdFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::missionFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::rtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::followMeFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::offboardFlightMode, true, true, true }, + //main_mode sub_mode name canBeSet FW MC + { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::_manualFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::_stabilizedFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::_acroFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::_altCtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::_posCtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::_missionFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::_rtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::_followMeFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::_offboardFlightMode, true, true, true }, // modes that can't be directly set by the user - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::landingFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::readyFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTGS, PX4FirmwarePlugin::rtgsFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF, PX4FirmwarePlugin::takeoffFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::_landingFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::_readyFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTGS, PX4FirmwarePlugin::_rtgsFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF, PX4FirmwarePlugin::_takeoffFlightMode, false, true, true }, }; PX4FirmwarePlugin::PX4FirmwarePlugin(void) @@ -297,12 +295,12 @@ void PX4FirmwarePlugin::pauseVehicle(Vehicle* vehicle) void PX4FirmwarePlugin::guidedModeRTL(Vehicle* vehicle) { - vehicle->setFlightMode(rtlFlightMode); + vehicle->setFlightMode(_rtlFlightMode); } void PX4FirmwarePlugin::guidedModeLand(Vehicle* vehicle) { - vehicle->setFlightMode(landingFlightMode); + vehicle->setFlightMode(_landingFlightMode); } void PX4FirmwarePlugin::guidedModeOrbit(Vehicle* vehicle, const QGeoCoordinate& centerCoord, double radius, double velocity, double altitude) @@ -388,7 +386,7 @@ void PX4FirmwarePlugin::guidedModeChangeAltitude(Vehicle* vehicle, double altitu void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) { if (guidedMode) { - vehicle->setFlightMode(holdFlightMode); + vehicle->setFlightMode(_holdFlightMode); } else { pauseVehicle(vehicle); } @@ -397,8 +395,8 @@ void PX4FirmwarePlugin::setGuidedMode(Vehicle* vehicle, bool guidedMode) bool PX4FirmwarePlugin::isGuidedMode(const Vehicle* vehicle) const { // Not supported by generic vehicle - return (vehicle->flightMode() == holdFlightMode || vehicle->flightMode() == takeoffFlightMode - || vehicle->flightMode() == landingFlightMode); + return (vehicle->flightMode() == _holdFlightMode || vehicle->flightMode() == _takeoffFlightMode + || vehicle->flightMode() == _landingFlightMode); } bool PX4FirmwarePlugin::adjustIncomingMavlinkMessage(Vehicle* vehicle, mavlink_message_t* message) @@ -456,3 +454,18 @@ void PX4FirmwarePlugin::_handleAutopilotVersion(Vehicle* vehicle, mavlink_messag } } } + +QString PX4FirmwarePlugin::missionFlightMode(void) +{ + return QString(_missionFlightMode); +} + +QString PX4FirmwarePlugin::rtlFlightMode(void) +{ + return QString(_rtlFlightMode); +} + +QString PX4FirmwarePlugin::takeControlFlightMode(void) +{ + return QString(_manualFlightMode); +} diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index c4a340f2d..ac1ab600e 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -60,25 +60,28 @@ public: GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new PX4GeoFenceManager(vehicle); } QString offlineEditingParamFile(Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/PX4/PX4.OfflineEditing.params"); } QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/PX4/BrandImage"); } + QString missionFlightMode (void) final; + QString rtlFlightMode (void) final; + QString takeControlFlightMode (void) final; + // NOTE: For internal use only. Do not use in mainline QGC code. // Use these constants to set flight modes using setFlightMode method. Don't use hardcoded string names since the // names may change. - - static const char* manualFlightMode; - static const char* acroFlightMode; - static const char* stabilizedFlightMode; - static const char* rattitudeFlightMode; - static const char* altCtlFlightMode; - static const char* posCtlFlightMode; - static const char* offboardFlightMode; - static const char* readyFlightMode; - static const char* takeoffFlightMode; - static const char* holdFlightMode; - static const char* missionFlightMode; - static const char* rtlFlightMode; - static const char* landingFlightMode; - static const char* rtgsFlightMode; - static const char* followMeFlightMode; + static const char* _manualFlightMode; + static const char* _acroFlightMode; + static const char* _stabilizedFlightMode; + static const char* _rattitudeFlightMode; + static const char* _altCtlFlightMode; + static const char* _posCtlFlightMode; + static const char* _offboardFlightMode; + static const char* _readyFlightMode; + static const char* _takeoffFlightMode; + static const char* _holdFlightMode; + static const char* _missionFlightMode; + static const char* _rtlFlightMode; + static const char* _landingFlightMode; + static const char* _rtgsFlightMode; + static const char* _followMeFlightMode; private: void _handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message); diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index ed01d1c6f..be355f7a4 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -41,14 +41,15 @@ QGCView { property real _pitch: _activeVehicle ? _activeVehicle.pitch.value : _defaultPitch property real _heading: _activeVehicle ? _activeVehicle.heading.value : _defaultHeading - property Fact _emptyFact: Fact { } property Fact _groundSpeedFact: _activeVehicle ? _activeVehicle.groundSpeed : _emptyFact property Fact _airSpeedFact: _activeVehicle ? _activeVehicle.airSpeed : _emptyFact property bool activeVehicleJoystickEnabled: _activeVehicle ? _activeVehicle.joystickEnabled : false - property real _savedZoomLevel: 0 + property real _savedZoomLevel: 0 + property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real pipSize: mainWindow.width * 0.2 @@ -120,6 +121,8 @@ QGCView { px4JoystickCheck() } + QGCMapPalette { id: mapPal; lightColors: _mainIsMap ? _flightMap.isSatelliteMap : true } + QGCViewPanel { id: _panel anchors.fill: parent @@ -218,6 +221,33 @@ QGCView { } } + Row { + id: singleMultiSelector + anchors.topMargin: ScreenTools.toolbarHeight + _margins + anchors.rightMargin: _margins + anchors.right: parent.right + anchors.top: parent.top + spacing: ScreenTools.defaultFontPixelWidth + z: _panel.z + 4 + visible: QGroundControl.multiVehicleManager.vehicles.count > 1 + + ExclusiveGroup { id: multiVehicleSelectorGroup } + + QGCRadioButton { + id: singleVehicleView + exclusiveGroup: multiVehicleSelectorGroup + text: qsTr("Single") + checked: true + color: mapPal.text + } + + QGCRadioButton { + exclusiveGroup: multiVehicleSelectorGroup + text: qsTr("Multi-Vehicle (WIP)") + color: mapPal.text + } + } + FlightDisplayViewWidgets { id: flightDisplayViewWidgets z: _panel.z + 4 @@ -227,8 +257,20 @@ QGCView { anchors.bottom: parent.bottom qgcView: root isBackgroundDark: root.isBackgroundDark + visible: singleVehicleView.checked + } + + MultiVehicleList { + anchors.margins: _margins + anchors.top: singleMultiSelector.bottom + anchors.right: parent.right + anchors.bottom: parent.bottom + width: ScreenTools.defaultFontPixelWidth * 30 + visible: !singleVehicleView.checked + z: _panel.z + 4 } + //-- Virtual Joystick Loader { id: virtualJoystickMultiTouch diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml new file mode 100644 index 000000000..fbb78e729 --- /dev/null +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -0,0 +1,138 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.FlightMap 1.0 + +QGCListView { + id: missionItemEditorListView + spacing: ScreenTools.defaultFontPixelHeight / 2 + orientation: ListView.Vertical + model: QGroundControl.multiVehicleManager.vehicles + cacheBuffer: _cacheBuffer < 0 ? 0 : _cacheBuffer + clip: true + + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _cacheBuffer: height * 2 + property real _widgetHeight: ScreenTools.defaultFontPixelHeight * 3 + + delegate: Rectangle { + width: parent.width + height: innerColumn.y + innerColumn.height + _margin + color: qgcPal.buttonHighlight + opacity: 0.8 + radius: _margin + + property var _vehicle: object + property color _textColor: "black" + + QGCPalette { id: qgcPal } + + Row { + id: widgetLayout + anchors.margins: _margin + anchors.top: parent.top + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelWidth / 2 + layoutDirection: Qt.RightToLeft + + QGCCompassWidget { + size: _widgetHeight + active: true + heading: _vehicle.heading.rawValue + } + + QGCAttitudeWidget { + size: _widgetHeight + active: true + rollAngle: _vehicle.roll.rawValue + pitchAngle: _vehicle.pitch.rawValue + } + } + + RowLayout { + anchors.top: widgetLayout.top + anchors.bottom: widgetLayout.bottom + anchors.left: parent.left + anchors.right: widgetLayout.left + spacing: ScreenTools.defaultFontPixelWidth / 2 + + QGCLabel { + Layout.alignment: Qt.AlignTop + text: _vehicle.id + color: _textColor + } + + QGCLabel { + text: _vehicle.flightMode + font.pointSize: ScreenTools.largeFontPointSize + color: _textColor + } + } + + Column { + id: innerColumn + anchors.margins: _margin + anchors.left: parent.left + anchors.right: parent.right + anchors.top: widgetLayout.bottom + spacing: _margin + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 5 + color: "green" + } + + Row { + spacing: ScreenTools.defaultFontPixelWidth + + QGCButton { + text: "Arm" + visible: !_vehicle.armed + onClicked: _vehicle.armed = true + } + + QGCButton { + text: "Start" + visible: _vehicle.armed && _vehicle.flightMode != _vehicle.missionFlightMode + onClicked: _vehicle.flightMode = _vehicle.missionFlightMode + } + + QGCButton { + text: "Stop" + visible: _vehicle.armed && _vehicle.pauseVehicleSupported + onClicked: _vehicle.pauseVehicle() + } + + QGCButton { + text: "RTL" + visible: _vehicle.armed && _vehicle.flightMode != _vehicle.rtlFlightMode + onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode + } + + QGCButton { + text: "Take control" + visible: _vehicle.armed && _vehicle.flightMode != _vehicle.takeControlFlightMode + onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode + } + + } + } + } +} // QGCListView diff --git a/src/FlightDisplay/qmldir b/src/FlightDisplay/qmldir index ccd5c34b8..bdcc8b713 100644 --- a/src/FlightDisplay/qmldir +++ b/src/FlightDisplay/qmldir @@ -4,4 +4,5 @@ FlightDisplayView 1.0 FlightDisplayView.qml FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml FlightDisplayViewVideo 1.0 FlightDisplayViewVideo.qml FlightDisplayViewWidgets 1.0 FlightDisplayViewWidgets.qml +MultiVehicleList 1.0 MultiVehicleList.qml diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index fc63d8089..ae28afba1 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -48,7 +48,19 @@ void GeoFenceController::start(bool editMode) qCDebug(GeoFenceControllerLog) << "start editMode" << editMode; PlanElementController::start(editMode); + _init(); +} + +void GeoFenceController::startStaticActiveVehicle(Vehicle* vehicle) +{ + qCDebug(GeoFenceControllerLog) << "startStaticActiveVehicle"; + PlanElementController::startStaticActiveVehicle(vehicle); + _init(); +} + +void GeoFenceController::_init(void) +{ connect(&_polygon, &QGCMapPolygon::dirtyChanged, this, &GeoFenceController::_polygonDirtyChanged); } diff --git a/src/MissionManager/GeoFenceController.h b/src/MissionManager/GeoFenceController.h index 34c87bdad..aff901817 100644 --- a/src/MissionManager/GeoFenceController.h +++ b/src/MissionManager/GeoFenceController.h @@ -50,17 +50,18 @@ public: Q_PROPERTY(bool breachReturnSupported READ breachReturnSupported NOTIFY breachReturnSupportedChanged) #endif - void start (bool editMode) final; - void loadFromVehicle (void) final; - void sendToVehicle (void) final; - void loadFromFilePicker (void) final; - void loadFromFile (const QString& filename) final; - void saveToFilePicker (void) final; - void saveToFile (const QString& filename) final; - void removeAll (void) final; - bool syncInProgress (void) const final; - bool dirty (void) const final; - void setDirty (bool dirty) final; + void start (bool editMode) final; + void startStaticActiveVehicle (Vehicle* vehicle) final; + void loadFromVehicle (void) final; + void sendToVehicle (void) final; + void loadFromFilePicker (void) final; + void loadFromFile (const QString& filename) final; + void saveToFilePicker (void) final; + void saveToFile (const QString& filename) final; + void removeAll (void) final; + bool syncInProgress (void) const final; + bool dirty (void) const final; + void setDirty (bool dirty) final; QString fileExtension(void) const final; @@ -96,6 +97,7 @@ private slots: void _loadComplete(const QGeoCoordinate& breachReturn, const QList& polygon); private: + void _init(void); void _signalAll(void); bool _loadJsonFile(QJsonDocument& jsonDoc, QString& errorString); diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 2f51f25c5..ca16bee56 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -66,7 +66,19 @@ void MissionController::start(bool editMode) qCDebug(MissionControllerLog) << "start editMode" << editMode; PlanElementController::start(editMode); + _init(); +} + +void MissionController::startStaticActiveVehicle(Vehicle *vehicle) +{ + qCDebug(MissionControllerLog) << "startStaticActiveVehicle"; + PlanElementController::startStaticActiveVehicle(vehicle); + _init(); +} + +void MissionController::_init(void) +{ // We start with an empty mission _visualItems = new QmlObjectListModel(this); _addPlannedHomePosition(_visualItems, false /* addToCenter */); diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 42e495e48..c03becf26 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -57,17 +57,18 @@ public: Q_INVOKABLE int insertComplexMissionItem(QGeoCoordinate coordinate, int i); // Overrides from PlanElementController - void start (bool editMode) final; - void loadFromVehicle (void) final; - void sendToVehicle (void) final; - void loadFromFilePicker (void) final; - void loadFromFile (const QString& filename) final; - void saveToFilePicker (void) final; - void saveToFile (const QString& filename) final; - void removeAll (void) final; - bool syncInProgress (void) const final; - bool dirty (void) const final; - void setDirty (bool dirty) final; + void start (bool editMode) final; + void startStaticActiveVehicle (Vehicle* vehicle) final; + void loadFromVehicle (void) final; + void sendToVehicle (void) final; + void loadFromFilePicker (void) final; + void loadFromFile (const QString& filename) final; + void saveToFilePicker (void) final; + void saveToFile (const QString& filename) final; + void removeAll (void) final; + bool syncInProgress (void) const final; + bool dirty (void) const final; + void setDirty (bool dirty) final; QString fileExtension(void) const final; @@ -112,6 +113,7 @@ private slots: void _homeCoordinateChanged(void); private: + void _init(void); void _recalcSequence(void); void _recalcChildItems(void); void _recalcAll(void); diff --git a/src/MissionManager/PlanElementController.cc b/src/MissionManager/PlanElementController.cc index c384d259e..1c4d7eba0 100644 --- a/src/MissionManager/PlanElementController.cc +++ b/src/MissionManager/PlanElementController.cc @@ -32,6 +32,12 @@ void PlanElementController::start(bool editMode) _activeVehicleChanged(_multiVehicleMgr->activeVehicle()); } +void PlanElementController::startStaticActiveVehicle(Vehicle* vehicle) +{ + _editMode = false; + _activeVehicleChanged(vehicle); +} + void PlanElementController::_activeVehicleChanged(Vehicle* activeVehicle) { if (_activeVehicle) { diff --git a/src/MissionManager/PlanElementController.h b/src/MissionManager/PlanElementController.h index 63c4abe35..b8ff725d6 100644 --- a/src/MissionManager/PlanElementController.h +++ b/src/MissionManager/PlanElementController.h @@ -39,6 +39,10 @@ public: /// @param editMode true: controller being used in Plan view, false: controller being used in Fly view Q_INVOKABLE virtual void start(bool editMode); + /// Starts the controller using a single static active vehicle. Will not track global active vehicle changes. + /// @param editMode true: controller being used in Plan view, false: controller being used in Fly view + Q_INVOKABLE virtual void startStaticActiveVehicle(Vehicle* vehicle); + Q_INVOKABLE virtual void loadFromVehicle(void) = 0; Q_INVOKABLE virtual void sendToVehicle(void) = 0; Q_INVOKABLE virtual void loadFromFilePicker(void) = 0; diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 3a653e776..f6b6091ba 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -46,13 +46,6 @@ RallyPointController::~RallyPointController() } -void RallyPointController::start(bool editMode) -{ - qCDebug(RallyPointControllerLog) << "start editMode" << editMode; - - PlanElementController::start(editMode); -} - void RallyPointController::_activeVehicleBeingRemoved(void) { _activeVehicle->rallyPointManager()->disconnect(this); diff --git a/src/MissionManager/RallyPointController.h b/src/MissionManager/RallyPointController.h index 4585d93e9..1c040a689 100644 --- a/src/MissionManager/RallyPointController.h +++ b/src/MissionManager/RallyPointController.h @@ -37,7 +37,6 @@ public: Q_INVOKABLE void addPoint(QGeoCoordinate point); Q_INVOKABLE void removePoint(QObject* rallyPoint); - void start (bool editMode) final; void loadFromVehicle (void) final; void sendToVehicle (void) final; void loadFromFilePicker (void) final; diff --git a/src/MultiVehicle/MultiVehicleView.qml b/src/MultiVehicle/MultiVehicleView.qml new file mode 100644 index 000000000..2aa15bf61 --- /dev/null +++ b/src/MultiVehicle/MultiVehicleView.qml @@ -0,0 +1,268 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.FactSystem 1.0 + +/// Multi-Vehicle View +QGCView { + id: qgcView + viewPanel: panel + + property real _margins: ScreenTools.defaultFontPixelWidth + property var _fileDialogController + + readonly property string _loadingText: qsTr("Loading...") + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + QGCViewPanel { + id: panel + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: qgcPal.window + + QGCFlickable { + anchors.fill: parent + contentHeight: vehicleColumn.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Column { + id: vehicleColumn + anchors.margins: _margins + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: _margins + + QGCLabel { text: qsTr("All Vehicles") } + + Repeater { + model: QGroundControl.multiVehicleManager.vehicles + + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight / 2 + + MissionController { + id: missionController + + Component.onCompleted: startStaticActiveVehicle(object) + + property bool missionAvailable: visualItems && visualItems.count > 1 + + function loadFromSelectedFile() { + if (ScreenTools.isMobile) { + _fileDialogController = missionController + qgcView.showDialog(mobileFilePicker, qsTr("Select Mission File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel) + } else { + missionController.loadFromFilePicker() + missionController.sendToVehicle() + } + } + } // MissionController + + GeoFenceController { + id: geoFenceController + + Component.onCompleted: startStaticActiveVehicle(object) + + property bool fenceAvailable: fenceSupported && (circleSupported || polygonSupported) + + function loadFromSelectedFile() { + if (ScreenTools.isMobile) { + _fileDialogController = geoFenceController + qgcView.showDialog(mobileFilePicker, qsTr("Select Fence File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel) + } else { + geoFenceController.loadFromFilePicker() + geoFenceController.sendToVehicle() + } + } + } // GeoFenceController + + RallyPointController { + id: rallyPointController + + Component.onCompleted: startStaticActiveVehicle(object) + + property bool pointsAvailable: rallyPointsSupported && points.count + + function loadFromSelectedFile() { + if (ScreenTools.isMobile) { + _fileDialogController = rallyPointController + qgcView.showDialog(mobileFilePicker, qsTr("Select Rally Point File"), qgcView.showDialogDefaultWidth, StandardButton.Yes | StandardButton.Cancel) + } else { + rallyPointController.loadFromFilePicker() + rallyPointController.sendToVehicle() + } + } + } // RallyPointController + + QGCLabel { + text: "Vehicle #" + object.id + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: vehicleDisplayColumn.height + (_margins * 2) + color: qgcPal.windowShade + + Column { + id: vehicleDisplayColumn + anchors.margins: _margins + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + Row { + id: indicatorRow + spacing: _margins + visible: !object.connectionLost + + Rectangle { + width: missionLabel.contentWidth + _margins + height: ScreenTools.defaultFontPixelHeight + _margins + radius: height / 4 + color: missionController.missionAvailable ? "green" : qgcPal.window + border.width: 1 + border.color: qgcPal.text + + QGCLabel { + id: missionLabel + anchors.margins: _margins / 2 + anchors.left: parent.left + anchors.top: parent.top + text: missionController.syncInProgress ? _loadingText : qsTr("Mission") + } + + MouseArea { + anchors.fill: parent + enabled: !missionController.syncInProgress + onClicked: missionController.loadFromSelectedFile() + } + } + + Rectangle { + width: fenceLabel.contentWidth + _margins + height: ScreenTools.defaultFontPixelHeight + _margins + radius: height / 4 + color: geoFenceController.fenceAvailable ? "green" : qgcPal.window + border.width: 1 + border.color: qgcPal.text + + QGCLabel { + id: fenceLabel + anchors.margins: _margins / 2 + anchors.left: parent.left + anchors.top: parent.top + text: geoFenceController.syncInProgress ? _loadingText : qsTr("Fence") + } + + MouseArea { + anchors.fill: parent + enabled: !geoFenceController.syncInProgress + onClicked: geoFenceController.loadFromSelectedFile() + } + } + + Rectangle { + width: rallyLabel.contentWidth + _margins + height: ScreenTools.defaultFontPixelHeight + _margins + radius: height / 4 + color: rallyPointController.pointsAvailable ? "green" : qgcPal.window + border.width: 1 + border.color: qgcPal.text + + QGCLabel { + id: rallyLabel + anchors.margins: _margins / 2 + anchors.left: parent.left + anchors.top: parent.top + text: rallyPointController.syncInProgress ? _loadingText : qsTr("Rally") + } + + MouseArea { + anchors.fill: parent + enabled: !rallyPointController.syncInProgress + onClicked: rallyPointController.loadFromSelectedFile() + } + } + + FlightModeDropdown { activeVehicle: object } + + GuidedBar { activeVehicle: object } + } // Row - contents display + + Flow { + anchors.left: parent.left + anchors.right: parent.right + layoutDirection: Qt.LeftToRight + spacing: _margins + + Repeater { + model: [ "battery.voltage", "battery.percentRemaining", "altitudeRelative", "altitudeAMSL", "groundSpeed", "heading"] + + Column { + property Fact fact: object.getFact(modelData) + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: fact.shortDescription + } + Row { + anchors.horizontalCenter: parent.horizontalCenter + //spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { + text: fact.enumOrValueString + } + QGCLabel { + text: fact.units + } + } + } + } // Repeater - Small + } // Flow + } // Column + } // Rectangle - contents display + } // Column - layout for vehicle + } // Repeater - vehicle repeater + } // Column + } // QGCFlickable + } // Rectangle - View background + } // QGCViewPanel + + Component { + id: mobileFilePicker + + QGCMobileFileDialog { + openDialog: true + fileExtension: _fileDialogController.fileExtension + + onFilenameReturned: { + _fileDialogController.loadFromFile(filename) + _fileDialogController.sendToVehicle() + } + } + } +} // QGCView diff --git a/src/QmlControls/FlightModeDropdown.qml b/src/QmlControls/FlightModeDropdown.qml new file mode 100644 index 000000000..805438606 --- /dev/null +++ b/src/QmlControls/FlightModeDropdown.qml @@ -0,0 +1,65 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.5 + +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +Item { + width: flightModeLabel.visible ? flightModeLabel.width : flightModeCombo.width + height: flightModeLabel.visible ? flightModeLabel.height : flightModeCombo.height + + property var activeVehicle ///< Vehicle to show flight modes for + + property int _maxFMCharLength: 10 ///< Maximum number of chars in a flight mode + property string flightMode: activeVehicle ? activeVehicle.flightMode : qsTr("N/A", "No data to display") + + onActiveVehicleChanged: _activeVehicleChanged() + + onFlightModeChanged: { + if (flightModeCombo.visible) { + flightModeCombo.currentIndex = flightModeCombo.find(flightMode) + } + } + + Component.onCompleted: _activeVehicleChanged() + + function _activeVehicleChanged() { + if (activeVehicle.flightModeSetAvailable) { + var maxFMChars = 0 + for (var i=0; i + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.5 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//-- Guided mode bar + +Rectangle { + id: guidedModeBar + width: guidedModeColumn.width + (_margins * 2) + height: guidedModeColumn.height + (_margins * 2) + radius: ScreenTools.defaultFontPixelHeight * 0.25 + color: backgroundColor + + property var activeVehicle ///< Vehicle to show guided bar for + property real fontPointSize: ScreenTools.defaultFontPointSize ///< point size for fonts in control + property color backgroundColor: qgcPal.windowShadeDark ///< Background color for bar + + // Values for _confirmActionCode + readonly property int confirmHome: 1 + readonly property int confirmLand: 2 + readonly property int confirmTakeoff: 3 + readonly property int confirmArm: 4 + readonly property int confirmDisarm: 5 + readonly property int confirmEmergencyStop: 6 + readonly property int confirmChangeAlt: 7 + readonly property int confirmGoTo: 8 + readonly property int confirmRetask: 9 + readonly property int confirmOrbit: 10 + + property int _confirmActionCode + property real _showMargin: _margins + property real _hideMargin: _margins - guidedModeBar.height + property real _barMargin: _showMargin + property bool _showConfirm: false + property string _confirmText + property bool _showAltitude: false + property real _confirmAltitude + + function actionConfirmed(altitude) { + _showConfirm = false + switch (_confirmActionCode) { + case confirmHome: + activeVehicle.guidedModeRTL() + break; + case confirmLand: + activeVehicle.guidedModeLand() + break; + case confirmTakeoff: + activeVehicle.guidedModeTakeoff(altitude) + break; + case confirmArm: + activeVehicle.armed = true + break; + case confirmDisarm: + activeVehicle.armed = false + break; + case confirmEmergencyStop: + activeVehicle.emergencyStop() + break; + case confirmChangeAlt: + activeVehicle.guidedModeChangeAltitude(altitude) + break; + case confirmGoTo: + activeVehicle.guidedModeGotoLocation(_flightMap._gotoHereCoordinate) + break; + case confirmRetask: + activeVehicle.setCurrentMissionSequence(_flightMap._retaskSequence) + break; + case confirmOrbit: + //-- All parameters controlled by RC + activeVehicle.guidedModeOrbit() + //-- Center on current flight map position and orbit with a 50m radius (velocity/direction controlled by the RC) + //activeVehicle.guidedModeOrbit(QGroundControl.flightMapPosition, 50.0) + break; + default: + console.warn(qsTr("Internal error: unknown _confirmActionCode"), _confirmActionCode) + } + } + + function actionRejected() { + _showConfirm = false + /* + altitudeSlider.visible = false + _flightMap._gotoHereCoordinate = QtPositioning.coordinate() + guidedModeHideTimer.restart() + */ + } + + function confirmAction(actionCode) { + //guidedModeHideTimer.stop() + _confirmActionCode = actionCode + _showAltitude = false + switch (_confirmActionCode) { + case confirmArm: + _confirmText = qsTr("Arm vehicle?") + break; + case confirmDisarm: + _confirmText = qsTr("Disarm vehicle?") + break; + case confirmEmergencyStop: + _confirmText = qsTr("STOP ALL MOTORS?") + break; + case confirmTakeoff: + _showAltitude = true + setInitialValueMeters(3) + _confirmText = qsTr("Takeoff vehicle?") + break; + case confirmLand: + _confirmText = qsTr("Land vehicle?") + break; + case confirmHome: + _confirmText = qsTr("Return to land?") + break; + case confirmChangeAlt: + _showAltitude = true + setInitialValueAppSettingsDistanceUnits(activeVehicle.altitudeRelative.value) + _confirmText = qsTr("Change altitude?") + break; + case confirmGoTo: + _confirmText = qsTr("Move vehicle?") + break; + case confirmRetask: + _confirmText = qsTr("Change active waypoint?") + break; + case confirmOrbit: + _confirmText = qsTr("Enter orbit mode?") + break; + } + _showConfirm = true + } + + function setInitialValueMeters(meters) { + _confirmAltitude = QGroundControl.metersToAppSettingsDistanceUnits(meters) + } + + function setInitialValueAppSettingsDistanceUnits(height) { + _confirmAltitude = height + } + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + Column { + id: guidedModeColumn + anchors.margins: _margins + anchors.top: parent.top + anchors.left: parent.left + spacing: _margins + + /* + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + color: _lightWidgetBorders ? qgcPal.mapWidgetBorderDark : qgcPal.mapWidgetBorderLight + text: "Click in map to move vehicle" + visible: gotoEnabled + }*/ + + Row { + spacing: _margins * 2 + visible: !_showConfirm + + QGCButton { + pointSize: fontPointSize + text: (activeVehicle && activeVehicle.armed) ? (activeVehicle.flying ? qsTr("Emergency Stop") : qsTr("Disarm")) : qsTr("Arm") + visible: activeVehicle + onClicked: confirmAction(activeVehicle.armed ? (activeVehicle.flying ? confirmEmergencyStop : confirmDisarm) : confirmArm) + } + + QGCButton { + pointSize: fontPointSize + text: qsTr("RTL") + visible: (activeVehicle && activeVehicle.armed) && activeVehicle.guidedModeSupported && activeVehicle.flying + onClicked: confirmAction(confirmHome) + } + + QGCButton { + pointSize: fontPointSize + text: (activeVehicle && activeVehicle.flying) ? qsTr("Land"): qsTr("Takeoff") + visible: activeVehicle && activeVehicle.guidedModeSupported && activeVehicle.armed + onClicked: confirmAction(activeVehicle.flying ? confirmLand : confirmTakeoff) + } + + QGCButton { + pointSize: fontPointSize + text: qsTr("Pause") + visible: (activeVehicle && activeVehicle.armed) && activeVehicle.pauseVehicleSupported && activeVehicle.flying + onClicked: { + guidedModeHideTimer.restart() + activeVehicle.pauseVehicle() + } + } + + QGCButton { + pointSize: fontPointSize + text: qsTr("Change Altitude") + visible: (activeVehicle && activeVehicle.flying) && activeVehicle.guidedModeSupported && activeVehicle.armed + onClicked: confirmAction(confirmChangeAlt) + } + + QGCButton { + pointSize: fontPointSize + text: qsTr("Orbit") + visible: (activeVehicle && activeVehicle.flying) && activeVehicle.orbitModeSupported && activeVehicle.armed + onClicked: confirmAction(confirmOrbit) + } + + } // Row + + Column { + visible: _showConfirm + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + text: _confirmText + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelWidth + + Row { + visible: _showAltitude + + QGCLabel { + text: qsTr("Alt (rel)") + } + + QGCLabel { + text: QGroundControl.appSettingsDistanceUnitsString + } + + QGCTextField { + id: altField + text: _confirmAltitude.toFixed(1) + } + } + + QGCButton { + text: qsTr("Yes") + + onClicked: { + var value = parseFloat(altField.text) + if (isNaN(value)) { + actionRejected() + } else { + actionConfirmed(QGroundControl.appSettingsDistanceUnitsToMeters(value)) + } + } + } + + QGCButton { + text: qsTr("No") + onClicked: actionRejected() + } + } + } // Column + } // Column +} // Rectangle - Guided mode buttons diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 1450309a1..c3d900910 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -6,6 +6,8 @@ ClickableColor 1.0 ClickableColor.qml DropButton 1.0 DropButton.qml ExclusiveGroupItem 1.0 ExclusiveGroupItem.qml FactSliderPanel 1.0 FactSliderPanel.qml +FlightModeDropdown 1.0 FlightModeDropdown.qml +GuidedBar 1.0 GuidedBar.qml IndicatorButton 1.0 IndicatorButton.qml JoystickThumbPad 1.0 JoystickThumbPad.qml MainToolBar 1.0 MainToolBar.qml diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 3f546a30e..84bb0a099 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -47,6 +47,7 @@ Item { property real largeFontPointSize: 10 property real availableHeight: 0 + property real toolbarHeight: defaultFontPixelHeight * 3 readonly property real smallFontPointRatio: 0.75 readonly property real mediumFontPointRatio: 1.25 diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index f0caf147a..f5a8b5b59 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1346,6 +1346,8 @@ void Vehicle::setFlightMode(const QString& flightMode) uint8_t base_mode; uint32_t custom_mode; + qDebug() << flightMode; + if (_firmwarePlugin->setFlightMode(flightMode, &base_mode, &custom_mode)) { // setFlightMode will only set MAV_MODE_FLAG_CUSTOM_MODE_ENABLED in base_mode, we need to move back in the existing // states. @@ -2149,6 +2151,21 @@ void Vehicle::setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData) _firmwarePluginInstanceData = firmwarePluginInstanceData; } +QString Vehicle::missionFlightMode(void) const +{ + return _firmwarePlugin->missionFlightMode(); +} + +QString Vehicle::rtlFlightMode(void) const +{ + return _firmwarePlugin->rtlFlightMode(); +} + +QString Vehicle::takeControlFlightMode(void) const +{ + return _firmwarePlugin->takeControlFlightMode(); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 29e6477a4..ff299e348 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -271,6 +271,9 @@ public: Q_PROPERTY(bool isOfflineEditingVehicle READ isOfflineEditingVehicle CONSTANT) Q_PROPERTY(QString brandImage READ brandImage CONSTANT) Q_PROPERTY(QStringList unhealthySensors READ unhealthySensors NOTIFY unhealthySensorsChanged) + Q_PROPERTY(QString missionFlightMode READ missionFlightMode CONSTANT) + Q_PROPERTY(QString rtlFlightMode READ rtlFlightMode CONSTANT) + Q_PROPERTY(QString takeControlFlightMode READ takeControlFlightMode CONSTANT) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -522,6 +525,9 @@ public: bool isOfflineEditingVehicle () const { return _offlineEditingVehicle; } QString brandImage () const; QStringList unhealthySensors () const; + QString missionFlightMode () const; + QString rtlFlightMode () const; + QString takeControlFlightMode () const; Fact* roll (void) { return &_rollFact; } Fact* heading (void) { return &_headingFact; } diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index b11b073ab..dc374d571 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -255,7 +255,7 @@ Item { MainToolBar { id: toolBar - height: ScreenTools.defaultFontPixelHeight * 3 + height: ScreenTools.toolbarHeight anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top -- GitLab From b789a6903aa30272c00eef37eacc24a1872b6c75 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 7 Jan 2017 12:32:20 -0800 Subject: [PATCH 170/398] Remove unused code --- qgroundcontrol.pro | 2 - src/HomePositionManager.cc | 186 --------------------- src/HomePositionManager.h | 108 ------------ src/QGCApplication.cc | 2 - src/QGCApplication.h | 1 - src/QGCToolbox.cc | 5 - src/QGCToolbox.h | 3 - src/QmlControls/QGroundControlQmlGlobal.cc | 2 - src/QmlControls/QGroundControlQmlGlobal.h | 4 - src/comm/QGCFlightGearLink.cc | 8 +- src/comm/QGCXPlaneLink.cc | 1 - src/uas/UAS.cc | 1 - src/ui/MainWindow.cc | 1 - 13 files changed, 4 insertions(+), 320 deletions(-) delete mode 100644 src/HomePositionManager.cc delete mode 100644 src/HomePositionManager.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 356059415..3ef7283da 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -423,7 +423,6 @@ HEADERS += \ src/FlightMap/Widgets/ValuesWidgetController.h \ src/FollowMe/FollowMe.h \ src/GAudioOutput.h \ - src/HomePositionManager.h \ src/Joystick/Joystick.h \ src/Joystick/JoystickManager.h \ src/JsonHelper.h \ @@ -588,7 +587,6 @@ SOURCES += \ src/FlightMap/Widgets/ValuesWidgetController.cc \ src/FollowMe/FollowMe.cc \ src/GAudioOutput.cc \ - src/HomePositionManager.cc \ src/Joystick/Joystick.cc \ src/Joystick/JoystickManager.cc \ src/JsonHelper.cc \ diff --git a/src/HomePositionManager.cc b/src/HomePositionManager.cc deleted file mode 100644 index 7d5ef9527..000000000 --- a/src/HomePositionManager.cc +++ /dev/null @@ -1,186 +0,0 @@ -/**************************************************************************** - * - * (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 -#include -#include -#include -#include - -#include "UAS.h" -#include "UASInterface.h" -#include "HomePositionManager.h" -#include "QGC.h" -#include "QGCApplication.h" -#include "MultiVehicleManager.h" - -#define PI 3.1415926535897932384626433832795 -#define MEAN_EARTH_DIAMETER 12756274.0 -#define UMR 0.017453292519943295769236907684886 - -const char* HomePositionManager::_settingsGroup = "HomePositionManager"; -const char* HomePositionManager::_latitudeKey = "Latitude"; -const char* HomePositionManager::_longitudeKey = "Longitude"; -const char* HomePositionManager::_altitudeKey = "Altitude"; - -HomePositionManager::HomePositionManager(QGCApplication* app) - : QGCTool(app) - , homeLat(47.3769) - , homeLon(8.549444) - , homeAlt(470.0) -{ - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "HomePositionManager", "Reference only"); -} - -void HomePositionManager::setToolbox(QGCToolbox *toolbox) -{ - QGCTool::setToolbox(toolbox); - - - _loadSettings(); -} - -void HomePositionManager::_storeSettings(void) -{ - QSettings settings; - - settings.remove(_settingsGroup); - settings.beginGroup(_settingsGroup); - - for (int i=0; i<_homePositions.count(); i++) { - HomePosition* homePos = qobject_cast(_homePositions[i]); - - qDebug() << "Saving" << homePos->name(); - - settings.beginGroup(homePos->name()); - settings.setValue(_latitudeKey, homePos->coordinate().latitude()); - settings.setValue(_longitudeKey, homePos->coordinate().longitude()); - settings.setValue(_altitudeKey, homePos->coordinate().altitude()); - settings.endGroup(); - } - - settings.endGroup(); - - // Deprecated settings for old editor - settings.beginGroup("QGC_UASMANAGER"); - settings.setValue("HOMELAT", homeLat); - settings.setValue("HOMELON", homeLon); - settings.setValue("HOMEALT", homeAlt); - settings.endGroup(); -} - -void HomePositionManager::_loadSettings(void) -{ - QSettings settings; - - _homePositions.clear(); - - settings.beginGroup(_settingsGroup); - - foreach(const QString &name, settings.childGroups()) { - QGeoCoordinate coordinate; - - qDebug() << "Load setting" << name; - - settings.beginGroup(name); - coordinate.setLatitude(settings.value(_latitudeKey).toDouble()); - coordinate.setLongitude(settings.value(_longitudeKey).toDouble()); - coordinate.setAltitude(settings.value(_altitudeKey).toDouble()); - settings.endGroup(); - - _homePositions.append(new HomePosition(name, coordinate, this)); - } - - settings.endGroup(); - - if (_homePositions.count() == 0) { - _homePositions.append(new HomePosition("ETH Campus", QGeoCoordinate(47.3769, 8.549444, 470.0), this)); - } -} - -void HomePositionManager::updateHomePosition(const QString& name, const QGeoCoordinate& coordinate) -{ - HomePosition * homePos = NULL; - - for (int i=0; i<_homePositions.count(); i++) { - homePos = qobject_cast(_homePositions[i]); - if (homePos->name() == name) { - break; - } - homePos = NULL; - } - - if (homePos == NULL) { - HomePosition* homePos = new HomePosition(name, coordinate, this); - _homePositions.append(homePos); - } else { - homePos->setName(name); - homePos->setCoordinate(coordinate); - } - - _storeSettings(); -} - -void HomePositionManager::deleteHomePosition(const QString& name) -{ - // Don't allow delete of last position - if (_homePositions.count() == 1) { - return; - } - - qDebug() << "Attempting delete" << name; - - for (int i=0; i<_homePositions.count(); i++) { - if (qobject_cast(_homePositions[i])->name() == name) { - qDebug() << "Deleting" << name; - _homePositions.removeAt(i); - break; - } - } - - _storeSettings(); -} - -HomePosition::HomePosition(const QString& name, const QGeoCoordinate& coordinate, HomePositionManager* homePositionManager, QObject* parent) - : QObject(parent) - , _coordinate(coordinate) - , _homePositionManager(homePositionManager) -{ - setObjectName(name); -} - -HomePosition::~HomePosition() -{ - -} - -QString HomePosition::name(void) -{ - return objectName(); -} - -void HomePosition::setName(const QString& name) -{ - setObjectName(name); - _homePositionManager->_storeSettings(); - emit nameChanged(name); -} - -QGeoCoordinate HomePosition::coordinate(void) -{ - return _coordinate; -} - -void HomePosition::setCoordinate(const QGeoCoordinate& coordinate) -{ - _coordinate = coordinate; - _homePositionManager->_storeSettings(); - emit coordinateChanged(coordinate); -} diff --git a/src/HomePositionManager.h b/src/HomePositionManager.h deleted file mode 100644 index 049b818a7..000000000 --- a/src/HomePositionManager.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** - * - * (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 HomePositionManager_H -#define HomePositionManager_H - -#include "QmlObjectListModel.h" -#include "QGCToolbox.h" - -#include - -class HomePositionManager; - -class HomePosition : public QObject -{ - Q_OBJECT - -public: - HomePosition(const QString& name, const QGeoCoordinate& coordinate, HomePositionManager* homePositionManager, QObject* parent = NULL); - ~HomePosition(); - - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) - - // Property accessors - - QString name(void); - void setName(const QString& name); - - QGeoCoordinate coordinate(void); - void setCoordinate(const QGeoCoordinate& coordinate); - -signals: - void nameChanged(const QString& name); - void coordinateChanged(const QGeoCoordinate& coordinate); - -private: - QGeoCoordinate _coordinate; - HomePositionManager* _homePositionManager; -}; - -class HomePositionManager : public QGCTool -{ - Q_OBJECT - -public: - HomePositionManager(QGCApplication* app); - - Q_PROPERTY(QmlObjectListModel* homePositions READ homePositions CONSTANT) - - /// If name is not already a home position a new one will be added, otherwise the existing - /// home position will be updated - Q_INVOKABLE void updateHomePosition(const QString& name, const QGeoCoordinate& coordinate); - - Q_INVOKABLE void deleteHomePosition(const QString& name); - - // Property accesors - - QmlObjectListModel* homePositions(void) { return &_homePositions; } - - // Should only be called by HomePosition - void _storeSettings(void); - - // Override from QGCTool - virtual void setToolbox(QGCToolbox *toolbox); - -private: - void _loadSettings(void); - - QmlObjectListModel _homePositions; - - static const char* _settingsGroup; - static const char* _latitudeKey; - static const char* _longitudeKey; - static const char* _altitudeKey; - -// Everything below is deprecated and will be removed once old Map code is removed -public: - - // Deprecated methods - - /** @brief Get home position latitude */ - double getHomeLatitude() const { - return homeLat; - } - /** @brief Get home position longitude */ - double getHomeLongitude() const { - return homeLon; - } - /** @brief Get home position altitude */ - double getHomeAltitude() const { - return homeAlt; - } - -protected: - double homeLat; - double homeLon; - double homeAlt; -}; - -#endif diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 578634b6d..8e2295509 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -39,7 +39,6 @@ #include "CmdLineOptParser.h" #include "UDPLink.h" #include "LinkManager.h" -#include "HomePositionManager.h" #include "UASMessageHandler.h" #include "QGCTemporaryFile.h" #include "QGCPalette.h" @@ -63,7 +62,6 @@ #include "QmlObjectListModel.h" #include "MissionManager.h" #include "QGroundControlQmlGlobal.h" -#include "HomePositionManager.h" #include "FlightMapSettings.h" #include "CoordinateVector.h" #include "MainToolBarController.h" diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 002c88e39..07bc901c3 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -27,7 +27,6 @@ #include "LinkManager.h" #include "MAVLinkProtocol.h" #include "FlightMapSettings.h" -#include "HomePositionManager.h" #include "FirmwarePluginManager.h" #include "MultiVehicleManager.h" #include "JoystickManager.h" diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 28e1afbe2..5ca81d981 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -15,7 +15,6 @@ #ifndef __mobile__ #include "GPSManager.h" #endif -#include "HomePositionManager.h" #include "JoystickManager.h" #include "LinkManager.h" #include "MAVLinkProtocol.h" @@ -43,7 +42,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) #ifndef __mobile__ , _gpsManager(NULL) #endif - , _homePositionManager(NULL) , _imageProvider(NULL) , _joystickManager(NULL) , _linkManager(NULL) @@ -67,7 +65,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) #ifndef __mobile__ _gpsManager = new GPSManager(app); #endif - _homePositionManager = new HomePositionManager(app); _imageProvider = new QGCImageProvider(app); _joystickManager = new JoystickManager(app); _linkManager = new LinkManager(app); @@ -92,7 +89,6 @@ void QGCToolbox::setChildToolboxes(void) #ifndef __mobile__ _gpsManager->setToolbox(this); #endif - _homePositionManager->setToolbox(this); _imageProvider->setToolbox(this); _joystickManager->setToolbox(this); _linkManager->setToolbox(this); @@ -115,7 +111,6 @@ QGCToolbox::~QGCToolbox() delete _factSystem; delete _firmwarePluginManager; delete _flightMapSettings; - delete _homePositionManager; delete _joystickManager; delete _linkManager; delete _mavlinkProtocol; diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 607ab0a66..dc6ab6d7f 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -18,7 +18,6 @@ class FirmwarePluginManager; class FlightMapSettings; class GAudioOutput; class GPSManager; -class HomePositionManager; class JoystickManager; class FollowMe; class LinkManager; @@ -44,7 +43,6 @@ public: FirmwarePluginManager* firmwarePluginManager(void) { return _firmwarePluginManager; } FlightMapSettings* flightMapSettings(void) { return _flightMapSettings; } GAudioOutput* audioOutput(void) { return _audioOutput; } - HomePositionManager* homePositionManager(void) { return _homePositionManager; } JoystickManager* joystickManager(void) { return _joystickManager; } LinkManager* linkManager(void) { return _linkManager; } MAVLinkProtocol* mavlinkProtocol(void) { return _mavlinkProtocol; } @@ -75,7 +73,6 @@ private: #ifndef __mobile__ GPSManager* _gpsManager; #endif - HomePositionManager* _homePositionManager; QGCImageProvider* _imageProvider; JoystickManager* _joystickManager; LinkManager* _linkManager; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 27966dd36..c93c0e5d5 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -37,7 +37,6 @@ const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFon QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) : QGCTool(app) , _flightMapSettings(NULL) - , _homePositionManager(NULL) , _linkManager(NULL) , _multiVehicleManager(NULL) , _mapEngineManager(NULL) @@ -66,7 +65,6 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); _flightMapSettings = toolbox->flightMapSettings(); - _homePositionManager = toolbox->homePositionManager(); _linkManager = toolbox->linkManager(); _multiVehicleManager = toolbox->multiVehicleManager(); _mapEngineManager = toolbox->mapEngineManager(); diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index b0b01d092..97e3a3394 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -17,7 +17,6 @@ #include "QGCToolbox.h" #include "QGCApplication.h" #include "LinkManager.h" -#include "HomePositionManager.h" #include "FlightMapSettings.h" #include "SettingsFact.h" #include "FactMetaData.h" @@ -65,7 +64,6 @@ public: Q_ENUMS(SpeedUnits) Q_PROPERTY(FlightMapSettings* flightMapSettings READ flightMapSettings CONSTANT) - Q_PROPERTY(HomePositionManager* homePositionManager READ homePositionManager CONSTANT) Q_PROPERTY(LinkManager* linkManager READ linkManager CONSTANT) Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager CONSTANT) Q_PROPERTY(QGCMapEngineManager* mapEngineManager READ mapEngineManager CONSTANT) @@ -163,7 +161,6 @@ public: // Property accesors FlightMapSettings* flightMapSettings () { return _flightMapSettings; } - HomePositionManager* homePositionManager () { return _homePositionManager; } LinkManager* linkManager () { return _linkManager; } MultiVehicleManager* multiVehicleManager () { return _multiVehicleManager; } QGCMapEngineManager* mapEngineManager () { return _mapEngineManager; } @@ -235,7 +232,6 @@ private: static QMap& nameToMetaDataMap(void); FlightMapSettings* _flightMapSettings; - HomePositionManager* _homePositionManager; LinkManager* _linkManager; MultiVehicleManager* _multiVehicleManager; QGCMapEngineManager* _mapEngineManager; diff --git a/src/comm/QGCFlightGearLink.cc b/src/comm/QGCFlightGearLink.cc index fcbb4029f..ec5112e6f 100644 --- a/src/comm/QGCFlightGearLink.cc +++ b/src/comm/QGCFlightGearLink.cc @@ -32,7 +32,6 @@ #include "QGC.h" #include "QGCFileDialog.h" #include "QGCMessageBox.h" -#include "HomePositionManager.h" #include "QGCApplication.h" #include "Vehicle.h" #include "UAS.h" @@ -939,11 +938,12 @@ bool QGCFlightGearLink::connectSimulation() } // We start out at our home position - _fgArgList << QString("--lat=%1").arg(qgcApp()->toolbox()->homePositionManager()->getHomeLatitude()); - _fgArgList << QString("--lon=%1").arg(qgcApp()->toolbox()->homePositionManager()->getHomeLongitude()); + QGeoCoordinate homePosition = qgcApp()->lastKnownHomePosition(); + _fgArgList << QString("--lat=%1").arg(homePosition.latitude()); + _fgArgList << QString("--lon=%1").arg(homePosition.longitude()); // The altitude is not set because an altitude not equal to the ground altitude leads to a non-zero default throttle in flightgear // Without the altitude-setting the aircraft is positioned on the ground - //_fgArgList << QString("--altitude=%1").arg(qgcApp()->toolbox()->homePositionManager()->getHomeAltitude()); + //_fgArgList << QString("--altitude=%1").arg(homePosition.altitude()); #ifdef DEBUG_FLIGHTGEAR_CONNECT // This tell FlightGear to output highest debug level of log output. Handy for debuggin failures by looking at the FG diff --git a/src/comm/QGCXPlaneLink.cc b/src/comm/QGCXPlaneLink.cc index a186a8011..87b4247eb 100644 --- a/src/comm/QGCXPlaneLink.cc +++ b/src/comm/QGCXPlaneLink.cc @@ -30,7 +30,6 @@ #include "UAS.h" #include "UASInterface.h" #include "QGCMessageBox.h" -#include "HomePositionManager.h" QGCXPlaneLink::QGCXPlaneLink(Vehicle* vehicle, QString remoteHost, QHostAddress localHost, quint16 localPort) : _vehicle(vehicle), diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 55a2c2ee7..6be68ccaf 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -23,7 +23,6 @@ #include "UAS.h" #include "LinkInterface.h" -#include "HomePositionManager.h" #include "QGC.h" #include "GAudioOutput.h" #include "MAVLinkProtocol.h" diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index d2bce2a37..6d925bc46 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -37,7 +37,6 @@ #include "MAVLinkDecoder.h" #include "QGCApplication.h" #include "MultiVehicleManager.h" -#include "HomePositionManager.h" #include "LogCompressor.h" #include "UAS.h" #include "QGCImageProvider.h" -- GitLab From b6b222ba716e5ff4f350fcd5cd4a861364623b4b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 7 Jan 2017 16:43:31 -0800 Subject: [PATCH 171/398] Switch away from deprecated APIs --- src/audio/QGCAudioWorker.cpp | 54 ++++++++++++------------------------ 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/src/audio/QGCAudioWorker.cpp b/src/audio/QGCAudioWorker.cpp index 5e79774f6..8a19f0db5 100644 --- a/src/audio/QGCAudioWorker.cpp +++ b/src/audio/QGCAudioWorker.cpp @@ -10,47 +10,27 @@ #if (defined __macos__) && defined QGC_SPEECH_ENABLED #include +#include -static SpeechChannel sc; -static Fixed volume; +void macSpeak(const char* words) +{ + static SpeechChannel sc = NULL; -static void speechDone(SpeechChannel sc2, void *) { - if (sc2 == sc) - { - DisposeSpeechChannel(sc); + while (SpeechBusy()) { + QGC::SLEEP::msleep(100); } -} + if (sc == NULL) { + Float32 volume = 1.0; -class MacSpeech -{ -public: - MacSpeech() - { - setVolume(100); - } - ~MacSpeech() - { - } - void say(const char* words) - { - while (SpeechBusy()) { - QGC::SLEEP::msleep(100); - } NewSpeechChannel(NULL, &sc); - SetSpeechInfo(sc, soVolume, &volume); - SetSpeechInfo(sc, soSpeechDoneCallBack, reinterpret_cast(speechDone)); - CFStringRef cfstr = CFStringCreateWithCString(NULL, words, kCFStringEncodingUTF8); - SpeakCFString(sc, cfstr, NULL); - } - void setVolume(int v) - { - volume = FixRatio(v, 100); + CFNumberRef volumeRef = CFNumberCreate(NULL, kCFNumberFloat32Type, &volume); + SetSpeechProperty(sc, kSpeechVolumeProperty, volumeRef); + CFRelease(volumeRef); } -}; - -//-- Singleton -MacSpeech macSpeech; - + CFStringRef strRef = CFStringCreateWithCString(NULL, words, kCFStringEncodingUTF8); + SpeakCFString(sc, strRef, NULL); + CFRelease(strRef); +} #endif #if (defined __ios__) && defined QGC_SPEECH_ENABLED @@ -154,7 +134,7 @@ void QGCAudioWorker::say(QString inText) espeak_Synth(text.toStdString().c_str(), espeak_size, 0, POS_CHARACTER, 0, espeakCHARS_AUTO, NULL, NULL); espeak_Synchronize(); #elif (defined __macos__) && defined QGC_SPEECH_ENABLED - macSpeech.say(text.toStdString().c_str()); + macSpeak(text.toStdString().c_str()); #elif (defined __ios__) && defined QGC_SPEECH_ENABLED iOSSpeak(text); #else @@ -172,7 +152,7 @@ void QGCAudioWorker::mute(bool mute) this->muted = mute; QSettings settings; settings.setValue(QGC_GAUDIOOUTPUT_KEY + "muted", this->muted); -// emit mutedChanged(muted); + // emit mutedChanged(muted); } } -- GitLab From 373245491a4bf51d529a18db98b71eb40dbbbcdf Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 7 Jan 2017 20:57:55 -0800 Subject: [PATCH 172/398] New QGCMapLabel using outline font This makes text show up better on map images --- qgroundcontrol.qrc | 1 + src/FlightDisplay/FlightDisplayViewMap.qml | 4 ++-- src/FlightMap/FlightMap.qml | 5 +++-- src/FlightMap/MapScale.qml | 4 ++-- src/MissionEditor/MissionEditor.qml | 10 ++++++++-- src/QGCMapPalette.cc | 1 + src/QGCMapPalette.h | 3 +++ src/QmlControls/QGCMapLabel.qml | 16 +++++++++++++++ src/QmlControls/QGCRadioButton.qml | 20 ++++++++++++------- .../QGroundControl.Controls.qmldir | 1 + .../QMLControl/OfflineMap.qml | 12 +++++++---- 11 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 src/QmlControls/QGCMapLabel.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index e5bc99415..d032df6f2 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -70,6 +70,7 @@ src/QmlControls/QGCFlickableVerticalIndicator.qml src/QmlControls/QGCLabel.qml src/QmlControls/QGCListView.qml + src/QmlControls/QGCMapLabel.qml src/QmlControls/QGCMobileFileOpenDialog.qml src/QmlControls/QGCMobileFileSaveDialog.qml src/QmlControls/QGCMovableItem.qml diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index 3ccdeb548..c7a2258c4 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -71,10 +71,10 @@ FlightMap { Component.onCompleted: start(false /* editMode */) } - QGCLabel { + QGCMapLabel { id: flyLabel + map: flightMap text: qsTr("Fly") - color: mapPal.text visible: !ScreenTools.isShortScreen anchors.topMargin: _toolButtonTopMargin anchors.horizontalCenter: centerMapDropButton.horizontalCenter diff --git a/src/FlightMap/FlightMap.qml b/src/FlightMap/FlightMap.qml index 8d9830e43..ed8042802 100644 --- a/src/FlightMap/FlightMap.qml +++ b/src/FlightMap/FlightMap.qml @@ -161,13 +161,14 @@ Map { // Not sure why this is needed, but trying to reference polygonDrawer directly from other code doesn't work property alias polygonDraw: polygonDrawer - QGCLabel { - id: polygonHelp + QGCMapLabel { + id: polygonHelp anchors.topMargin: parent.height - ScreenTools.availableHeight anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right horizontalAlignment: Text.AlignHCenter + map: _map text: qsTr("Click to add point %1").arg(ScreenTools.isMobile || !polygonDrawer.polygonReady ? "" : qsTr("- Right Click to end polygon")) visible: polygonDrawer.drawingPolygon diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index 7c0d878be..26a5dfb6f 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -138,9 +138,9 @@ Item { onTriggered: calculateScale() } - QGCLabel { + QGCMapLabel { id: scaleText - color: _color + map: mapControl font.family: ScreenTools.demiboldFontFamily anchors.left: parent.left anchors.right: parent.right diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 738a9864c..0b180e910 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -584,6 +584,8 @@ QGCView { text: qsTr("Mission") checked: true color: mapPal.text + textStyle: Text.Outline + textStyleColor: mapPal.textOutline } Item { height: 1; width: 1 } @@ -593,6 +595,8 @@ QGCView { exclusiveGroup: planElementSelectorGroup text: qsTr("Fence") color: mapPal.text + textStyle: Text.Outline + textStyleColor: mapPal.textOutline } Item { height: 1; width: 1 } @@ -602,6 +606,8 @@ QGCView { exclusiveGroup: planElementSelectorGroup text: qsTr("Rally") color: mapPal.text + textStyle: Text.Outline + textStyleColor: mapPal.textOutline } } // Row - Plan Element Selector @@ -770,10 +776,10 @@ QGCView { } } - QGCLabel { + QGCMapLabel { id: planLabel + map: editorMap text: qsTr("Plan") - color: mapPal.text visible: !ScreenTools.isShortScreen anchors.topMargin: _toolButtonTopMargin anchors.horizontalCenter: addMissionItemsButton.horizontalCenter diff --git a/src/QGCMapPalette.cc b/src/QGCMapPalette.cc index 5779dfff8..0bdd60f77 100644 --- a/src/QGCMapPalette.cc +++ b/src/QGCMapPalette.cc @@ -15,6 +15,7 @@ QColor QGCMapPalette::_thumbJoystick[QGCMapPalette::_cColorGroups] = { QColor(255,255,255,127), QColor(0,0,0,127) }; QColor QGCMapPalette::_text [QGCMapPalette::_cColorGroups] = { QColor(255,255,255), QColor(0,0,0) }; +QColor QGCMapPalette::_textOutline [QGCMapPalette::_cColorGroups] = { QColor(0,0,0), QColor(255,255,255) }; QGCMapPalette::QGCMapPalette(QObject* parent) : QObject(parent) diff --git a/src/QGCMapPalette.h b/src/QGCMapPalette.h index 14698c06b..7896c6746 100644 --- a/src/QGCMapPalette.h +++ b/src/QGCMapPalette.h @@ -43,6 +43,7 @@ class QGCMapPalette : public QObject Q_PROPERTY(bool lightColors READ lightColors WRITE setLightColors NOTIFY paletteChanged) Q_PROPERTY(QColor text READ text NOTIFY paletteChanged) + Q_PROPERTY(QColor textOutline READ textOutline NOTIFY paletteChanged) Q_PROPERTY(QColor thumbJoystick READ thumbJoystick NOTIFY paletteChanged) public: @@ -50,6 +51,7 @@ public: /// Text color QColor text(void) const { return _text[_lightColors ? 0 : 1]; } + QColor textOutline(void) const { return _textOutline[_lightColors ? 0 : 1]; } /// Thumb joystick indicator QColor thumbJoystick(void) const { return _thumbJoystick[_lightColors ? 0 : 1]; } @@ -68,6 +70,7 @@ private: static QColor _thumbJoystick[_cColorGroups]; static QColor _text[_cColorGroups]; + static QColor _textOutline[_cColorGroups]; }; #endif diff --git a/src/QmlControls/QGCMapLabel.qml b/src/QmlControls/QGCMapLabel.qml new file mode 100644 index 000000000..ebc49222c --- /dev/null +++ b/src/QmlControls/QGCMapLabel.qml @@ -0,0 +1,16 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 + +/// Text control used for displaying text of Maps +QGCLabel { + property var map + + QGCMapPalette { id: mapPal; lightColors: map.isSatelliteMap } + + color: mapPal.text + style: Text.Outline + styleColor: mapPal.textOutline +} diff --git a/src/QmlControls/QGCRadioButton.qml b/src/QmlControls/QGCRadioButton.qml index 4747e3d41..cfcb5072c 100644 --- a/src/QmlControls/QGCRadioButton.qml +++ b/src/QmlControls/QGCRadioButton.qml @@ -1,20 +1,23 @@ -import QtQuick 2.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 -import QGroundControl.Palette 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 RadioButton { - property var color: _qgcPal.text ///< Text color + property var color: qgcPal.text ///< Text color + property int textStyle: Text.Normal + property color textStyleColor: qgcPal.text - property var _qgcPal: QGCPalette { colorGroupEnabled: enabled } + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } style: RadioButtonStyle { label: Item { implicitWidth: text.implicitWidth + ScreenTools.defaultFontPixelWidth * 0.25 implicitHeight: text.implicitHeight baselineOffset: text.y + text.baselineOffset + Rectangle { anchors.fill: text anchors.margins: -1 @@ -27,6 +30,7 @@ RadioButton { border.color: "#47b" opacity: 0.6 } + Text { id: text text: control.text @@ -34,6 +38,8 @@ RadioButton { font.family: ScreenTools.normalFontFamily antialiasing: true color: control.color + style: control.textStyle + styleColor: control.textStyleColor anchors.centerIn: parent } } diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index c3d900910..f1a1caf12 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -30,6 +30,7 @@ QGCComboBox 1.0 QGCComboBox.qml QGCFlickable 1.0 QGCFlickable.qml QGCLabel 1.0 QGCLabel.qml QGCListView 1.0 QGCListView.qml +QGCMapLabel 1.0 QGCMapLabel.qml QGCMobileFileOpenDialog 1.0 QGCMobileFileOpenDialog.qml QGCMobileFileSaveDialog 1.0 QGCMobileFileSaveDialog.qml QGCMovableItem 1.0 QGCMovableItem.qml diff --git a/src/QtLocationPlugin/QMLControl/OfflineMap.qml b/src/QtLocationPlugin/QMLControl/OfflineMap.qml index 450a51058..985afec75 100644 --- a/src/QtLocationPlugin/QMLControl/OfflineMap.qml +++ b/src/QtLocationPlugin/QMLControl/OfflineMap.qml @@ -508,6 +508,8 @@ QGCView { gesture.enabled: false visible: _showPreview + property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1 + plugin: Plugin { name: "QGroundControl" } MapScale { @@ -523,9 +525,9 @@ QGCView { border.color: _mapAdjustedColor color: "transparent" - QGCLabel { + QGCMapLabel { anchors.centerIn: parent - color: _mapAdjustedColor + map: minZoomPreview text: qsTr("Min Zoom: %1").arg(sliderMinZoom.value) } MouseArea { @@ -545,6 +547,8 @@ QGCView { gesture.enabled: false visible: _showPreview + property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1 + plugin: Plugin { name: "QGroundControl" } MapScale { @@ -560,9 +564,9 @@ QGCView { border.color: _mapAdjustedColor color: "transparent" - QGCLabel { + QGCMapLabel { anchors.centerIn: parent - color: _mapAdjustedColor + map: maxZoomPreview text: qsTr("Max Zoom: %1").arg(sliderMaxZoom.value) } MouseArea { -- GitLab From 8333bc68761b8329c1f3131c834f17b8927f65d6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 8 Jan 2017 09:58:00 -0800 Subject: [PATCH 173/398] Allow fallback to first link even with no serial --- src/Vehicle/Vehicle.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index f5a8b5b59..9b3bc66be 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -979,9 +979,9 @@ void Vehicle::_sendMessageOnLink(LinkInterface* link, mavlink_message_t message) void Vehicle::_updatePriorityLink(void) { -#ifndef NO_SERIAL_LINK LinkInterface* newPriorityLink = NULL; +#ifndef NO_SERIAL_LINK // Note that this routine specificallty does not clear _priorityLink when there are no links remaining. // By doing this we hold a reference on the last link as the Vehicle shuts down. Thus preventing shutdown // ordering NULL pointer crashes where priorityLink() is still called during shutdown sequence. @@ -1004,6 +1004,7 @@ void Vehicle::_updatePriorityLink(void) } } } +#endif if (!newPriorityLink && !_priorityLink.data() && _links.count()) { newPriorityLink = _links[0]; @@ -1012,7 +1013,6 @@ void Vehicle::_updatePriorityLink(void) if (newPriorityLink) { _priorityLink = qgcApp()->toolbox()->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink); } -#endif } void Vehicle::setLatitude(double latitude) -- GitLab From 89ad9836e7375884dba1d973ebf1bf2fdb05ae25 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 8 Jan 2017 10:22:28 -0800 Subject: [PATCH 174/398] Keep first link fallback when NO_SERIAL_LINK --- src/Vehicle/Vehicle.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index f0caf147a..8d8e9f479 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -979,9 +979,9 @@ void Vehicle::_sendMessageOnLink(LinkInterface* link, mavlink_message_t message) void Vehicle::_updatePriorityLink(void) { -#ifndef NO_SERIAL_LINK LinkInterface* newPriorityLink = NULL; +#ifndef NO_SERIAL_LINK // Note that this routine specificallty does not clear _priorityLink when there are no links remaining. // By doing this we hold a reference on the last link as the Vehicle shuts down. Thus preventing shutdown // ordering NULL pointer crashes where priorityLink() is still called during shutdown sequence. @@ -1004,6 +1004,7 @@ void Vehicle::_updatePriorityLink(void) } } } +#endif if (!newPriorityLink && !_priorityLink.data() && _links.count()) { newPriorityLink = _links[0]; @@ -1012,7 +1013,6 @@ void Vehicle::_updatePriorityLink(void) if (newPriorityLink) { _priorityLink = qgcApp()->toolbox()->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink); } -#endif } void Vehicle::setLatitude(double latitude) -- GitLab From e3566933dc664710c8a8420b00d61fa2a91688cd Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 10 Jan 2017 15:34:41 -0800 Subject: [PATCH 175/398] Mission Settings replaces Planned Home Position - Shot Interval in Survey - Much change to time/distance calc --- qgroundcontrol.qrc | 1 + src/MissionEditor/MissionEditor.qml | 3 +- src/MissionEditor/MissionItemEditor.qml | 19 +- src/MissionEditor/MissionItemStatus.qml | 117 ++------ src/MissionEditor/MissionSettingsEditor.qml | 196 +++++++++++++ src/MissionEditor/SurveyItemEditor.qml | 7 +- src/MissionManager/ComplexMissionItem.h | 3 + src/MissionManager/MavCmdInfoCommon.json | 2 +- src/MissionManager/MissionController.cc | 277 ++++++++++++------- src/MissionManager/MissionController.h | 62 +++-- src/MissionManager/PlanElementController.cc | 2 + src/MissionManager/PlanElementController.h | 5 + src/MissionManager/SurveyMissionItem.cc | 30 +- src/MissionManager/SurveyMissionItem.h | 8 +- src/QmlControls/QGroundControlQmlGlobal.json | 4 +- src/Vehicle/Vehicle.cc | 139 +++++----- src/Vehicle/Vehicle.h | 46 +-- src/ui/preferences/GeneralSettings.qml | 91 +----- 18 files changed, 606 insertions(+), 406 deletions(-) create mode 100644 src/MissionEditor/MissionSettingsEditor.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index d032df6f2..b50a6e7fb 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -32,6 +32,7 @@ src/ui/MainWindowNative.qml src/ui/preferences/MavlinkSettings.qml src/MissionEditor/MissionEditor.qml + src/MissionEditor/MissionSettingsEditor.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml src/MultiVehicle/MultiVehicleView.qml diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 0b180e910..b05d49231 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -944,9 +944,8 @@ QGCView { missionItems: missionController.visualItems expandedWidth: missionItemEditor.x - (ScreenTools.defaultFontPixelWidth * 2) missionDistance: missionController.missionDistance + missionTime: missionController.missionTime missionMaxTelemetry: missionController.missionMaxTelemetry - cruiseDistance: missionController.cruiseDistance - hoverDistance: missionController.hoverDistance visible: _editingLayer == _layerMission && !ScreenTools.isShortScreen } } // FlightMap diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index d7be22e88..7761d9660 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -26,8 +26,9 @@ Rectangle { signal insert signal moveHomeToMapCenter - property bool _currentItem: missionItem.isCurrentItem - property color _outerTextColor: _currentItem ? "black" : qgcPal.text + property bool _currentItem: missionItem.isCurrentItem + property color _outerTextColor: _currentItem ? "black" : qgcPal.text + property bool _noMissionItemsAdded: ListView.view.model.count == 1 readonly property real _editFieldWidth: Math.min(width - _margin * 2, ScreenTools.defaultFontPixelWidth * 12) readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 @@ -38,7 +39,6 @@ Rectangle { colorGroupEnabled: enabled } - MouseArea { anchors.fill: parent visible: !missionItem.isCurrentItem @@ -121,7 +121,6 @@ Rectangle { anchors.rightMargin: ScreenTools.defaultFontPixelWidth anchors.left: label.right anchors.top: parent.top - //anchors.right: hamburger.left visible: missionItem.sequenceNumber != 0 && missionItem.isCurrentItem && !missionItem.rawEdit && missionItem.isSimpleItem text: missionItem.commandName @@ -133,7 +132,7 @@ Rectangle { } } - onClicked: qgcView.showDialog(commandDialog, qsTr("Select Mission Command"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) + onClicked: qgcView.showDialog(commandDialog, qsTr("Select Mission Command"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) } QGCLabel { @@ -141,7 +140,7 @@ Rectangle { visible: missionItem.sequenceNumber == 0 || !missionItem.isCurrentItem || !missionItem.isSimpleItem verticalAlignment: Text.AlignVCenter text: missionItem.sequenceNumber == 0 ? - qsTr("Planned Home Position") : + qsTr("Mission Settings") : (missionItem.isSimpleItem ? missionItem.commandName : qsTr("Survey")) color: _outerTextColor } @@ -153,8 +152,12 @@ Rectangle { anchors.left: parent.left anchors.top: commandPicker.bottom height: item ? item.height : 0 - source: missionItem.isSimpleItem ? "qrc:/qml/SimpleItemEditor.qml" : "qrc:/qml/SurveyItemEditor.qml" - onLoaded: { item.visible = Qt.binding(function() { return _currentItem; }) } + source: missionItem.sequenceNumber == 0 ? "qrc:/qml/MissionSettingsEditor.qml" : (missionItem.isSimpleItem ? "qrc:/qml/SimpleItemEditor.qml" : "qrc:/qml/SurveyItemEditor.qml") + + onLoaded: { + item.visible = Qt.binding(function() { return _currentItem; }) + } + property real availableWidth: _root.width - (_margin * 2) ///< How wide the editor should be property var editorRoot: _root } diff --git a/src/MissionEditor/MissionItemStatus.qml b/src/MissionEditor/MissionItemStatus.qml index 60c8e09f8..2aad79b34 100644 --- a/src/MissionEditor/MissionItemStatus.qml +++ b/src/MissionEditor/MissionItemStatus.qml @@ -20,20 +20,6 @@ import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 Rectangle { - readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - - property Fact _offlineEditingVehicleType: QGroundControl.offlineEditingVehicleType - property Fact _offlineEditingCruiseSpeed: QGroundControl.offlineEditingCruiseSpeed - property Fact _offlineEditingHoverSpeed: QGroundControl.offlineEditingHoverSpeed - - property var currentMissionItem ///< Mission item to display status for - property var missionItems ///< List of all available mission items - property real expandedWidth ///< Width of control when expanded - property real missionDistance - property real missionMaxTelemetry - property real cruiseDistance - property real hoverDistance - width: _expanded ? expandedWidth : _collapsedWidth height: Math.max(valueGrid.height, valueMissionGrid.height) + (_margins * 2) radius: ScreenTools.defaultFontPixelWidth * 0.5 @@ -41,42 +27,35 @@ Rectangle { opacity: 0.80 clip: true - readonly property real margins: ScreenTools.defaultFontPixelWidth - - property real _collapsedWidth: valueGrid.width + (margins * 2) - property bool _expanded: true - - property real _distance: _statusValid ? _currentMissionItem.distance : 0 - property real _altDifference: _statusValid ? _currentMissionItem.altDifference : 0 - property real _gradient: _statusValid && _currentMissionItem.distance > 0 ? Math.atan(_currentMissionItem.altDifference / _currentMissionItem.distance) : 0 - property real _gradientPercent: isNaN(_gradient) ? 0 : _gradient * 100 - property real _azimuth: _statusValid ? _currentMissionItem.azimuth : -1 - property real _missionDistance: _missionValid ? missionDistance : 0 - property real _missionMaxTelemetry: _missionValid ? missionMaxTelemetry : 0 - property real _missionTime: _missionValid && _missionSpeed > 0 ? (_isVTOL ? _hoverTime + _cruiseTime : _missionDistance / _missionSpeed) : 0 - property real _hoverDistance: _missionValid ? hoverDistance : 0 - property real _cruiseDistance: _missionValid ? cruiseDistance : 0 - property real _hoverTime: _missionValid && _offlineEditingHoverSpeed.value > 0 ? _hoverDistance / _offlineEditingHoverSpeed.value : 0 - property real _cruiseTime: _missionValid && _offlineEditingCruiseSpeed.value > 0 ? _cruiseDistance / _offlineEditingCruiseSpeed.value : 0 - - property bool _statusValid: currentMissionItem != undefined - property bool _vehicleValid: _activeVehicle != undefined - property bool _missionValid: missionItems != undefined - property bool _currentSurvey: _statusValid ? _currentMissionItem.commandName == "Survey" : false - property bool _isVTOL: _vehicleValid ? _activeVehicle.vtol : _offlineEditingVehicleType.enumStringValue == "VTOL" //hardcoded - property real _missionSpeed: _offlineEditingVehicleType.enumStringValue == "Fixedwing" ? _offlineEditingCruiseSpeed.value : _offlineEditingHoverSpeed.value - - property string _distanceText: _statusValid ? QGroundControl.metersToAppSettingsDistanceUnits(_distance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _altText: _statusValid ? QGroundControl.metersToAppSettingsDistanceUnits(_altDifference).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _gradientText: _statusValid ? _gradientPercent.toFixed(0) + "%" : " " - property string _azimuthText: _statusValid ? Math.round(_azimuth) : " " - property string _missionDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_missionDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _missionTimeText: _missionValid ? Number(_missionTime / 60).toFixed(1) + " min" : " " - property string _missionMaxTelemetryText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_missionMaxTelemetry).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _hoverDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_hoverDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _cruiseDistanceText: _missionValid ? QGroundControl.metersToAppSettingsDistanceUnits(_cruiseDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString : " " - property string _hoverTimeText: _missionValid ? _hoverTime.toFixed(0) + "s" : " " - property string _cruiseTimeText: _missionValid ? _cruiseTime.toFixed(0) + "s" : " " + property var currentMissionItem ///< Mission item to display status for + property var missionItems ///< List of all available mission items + property real expandedWidth ///< Width of control when expanded + property real missionDistance ///< Total mission distance + property real missionTime ///< Total mission time + property real missionMaxTelemetry + + property real _collapsedWidth: valueGrid.width + (_margins * 2) + property bool _expanded: true + + property bool _statusValid: currentMissionItem != undefined + property bool _missionValid: missionItems != undefined + + property real _distance: _statusValid ? _currentMissionItem.distance : NaN + property real _altDifference: _statusValid ? _currentMissionItem.altDifference : NaN + property real _gradient: _statusValid && _currentMissionItem.distance > 0 ? Math.atan(_currentMissionItem.altDifference / _currentMissionItem.distance) : NaN + property real _gradientPercent: isNaN(_gradient) ? NaN : _gradient * 100 + property real _azimuth: _statusValid ? _currentMissionItem.azimuth : NaN + property real _missionDistance: _missionValid ? missionDistance : NaN + property real _missionMaxTelemetry: _missionValid ? missionMaxTelemetry : NaN + property real _missionTime: _missionValid ? missionTime : NaN + + property string _distanceText: isNaN(_distance) ? "-.-" : QGroundControl.metersToAppSettingsDistanceUnits(_distance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString + property string _altDifferenceText: isNaN(_altDifference) ? "-.-" : QGroundControl.metersToAppSettingsDistanceUnits(_altDifference).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString + property string _gradientText: isNaN(_gradient) ? "-.-" : _gradientPercent.toFixed(0) + "%" + property string _azimuthText: isNaN(_azimuth) ? "-.-" : Math.round(_azimuth) + property string _missionDistanceText: isNaN(_missionDistance) ? "-.-" : QGroundControl.metersToAppSettingsDistanceUnits(_missionDistance).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString + property string _missionTimeText: isNaN(_missionTime) ? "-.-" : Number(_missionTime / 60).toFixed(1) + " min" + property string _missionMaxTelemetryText: isNaN(_missionMaxTelemetry) ? "-.-" : QGroundControl.metersToAppSettingsDistanceUnits(_missionMaxTelemetry).toFixed(2) + " " + QGroundControl.appSettingsDistanceUnitsString readonly property real _margins: ScreenTools.defaultFontPixelWidth @@ -103,7 +82,7 @@ Rectangle { QGCLabel { text: _distanceText } QGCLabel { text: qsTr("Alt diff:") } - QGCLabel { text: _altText } + QGCLabel { text: _altDifferenceText } QGCLabel { text: qsTr("Gradient:") } QGCLabel { text: _gradientText } @@ -166,42 +145,6 @@ Rectangle { QGCLabel { text: qsTr("Max telem dist:") } QGCLabel { text: _missionMaxTelemetryText } - - QGCLabel { - text: qsTr("Hover distance:") - visible: _isVTOL - } - QGCLabel { - text: _hoverDistanceText - visible: _isVTOL - } - - QGCLabel { - text: qsTr("Cruise distance:") - visible: _isVTOL - } - QGCLabel { - text: _cruiseDistanceText - visible: _isVTOL - } - - QGCLabel { - text: qsTr("Hover time:") - visible: _isVTOL - } - QGCLabel { - text: _hoverTimeText - visible: _isVTOL - } - - QGCLabel { - text: qsTr("Cruise time:") - visible: _isVTOL - } - QGCLabel { - text: _cruiseTimeText - visible: _isVTOL - } } } } diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml new file mode 100644 index 000000000..b6a062924 --- /dev/null +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -0,0 +1,196 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 + +// Editor for Mission Settings +Rectangle { + id: valuesRect + width: availableWidth + height: deferedload.status == Loader.Ready ? (visible ? deferedload.item.height : 0) : 0 + color: qgcPal.windowShadeDark + visible: missionItem.isCurrentItem + radius: _radius + + Loader { + id: deferedload + active: valuesRect.visible + asynchronous: true + anchors.margins: _margin + anchors.left: valuesRect.left + anchors.right: valuesRect.right + anchors.top: valuesRect.top + + sourceComponent: Component { + Item { + id: valuesItem + height: valuesColumn.height + (_margin * 2) + + property var _missionVehicle: missionController.vehicle + property bool _offlineEditing: _missionVehicle.isOfflineEditingVehicle + property bool _showOfflineEditingCombos: _offlineEditing && _noMissionItemsAdded + property bool _showCruiseSpeed: !_missionVehicle.multiRotor + property bool _showHoverSpeed: _missionVehicle.multiRotor || missionController.vehicle.vtol + + readonly property string _firmwareLabel: qsTr("Firmware:") + readonly property string _vehicleLabel: qsTr("Vehicle:") + + QGCPalette { id: qgcPal } + + Column { + id: valuesColumn + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: _margin + + QGCLabel { text: qsTr("Planned Home Position:") } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + } + + Repeater { + model: missionItem.textFieldFacts + + Item { + width: valuesColumn.width + height: textField.height + + QGCLabel { + id: textFieldLabel + anchors.baseline: textField.baseline + text: object.name + } + + FactTextField { + id: textField + anchors.right: parent.right + width: _editFieldWidth + showUnits: true + fact: object + visible: !_root.readOnly + } + + FactLabel { + anchors.baseline: textFieldLabel.baseline + anchors.right: parent.right + fact: object + visible: _root.readOnly + } + } + } + + QGCButton { + text: qsTr("Move Home to map center") + visible: missionItem.homePosition + onClicked: editorRoot.moveHomeToMapCenter() + anchors.horizontalCenter: parent.horizontalCenter + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("Note: Planned home position for mission display only. Actual home position set by vehicle at flight time.") + } + + QGCLabel { text: qsTr("Vehicle Info:") } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + } + + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: ScreenTools.defaultFontPixelWidth + rowSpacing: columnSpacing + columns: 2 + + QGCLabel { + text: _firmwareLabel + visible: _showOfflineEditingCombos + } + FactComboBox { + Layout.fillWidth: true + fact: QGroundControl.offlineEditingFirmwareType + indexModel: false + visible: _showOfflineEditingCombos + } + + QGCLabel { + text: _firmwareLabel + visible: !_showOfflineEditingCombos + } + QGCLabel { + text: _missionVehicle.firmwareTypeString + visible: !_showOfflineEditingCombos + } + + QGCLabel { + text: _vehicleLabel + visible: _showOfflineEditingCombos + } + FactComboBox { + id: offlineVehicleCombo + Layout.fillWidth: true + fact: QGroundControl.offlineEditingVehicleType + indexModel: false + visible: _showOfflineEditingCombos + } + + QGCLabel { + text: _vehicleLabel + visible: !_showOfflineEditingCombos + } + QGCLabel { + text: _missionVehicle.vehicleTypeString + visible: !_showOfflineEditingCombos + } + QGCLabel { + Layout.row: 2 + text: qsTr("Cruise speed:") + visible: _showCruiseSpeed + } + FactTextField { + Layout.fillWidth: true + fact: QGroundControl.offlineEditingCruiseSpeed + visible: _showCruiseSpeed + } + + QGCLabel { + Layout.row: 3 + text: qsTr("Hover speed:") + visible: _showHoverSpeed + } + FactTextField { + Layout.fillWidth: true + fact: QGroundControl.offlineEditingHoverSpeed + visible: _showHoverSpeed + } + } // GridLayout + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("Note: Speeds are planned speeds only for time calculations. Actual vehicle will not be affected.") + } + } // Column + } // Item + } // Component + } // Loader +} // Rectangle diff --git a/src/MissionEditor/SurveyItemEditor.qml b/src/MissionEditor/SurveyItemEditor.qml index dca4a2b8f..b5fb661e2 100644 --- a/src/MissionEditor/SurveyItemEditor.qml +++ b/src/MissionEditor/SurveyItemEditor.qml @@ -579,14 +579,17 @@ Rectangle { } Grid { - columns: 2 - spacing: ScreenTools.defaultFontPixelWidth + columns: 2 + columnSpacing: ScreenTools.defaultFontPixelWidth QGCLabel { text: qsTr("Survey area:") } QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(missionItem.coveredArea).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString } QGCLabel { text: qsTr("# shots:") } QGCLabel { text: missionItem.cameraShots } + + QGCLabel { text: qsTr("Shot interval:") } + QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs")} } } } diff --git a/src/MissionManager/ComplexMissionItem.h b/src/MissionManager/ComplexMissionItem.h index cb278c6b9..ad512e6ee 100644 --- a/src/MissionManager/ComplexMissionItem.h +++ b/src/MissionManager/ComplexMissionItem.h @@ -46,6 +46,9 @@ public: /// @return the greatest distance from any point of the complex item to some coordinate virtual double greatestDistanceTo(const QGeoCoordinate &other) const = 0; + /// Informs the complex item of the cruise speed it will fly at + virtual void setCruiseSpeed(double cruiseSpeed) = 0; + /// This mission item attribute specifies the type of the complex item. static const char* jsonComplexItemTypeKey; diff --git a/src/MissionManager/MavCmdInfoCommon.json b/src/MissionManager/MavCmdInfoCommon.json index e0567057c..c831f7e1b 100644 --- a/src/MissionManager/MavCmdInfoCommon.json +++ b/src/MissionManager/MavCmdInfoCommon.json @@ -5,7 +5,7 @@ "mavCmdInfo": [ { - "comment": "MAV_CMD_NAV_LAST: Used for fake home position waypoint", + "comment": "MAV_CMD_NAV_LAST: Used for mission settings / planned home position waypoint", "id": 95, "rawName": "HomeRaw", "friendlyName": "Home Position", diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index ca16bee56..791e365b2 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -33,6 +33,9 @@ const char* MissionController::_jsonFileTypeValue = "Mission"; const char* MissionController::_jsonItemsKey = "items"; const char* MissionController::_jsonPlannedHomePositionKey = "plannedHomePosition"; const char* MissionController::_jsonFirmwareTypeKey = "firmwareType"; +const char* MissionController::_jsonVehicleTypeKey = "vehicleType"; +const char* MissionController::_jsonCruiseSpeedKey = "cruiseSpeed"; +const char* MissionController::_jsonHoverSpeedKey = "hoverSpeed"; const char* MissionController::_jsonParamsKey = "params"; // Deprecated V1 format keys @@ -49,9 +52,12 @@ MissionController::MissionController(QObject *parent) , _missionItemsRequested(false) , _queuedSend(false) , _missionDistance(0.0) + , _missionTime(0.0) + , _missionHoverDistance(0.0) + , _missionHoverTime(0.0) + , _missionCruiseDistance(0.0) + , _missionCruiseTime(0.0) , _missionMaxTelemetry(0.0) - , _cruiseDistance(0.0) - , _hoverDistance(0.0) { } @@ -396,6 +402,9 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec { _jsonPlannedHomePositionKey, QJsonValue::Array, true }, { _jsonItemsKey, QJsonValue::Array, true }, { _jsonFirmwareTypeKey, QJsonValue::Double, true }, + { _jsonVehicleTypeKey, QJsonValue::Double, false }, + { _jsonCruiseSpeedKey, QJsonValue::Double, false }, + { _jsonHoverSpeedKey, QJsonValue::Double, false }, }; if (!JsonHelper::validateKeys(json, rootKeyInfoList, errorString)) { return false; @@ -403,11 +412,21 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec qCDebug(MissionControllerLog) << "MissionController::_loadJsonMissionFileV2 itemCount:" << json[_jsonItemsKey].toArray().count(); - // Planned home position + // Mission Settings QGeoCoordinate homeCoordinate; if (!JsonHelper::loadGeoCoordinate(json[_jsonPlannedHomePositionKey], true /* altitudeRequired */, homeCoordinate, errorString)) { return false; } + if (json.contains(_jsonVehicleTypeKey) && _activeVehicle->isOfflineEditingVehicle()) { + QGroundControlQmlGlobal::offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); + } + if (json.contains(_jsonCruiseSpeedKey)) { + QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); + } + if (json.contains(_jsonHoverSpeedKey)) { + QGroundControlQmlGlobal::offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); + } + SimpleMissionItem* homeItem = new SimpleMissionItem(_activeVehicle, this); homeItem->setCoordinate(homeCoordinate); visualItems->insert(0, homeItem); @@ -645,19 +664,8 @@ void MissionController::saveToFile(const QString& filename) missionFileObject[JsonHelper::jsonVersionKey] = _missionFileVersion; missionFileObject[JsonHelper::jsonGroundStationKey] = JsonHelper::jsonGroundStationValue; - MAV_AUTOPILOT firmwareType = MAV_AUTOPILOT_GENERIC; - if (_activeVehicle) { - firmwareType = _activeVehicle->firmwareType(); - } else { - // FIXME: Hack duplicated code from QGroundControlQmlGlobal. Had to do this for now since - // QGroundControlQmlGlobal is not available from C++ side. - - QSettings settings; - firmwareType = (MAV_AUTOPILOT)settings.value("OfflineEditingFirmwareType", MAV_AUTOPILOT_ARDUPILOTMEGA).toInt(); - } - missionFileObject[_jsonFirmwareTypeKey] = firmwareType; + // Mission settings - // Save planned home position SimpleMissionItem* homeItem = qobject_cast(_visualItems->get(0)); if (!homeItem) { qgcApp()->showMessage(QStringLiteral("Internal error: VisualMissionItem at index 0 not SimpleMissionItem")); @@ -666,6 +674,10 @@ void MissionController::saveToFile(const QString& filename) QJsonValue coordinateValue; JsonHelper::saveGeoCoordinate(homeItem->coordinate(), true /* writeAltitude */, coordinateValue); missionFileObject[_jsonPlannedHomePositionKey] = coordinateValue; + missionFileObject[_jsonFirmwareTypeKey] = _activeVehicle->firmwareType(); + missionFileObject[_jsonVehicleTypeKey] = _activeVehicle->vehicleType(); + missionFileObject[_jsonCruiseSpeedKey] = _activeVehicle->cruiseSpeed(); + missionFileObject[_jsonHoverSpeedKey] = _activeVehicle->hoverSpeed(); // Save the visual items QJsonArray rgMissionItems; @@ -730,7 +742,7 @@ void MissionController::_calcPrevWaypointValues(double homeAlt, VisualMissionIte } } -void MissionController::_calcHomeDist(VisualMissionItem* currentItem, VisualMissionItem* homeItem, double* distance) +double MissionController::_calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem) { QGeoCoordinate currentCoord = currentItem->coordinate(); QGeoCoordinate homeCoord = homeItem->exitCoordinate(); @@ -740,11 +752,7 @@ void MissionController::_calcHomeDist(VisualMissionItem* currentItem, VisualMiss qCDebug(MissionControllerLog) << "distanceOk" << distanceOk; - if (distanceOk) { - *distance = homeCoord.distanceTo(currentCoord); - } else { - *distance = 0.0; - } + return distanceOk ? homeCoord.distanceTo(currentCoord) : 0.0; } void MissionController::_recalcWaypointLines(void) @@ -771,7 +779,7 @@ void MissionController::_recalcWaypointLines(void) VisualMissionItem* item = qobject_cast(_visualItems->get(i)); - // If we still haven't found the first coordinate item and we hit a a takeoff command link back to home + // If we still haven't found the first coordinate item and we hit a takeoff command, link back to home if (firstCoordinateItem && item->isSimpleItem() && (qobject_cast(item)->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF || @@ -842,7 +850,7 @@ void MissionController::_recalcAltitudeRangeBearing() qWarning() << "Home item is not SimpleMissionItem"; } - bool showHomePosition = homeItem->showHomePosition(); + bool showHomePosition = homeItem->showHomePosition(); qCDebug(MissionControllerLog) << "_recalcAltitudeRangeBearing"; @@ -862,47 +870,68 @@ void MissionController::_recalcAltitudeRangeBearing() double missionDistance = 0.0; double missionMaxTelemetry = 0.0; + double missionTime = 0.0; + double vtolHoverTime = 0.0; + double vtolCruiseTime = 0.0; + double vtolHoverDistance = 0.0; + double vtolCruiseDistance = 0.0; + double currentCruiseSpeed = _activeVehicle->cruiseSpeed(); + double currentHoverSpeed = _activeVehicle->hoverSpeed(); - bool vtolCalc = (QGroundControlQmlGlobal::offlineEditingVehicleType()->enumStringValue() == "VTOL" || (_activeVehicle && _activeVehicle->vtol())) ? true : false ; - double cruiseDistance = 0.0; - double hoverDistance = 0.0; - bool hoverDistanceCalc = false; - bool hoverTransition = false; - bool cruiseTransition = false; - bool hoverDistanceReset = false; + bool vtolVehicle = _activeVehicle->vtol(); + bool vtolInHover = true; bool linkBackToHome = false; - for (int i=1; i<_visualItems->count(); i++) { VisualMissionItem* item = qobject_cast(_visualItems->get(i)); + SimpleMissionItem* simpleItem = qobject_cast(item); + ComplexMissionItem* complexItem = qobject_cast(item); + // Assume the worst item->setAzimuth(0.0); item->setDistance(0.0); - // If we still haven't found the first coordinate item and we hit a takeoff command link back to home - if (firstCoordinateItem && - item->isSimpleItem() && - qobject_cast(item)->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF) { - linkBackToHome = true; - hoverDistanceCalc = true; + if (simpleItem && simpleItem->command() == MavlinkQmlSingleton::MAV_CMD_DO_CHANGE_SPEED) { + // Adjust cruise speed for time calculations + double newSpeed = simpleItem->missionItem().param2(); + if (newSpeed > 0) { + if (_activeVehicle->multiRotor()) { + currentHoverSpeed = newSpeed; + } else { + currentCruiseSpeed = newSpeed; + } + } } - if (item->isSimpleItem() && vtolCalc) { - SimpleMissionItem* simpleItem = qobject_cast(item); - if (simpleItem->command() == MavlinkQmlSingleton::MAV_CMD_DO_VTOL_TRANSITION){ //transition waypoint value - if (simpleItem->missionItem().param1() == 3){ //hover mode value - hoverDistanceCalc = true; - hoverTransition = true; - } - else if (simpleItem->missionItem().param1() == 4){ - hoverDistanceCalc = false; - cruiseTransition = true; + // Link back to home if first item is takeoff and we have home position + if (firstCoordinateItem && simpleItem && simpleItem->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF) { + if (showHomePosition) { + linkBackToHome = true; + } + } + + // Update VTOL state + if (simpleItem && vtolVehicle) { + switch (simpleItem->command()) { + case MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF: + vtolInHover = false; + break; + case MavlinkQmlSingleton::MAV_CMD_NAV_LAND: + vtolInHover = false; + break; + case MavlinkQmlSingleton::MAV_CMD_DO_VTOL_TRANSITION: + { + int transitionState = simpleItem->missionItem().param1(); + if (transitionState == MAV_VTOL_STATE_TRANSITION_TO_MC) { + vtolInHover = true; + } else if (transitionState == MAV_VTOL_STATE_TRANSITION_TO_FW) { + vtolInHover = false; } } - if(!hoverTransition && cruiseTransition && !hoverDistanceReset && !linkBackToHome){ - hoverDistance = missionDistance; - hoverDistanceReset = true; + break; + default: + break; } } @@ -927,62 +956,60 @@ void MissionController::_recalcAltitudeRangeBearing() if (!item->isStandaloneCoordinate()) { firstCoordinateItem = false; - if (lastCoordinateItem != homeItem || (showHomePosition && linkBackToHome)) { - double azimuth, distance, altDifference, telemetryDistance; + if (lastCoordinateItem != homeItem || linkBackToHome) { + // This is a subsequent waypoint or we are forcing the first waypoint back to home + double azimuth, distance, altDifference; - // Subsequent coordinate items link to last coordinate item. If the last coordinate item - // is an invalid home position we skip the line _calcPrevWaypointValues(homePositionAltitude, item, lastCoordinateItem, &azimuth, &distance, &altDifference); item->setAltDifference(altDifference); item->setAzimuth(azimuth); item->setDistance(distance); missionDistance += distance; - - if (item->isSimpleItem()) { - _calcHomeDist(item, homeItem, &telemetryDistance); - - if (vtolCalc) { - SimpleMissionItem* simpleItem = qobject_cast(item); - if (simpleItem->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF || hoverDistanceCalc){ - hoverDistance += distance; - } - cruiseDistance = missionDistance - hoverDistance; - if(simpleItem->command() == MavlinkQmlSingleton::MAV_CMD_NAV_LAND && !linkBackToHome && !cruiseTransition && !hoverTransition){ - hoverDistance = cruiseDistance; - cruiseDistance = missionDistance - hoverDistance; - } + missionMaxTelemetry = qMax(missionMaxTelemetry, _calcDistanceToHome(item, homeItem)); + + // Calculate mission time + if (vtolVehicle) { + if (vtolInHover) { + double hoverTime = distance / _activeVehicle->hoverSpeed(); + missionTime += hoverTime; + vtolHoverTime += hoverTime; + vtolHoverDistance += distance; + } else { + double cruiseTime = distance / currentCruiseSpeed; + missionTime += cruiseTime; + vtolCruiseTime += cruiseTime; + vtolCruiseDistance += distance; } } else { - missionDistance += qobject_cast(item)->complexDistance(); - telemetryDistance = qobject_cast(item)->greatestDistanceTo(homeItem->exitCoordinate()); - - if (vtolCalc){ - cruiseDistance += qobject_cast(item)->complexDistance(); //assume all survey missions undertaken in cruise - } - } - - if (telemetryDistance > missionMaxTelemetry) { - missionMaxTelemetry = telemetryDistance; + missionTime += distance / (_activeVehicle->multiRotor() ? currentHoverSpeed : currentCruiseSpeed); } } - else if (lastCoordinateItem == homeItem && !item->isSimpleItem()){ - missionDistance += qobject_cast(item)->complexDistance(); - missionMaxTelemetry = qobject_cast(item)->greatestDistanceTo(homeItem->exitCoordinate()); - - if (vtolCalc){ - cruiseDistance += qobject_cast(item)->complexDistance(); //assume all survey missions undertaken in cruise - } + if (complexItem) { + // Add in distance/time inside survey as well + // This code assumes all surveys are done cruise not hover + double complexDistance = complexItem->complexDistance(); + double cruiseSpeed = _activeVehicle->multiRotor() ? currentHoverSpeed : currentCruiseSpeed; + missionDistance += complexDistance; + missionTime += complexDistance / cruiseSpeed; + missionMaxTelemetry = qMax(missionMaxTelemetry, complexItem->greatestDistanceTo(homeItem->exitCoordinate())); + + // Let the complex item know the current cruise speed + complexItem->setCruiseSpeed(cruiseSpeed); } - lastCoordinateItem = item; } + + lastCoordinateItem = item; } } - setMissionDistance(missionDistance); - setMissionMaxTelemetry(missionMaxTelemetry); - setCruiseDistance(cruiseDistance); - setHoverDistance(hoverDistance); + _setMissionMaxTelemetry(missionMaxTelemetry); + _setMissionDistance(missionDistance); + _setMissionTime(missionTime); + _setMissionHoverDistance(vtolHoverDistance); + _setMissionHoverTime(vtolHoverTime); + _setMissionCruiseDistance(vtolCruiseDistance); + _setMissionCruiseTime(vtolCruiseTime); // Walk the list again calculating altitude percentages double altRange = maxAltSeen - minAltSeen; @@ -1187,6 +1214,8 @@ void MissionController::_activeVehicleSet(void) connect(missionManager, &MissionManager::currentItemChanged, this, &MissionController::_currentMissionItemChanged); connect(_activeVehicle, &Vehicle::homePositionAvailableChanged, this, &MissionController::_activeVehicleHomePositionAvailableChanged); connect(_activeVehicle, &Vehicle::homePositionChanged, this, &MissionController::_activeVehicleHomePositionChanged); + connect(_activeVehicle, &Vehicle::cruiseSpeedChanged, this, &MissionController::_recalcAltitudeRangeBearing); + connect(_activeVehicle, &Vehicle::hoverSpeedChanged, this, &MissionController::_recalcAltitudeRangeBearing); if (_activeVehicle->parameterManager()->parametersReady() && !syncInProgress()) { // We are switching between two previously existing vehicles. We have to manually ask for the items from the Vehicle. @@ -1230,7 +1259,15 @@ void MissionController::_activeVehicleHomePositionChanged(const QGeoCoordinate& } } -void MissionController::setMissionDistance(double missionDistance) +void MissionController::_setMissionMaxTelemetry(double missionMaxTelemetry) +{ + if (!qFuzzyCompare(_missionMaxTelemetry, missionMaxTelemetry)) { + _missionMaxTelemetry = missionMaxTelemetry; + emit missionMaxTelemetryChanged(_missionMaxTelemetry); + } +} + +void MissionController::_setMissionDistance(double missionDistance) { if (!qFuzzyCompare(_missionDistance, missionDistance)) { _missionDistance = missionDistance; @@ -1238,27 +1275,43 @@ void MissionController::setMissionDistance(double missionDistance) } } -void MissionController::setMissionMaxTelemetry(double missionMaxTelemetry) +void MissionController::_setMissionTime(double missionTime) { - if (!qFuzzyCompare(_missionMaxTelemetry, missionMaxTelemetry)) { - _missionMaxTelemetry = missionMaxTelemetry; - emit missionMaxTelemetryChanged(_missionMaxTelemetry); + if (!qFuzzyCompare(_missionTime, missionTime)) { + _missionTime = missionTime; + emit missionTimeChanged(); + } +} + +void MissionController::_setMissionHoverTime(double missionHoverTime) +{ + if (!qFuzzyCompare(_missionHoverTime, missionHoverTime)) { + _missionHoverTime = missionHoverTime; + emit missionHoverTimeChanged(); } } -void MissionController::setCruiseDistance(double cruiseDistance) +void MissionController::_setMissionHoverDistance(double missionHoverDistance) { - if (!qFuzzyCompare(_cruiseDistance, cruiseDistance)) { - _cruiseDistance = cruiseDistance; - emit cruiseDistanceChanged(_cruiseDistance); + if (!qFuzzyCompare(_missionHoverDistance, missionHoverDistance)) { + _missionHoverDistance = missionHoverDistance; + emit missionHoverDistanceChanged(_missionHoverDistance); } } -void MissionController::setHoverDistance(double hoverDistance) +void MissionController::_setMissionCruiseTime(double missionCruiseTime) { - if (!qFuzzyCompare(_hoverDistance, hoverDistance)) { - _hoverDistance = hoverDistance; - emit hoverDistanceChanged(_hoverDistance); + if (!qFuzzyCompare(_missionCruiseTime, missionCruiseTime)) { + _missionCruiseTime = missionCruiseTime; + emit missionCruiseTimeChanged(); + } +} + +void MissionController::_setMissionCruiseDistance(double missionCruiseDistance) +{ + if (!qFuzzyCompare(_missionCruiseDistance, missionCruiseDistance)) { + _missionCruiseDistance = missionCruiseDistance; + emit missionCruiseDistanceChanged(_missionCruiseDistance); } } @@ -1437,3 +1490,21 @@ QString MissionController::fileExtension(void) const { return QGCApplication::missionFileExtension; } + +double MissionController::cruiseSpeed(void) const +{ + if (_activeVehicle) { + return _activeVehicle->cruiseSpeed(); + } else { + return 0.0f; + } +} + +double MissionController::hoverSpeed(void) const +{ + if (_activeVehicle) { + return _activeVehicle->hoverSpeed(); + } else { + return 0.0f; + } +} diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index c03becf26..bac54c891 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -34,15 +34,18 @@ public: MissionController(QObject* parent = NULL); ~MissionController(); - Q_PROPERTY(QGeoCoordinate plannedHomePosition READ plannedHomePosition NOTIFY plannedHomePositionChanged) - Q_PROPERTY(QmlObjectListModel* visualItems READ visualItems NOTIFY visualItemsChanged) - Q_PROPERTY(QmlObjectListModel* complexVisualItems READ complexVisualItems NOTIFY complexVisualItemsChanged) - Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) - - Q_PROPERTY(double missionDistance READ missionDistance NOTIFY missionDistanceChanged) - Q_PROPERTY(double missionMaxTelemetry READ missionMaxTelemetry NOTIFY missionMaxTelemetryChanged) - Q_PROPERTY(double cruiseDistance READ cruiseDistance NOTIFY cruiseDistanceChanged) - Q_PROPERTY(double hoverDistance READ hoverDistance NOTIFY hoverDistanceChanged) + Q_PROPERTY(QGeoCoordinate plannedHomePosition READ plannedHomePosition NOTIFY plannedHomePositionChanged) + Q_PROPERTY(QmlObjectListModel* visualItems READ visualItems NOTIFY visualItemsChanged) + Q_PROPERTY(QmlObjectListModel* complexVisualItems READ complexVisualItems NOTIFY complexVisualItemsChanged) + Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) + + Q_PROPERTY(double missionDistance READ missionDistance NOTIFY missionDistanceChanged) + Q_PROPERTY(double missionTime READ missionTime NOTIFY missionTimeChanged) + Q_PROPERTY(double missionHoverDistance READ missionHoverDistance NOTIFY missionHoverDistanceChanged) + Q_PROPERTY(double missionCruiseDistance READ missionCruiseDistance NOTIFY missionCruiseDistanceChanged) + Q_PROPERTY(double missionHoverTime READ missionHoverTime NOTIFY missionHoverTimeChanged) + Q_PROPERTY(double missionCruiseTime READ missionCruiseTime NOTIFY missionCruiseTimeChanged) + Q_PROPERTY(double missionMaxTelemetry READ missionMaxTelemetry NOTIFY missionMaxTelemetryChanged) Q_INVOKABLE void removeMissionItem(int index); @@ -80,15 +83,14 @@ public: QmlObjectListModel* waypointLines (void) { return &_waypointLines; } double missionDistance (void) const { return _missionDistance; } + double missionTime (void) const { return _missionTime; } + double missionHoverDistance (void) const { return _missionHoverDistance; } + double missionHoverTime (void) const { return _missionHoverTime; } + double missionCruiseDistance (void) const { return _missionCruiseDistance; } + double missionCruiseTime (void) const { return _missionCruiseTime; } double missionMaxTelemetry (void) const { return _missionMaxTelemetry; } - double cruiseDistance (void) const { return _cruiseDistance; } - double hoverDistance (void) const { return _hoverDistance; } - - void setMissionDistance (double missionDistance ); - void setMissionMaxTelemetry (double missionMaxTelemetry); - void setCruiseDistance (double cruiseDistance ); - void setHoverDistance (double hoverDistance ); - + double cruiseSpeed (void) const; + double hoverSpeed (void) const; signals: void plannedHomePositionChanged(QGeoCoordinate plannedHomePosition); @@ -97,9 +99,16 @@ signals: void waypointLinesChanged(void); void newItemsFromVehicle(void); void missionDistanceChanged(double missionDistance); + void missionTimeChanged(void); + void missionHoverDistanceChanged(double missionHoverDistance); + void missionHoverTimeChanged(void); + void missionCruiseDistanceChanged(double missionCruiseDistance); + void missionCruiseTimeChanged(void); void missionMaxTelemetryChanged(double missionMaxTelemetry); void cruiseDistanceChanged(double cruiseDistance); void hoverDistanceChanged(double hoverDistance); + void cruiseSpeedChanged(double cruiseSpeed); + void hoverSpeedChanged(double hoverSpeed); private slots: void _newMissionItemsAvailableFromVehicle(); @@ -123,7 +132,7 @@ private: void _deinitVisualItem(VisualMissionItem* item); void _setupActiveVehicle(Vehicle* activeVehicle, bool forceLoadFromVehicle); static void _calcPrevWaypointValues(double homeAlt, VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference); - static void _calcHomeDist(VisualMissionItem* currentItem, VisualMissionItem* homeItem, double* distance); + static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem); bool _findLastAltitude(double* lastAltitude, MAV_FRAME* frame); bool _findLastAcceptanceRadius(double* lastAcceptanceRadius); void _addPlannedHomePosition(QmlObjectListModel* visualItems, bool addToCenter); @@ -134,6 +143,13 @@ private: bool _loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); bool _loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); int _nextSequenceNumber(void); + void _setMissionDistance(double missionDistance); + void _setMissionTime(double missionTime); + void _setMissionHoverDistance(double missionHoverDistance); + void _setMissionHoverTime(double missionHoverTime); + void _setMissionCruiseDistance(double missionCruiseDistance); + void _setMissionCruiseTime(double missionCruiseTime); + void _setMissionMaxTelemetry(double missionMaxTelemetry); // Overrides from PlanElementController void _activeVehicleBeingRemoved(void) final; @@ -148,13 +164,19 @@ private: bool _missionItemsRequested; bool _queuedSend; double _missionDistance; + double _missionTime; + double _missionHoverDistance; + double _missionHoverTime; + double _missionCruiseDistance; + double _missionCruiseTime; double _missionMaxTelemetry; - double _cruiseDistance; - double _hoverDistance; static const char* _settingsGroup; static const char* _jsonFileTypeValue; static const char* _jsonFirmwareTypeKey; + static const char* _jsonVehicleTypeKey; + static const char* _jsonCruiseSpeedKey; + static const char* _jsonHoverSpeedKey; static const char* _jsonItemsKey; static const char* _jsonPlannedHomePositionKey; static const char* _jsonParamsKey; diff --git a/src/MissionManager/PlanElementController.cc b/src/MissionManager/PlanElementController.cc index 1c4d7eba0..a904796db 100644 --- a/src/MissionManager/PlanElementController.cc +++ b/src/MissionManager/PlanElementController.cc @@ -54,4 +54,6 @@ void PlanElementController::_activeVehicleChanged(Vehicle* activeVehicle) // Whenever vehicle changes we need to update syncInProgress emit syncInProgressChanged(syncInProgress()); + + emit vehicleChanged(_activeVehicle); } diff --git a/src/MissionManager/PlanElementController.h b/src/MissionManager/PlanElementController.h index b8ff725d6..a498d07d6 100644 --- a/src/MissionManager/PlanElementController.h +++ b/src/MissionManager/PlanElementController.h @@ -35,6 +35,8 @@ public: Q_PROPERTY(QString fileExtension READ fileExtension CONSTANT) virtual QString fileExtension(void) const = 0; + Q_PROPERTY(Vehicle* vehicle READ vehicle NOTIFY vehicleChanged) + /// Should be called immediately upon Component.onCompleted. /// @param editMode true: controller being used in Plan view, false: controller being used in Fly view Q_INVOKABLE virtual void start(bool editMode); @@ -55,9 +57,12 @@ public: virtual bool dirty (void) const = 0; virtual void setDirty (bool dirty) = 0; + Vehicle* vehicle(void) { return _activeVehicle; } + signals: void syncInProgressChanged (bool syncInProgress); void dirtyChanged (bool dirty); + void vehicleChanged (Vehicle* vehicle); protected: MultiVehicleManager* _multiVehicleMgr; diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index 0c0a4ff18..38ea5f11b 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -12,6 +12,7 @@ #include "JsonHelper.h" #include "MissionController.h" #include "QGCGeo.h" +#include "QGroundControlQmlGlobal.h" #include @@ -70,6 +71,7 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) , _surveyDistance(0.0) , _cameraShots(0) , _coveredArea(0.0) + , _timeBetweenShots(0.0) , _gridAltitudeFact (0, _gridAltitudeFactName, FactMetaData::valueTypeDouble) , _gridAngleFact (0, _gridAngleFactName, FactMetaData::valueTypeDouble) , _gridSpacingFact (0, _gridSpacingFactName, FactMetaData::valueTypeDouble) @@ -132,7 +134,11 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) connect(&_cameraResolutionHeightFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); connect(&_cameraFocalLengthFact, &Fact::valueChanged, this, &SurveyMissionItem::_cameraValueChanged); - connect(this, &SurveyMissionItem::cameraTriggerChanged, this, &SurveyMissionItem::_cameraTriggerChanged); + connect(this, &SurveyMissionItem::cameraTriggerChanged, this, &SurveyMissionItem::_cameraTriggerChanged); + + connect(&_cameraTriggerDistanceFact, &Fact::valueChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + connect(_vehicle, &Vehicle::cruiseSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + connect(_vehicle, &Vehicle::hoverSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); } void SurveyMissionItem::_setSurveyDistance(double surveyDistance) @@ -780,6 +786,7 @@ QmlObjectListModel* SurveyMissionItem::getMissionItems(void) const pMissionItems->append(item); if (_cameraTrigger && i == 0) { + // Turn on camera MissionItem* item = new MissionItem(seqNum++, // sequence number MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD MAV_FRAME_MISSION, // MAV_FRAME @@ -793,6 +800,7 @@ QmlObjectListModel* SurveyMissionItem::getMissionItems(void) const } if (_cameraTrigger) { + // Turn off camera MissionItem* item = new MissionItem(seqNum++, // sequence number MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD MAV_FRAME_MISSION, // MAV_FRAME @@ -810,10 +818,9 @@ QmlObjectListModel* SurveyMissionItem::getMissionItems(void) const void SurveyMissionItem::_cameraTriggerChanged(void) { setDirty(true); - if (_gridPoints.count()) { - // If we have grid turn on/off camera trigger will add/remove two camera trigger mission items - emit lastSequenceNumberChanged(lastSequenceNumber()); - } + // Camera trigger adds items + emit lastSequenceNumberChanged(lastSequenceNumber()); + // We now have camera shot count emit cameraShotsChanged(cameraShots()); } @@ -826,3 +833,16 @@ void SurveyMissionItem::_cameraValueChanged(void) { emit cameraValueChanged(); } + +double SurveyMissionItem::timeBetweenShots(void) const +{ + return _cruiseSpeed == 0 ? 0 : _cameraTriggerDistanceFact.rawValue().toDouble() / _cruiseSpeed; +} + +void SurveyMissionItem::setCruiseSpeed(double cruiseSpeed) +{ + if (!qFuzzyCompare(_cruiseSpeed, cruiseSpeed)) { + _cruiseSpeed = cruiseSpeed; + emit timeBetweenShotsChanged(); + } +} diff --git a/src/MissionManager/SurveyMissionItem.h b/src/MissionManager/SurveyMissionItem.h index aeb13a15b..d00974e77 100644 --- a/src/MissionManager/SurveyMissionItem.h +++ b/src/MissionManager/SurveyMissionItem.h @@ -48,6 +48,7 @@ public: Q_PROPERTY(bool cameraOrientationLandscape MEMBER _cameraOrientationLandscape NOTIFY cameraOrientationLandscapeChanged) Q_PROPERTY(bool manualGrid MEMBER _manualGrid NOTIFY manualGridChanged) Q_PROPERTY(QString camera MEMBER _camera NOTIFY cameraChanged) + Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) Q_INVOKABLE void clearPolygon(void); Q_INVOKABLE void addPolygonCoordinate(const QGeoCoordinate coordinate); @@ -72,6 +73,7 @@ public: int cameraShots(void) const; double coveredArea(void) const { return _coveredArea; } + double timeBetweenShots(void) const; // Overrides from ComplexMissionItem @@ -80,6 +82,8 @@ public: QmlObjectListModel* getMissionItems (void) const final; bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; double greatestDistanceTo (const QGeoCoordinate &other) const final; + void setCruiseSpeed (double cruiseSpeed) final; + // Overrides from VisualMissionItem @@ -121,6 +125,7 @@ signals: void cameraOrientationLandscapeChanged (bool cameraOrientationLandscape); void cameraChanged (QString camera); void manualGridChanged (bool manualGrid); + void timeBetweenShotsChanged (void); private slots: void _cameraTriggerChanged(void); @@ -158,6 +163,8 @@ private: double _surveyDistance; int _cameraShots; double _coveredArea; + double _timeBetweenShots; + double _cruiseSpeed; Fact _gridAltitudeFact; Fact _gridAngleFact; @@ -198,7 +205,6 @@ private: static const char* _jsonCameraOrientationLandscapeKey; static const char* _jsonFixedValueIsAltitudeKey; - static const char* _gridAltitudeFactName; static const char* _gridAngleFactName; static const char* _gridSpacingFactName; diff --git a/src/QmlControls/QGroundControlQmlGlobal.json b/src/QmlControls/QGroundControlQmlGlobal.json index f32ee0b63..f48c1b2a1 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.json +++ b/src/QmlControls/QGroundControlQmlGlobal.json @@ -3,7 +3,7 @@ "name": "OfflineEditingFirmwareType", "shortDescription": "Offline editing firmware type", "type": "uint32", - "enumStrings": "ArduPilot Firmware,PX4 Pro Firmware,Mavlink Generic Firmware", + "enumStrings": "ArduPilot,PX4 Pro,Mavlink Generic", "enumValues": "3,12,0", "defaultValue": 3 }, @@ -11,7 +11,7 @@ "name": "OfflineEditingVehicleType", "shortDescription": "Offline editing vehicle type", "type": "uint32", - "enumStrings": "Fixedwing,Multicopter,VTOL,Rover,Sub", + "enumStrings": "Fixed Wing,Multi-Rotor,VTOL,Rover,Sub", "enumValues": "1,2,19,10,12", "defaultValue": 1 }, diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 9b3bc66be..1ce413258 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -96,6 +96,8 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) + , _cruiseSpeed(QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(QGroundControlQmlGlobal::offlineEditingHoverSpeed()->rawValue().toDouble()) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -150,7 +152,7 @@ Vehicle::Vehicle(LinkInterface* link, connect(_uas, &UAS::imageReady, this, &Vehicle::_imageReady); connect(this, &Vehicle::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged); - _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); + _commonInit(); _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this); // connect this vehicle to the follow me handle manager @@ -183,21 +185,6 @@ Vehicle::Vehicle(LinkInterface* link, _loadSettings(); - _missionManager = new MissionManager(this); - connect(_missionManager, &MissionManager::error, this, &Vehicle::_missionManagerError); - connect(_missionManager, &MissionManager::newMissionItemsAvailable, this, &Vehicle::_newMissionItemsAvailable); - - _parameterManager = new ParameterManager(this); - connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady); - - // GeoFenceManager needs to access ParameterManager so make sure to create after - _geoFenceManager = _firmwarePlugin->newGeoFenceManager(this); - connect(_geoFenceManager, &GeoFenceManager::error, this, &Vehicle::_geoFenceManagerError); - connect(_geoFenceManager, &GeoFenceManager::loadComplete, this, &Vehicle::_newGeoFenceAvailable); - - _rallyPointManager = _firmwarePlugin->newRallyPointManager(this); - connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); - // Ask the vehicle for firmware version info. sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES, @@ -214,27 +201,6 @@ Vehicle::Vehicle(LinkInterface* link, // Invalidate the timer to signal first announce _lowBatteryAnnounceTimer.invalidate(); - - // Build FactGroup object model - - _addFact(&_rollFact, _rollFactName); - _addFact(&_pitchFact, _pitchFactName); - _addFact(&_headingFact, _headingFactName); - _addFact(&_groundSpeedFact, _groundSpeedFactName); - _addFact(&_airSpeedFact, _airSpeedFactName); - _addFact(&_climbRateFact, _climbRateFactName); - _addFact(&_altitudeRelativeFact, _altitudeRelativeFactName); - _addFact(&_altitudeAMSLFact, _altitudeAMSLFactName); - - _addFactGroup(&_gpsFactGroup, _gpsFactGroupName); - _addFactGroup(&_batteryFactGroup, _batteryFactGroupName); - _addFactGroup(&_windFactGroup, _windFactGroupName); - _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName); - - _gpsFactGroup.setVehicle(this); - _batteryFactGroup.setVehicle(this); - _windFactGroup.setVehicle(this); - _vibrationFactGroup.setVehicle(this); } // Disconnected Vehicle for offline editing @@ -275,6 +241,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) + , _cruiseSpeed(QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(QGroundControlQmlGlobal::offlineEditingHoverSpeed()->rawValue().toDouble()) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -314,8 +282,13 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _windFactGroup(this) , _vibrationFactGroup(this) { - _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); + _commonInit(); _firmwarePlugin->initializeVehicle(this); +} + +void Vehicle::_commonInit(void) +{ + _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); _missionManager = new MissionManager(this); connect(_missionManager, &MissionManager::error, this, &Vehicle::_missionManagerError); @@ -329,6 +302,12 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, _rallyPointManager = _firmwarePlugin->newRallyPointManager(this); connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); + // Offline editing vehicle tracks settings changes for offline editing settings + connect(QGroundControlQmlGlobal::offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); + connect(QGroundControlQmlGlobal::offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); + connect(QGroundControlQmlGlobal::offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); + connect(QGroundControlQmlGlobal::offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); + // Build FactGroup object model _addFact(&_rollFact, _rollFactName); @@ -344,11 +323,6 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, _addFactGroup(&_batteryFactGroup, _batteryFactGroupName); _addFactGroup(&_windFactGroup, _windFactGroupName); _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName); - - _gpsFactGroup.setVehicle(NULL); - _batteryFactGroup.setVehicle(NULL); - _windFactGroup.setVehicle(NULL); - _vibrationFactGroup.setVehicle(NULL); } Vehicle::~Vehicle() @@ -366,8 +340,59 @@ Vehicle::~Vehicle() } -void -Vehicle::resetCounters() +void Vehicle::_offlineFirmwareTypeSettingChanged(QVariant value) +{ + _firmwareType = static_cast(value.toInt()); + emit firmwareTypeChanged(); +} + +void Vehicle::_offlineVehicleTypeSettingChanged(QVariant value) +{ + _vehicleType = static_cast(value.toInt()); + emit vehicleTypeChanged(); +} + +void Vehicle::_offlineCruiseSpeedSettingChanged(QVariant value) +{ + _cruiseSpeed = value.toDouble(); + emit cruiseSpeedChanged(_cruiseSpeed); +} + +void Vehicle::_offlineHoverSpeedSettingChanged(QVariant value) +{ + _hoverSpeed = value.toDouble(); + emit hoverSpeedChanged(_hoverSpeed); +} + +QString Vehicle::firmwareTypeString(void) const +{ + if (px4Firmware()) { + return QStringLiteral("PX4 Pro"); + } else if (apmFirmware()) { + return QStringLiteral("ArduPilot"); + } else { + return tr("MAVLink Generic"); + } +} + +QString Vehicle::vehicleTypeString(void) const +{ + if (fixedWing()) { + return tr("Fixed Wing"); + } else if (multiRotor()) { + return tr("Multi-Rotor"); + } else if (vtol()) { + return tr("VTOL"); + } else if (rover()) { + return tr("Rover"); + } else if (sub()) { + return tr("Sub"); + } else { + return tr("Unknown"); + } +} + +void Vehicle::resetCounters() { _messagesReceived = 0; _messagesSent = 0; @@ -2084,7 +2109,6 @@ const char* VehicleGPSFactGroup::_lockFactName = "lock"; VehicleGPSFactGroup::VehicleGPSFactGroup(QObject* parent) : FactGroup(1000, ":/json/Vehicle/GPSFact.json", parent) - , _vehicle(NULL) , _hdopFact (0, _hdopFactName, FactMetaData::valueTypeDouble) , _vdopFact (0, _vdopFactName, FactMetaData::valueTypeDouble) , _courseOverGroundFact (0, _courseOverGroundFactName, FactMetaData::valueTypeDouble) @@ -2169,11 +2193,6 @@ QString Vehicle::takeControlFlightMode(void) const //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void VehicleGPSFactGroup::setVehicle(Vehicle* vehicle) -{ - _vehicle = vehicle; -} - const char* VehicleBatteryFactGroup::_voltageFactName = "voltage"; const char* VehicleBatteryFactGroup::_percentRemainingFactName = "percentRemaining"; const char* VehicleBatteryFactGroup::_mahConsumedFactName = "mahConsumed"; @@ -2192,7 +2211,6 @@ const int VehicleBatteryFactGroup::_cellCountUnavailable = -1.0; VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) : FactGroup(1000, ":/json/Vehicle/BatteryFact.json", parent) - , _vehicle(NULL) , _voltageFact (0, _voltageFactName, FactMetaData::valueTypeDouble) , _percentRemainingFact (0, _percentRemainingFactName, FactMetaData::valueTypeInt32) , _mahConsumedFact (0, _mahConsumedFactName, FactMetaData::valueTypeInt32) @@ -2216,18 +2234,12 @@ VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) _cellCountFact.setRawValue (_cellCountUnavailable); } -void VehicleBatteryFactGroup::setVehicle(Vehicle* vehicle) -{ - _vehicle = vehicle; -} - const char* VehicleWindFactGroup::_directionFactName = "direction"; const char* VehicleWindFactGroup::_speedFactName = "speed"; const char* VehicleWindFactGroup::_verticalSpeedFactName = "verticalSpeed"; VehicleWindFactGroup::VehicleWindFactGroup(QObject* parent) : FactGroup(1000, ":/json/Vehicle/WindFact.json", parent) - , _vehicle(NULL) , _directionFact (0, _directionFactName, FactMetaData::valueTypeDouble) , _speedFact (0, _speedFactName, FactMetaData::valueTypeDouble) , _verticalSpeedFact(0, _verticalSpeedFactName, FactMetaData::valueTypeDouble) @@ -2242,11 +2254,6 @@ VehicleWindFactGroup::VehicleWindFactGroup(QObject* parent) _verticalSpeedFact.setRawValue (std::numeric_limits::quiet_NaN()); } -void VehicleWindFactGroup::setVehicle(Vehicle* vehicle) -{ - _vehicle = vehicle; -} - const char* VehicleVibrationFactGroup::_xAxisFactName = "xAxis"; const char* VehicleVibrationFactGroup::_yAxisFactName = "yAxis"; const char* VehicleVibrationFactGroup::_zAxisFactName = "zAxis"; @@ -2256,7 +2263,6 @@ const char* VehicleVibrationFactGroup::_clipCount3FactName = "clipCount3"; VehicleVibrationFactGroup::VehicleVibrationFactGroup(QObject* parent) : FactGroup(1000, ":/json/Vehicle/VibrationFact.json", parent) - , _vehicle(NULL) , _xAxisFact (0, _xAxisFactName, FactMetaData::valueTypeDouble) , _yAxisFact (0, _yAxisFactName, FactMetaData::valueTypeDouble) , _zAxisFact (0, _zAxisFactName, FactMetaData::valueTypeDouble) @@ -2276,8 +2282,3 @@ VehicleVibrationFactGroup::VehicleVibrationFactGroup(QObject* parent) _yAxisFact.setRawValue(std::numeric_limits::quiet_NaN()); _zAxisFact.setRawValue(std::numeric_limits::quiet_NaN()); } - -void VehicleVibrationFactGroup::setVehicle(Vehicle* vehicle) -{ - _vehicle = vehicle; -} diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index ff299e348..c9d2c0114 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -63,8 +63,6 @@ public: Fact* clipCount2 (void) { return &_clipCount2Fact; } Fact* clipCount3 (void) { return &_clipCount3Fact; } - void setVehicle(Vehicle* vehicle); - static const char* _xAxisFactName; static const char* _yAxisFactName; static const char* _zAxisFactName; @@ -73,7 +71,6 @@ public: static const char* _clipCount3FactName; private: - Vehicle* _vehicle; Fact _xAxisFact; Fact _yAxisFact; Fact _zAxisFact; @@ -97,14 +94,11 @@ public: Fact* speed (void) { return &_speedFact; } Fact* verticalSpeed (void) { return &_verticalSpeedFact; } - void setVehicle(Vehicle* vehicle); - static const char* _directionFactName; static const char* _speedFactName; static const char* _verticalSpeedFactName; private: - Vehicle* _vehicle; Fact _directionFact; Fact _speedFact; Fact _verticalSpeedFact; @@ -129,8 +123,6 @@ public: Fact* count (void) { return &_countFact; } Fact* lock (void) { return &_lockFact; } - void setVehicle(Vehicle* vehicle); - static const char* _hdopFactName; static const char* _vdopFactName; static const char* _courseOverGroundFactName; @@ -138,7 +130,6 @@ public: static const char* _lockFactName; private: - Vehicle* _vehicle; Fact _hdopFact; Fact _vdopFact; Fact _courseOverGroundFact; @@ -168,8 +159,6 @@ public: Fact* cellCount (void) { return &_cellCountFact; } - void setVehicle(Vehicle* vehicle); - static const char* _voltageFactName; static const char* _percentRemainingFactName; static const char* _mahConsumedFactName; @@ -187,7 +176,6 @@ public: static const int _cellCountUnavailable; private: - Vehicle* _vehicle; Fact _voltageFact; Fact _percentRemainingFact; Fact _mahConsumedFact; @@ -245,8 +233,8 @@ public: Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(int flowImageIndex READ flowImageIndex NOTIFY flowImageIndexChanged) Q_PROPERTY(int rcRSSI READ rcRSSI NOTIFY rcRSSIChanged) - Q_PROPERTY(bool px4Firmware READ px4Firmware CONSTANT) - Q_PROPERTY(bool apmFirmware READ apmFirmware CONSTANT) + Q_PROPERTY(bool px4Firmware READ px4Firmware NOTIFY firmwareTypeChanged) + Q_PROPERTY(bool apmFirmware READ apmFirmware NOTIFY firmwareTypeChanged) Q_PROPERTY(bool soloFirmware READ soloFirmware WRITE setSoloFirmware NOTIFY soloFirmwareChanged) Q_PROPERTY(bool genericFirmware READ genericFirmware CONSTANT) Q_PROPERTY(bool connectionLost READ connectionLost NOTIFY connectionLostChanged) @@ -254,26 +242,28 @@ public: Q_PROPERTY(uint messagesReceived READ messagesReceived NOTIFY messagesReceivedChanged) Q_PROPERTY(uint messagesSent READ messagesSent NOTIFY messagesSentChanged) Q_PROPERTY(uint messagesLost READ messagesLost NOTIFY messagesLostChanged) - Q_PROPERTY(bool fixedWing READ fixedWing CONSTANT) - Q_PROPERTY(bool multiRotor READ multiRotor CONSTANT) - Q_PROPERTY(bool vtol READ vtol CONSTANT) - Q_PROPERTY(bool rover READ rover CONSTANT) + Q_PROPERTY(bool fixedWing READ fixedWing NOTIFY vehicleTypeChanged) + Q_PROPERTY(bool multiRotor READ multiRotor NOTIFY vehicleTypeChanged) + Q_PROPERTY(bool vtol READ vtol NOTIFY vehicleTypeChanged) + Q_PROPERTY(bool rover READ rover NOTIFY vehicleTypeChanged) + Q_PROPERTY(bool sub READ sub NOTIFY vehicleTypeChanged) Q_PROPERTY(bool supportsManualControl READ supportsManualControl CONSTANT) Q_PROPERTY(bool supportsThrottleModeCenterZero READ supportsThrottleModeCenterZero CONSTANT) Q_PROPERTY(bool supportsJSButton READ supportsJSButton CONSTANT) Q_PROPERTY(bool supportsRadio READ supportsRadio CONSTANT) - Q_PROPERTY(bool sub READ sub CONSTANT) Q_PROPERTY(bool autoDisconnect MEMBER _autoDisconnect NOTIFY autoDisconnectChanged) Q_PROPERTY(QString prearmError READ prearmError WRITE setPrearmError NOTIFY prearmErrorChanged) Q_PROPERTY(int motorCount READ motorCount CONSTANT) Q_PROPERTY(bool coaxialMotors READ coaxialMotors CONSTANT) Q_PROPERTY(bool xConfigMotors READ xConfigMotors CONSTANT) Q_PROPERTY(bool isOfflineEditingVehicle READ isOfflineEditingVehicle CONSTANT) - Q_PROPERTY(QString brandImage READ brandImage CONSTANT) + Q_PROPERTY(QString brandImage READ brandImage NOTIFY firmwareTypeChanged) Q_PROPERTY(QStringList unhealthySensors READ unhealthySensors NOTIFY unhealthySensorsChanged) Q_PROPERTY(QString missionFlightMode READ missionFlightMode CONSTANT) Q_PROPERTY(QString rtlFlightMode READ rtlFlightMode CONSTANT) Q_PROPERTY(QString takeControlFlightMode READ takeControlFlightMode CONSTANT) + Q_PROPERTY(QString firmwareTypeString READ firmwareTypeString NOTIFY firmwareTypeChanged) + Q_PROPERTY(QString vehicleTypeString READ vehicleTypeString NOTIFY vehicleTypeChanged) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -528,6 +518,10 @@ public: QString missionFlightMode () const; QString rtlFlightMode () const; QString takeControlFlightMode () const; + double cruiseSpeed () const { return _cruiseSpeed; } + double hoverSpeed () const { return _hoverSpeed; } + QString firmwareTypeString () const; + QString vehicleTypeString () const; Fact* roll (void) { return &_rollFact; } Fact* heading (void) { return &_headingFact; } @@ -615,6 +609,10 @@ signals: void prearmErrorChanged(const QString& prearmError); void soloFirmwareChanged(bool soloFirmware); void unhealthySensorsChanged(void); + void cruiseSpeedChanged(double cruiseSpeed); + void hoverSpeedChanged(double hoverSpeed); + void firmwareTypeChanged(void); + void vehicleTypeChanged(void); void messagesReceivedChanged (); void messagesSentChanged (); @@ -673,6 +671,10 @@ private slots: void _remoteControlRSSIChanged(uint8_t rssi); void _handleFlightModeChanged(const QString& flightMode); void _announceArmedChanged(bool armed); + void _offlineFirmwareTypeSettingChanged(QVariant value); + void _offlineVehicleTypeSettingChanged(QVariant value); + void _offlineCruiseSpeedSettingChanged(QVariant value); + void _offlineHoverSpeedSettingChanged(QVariant value); void _handleTextMessage (int newCount); void _handletextMessageReceived (UASMessage* message); @@ -724,8 +726,8 @@ private: void _ackMavlinkLogData(uint16_t sequence); void _sendNextQueuedMavCommand(void); void _updatePriorityLink(void); + void _commonInit(void); -private: int _id; ///< Mavlink system id bool _active; bool _offlineEditingVehicle; ///< This Vehicle is a "disconnected" vehicle for ui use while offline editing @@ -771,6 +773,8 @@ private: uint32_t _onboardControlSensorsUnhealthy; bool _gpsRawIntMessageAvailable; bool _globalPositionIntMessageAvailable; + double _cruiseSpeed; + double _hoverSpeed; typedef struct { int component; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index d1975d189..d22c60515 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -13,6 +13,7 @@ import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QtQuick.Dialogs 1.1 import QtMultimedia 5.5 +import QtQuick.Layouts 1.2 import QGroundControl 1.0 import QGroundControl.FactSystem 1.0 @@ -33,6 +34,10 @@ QGCView { property Fact _percentRemainingAnnounce: QGroundControl.batteryPercentRemainingAnnounce property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 + property bool _showCruiseSpeed: offlineVehicleCombo.currentText != "Multicopter" + property bool _showHoverSpeed: offlineVehicleCombo.currentText != "FixedWing" + + readonly property string _requiresRestart: qsTr("(Requires Restart)") QGCPalette { id: qgcPal } @@ -117,90 +122,6 @@ QGCView { } } //----------------------------------------------------------------- - //-- Offline mission editing - Item { - width: qgcView.width * 0.8 - height: offlineLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - QGCLabel { - id: offlineLabel - text: qsTr("Offline Mission Editing (Requires Restart)") - font.family: ScreenTools.demiboldFontFamily - } - } - Rectangle { - height: offlineCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - Column { - id: offlineCol - spacing: ScreenTools.defaultFontPixelWidth - anchors.centerIn: parent - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - text: qsTr("Firmware:") - width: _labelWidth - anchors.baseline: offlineTypeCombo.baseline - } - FactComboBox { - id: offlineTypeCombo - width: _editFieldWidth - fact: QGroundControl.offlineEditingFirmwareType - indexModel: false - } - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - text: qsTr("Vehicle:") - width: _labelWidth - anchors.baseline: offlineVehicleCombo.baseline - } - FactComboBox { - id: offlineVehicleCombo - width: _editFieldWidth - fact: QGroundControl.offlineEditingVehicleType - indexModel: false - } - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - visible: offlineVehicleCombo.currentText != "Multicopter" - QGCLabel { - text: qsTr("Cruise speed:") - width: _labelWidth - anchors.baseline: cruiseSpeedField.baseline - } - FactTextField { - id: cruiseSpeedField - width: _editFieldWidth - fact: QGroundControl.offlineEditingCruiseSpeed - enabled: true - } - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - visible: offlineVehicleCombo.currentText != "Fixedwing" - QGCLabel { - id: hoverSpeedLabel - text: qsTr("Hover speed:") - width: _labelWidth - anchors.baseline: hoverSpeedField.baseline - } - FactTextField { - id: hoverSpeedField - width: _editFieldWidth - fact: QGroundControl.offlineEditingHoverSpeed - enabled: true - } - } - } - } - //----------------------------------------------------------------- //-- Miscelanous Item { width: qgcView.width * 0.8 @@ -274,7 +195,7 @@ QGCView { } QGCLabel { anchors.verticalCenter: parent.verticalCenter - text: qsTr("(Requires Restart)") + text: _requiresRestart } } //----------------------------------------------------------------- -- GitLab From dce91913d34b70b3074944500f386620245e106e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 10 Jan 2017 16:04:28 -0800 Subject: [PATCH 176/398] Update README.md XCode 8 workaround update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fb9e497c..13a5c0693 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ https://donlakeflyer.gitbooks.io/qgroundcontrol-user-guide/content/ #### Native Builds QGroundControl builds are supported for OSX, Linux, Windows, iOS and Android. QGroundControl uses [Qt](http://www.qt.io) as its cross-platform support library and uses [QtCreator](http://doc.qt.io/qtcreator/index.html) as its default build environment. -* OSX: OSX 10.7 or higher, 64 bit, clang compiler (IMPORTANT: XCode 8 not supported due to Qt bug. Currently only workaround is to use XCode 7.3.1) +* OSX: OSX 10.7 or higher, 64 bit, clang compiler (IMPORTANT: XCode 8 requires a workaround described below) * Ubuntu: 64 bit, gcc compiler * Windows: Vista or higher, 32 bit, [Visual Studio 2013 compiler](http://www.visualstudio.com/downloads/download-visual-studio-vs#d-express-windows-desktop) * iOS: 8.0 and higher -- GitLab From 56cb6116c6ebd7d03ada967ba9cf651899b24100 Mon Sep 17 00:00:00 2001 From: Sander Smeets Date: Wed, 11 Jan 2017 16:08:18 +0100 Subject: [PATCH 177/398] UDP join multicast group for windows client --- src/comm/UDPLink.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index 4828c6f9b..a286a078a 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -245,6 +245,7 @@ bool UDPLink::_hardwareConnect() _socket->setProxy(QNetworkProxy::NoProxy); _connectState = _socket->bind(host, _udpConfig->localPort(), QAbstractSocket::ReuseAddressHint | QUdpSocket::ShareAddress); if (_connectState) { + _socket->joinMulticastGroup(QHostAddress("224.0.0.1")); //-- Make sure we have a large enough IO buffers #ifdef __mobile__ _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 64 * 1024); -- GitLab From 1e201fff082daaa25e23dfa372c13f8802316bd9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 11 Jan 2017 09:37:19 -0800 Subject: [PATCH 178/398] Track changes by DO_CHANGE_SPEED items --- src/MissionManager/MavCmdInfoCommon.json | 8 ++++---- src/MissionManager/MissionController.cc | 1 + src/MissionManager/MissionItem.cc | 26 ++++++++++++++++++++++++ src/MissionManager/MissionItem.h | 11 ++++++++-- src/MissionManager/SimpleMissionItem.cc | 7 +++++++ src/MissionManager/SimpleMissionItem.h | 1 + src/MissionManager/SurveyMissionItem.h | 1 + src/MissionManager/VisualMissionItem.h | 7 ++++++- 8 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/MissionManager/MavCmdInfoCommon.json b/src/MissionManager/MavCmdInfoCommon.json index c831f7e1b..622e1cff1 100644 --- a/src/MissionManager/MavCmdInfoCommon.json +++ b/src/MissionManager/MavCmdInfoCommon.json @@ -503,23 +503,23 @@ "label": "Type:", "enumStrings": "Airspeed,Ground Speed", "enumValues": "0,1", - "default": 1 + "default": 0 }, "param2": { "label": "Speed:", "units": "m/s", - "default": -1 + "default": 0 }, "param3": { "label": "Throttle:", "units": "%", - "default": -1 + "default": 0 }, "param4": { "label": "Offset:", "enumStrings": "Relative,Absolute", "enumValues": "1,0", - "default": 1 + "default": 0 } }, { diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 791e365b2..38355adc3 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -1155,6 +1155,7 @@ void MissionController::_initVisualItem(VisualMissionItem* visualItem) connect(visualItem, &VisualMissionItem::specifiesCoordinateChanged, this, &MissionController::_recalcWaypointLines); connect(visualItem, &VisualMissionItem::coordinateHasRelativeAltitudeChanged, this, &MissionController::_recalcWaypointLines); connect(visualItem, &VisualMissionItem::exitCoordinateHasRelativeAltitudeChanged, this, &MissionController::_recalcWaypointLines); + connect(visualItem, &VisualMissionItem::flightSpeedChanged, this, &MissionController::_recalcAltitudeRangeBearing); if (visualItem->isSimpleItem()) { // We need to track commandChanged on simple item since recalc has special handling for takeoff command diff --git a/src/MissionManager/MissionItem.cc b/src/MissionManager/MissionItem.cc index 0a50253a9..bf8de33f9 100644 --- a/src/MissionManager/MissionItem.cc +++ b/src/MissionManager/MissionItem.cc @@ -51,6 +51,8 @@ MissionItem::MissionItem(QObject* parent) _frameFact.setRawValue(MAV_FRAME_GLOBAL_RELATIVE_ALT); setAutoContinue(true); + + connect(&_param2Fact, &Fact::rawValueChanged, this, &MissionItem::_param2Changed); } MissionItem::MissionItem(int sequenceNumber, @@ -95,6 +97,8 @@ MissionItem::MissionItem(int sequenceNumber, _param5Fact.setRawValue(param5); _param6Fact.setRawValue(param6); _param7Fact.setRawValue(param7); + + connect(&_param2Fact, &Fact::rawValueChanged, this, &MissionItem::_param2Changed); } MissionItem::MissionItem(const MissionItem& other, QObject* parent) @@ -117,6 +121,8 @@ MissionItem::MissionItem(const MissionItem& other, QObject* parent) _frameFact.setRawValue(MAV_FRAME_GLOBAL_RELATIVE_ALT); *this = other; + + connect(&_param2Fact, &Fact::rawValueChanged, this, &MissionItem::_param2Changed); } const MissionItem& MissionItem::operator=(const MissionItem& other) @@ -139,8 +145,10 @@ const MissionItem& MissionItem::operator=(const MissionItem& other) return *this; } + MissionItem::~MissionItem() { + } void MissionItem::save(QJsonObject& json) const @@ -375,3 +383,21 @@ QGeoCoordinate MissionItem::coordinate(void) const { return QGeoCoordinate(param5(), param6(), param7()); } + +double MissionItem::flightSpeed(void) const +{ + double flightSpeed = std::numeric_limits::quiet_NaN(); + + if (_commandFact.rawValue().toInt() == MAV_CMD_DO_CHANGE_SPEED && _param2Fact.rawValue().toDouble() > 0) { + flightSpeed = _param2Fact.rawValue().toDouble(); + } + + return flightSpeed; +} + +void MissionItem::_param2Changed(QVariant value) +{ + if (_commandFact.rawValue().toInt() == MAV_CMD_DO_CHANGE_SPEED && _param2Fact.rawValue().toDouble() > 0) { + emit flightSpeedChanged(value.toDouble()); + } +} diff --git a/src/MissionManager/MissionItem.h b/src/MissionManager/MissionItem.h index 5d4b0f2dc..5ed2b4ef0 100644 --- a/src/MissionManager/MissionItem.h +++ b/src/MissionManager/MissionItem.h @@ -76,6 +76,9 @@ public: QGeoCoordinate coordinate (void) const; int doJumpId (void) const { return _doJumpId; } + /// @return Flight speed change value if this item supports it. If not it returns NaN. + double flightSpeed (void) const; + void setCommand (MAV_CMD command); void setSequenceNumber (int sequenceNumber); void setIsCurrentItem (bool isCurrentItem); @@ -97,8 +100,12 @@ public: bool relativeAltitude(void) const { return frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; } signals: - void isCurrentItemChanged (bool isCurrentItem); - void sequenceNumberChanged (int sequenceNumber); + void isCurrentItemChanged (bool isCurrentItem); + void sequenceNumberChanged (int sequenceNumber); + void flightSpeedChanged (double flightSpeed); + +private slots: + void _param2Changed (QVariant value); private: bool _convertJsonV1ToV2(const QJsonObject& json, QJsonObject& v2Json, QString& errorString); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 22ecd6252..ccfdde11d 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -71,6 +71,8 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent) _connectSignals(); setDefaultsForCommand(); + + connect(&_missionItem, &MissionItem::flightSpeedChanged, this, &SimpleMissionItem::flightSpeedChanged); } SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, const MissionItem& missionItem, QObject* parent) @@ -607,3 +609,8 @@ void SimpleMissionItem::setSequenceNumber(int sequenceNumber) emit abbreviationChanged(); } } + +double SimpleMissionItem::flightSpeed(void) +{ + return missionItem().flightSpeed(); +} diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index 9d3650c0c..7efca0a64 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -91,6 +91,7 @@ public: QGeoCoordinate coordinate (void) const final { return _missionItem.coordinate(); } QGeoCoordinate exitCoordinate (void) const final { return coordinate(); } int sequenceNumber (void) const final { return _missionItem.sequenceNumber(); } + double flightSpeed (void) final; bool coordinateHasRelativeAltitude (void) const final { return _missionItem.relativeAltitude(); } bool exitCoordinateHasRelativeAltitude (void) const final { return coordinateHasRelativeAltitude(); } diff --git a/src/MissionManager/SurveyMissionItem.h b/src/MissionManager/SurveyMissionItem.h index d00974e77..7f46a28ca 100644 --- a/src/MissionManager/SurveyMissionItem.h +++ b/src/MissionManager/SurveyMissionItem.h @@ -97,6 +97,7 @@ public: QGeoCoordinate coordinate (void) const final { return _coordinate; } QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } int sequenceNumber (void) const final { return _sequenceNumber; } + double flightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } bool coordinateHasRelativeAltitude (void) const final { return _gridAltitudeRelative; } bool exitCoordinateHasRelativeAltitude (void) const final { return _gridAltitudeRelative; } diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index 32bc65aad..ab2612b78 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -40,13 +40,16 @@ public: const VisualMissionItem& operator=(const VisualMissionItem& other); - // The following properties are calculated/set by the MissionControll recalc methods + // The following properties are calculated/set by the MissionController recalc methods Q_PROPERTY(double altDifference READ altDifference WRITE setAltDifference NOTIFY altDifferenceChanged) ///< Change in altitude from previous waypoint Q_PROPERTY(double altPercent READ altPercent WRITE setAltPercent NOTIFY altPercentChanged) ///< Percent of total altitude change in mission altitude Q_PROPERTY(double azimuth READ azimuth WRITE setAzimuth NOTIFY azimuthChanged) ///< Azimuth to previous waypoint Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged) ///< Distance to previous waypoint + /// This property returns whether the item supports changing flight speed. If it does not it will return NaN. + Q_PROPERTY(double flightSpeed READ flightSpeed NOTIFY flightSpeedChanged) + // Visual mission items have two coordinates associated with them: /// This is the entry point for a waypoint line into the item. For a simple item it is also the location of the item @@ -110,6 +113,7 @@ public: virtual QGeoCoordinate coordinate (void) const = 0; virtual QGeoCoordinate exitCoordinate (void) const = 0; virtual int sequenceNumber (void) const = 0; + virtual double flightSpeed (void) = 0; virtual bool coordinateHasRelativeAltitude (void) const = 0; virtual bool exitCoordinateHasRelativeAltitude (void) const = 0; @@ -143,6 +147,7 @@ signals: void isSimpleItemChanged (bool isSimpleItem); void specifiesCoordinateChanged (void); void isStandaloneCoordinateChanged (void); + void flightSpeedChanged (double flightSpeed); void coordinateHasRelativeAltitudeChanged (bool coordinateHasRelativeAltitude); void exitCoordinateHasRelativeAltitudeChanged (bool exitCoordinateHasRelativeAltitude); -- GitLab From cf97c4a0d7161b267b52cd818f90af44b78ca50a Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 12 Jan 2017 12:39:34 -0800 Subject: [PATCH 179/398] ArduPilot support for new FRAME_CLASS, FRAME_TYPE --- .../APM/APMAirframeComponent.cc | 25 +- .../APM/APMAirframeComponent.h | 5 + .../APM/APMAirframeComponent.qml | 45 +- .../APM/APMAirframeComponentController.cc | 15 +- .../APM/APMAirframeComponentController.h | 5 +- .../APM/APMAirframeComponentSummary.qml | 29 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 4 +- .../APMParameterFactMetaData.Copter.3.5.xml | 6762 +++++++++++++++++ src/FirmwarePlugin/APM/APMResources.qrc | 1 + 9 files changed, 6867 insertions(+), 24 deletions(-) create mode 100644 src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponent.cc b/src/AutoPilotPlugins/APM/APMAirframeComponent.cc index d6aedc86d..0c6017256 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponent.cc +++ b/src/AutoPilotPlugins/APM/APMAirframeComponent.cc @@ -11,16 +11,27 @@ #include "ArduCopterFirmwarePlugin.h" #include "ParameterManager.h" +const char* APMAirframeComponent::_oldFrameParam = "FRAME"; +const char* APMAirframeComponent::_newFrameParam = "FRAME_CLASS"; + APMAirframeComponent::APMAirframeComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : VehicleComponent(vehicle, autopilot, parent) , _requiresFrameSetup(false) , _name("Airframe") { if (qobject_cast(_vehicle->firmwarePlugin()) != NULL) { + ParameterManager* paramMgr = _vehicle->parameterManager(); _requiresFrameSetup = true; - MAV_TYPE vehicleType = vehicle->vehicleType(); - if (vehicleType == MAV_TYPE_TRICOPTER || vehicleType == MAV_TYPE_HELICOPTER) { - _requiresFrameSetup = false; + if (paramMgr->parameterExists(FactSystem::defaultComponentId, _oldFrameParam)) { + _useNewFrameParam = false; + _frameParamFact = paramMgr->getParameter(FactSystem::defaultComponentId, _oldFrameParam); + MAV_TYPE vehicleType = vehicle->vehicleType(); + if (vehicleType == MAV_TYPE_TRICOPTER || vehicleType == MAV_TYPE_HELICOPTER) { + _requiresFrameSetup = false; + } + } else { + _useNewFrameParam = true; + _frameParamFact = paramMgr->getParameter(FactSystem::defaultComponentId, _newFrameParam); } } } @@ -49,7 +60,11 @@ bool APMAirframeComponent::requiresSetup(void) const bool APMAirframeComponent::setupComplete(void) const { if (_requiresFrameSetup) { - return _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, QStringLiteral("FRAME"))->rawValue().toInt() >= 0; + if (_useNewFrameParam) { + return _frameParamFact->rawValue().toInt() > 0; + } else { + return _frameParamFact->rawValue().toInt() >= 0; + } } else { return true; } @@ -60,7 +75,7 @@ QStringList APMAirframeComponent::setupCompleteChangedTriggerList(void) const QStringList list; if (_requiresFrameSetup) { - list << QStringLiteral("FRAME"); + list << (_useNewFrameParam ? _newFrameParam : _oldFrameParam); } return list; diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponent.h b/src/AutoPilotPlugins/APM/APMAirframeComponent.h index 5a707b52d..0b7c6be18 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponent.h +++ b/src/AutoPilotPlugins/APM/APMAirframeComponent.h @@ -36,6 +36,11 @@ public: private: bool _requiresFrameSetup; ///< true: FRAME parameter must be set const QString _name; + Fact* _frameParamFact; + bool _useNewFrameParam; + + static const char* _oldFrameParam; + static const char* _newFrameParam; }; #endif diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponent.qml b/src/AutoPilotPlugins/APM/APMAirframeComponent.qml index 29ec60771..7c1cde362 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponent.qml +++ b/src/AutoPilotPlugins/APM/APMAirframeComponent.qml @@ -14,6 +14,7 @@ import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.2 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.Controllers 1.0 @@ -21,10 +22,13 @@ import QGroundControl.ScreenTools 1.0 SetupPage { id: airframePage - pageComponent: airframePageComponent + pageComponent: _useOldFrameParam ? oldFramePageComponent: newFramePageComponent - property real _margins: ScreenTools.defaultFontPixelWidth - property Fact _frame: controller.getParameterFact(-1, "FRAME") + property real _margins: ScreenTools.defaultFontPixelWidth + property bool _useOldFrameParam: controller.parameterExists(-1, "FRAME") + property Fact _oldFrameParam: controller.getParameterFact(-1, "FRAME", false) + property Fact _newFrameParam: controller.getParameterFact(-1, "FRAME_CLASS", false) + property Fact _frameTypeParam: controller.getParameterFact(-1, "FRAME_TYPE", false) APMAirframeComponentController { id: controller @@ -88,7 +92,7 @@ SetupPage { } Component { - id: airframePageComponent + id: oldFramePageComponent Column { width: availableWidth @@ -129,5 +133,36 @@ SetupPage { } } } // Column - } // Component - pageComponent + } // Component - oldFramePageComponent + + Component { + id: newFramePageComponent + + Grid { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margins + columns: 2 + + QGCLabel { + text: qsTr("Frame Class:") + } + + FactComboBox { + fact: _newFrameParam + indexModel: false + width: ScreenTools.defaultFontPixelWidth * 15 + } + + QGCLabel { + text: qsTr("Frame Type:") + } + + FactComboBox { + fact: _frameTypeParam + indexModel: false + width: ScreenTools.defaultFontPixelWidth * 15 + } + } + } } // SetupPage diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc index cc298091a..670005397 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc @@ -28,18 +28,25 @@ bool APMAirframeComponentController::_typesRegistered = false; +const char* APMAirframeComponentController::_oldFrameParam = "FRAME"; +const char* APMAirframeComponentController::_newFrameParam = "FRAME_CLASS"; + APMAirframeComponentController::APMAirframeComponentController(void) : _airframeTypesModel(new QmlObjectListModel(this)) { if (!_typesRegistered) { _typesRegistered = true; - qmlRegisterUncreatableType("QGroundControl.Controllers", 1, 0, "APMAiframeType", QStringLiteral("Can only reference APMAirframeType")); - qmlRegisterUncreatableType("QGroundControl.Controllers", 1, 0, "APMAiframe", QStringLiteral("Can only reference APMAirframe")); + qmlRegisterUncreatableType("QGroundControl.Controllers", 1, 0, "APMAirframeType", QStringLiteral("Can only reference APMAirframeType")); } _fillAirFrames(); - Fact *frame = getParameterFact(FactSystem::defaultComponentId, QStringLiteral("FRAME")); - connect(frame, &Fact::vehicleUpdated, this, &APMAirframeComponentController::_factFrameChanged); + Fact* frame; + if (parameterExists(FactSystem::defaultComponentId, _oldFrameParam)) { + frame = getParameterFact(FactSystem::defaultComponentId, _oldFrameParam); + } else { + frame = getParameterFact(FactSystem::defaultComponentId, _newFrameParam); + } + connect(frame, &Fact::rawValueChanged, this, &APMAirframeComponentController::_factFrameChanged); _factFrameChanged(frame->rawValue()); } diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentController.h b/src/AutoPilotPlugins/APM/APMAirframeComponentController.h index 936cd96ee..47e383529 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentController.h +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentController.h @@ -71,9 +71,12 @@ private slots: private: void _loadParametersFromDownloadFile(const QString& downloadedParamFile); - static bool _typesRegistered; APMAirframeType *_currentAirframeType; QmlObjectListModel *_airframeTypesModel; + + static bool _typesRegistered; + static const char* _oldFrameParam; + static const char* _newFrameParam; }; class APMAirframe : public QObject diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml b/src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml index 9097ce44f..8ba3da326 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentSummary.qml @@ -18,18 +18,31 @@ FactPanel { factPanel: panel } - property Fact sysIdFact: controller.getParameterFact(-1, "FRAME") + property bool _useOldFrameParam: controller.parameterExists(-1, "FRAME") + property Fact _oldFrameParam: controller.getParameterFact(-1, "FRAME", false) + property Fact _newFrameParam: controller.getParameterFact(-1, "FRAME_CLASS", false) + property Fact _frameTypeParam: controller.getParameterFact(-1, "FRAME_TYPE", false) Column { anchors.fill: parent + + VehicleSummaryRow { + labelText: qsTr("Frame Type:") + valueText: controller.currentAirframeTypeName() + " " + _oldFrameParam.enumStringValue + visible: _useOldFrameParam + } + + VehicleSummaryRow { + labelText: qsTr("Frame Class:") + valueText: _newFrameParam.enumStringValue + visible: !_useOldFrameParam + + } + VehicleSummaryRow { - id: nameRow; - labelText: qsTr("Frame Type:") - valueText: controller.currentAirframeTypeName() + " " + (sysIdFact.valueString === "0" ? "Plus" - : sysIdFact.valueString === "1" ? "X" - : sysIdFact.valueString === "2" ? "V" - : sysIdFact.valueString == "3" ? "H" - : /* Fact.value == 10 */ "New Y6"); + labelText: qsTr("Frame Type:") + valueText: _frameTypeParam.enumStringValue + visible: !_useOldFrameParam } diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 9d6f40fe2..54855385b 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -744,8 +744,10 @@ QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) case MAV_TYPE_HELICOPTER: if (vehicle->firmwareMajorVersion() < 3 || (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() <= 3)) { return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml"); - } else { + } else if (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() == 4) { return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.4.xml"); + } else { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml"); } case MAV_TYPE_FIXED_WING: if (vehicle->firmwareMajorVersion() < 3 || (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() <= 3)) { diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml new file mode 100644 index 000000000..1734b74b8 --- /dev/null +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml @@ -0,0 +1,6762 @@ + + + + + + + + +True + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +1 10 +1 + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + + +0 1 +0.01 + + +0 5 +0.01 + + +0 15 +0.1 +Degrees + + +0 1000 +1 +meters + + +0 100 +1 +meters + + + +Disabled +FBWMixing +DirectMixing + + + + +Disabled +Enabled + + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s/s + + +0 127 +1 +0.1 seconds + + +-100 100 +1 +Percent + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 10 +0.5 +seconds + + +0 100 +Percent + + + + +0 45 +1 +degrees + + +0:AUTO_ALWAYS,1:AUTO_LAND,2:AUTO_LOITER_TO_ALT,3:AUTO_LOITER_ALL,4:AUTO_WAYPOINTS,5:LOITER,6:RTL,7:CIRCLE,8:CRUISE,9:FBWB,10:GUIDED + + + +Default +L1Controller + + + +0 1 +0.1 +Percent + + + +Automatic + + + +-32767 32767 +1 +Meters + + +1 32767 +1 +Meters + + +0 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + + +None +GuidedMode +ReportOnly +GuidedModeThrPass +RTL_Mode + + + + + + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + + +NoAutoEnable +AutoEnable +AutoEnableDisableFloorOnly + + + + +FenceReturnPoint +NearestRallyPoint + + + + +Disabled +Enabled + + + +5 100 +1 +m/s + + +5 100 +1 +m/s + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 10000 +meters + + +1 10 +0.1 +m/s + + +-100 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 127 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +925 2200 +1 + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +CIRCLE/no change(if already in AUTO|GUIDED|LOITER) +CIRCLE +FBWA + + + +1 100 +0.5 +seconds + + + +Continue +ReturnToLaunch +Glide +Deploy Parachute + + + +1 300 +0.5 +seconds + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Heartbeat +HeartbeatAndREMRSSI +HeartbeatAndAUTO + + + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + +0 9000 +1 +centi-Degrees + + +0 9000 +1 +centi-Degrees + + +-9000 0 +1 +centi-Degrees + + +10 500 +1 +degrees/second + + +10 500 +1 +degrees/second + + + +Disabled +Enabled + + + +-100 100 +0.1 +Meters + + +10 360 +1 +degrees/second + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0.5 1.2 + + + +Disabled +Enabled + + + +-1000 1000 +percent + + +-1000 1000 +percent + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +PX4/Pixhawk-Default + + + + + + + +cm/s + + +m/s + + +cm/s + + +centi-Degrees + + +centimeters + + +centimeters + + + +Disabled +Enabled + + + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0 100 +Percent + + +0 100 +1 +m/s + + +0 100 +Percent + + +0 100 +1 +m/s + + + + + + + +Disabled +Channel1 +Channel2 +Channel3 +Channel4 +Channel5 +Channel6 +Channel7 +Channel8 + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 90 +0.1 +degrees + + + +Disable +Enable - go HOME then land +Enable - go directly to landing sequence + + + + +Disable +Enable + + + +10 127 +m/s/s + + +0:Disarm + + + + + +Disabled +Enabled + + + + + +True + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + + +Mission Planner and DroidPlanner + AP Planner 2 + + + + +Disabled +Enabled + + + +0 10 +.5 +Hz + + +0.0 1000.0 +10 +Centimeters + + +0.0 500.0 +10 + + +0:Feedback from mid stick,1:High throttle cancels landing,2:Disarm on land detection + +None +Feedback from mid stick +High throttle cancels landing +Disarm on land detection + + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0 8000 +1 +Centimeters + + +0.5 10.0 + +Disabled +Shallow +Steep + +.1 + + +0 2000 +50 +cm/s + + +0.01 2.0 +0.01 + + + +Disabled +Land +RTL + + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode + + + +100 900 + + + +Disabled +Enabled + + + + +Disabled +Mode1 +Mode2 +Mode1+2 +Mode3 +Mode1+3 +Mode2+3 +Mode1+2+3 +Mode4 +Mode1+4 +Mode2+4 +Mode1+2+4 +Mode3+4 +Mode1+3+4 +Mode2+3+4 +Mode1+2+3+4 +Mode5 +Mode1+5 +Mode2+5 +Mode1+2+5 +Mode3+5 +Mode1+3+5 +Mode2+3+5 +Mode1+2+3+5 +Mode4+5 +Mode1+4+5 +Mode2+4+5 +Mode1+2+4+5 +Mode3+4+5 +Mode1+3+4+5 +Mode2+3+4+5 +Mode1+2+3+4+5 +Mode6 +Mode1+6 +Mode2+6 +Mode1+2+6 +Mode3+6 +Mode1+3+6 +Mode2+3+6 +Mode1+2+3+6 +Mode4+6 +Mode1+4+6 +Mode2+4+6 +Mode1+2+4+6 +Mode3+4+6 +Mode1+3+4+6 +Mode2+3+4+6 +Mode1+2+3+4+6 +Mode5+6 +Mode1+5+6 +Mode2+5+6 +Mode1+2+5+6 +Mode3+5+6 +Mode1+3+5+6 +Mode2+3+5+6 +Mode1+2+3+5+6 +Mode4+5+6 +Mode1+4+5+6 +Mode2+4+5+6 +Mode1+2+4+5+6 +Mode3+4+5+6 +Mode1+3+4+5+6 +Mode2+3+4+5+6 +Mode1+2+3+4+5+6 + + + +-1 1000 +1 +Centimeters + + +0 3000 +10 +Centimeters + + + +Never change yaw +Face next waypoint +Face next waypoint except RTL +Face along GPS course + + + +0 60000 +1000 +ms + + +30 200 +10 +cm/s + + +0 500 +10 +cm/s + + +50 500 +10 +Centimeters/Second + + +50 500 +10 +cm/s/s + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode +Enabled always LAND + + + +925 1100 +1 +pwm + + +0 300 +1 +pwm + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW + +Default +Default+RCIN +Default+IMU +Default+Motors +NearlyAll-AC315 +NearlyAll +All+FastATT +All+MotBatt +All+FastIMU +All+FastIMU+PID +All+FullIMU +Disabled + + + + +Normal Start-up +Start-up in ESC Calibration mode if throttle high +Start-up in ESC Calibration mode regardless of throttle +Start-up and automatically calibrate ESCs +Disabled + + + + +None +Stab Roll/Pitch kP +Rate Roll/Pitch kP +Rate Roll/Pitch kI +Rate Roll/Pitch kD +Stab Yaw kP +Rate Yaw kP +Rate Yaw kD +Altitude Hold kP +Throttle Rate kP +Throttle Accel kP +Throttle Accel kI +Throttle Accel kD +Loiter Speed +Loiter Pos kP +Velocity XY kP +Velocity XY kI +WP Speed +Acro RollPitch kP +Acro Yaw kP +Heli Ext Gyro +OF Loiter kP +OF Loiter kI +OF Loiter kD +Declination +Circle Rate +RangeFinder Gain +Rate Pitch kP +Rate Pitch kI +Rate Pitch kD +Rate Roll kP +Rate Roll kI +Rate Roll kD +Rate Pitch FF +Rate Roll FF +Rate Yaw FF + + + +0 32767 + + +0 32767 + + + +Plus +X +V +H +V-Tail +A-Tail +Y6B + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance + + + +0:All,1:Baro,2:Compass,3:GPS,4:INS,5:Parameters+Rangefinder,6:RC,7:Voltage + +Disabled +Enabled +Skip Baro +Skip Compass +Skip GPS +Skip INS +Skip Params/Rangefinder +Skip RC +Skip Voltage + + + +0 127 +Seconds + + +1000 8000 +Centi-degrees + + +0 100 + +Very Soft +Soft +Medium +Crisp +Very Crisp + +10 + + +4 12 +deg/sec + + +2000 4500 +Centi-degrees + + + +No repositioning +Repositioning + + + + +Land +AltHold +Land even in Stabilize + + + +0.6:Strict, 0.8:Default, 1.0:Relaxed + + + +Disabled +Enabled + + + +50 490 +1 +Hz + + +1 10 + + +1 10 + + +0 3 +0.1 + + +0 3 +0.1 + + + +Disabled +Leveling +Leveling and Limited + + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0.1 6.0 +0.1 + + +0.02 1.00 +0.01 + + +0 4500 +10 +cm/s/s + + +1.000 8.000 + + +0.500 1.500 +0.05 + + +0.000 3.000 + + +0 1000 +Percent*10 + + +0.000 0.400 + + +1.000 100.000 +Hz + + +1.000 3.000 + + +0.500 2.000 + + +0:Roll,1:Pitch,2:Yaw + +All +Roll Only +Pitch Only +Yaw Only +Roll and Pitch +Roll and Yaw +Pitch and Yaw + + + +0.05 0.10 + + +0.001 0.006 + + + +Stopped +Running + + + + +Do Not Use in RTL and Land +Use in RTL and Land + + + +0 5 + + + +Auto +Guided +RTL +Land +Brake +Throw + + + + +Upward Throw +Drop + + + + +Disabled +Enabled + + + +0:ADSBMavlinkProcessing + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0 1 + + + +Undefined +Quad +Hexa +Octa +OctaQuad +Y6 +Heli +Tri +SingleCopter +CoaxCopter + + + + + + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 20 +0.1 +seconds + + +0 20 +0.1 +seconds + + +0 100 +1 +degrees/second + + +0 20 +1 +seconds + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10 +0.1 +seconds + + + +Position +OnOff +ContinuousRotation + + + + +Position +OnOff +ContinuousRotation + + + +0 50 +0.1 +degrees/second + + +0 50 +0.1 +degrees/second + + +0 2 +0.01 +seconds + + +0 2 +0.01 +seconds + + +-10 10 +0.1 +degrees + + +-10 10 +0.1 +degrees + + +0 360 +0.1 +degrees + + +0 100 +1 +meters + + + +Barometer +GPS +GPS vehicle only + + + +1 10 +1 +Hz + + +-90 0 +1 +Degrees + + +0 90 +1 +Degrees + + +0:ATTITUDE,1:GPS,2:RCIN,3:IMU,4:RCOUT,5:COMPASS + +Default +Disabled + + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +1 255 + + + + + + +True + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +APM2-Default +PX4/Pixhawk-Default + + + + + + + + +MANUAL +LEARNING +STEERING +HOLD +AUTO +RTL +GUIDED + + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 30 +1 +seconds + + +0:Steering,1:Throttle + +None +Steering +Throttle + + + + +Disabled +Enabled + + + + +Disabled +APM TriggerPin0 +APM TriggerPin1 +APM TriggerPin2 +APM TriggerPin3 +APM TriggerPin4 +APM TriggerPin5 +APM TriggerPin6 +APM TriggerPin7 +APM TriggerPin8 +Pixhawk TriggerPin50 +Pixhawk TriggerPin51 +Pixhawk TriggerPin52 +Pixhawk TriggerPin53 +Pixhawk TriggerPin54 +Pixhawk TriggerPin55 + + + +0 20 +0.1 +m/s/s + + +0 100 +0.1 +m/s + + +0 100 +1 +percent + + +0 100 +0.1 +meters + + +0 100 +1 +percent + + +0 100 +1 +m/s + + +0 360 +1 +degrees + + + +Nothing +LearnWaypoint + + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +SkidSteeringOutput + + + + +Disabled +SkidSteeringInput + + + + +Nothing +RTL +HOLD + + + +seconds + + + +Disabled +Enabled + + + +925 1100 +1 + + + +Disabled +Enabled + + + + +Disabled +HOLD +HoldAndDisarm + + + +0 1000 +1 +centimeters + + +-45 45 +1 +centimeters + + +0 100 +0.1 +seconds + + +1 100 +1 + + + + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + +0 1000 +0.1 +meters + + +0.2 10 +0.1 +gravities + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +0.1 +meters + + + +FirstBaro +2ndBaro +3rdBaro + + + + +Disabled +Bus0 + + + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G + + + + +Disabled +Enabled + + + + +Any +FloatRTK +IntegerRTK + +True + + + +Disabled +Enabled +NoChange + + + +-100 90 +Degrees + + + +send to first GPS +send to 2nd GPS +send to all + + + + +None +All +External only + + + + +Disabled +log every sample +log every 5 samples + +True + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Do not save config +Save config +Save only when needed + + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Disables automatic configuration +Enable automatic configuration + + + + +10Hz +8Hz +5Hz + +milliseconds + + + +10Hz +8Hz +5Hz + +milliseconds + + +m + + +m + + +m + + +m + + +m + + +m + + + + + +Servo +Relay + + + +0 50 +seconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 +meters + + + +Low +High + + + +0 10000 +milliseconds + + +0 180 +Degrees + + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + + + + +TriggerLow +TriggerHigh + + + + + + +Disabled +THR_MIN PWM when disarmed +0 PWM when disarmed + + + +0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration + +None +All +Barometer +Compass +GPS Lock +INS(INertial Sensors - accels & gyros) +Parameters(unused) +RC Failsafe +Board voltage +Battery Level +Airspeed +LoggingAvailable +Hardware safety switch +GPS configuration + + + +0.25 3.0 +m/s/s + + +0.1 +Volts + + +0.1 +Volts + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + + +Disabled +Enabled + + + + +First Relay +Second Relay +Third Relay +Fourth Relay +Servo + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + +0 32000 +1 +Meters + + +0 5000 +1 +Milliseconds + + + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-I2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + + + +Disable +Enable + + + +1 +meters + + + + + +Disabled +Enabled + + + +1 100 + + +1 100000 + + +-1 16777215 + + + +NoInfo +Light +Small +Large +HighVortexlarge +Heavy +HighlyManuv +Rotocraft +RESERVED +Glider +LightAir +Parachute +UltraLight +RESERVED +UAV +Space +RESERVED +EmergencySurface +ServiceSurface +PointObstacle + + + + +NO_DATA +L15W23 +L25W28P5 +L25W34 +L35W33 +L35W38 +L45W39P5 +L45W45 +L55W45 +L55W52 +L65W59P5 +L65W67 +L75W72P5 +L75W80 +L85W80 +L85W90 + + + + +NoData +Left2m +Left4m +Left6m +Center +Right2m +Right4m +Right6m + + + + +NO_DATA +AppliedBySensor + + + + +Disabled +Rx-Only +Tx-Only +Rx and Tx Enabled + + + + + + +Disabled +Enabled + + + + +Remain in AVOID_ADSB +Resume previous flight mode +RTL +Resume if AUTO else Loiter + + + + + +seconds + + +seconds + + +meters + + +meters + + +meters + + +meters + + + + + +Disable +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +900 2100 + + +900 2100 + + + +Disable +Chan1 +Chan3 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + + + + +Disable +Enable + + + +0 1 + + + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + + + +0.4 1.0 +0.1 +seconds + + +0.1 4.0 +0.1 + + +0 0.1 +0.01 + + +0 1.0 +0.05 + + +0 180 +1 +degrees/second + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0.4 1.0 +0.1 +seconds + + +0.1 3.0 +0.1 + + +0 0.1 +0.01 + + +0 0.5 +0.05 + + +0 100 +1 +degrees/second + + +0 100 +1 +degrees/second + + +0.7 1.5 +0.05 + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0 4 +0.25 + + +0 2 +0.25 + + +0 2 +0.25 + + +0.8 1.2 +0.05 + + +0 4500 +1 + + + + +0.4 1.0 +0.1 +seconds + + +0.1 10.0 +0.1 + + +0 1.0 +0.05 + + +0 0.1 +0.01 + + +0 4500 +1 + + +0 5 +0.1 +m/s + + +0.0 10.0 +0.1 + + +0.0 30.0 +0.1 +m/s + + +0.0 50.0 +0.1 +degree/(m/s) + + +0.0 4500.0 +0.1 +Centidegrees + + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-3.142 3.142 +0.01 +Radians + + + +Disabled +Internal-Learning +EKF-Learning + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Use Throttle +Use Current + + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +FirstCompass +SecondCompass +ThirdCompass + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + + + + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 32 + +Very Strict +Default +Relaxed +Very Relaxed + +0.1 + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0.05 50 + + + +Never +Start-up only + + + + +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle + + + + +IMU 1 +IMU 2 +IMU 3 + + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +True + + +True + + +True + + +True + + +True + + +True + + + + + + +0.0 1.0 +.01 + + + +Disabled +Enabled + + + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 + + + +0.001 0.5 +.01 + + +0 10 +1 + + + +Disabled +Enable EKF2 +Enable EKF3 + + + + + + +None +I2C-MS4525D0 +Analog +I2C-MS5525 + + + + +Use +Don't Use + + + +0.1 + + +0.1 + + + + + + + + + +Disable +Enable + + + + + + +Bus0 +Bus1 + + + + + +1 60 +1 +seconds + + +0.6 1.0 +0.05 + + +0 0.1 +0.01 + + + + +0.1 20.0 +0.1 + + +0.1 10.0 +0.1 + + +3.0 10.0 +0.2 + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +1.0 10.0 +0.5 + + +1.0 5.0 +0.05 + + +0.5 2.0 +0.05 + + +5.0 30.0 +1.0 + + +0.0 2.0 +0.1 + + +0.1 1.0 +0.1 + + +0.0 20.0 +0.1 + + +-1 127 +1 + + +-1 100 +0.1 + + +-1.0 2.0 +0.1 + + +0 45 +1 + + +-45 0 +1 + + +0.0 2.0 +0.1 + + +1.0 5.0 +0.2 + + +0.1 1.0 +0.1 + + +-5 40 +1 + + +0.0 20.0 +0.1 +m/s + + +-2.0 2.0 +0.1 +m/s/m + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +0.0 0.5 +0.02 + + +0.1 1.0 +0.1 + + + +Disable +Enable + + + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0 100 +1 + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + + + + + + +None +File +MAVLink +BothFileAndMAVLink + + + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +SMBus +Bebop + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Watts + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +SMBus +Bebop + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Amps + + + + + +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled + +True + + + +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz + +True + + +-32767 32768 + + + +Disabled +Enabled +Dynamic ID/Update + + + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 + +Disabled +Enabled + +True + + +-1 80 +degreesC + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + + + + + + + + + + + + + + + + +meters + + +meters + + +millibar + + + + + + + + + + + + + + +seconds + + + + + +Disabled +Enabled + + + +-200 +200 +1 + + +-200 +200 +1 + + +-18000 +18000 +1 + + +m + + +m + + +m + + +0 255 + + + + +0 32766 +1 + + + +Resume Mission +Restart Mission + + + + + + + +0.1 +kilometers + + + +DoNotIncludeHome +IncludeHome + + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +msec + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +msec + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +1 127 + + +50 200 +% + + +0.5 50.0 +m/s + + + +Disabled +FirstIMU +FirstAndSecondIMU +AllIMUs + + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + + +2.0 6.0 +0.5 +m + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +msec + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +msec + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +1 127 + + +50 200 +% + + +0.5 50.0 +m/s + + + +Disabled +FirstIMU +FirstAndSecondIMU +AllIMUs + + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + + +2.0 6.0 +0.5 +m + + +0.5 2.5 +0.1 +m/s/s + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +AnalogPin +RCChannelPwmValue + + + + +APM2 A0 +APM2 A1 +APM2 A13 +Pixracer +Pixhawk ADC4 +Pixhawk ADC3 + Pixhawk ADC6 +Pixhawk SBUS + + + +0 5.0 +0.01 +Volt + + +0 5.0 +0.01 +Volt + + + + + +0 2000 +Microseconds + + +0 2000 +Microseconds + + + + + +Off +Low +Medium +High + + + + +Disable +Enable + + + + +Disable +Enable + + + + + +0 5 +0.5 +meters + + +0 90 +0.1 +degrees + + +centi-Degrees + + +0.1 +meters + + +0.1 +seconds + + +0 30 +0.1 +meters + + +0 10 +0.1 +seconds + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 127 +1 +seconds + + + +Disabled +Servos to Neutral +Servos to Zero PWM + + + + +Disabled +Enabled + + + +0 100 +Percent + + + +Standard Glide Slope + + + + + + +Disabled +Enabled + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +0 3600 + + + + + +Disabled +Enabled + + + + +None +Chan1 +Chan2 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +0.1 5 +Seconds + + +1 10 +Seconds + + +100 100000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + + +None +RPM1 +RPM2 + + + +0 100 + + + + + + + + + + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + +Disable +Enable + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + + + +0 500 +1 +Percent*10 + + +0 500 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + + +Disabled +Very Low +Low +Medium +High +Very High + + + + + +20 2000 +50 +cm/s + + +100 1000 +1 +cm + + +0 1000 +50 +cm/s + + +0 500 +10 +cm/s + + +0 2000 +50 +cm/s + + +50 500 +10 +cm/s/s + + +50 500 +10 +cm/s/s + + +500 5000 +1 +cm/s/s/s + + +100 981 +1 +cm/s/s + + +25 250 +1 +cm/s/s + + + +Disable +Enable + + + + + +0 10000 +100 +cm + + +-90 90 +1 +deg/s + + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +3.000 12.000 + + +3.000 12.000 + + +3.000 6.000 + + +0.5 10.0 + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + + +Disabled 1 + + + +0 1000 +Centi-Degrees + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.180 0.60 +0.005 + + +0.01 0.06 +0.01 + + +0 1 +0.01 + + +0.000 0.02 +0.001 + + +1 20 +1 +Hz + + + + +0.5 5 +0.1 +Hz + + + + + +Disabled +Enabled + + + +0 100 +percentage + + +1000 2000 +ms + + +0 1000 +cm/s + + +0 100 +percentage + + + + + +Disabled +Enabled + + + +0:Altitude,1:Circle,2:Polygon + +None +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All + + + + +Report Only +RTL or Land + + + +10 1000 +1 +Meters + + +30 10000 +Meters + + +1 10 +Meters + + +1 20 + + + + +0:StopAtFence,1:UseProximitySensor + +None +StopAtFence +UseProximitySensor +All + + + +0 4500 + + +3 30 +meters + + + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + + +Servo only +Servo with ExtGyro +DirectDrive VarPitch +DirectDrive FixedPitch + + + + +3-Servo CCPM +H1 Mechanical Mixing + + + +0 1000 +1 +PWM + + +-90 90 +1 +Degrees + + +-10 10 +0.1 + + + +NoFlybar +Flybar + + + +0 1000 +1 +PWM + + +0 1000 +1 +PWM + + +0 2000 + + +0 2000 + + + +Reversed +Normal + + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + + +Disabled +Passthrough +Max collective +Mid collective +Min collective + + + +0 1000 +10 +PWM + + + +Ch8 Input +SetPoint +Throttle Curve + + + +0 500 +1 +pwm + + +0 60 +Seconds + + +0 60 +Seconds + + +0 1000 +10 + + +0 500 +10 + + +0 1000 +10 + + +0 1000 +10 + + +0 18000 +100 +Centi-Degrees + + +0 10 +1 + + +1 1000 +10 + + +0 500 +10 + + + + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 +Brushed16kHz + +True + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + +PWM enabled while disarmed +PWM disabled while disarmed + + + +5 80 +1 +Degrees + + + + + +Disabled +Enabled Always Land +Enabled Strict + + + + +None +CompanionComputer +IRLock +SITL_Gazebo +SITL + + + +0 360 +1 +Centi-degrees + + +-20 20 +1 +Centimeters + + +-20 20 +1 +Centimeters + + + + + +None +Pozyx + + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10000 +1 +meters + + +-180 +180 +1 +degrees + + + + + +None +LightWareSF40C +MAVLink + + + + +Default +Upside Down + + + +-180 180 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + + +None +LightWareSF40C +MAVLink + + + + +Default +Upside Down + + + +-180 180 + + + + + +Disabled +Enabled + + + + +None +Servo +EPM + + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +0 255 +seconds + + +0 255 + + + + + + + + + + + + + diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc index 4ca53d7fc..7e2b6a212 100644 --- a/src/FirmwarePlugin/APM/APMResources.qrc +++ b/src/FirmwarePlugin/APM/APMResources.qrc @@ -45,6 +45,7 @@ APMParameterFactMetaData.Plane.3.7.xml APMParameterFactMetaData.Copter.3.3.xml APMParameterFactMetaData.Copter.3.4.xml + APMParameterFactMetaData.Copter.3.5.xml APMParameterFactMetaData.Rover.3.0.xml APMParameterFactMetaData.Sub.3.4.xml CopterGeoFenceEditor.qml -- GitLab From 162fdd743569e41efb5c30ff24edade5641b0869 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 13 Jan 2017 10:12:23 -0800 Subject: [PATCH 180/398] New firmware format --- src/VehicleSetup/FirmwareUpgradeController.cc | 25 ++++++------------- src/VehicleSetup/FirmwareUpgradeController.h | 1 + 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index ef24f1de4..126f1baf4 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -233,12 +233,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v4.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v4.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-quad/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa-quad/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-hexa/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-tri/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-y6/ArduCopter-v4.px4"}, + { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v4.px4"}, { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v4.px4"}, { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v4.px4"}, { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v4.px4"} @@ -267,12 +262,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v2.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-hexa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-tri/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-y6/ArduCopter-v2.px4"}, + { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v2.px4"} @@ -335,12 +325,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v1.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v1.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-quad/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa-quad/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-hexa/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-octa/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-tri/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/latest/PX4-y6/ArduCopter-v1.px4"}, + { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v1.px4"}, { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v1.px4"}, { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v1.px4"}, { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v1.px4"} @@ -791,6 +776,10 @@ QStringList FirmwareUpgradeController::apmAvailableVersions(void) case HeliFirmware: version = "Heli - "; break; + case CopterFirmware: + version = "MultiRotor - "; + break; + break; case PlaneFirmware: case RoverFirmware: case DefaultVehicleFirmware: diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index 29b4f7d24..949429e09 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -60,6 +60,7 @@ public: YFirmware, Y6Firmware, HeliFirmware, + CopterFirmware, PlaneFirmware, RoverFirmware, DefaultVehicleFirmware -- GitLab From 47a2ae1c4c141be8ce6ab7cd5ca58d08913579ea Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 13 Jan 2017 14:45:22 -0500 Subject: [PATCH 181/398] Condition SDL counts so we don't pass negative numbers to new Should address #4425 --- src/Joystick/JoystickSDL.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index c23448026..854ac4407 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -49,7 +49,7 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl SDL_JoystickClose(sdlJoystick); qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController; - ret[name] = new JoystickSDL(name, axisCount, buttonCount, hatCount, i, isGameController, _multiVehicleManager); + ret[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); } else { qCDebug(JoystickLog) << "\tSkipping duplicate" << name; } -- GitLab From 453c6fd925038bea8467109c92dfdad5a1968fc8 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 13 Jan 2017 14:57:39 -0500 Subject: [PATCH 182/398] Use LZMA compression with NSIS for smaller installers --- deploy/qgroundcontrol_installer.nsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deploy/qgroundcontrol_installer.nsi b/deploy/qgroundcontrol_installer.nsi index 36833d1fe..2fee9698a 100644 --- a/deploy/qgroundcontrol_installer.nsi +++ b/deploy/qgroundcontrol_installer.nsi @@ -41,6 +41,8 @@ Var StartMenuFolder InstallDir $PROGRAMFILES\qgroundcontrol +SetCompressor /SOLID /FINAL lzma + !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "installheader.bmp"; -- GitLab From 9565d5244a3f4eac8d682088996caebdebc89026 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 15:01:44 -0500 Subject: [PATCH 183/398] Add icon to main toolbar to start/stop recording videostream to file --- src/VideoStreaming/VideoReceiver.cc | 215 +++++++++++++++++++++-- src/VideoStreaming/VideoReceiver.h | 36 +++- src/ui/toolbar/MainToolBarIndicators.qml | 21 +++ 3 files changed, 253 insertions(+), 19 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 594a028f0..b12189cad 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -17,11 +17,136 @@ #include "VideoReceiver.h" #include #include +#include +#include + +VideoReceiver::Sink* VideoReceiver::sink = NULL; +GstElement* VideoReceiver::_pipeline = NULL; +GstElement* VideoReceiver::_pipeline2 = NULL; +GstElement* VideoReceiver::tee = NULL; + +gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_data) +{ + Q_UNUSED(bus); + Q_UNUSED(message); + Q_UNUSED(user_data); + + gst_bin_remove(GST_BIN(_pipeline2), sink->queue); + gst_bin_remove(GST_BIN(_pipeline2), sink->mux); + gst_bin_remove(GST_BIN(_pipeline2), sink->filesink); + + gst_element_set_state(_pipeline2, GST_STATE_NULL); + gst_object_unref(_pipeline2); + + gst_element_set_state(sink->filesink, GST_STATE_NULL); + gst_element_set_state(sink->mux, GST_STATE_NULL); + gst_element_set_state(sink->queue, GST_STATE_NULL); + + gst_object_unref(sink->queue); + gst_object_unref(sink->mux); + gst_object_unref(sink->filesink); + + delete sink; + sink = NULL; + + return true; +} + +GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +{ + Q_UNUSED(pad); + Q_UNUSED(info); + Q_UNUSED(user_data); + + if(!g_atomic_int_compare_and_exchange(&sink->removing, FALSE, TRUE)) + return GST_PAD_PROBE_OK; + + GstPad* sinkpad = gst_element_get_static_pad(sink->queue, "sink"); + gst_pad_unlink (sink->teepad, sinkpad); + gst_object_unref (sinkpad); + + gst_element_release_request_pad(tee, sink->teepad); + gst_object_unref(sink->teepad); + + // Also unlinks and unrefs + gst_bin_remove (GST_BIN (_pipeline), sink->queue); + gst_bin_remove (GST_BIN (_pipeline), sink->mux); + gst_bin_remove (GST_BIN (_pipeline), sink->filesink); + + _pipeline2 = gst_pipeline_new("pipe2"); + + gst_bin_add_many(GST_BIN(_pipeline2), sink->queue, sink->mux, sink->filesink, NULL); + gst_element_link_many(sink->queue, sink->mux, sink->filesink, NULL); + + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline2)); + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCB), NULL); + + if(gst_element_set_state(_pipeline2, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qDebug() << "problem starting pipeline2"; + } + + GstPad* eosInjectPad = gst_element_get_static_pad(sink->queue, "sink"); + gst_pad_send_event(eosInjectPad, gst_event_new_eos()); + gst_object_unref(eosInjectPad); + + return GST_PAD_PROBE_REMOVE; +} + +void VideoReceiver::_startRecording(void) +{ + // exit immediately if we are already recording + if(_pipeline == NULL || _recording) { + return; + } + + sink = g_new0(Sink, 1); + + sink->teepad = gst_element_get_request_pad(tee, "src_%u"); + sink->queue = gst_element_factory_make("queue", NULL); + sink->mux = gst_element_factory_make("matroskamux", NULL); + sink->filesink = gst_element_factory_make("filesink", NULL); + sink->removing = false; + + QString filename = QDir::homePath() + "/" + QDateTime::currentDateTime().toString() + ".mkv"; + g_object_set(G_OBJECT(sink->filesink), "location", qPrintable(filename), NULL); + + gst_object_ref(sink->queue); + gst_object_ref(sink->mux); + gst_object_ref(sink->filesink); + + gst_bin_add_many(GST_BIN(_pipeline), sink->queue, sink->mux, sink->filesink, NULL); + gst_element_link_many(sink->queue, sink->mux, sink->filesink, NULL); + + gst_element_sync_state_with_parent(sink->queue); + gst_element_sync_state_with_parent(sink->mux); + gst_element_sync_state_with_parent(sink->filesink); + + GstPad* sinkpad = gst_element_get_static_pad(sink->queue, "sink"); + gst_pad_link(sink->teepad, sinkpad); + gst_object_unref(sinkpad); + + _recording = true; + emit recordingChanged(); +} + +void VideoReceiver::_stopRecording(void) +{ + // exit immediately if we are not recording + if(_pipeline == NULL || !_recording) { + return; + } + + gst_pad_add_probe(sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCB, sink, NULL); + + _recording = false; + emit recordingChanged(); +} VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) #if defined(QGC_GST_STREAMING) - , _pipeline(NULL) + , _recording(false) , _videoSink(NULL) , _socket(NULL) , _serverPresent(false) @@ -36,11 +161,12 @@ VideoReceiver::VideoReceiver(QObject* parent) VideoReceiver::~VideoReceiver() { #if defined(QGC_GST_STREAMING) - stop(); - setVideoSink(NULL); - if(_socket) { - delete _socket; - } +// stop(); +// setVideoSink(NULL); +// if(_socket) { +// delete _socket; +// } + EOS(); #endif } @@ -149,6 +275,15 @@ void VideoReceiver::start() GstElement* demux = NULL; GstElement* parser = NULL; GstElement* decoder = NULL; + GstElement* queue1 = NULL; + + // Pads to link queues and tee + GstPad* teeSrc1 = NULL; // tee source pad 1 + GstPad* q1Sink = NULL; // queue1 sink pad + + // /queue1---decoder---_videosink + //datasource---demux---parser---tee + // \queue2---matroskamux---filesink do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { @@ -196,21 +331,51 @@ void VideoReceiver::start() break; } - gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, decoder, _videoSink, NULL); + if((tee = gst_element_factory_make("tee", "stream-file-tee")) == NULL) { + qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('tee')"; + break; + } - gboolean res = FALSE; + if((queue1 = gst_element_factory_make("queue", NULL)) == NULL) { + qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('queue1')"; + break; + } - if(isUdp) { - res = gst_element_link_many(dataSource, demux, parser, decoder, _videoSink, NULL); - } else { - res = gst_element_link_many(demux, parser, decoder, _videoSink, NULL); + gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, tee, queue1, decoder, _videoSink, NULL); + +// if(isUdp) { +// res = gst_element_link_many(dataSource, demux, parser, decoder, tee, _videoSink, NULL); +// } else { +// res = gst_element_link_many(demux, parser, decoder, tee, _videoSink, NULL); +// } + + // Link the pipeline in front of the tee + if(!gst_element_link_many(dataSource, demux, parser, tee, NULL)) { + qCritical() << "Unable to link datasource and tee."; + break; } - if (!res) { - qCritical() << "VideoReceiver::start() failed. Error with gst_element_link_many()"; + // Link the videostream to queue1 + if(!gst_element_link_many(queue1, decoder, _videoSink, NULL)) { + qCritical() << "Unable to link queue1 and videosink."; break; } + // Link the queues to the tee + teeSrc1 = gst_element_get_request_pad(tee, "src_%u"); + q1Sink = gst_element_get_static_pad(queue1, "sink"); + + // Link the tee to queue1 + if (gst_pad_link(teeSrc1, q1Sink) != GST_PAD_LINK_OK ){ + qCritical() << "Tee for queue1 could not be linked.\n"; + break; + } + + gst_object_unref(teeSrc1); + gst_object_unref(q1Sink); + + teeSrc1 = q1Sink = NULL; + queue1 = NULL; dataSource = demux = parser = decoder = NULL; GstBus* bus = NULL; @@ -253,18 +418,35 @@ void VideoReceiver::start() dataSource = NULL; } + if (tee != NULL) { + gst_object_unref(tee); + dataSource = NULL; + } + + if (queue1 != NULL) { + gst_object_unref(queue1); + dataSource = NULL; + } + if (_pipeline != NULL) { gst_object_unref(_pipeline); _pipeline = NULL; } } + + qDebug() << "Video Receiver started."; #endif } +void VideoReceiver::EOS() { + gst_element_send_event(_pipeline, gst_event_new_eos()); +} + void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) if (_pipeline != NULL) { + qCritical() << "stopping pipeline"; gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); _pipeline = NULL; @@ -282,9 +464,12 @@ void VideoReceiver::setUri(const QString & uri) #if defined(QGC_GST_STREAMING) void VideoReceiver::_onBusMessage(GstMessage* msg) { + //qDebug() << "Got bus message"; + switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: - stop(); + qDebug() << "Got EOS"; + //stop(); break; case GST_MESSAGE_ERROR: do { diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 6069a99a1..4477b5109 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -29,6 +29,8 @@ class VideoReceiver : public QObject { Q_OBJECT public: + Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) + explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); @@ -36,10 +38,18 @@ public: void setVideoSink(GstElement* sink); #endif + bool recording() { return _recording; } + +signals: + void recordingChanged(); + public slots: void start (); + void EOS (); void stop (); void setUri (const QString& uri); + void _stopRecording(); + void _startRecording(); private slots: #if defined(QGC_GST_STREAMING) @@ -49,16 +59,34 @@ private slots: #endif private: - #if defined(QGC_GST_STREAMING) - void _onBusMessage(GstMessage* message); - static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer data); + typedef struct + { + GstPad* teepad; + GstElement* queue; + GstElement* mux; + GstElement* filesink; + gboolean removing; + } Sink; + + static Sink* sink; + + void _onBusMessage(GstMessage* message); + static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer user_data); + static gboolean _eosCB(GstBus* bus, GstMessage* message, gpointer user_data); + static GstPadProbeReturn _unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); + + bool _recording; + static GstElement* tee; + #endif QString _uri; #if defined(QGC_GST_STREAMING) - GstElement* _pipeline; + static GstElement* _pipeline; + static GstElement* _pipeline2; + GstElement* _videoSink; #endif diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index 73c339325..d2bfaa8a2 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -489,6 +489,27 @@ Item { } } + //------------------------------------------------------------------------- + //-- Video Recording + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height + color: colorRed + } + + MouseArea { + anchors.fill: parent + onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver._stopRecording() : QGroundControl.videoManager.videoReceiver._startRecording() + } + } + //------------------------------------------------------------------------- //-- Mode Selector QGCLabel { -- GitLab From efe64000c0fd175717e79ec2f87420e049d8e9f0 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 3 Jan 2017 19:35:20 -0500 Subject: [PATCH 184/398] Hide recording icon if no video stream present at startup Other minor formatting and stability improvements --- src/VideoStreaming/VideoReceiver.cc | 146 +++++++++++++---------- src/VideoStreaming/VideoReceiver.h | 41 ++++--- src/ui/toolbar/MainToolBarIndicators.qml | 5 +- 3 files changed, 109 insertions(+), 83 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index b12189cad..1f3b0fe1a 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -20,124 +20,143 @@ #include #include -VideoReceiver::Sink* VideoReceiver::sink = NULL; +VideoReceiver::Sink* VideoReceiver::_sink = NULL; GstElement* VideoReceiver::_pipeline = NULL; GstElement* VideoReceiver::_pipeline2 = NULL; -GstElement* VideoReceiver::tee = NULL; +GstElement* VideoReceiver::_tee = NULL; +// -EOS has appeared on the bus of the temporary pipeline +// -At this point all of the recoring elements have been flushed, and the video file has been finalized +// -Now we can remove the temporary pipeline and its elements gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_data) { Q_UNUSED(bus); Q_UNUSED(message); Q_UNUSED(user_data); - gst_bin_remove(GST_BIN(_pipeline2), sink->queue); - gst_bin_remove(GST_BIN(_pipeline2), sink->mux); - gst_bin_remove(GST_BIN(_pipeline2), sink->filesink); + gst_bin_remove(GST_BIN(_pipeline2), _sink->queue); + gst_bin_remove(GST_BIN(_pipeline2), _sink->mux); + gst_bin_remove(GST_BIN(_pipeline2), _sink->filesink); gst_element_set_state(_pipeline2, GST_STATE_NULL); gst_object_unref(_pipeline2); - gst_element_set_state(sink->filesink, GST_STATE_NULL); - gst_element_set_state(sink->mux, GST_STATE_NULL); - gst_element_set_state(sink->queue, GST_STATE_NULL); + gst_element_set_state(_sink->filesink, GST_STATE_NULL); + gst_element_set_state(_sink->mux, GST_STATE_NULL); + gst_element_set_state(_sink->queue, GST_STATE_NULL); - gst_object_unref(sink->queue); - gst_object_unref(sink->mux); - gst_object_unref(sink->filesink); + gst_object_unref(_sink->queue); + gst_object_unref(_sink->mux); + gst_object_unref(_sink->filesink); - delete sink; - sink = NULL; + delete _sink; + _sink = NULL; return true; } +// -Unlink the recording branch from the tee in the main pipeline +// -Create a second temporary pipeline, and place the recording branch elements into that pipeline +// -Setup watch and handler for EOS event on the temporary pipeline's bus +// -Send an EOS event at the beginning of that pipeline and set up a callback for GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { Q_UNUSED(pad); Q_UNUSED(info); Q_UNUSED(user_data); - if(!g_atomic_int_compare_and_exchange(&sink->removing, FALSE, TRUE)) + // We will only execute once + if(!g_atomic_int_compare_and_exchange(&_sink->removing, FALSE, TRUE)) return GST_PAD_PROBE_OK; - GstPad* sinkpad = gst_element_get_static_pad(sink->queue, "sink"); - gst_pad_unlink (sink->teepad, sinkpad); - gst_object_unref (sinkpad); - - gst_element_release_request_pad(tee, sink->teepad); - gst_object_unref(sink->teepad); - // Also unlinks and unrefs - gst_bin_remove (GST_BIN (_pipeline), sink->queue); - gst_bin_remove (GST_BIN (_pipeline), sink->mux); - gst_bin_remove (GST_BIN (_pipeline), sink->filesink); + gst_bin_remove_many(GST_BIN (_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); + + // Give tee its pad back + gst_element_release_request_pad(_tee, _sink->teepad); + gst_object_unref(_sink->teepad); + // Create temporary pipeline _pipeline2 = gst_pipeline_new("pipe2"); - gst_bin_add_many(GST_BIN(_pipeline2), sink->queue, sink->mux, sink->filesink, NULL); - gst_element_link_many(sink->queue, sink->mux, sink->filesink, NULL); + // Put our elements from the recording branch into the temporary pipeline + gst_bin_add_many(GST_BIN(_pipeline2), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); + // Add watch for EOS event GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline2)); gst_bus_add_signal_watch(bus); g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCB), NULL); + gst_object_unref(bus); if(gst_element_set_state(_pipeline2, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { qDebug() << "problem starting pipeline2"; } - GstPad* eosInjectPad = gst_element_get_static_pad(sink->queue, "sink"); - gst_pad_send_event(eosInjectPad, gst_event_new_eos()); - gst_object_unref(eosInjectPad); + // Send EOS at the beginning of the pipeline + GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); + gst_pad_send_event(sinkpad, gst_event_new_eos()); + gst_object_unref(sinkpad); return GST_PAD_PROBE_REMOVE; } -void VideoReceiver::_startRecording(void) +// When we finish our pipeline will look like this: +// +// +-->queue-->decoder-->_videosink +// | +// datasource-->demux-->parser-->tee +// | +// | +--------------_sink-------------------+ +// | | | +// we are adding these elements-> +->teepad-->queue-->matroskamux-->_filesink | +// | | +// +--------------------------------------+ +void VideoReceiver::startRecording(void) { // exit immediately if we are already recording if(_pipeline == NULL || _recording) { return; } - sink = g_new0(Sink, 1); + _sink = g_new0(Sink, 1); - sink->teepad = gst_element_get_request_pad(tee, "src_%u"); - sink->queue = gst_element_factory_make("queue", NULL); - sink->mux = gst_element_factory_make("matroskamux", NULL); - sink->filesink = gst_element_factory_make("filesink", NULL); - sink->removing = false; + _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); + _sink->queue = gst_element_factory_make("queue", NULL); + _sink->mux = gst_element_factory_make("matroskamux", NULL); + _sink->filesink = gst_element_factory_make("filesink", NULL); + _sink->removing = false; QString filename = QDir::homePath() + "/" + QDateTime::currentDateTime().toString() + ".mkv"; - g_object_set(G_OBJECT(sink->filesink), "location", qPrintable(filename), NULL); + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(filename), NULL); - gst_object_ref(sink->queue); - gst_object_ref(sink->mux); - gst_object_ref(sink->filesink); + gst_object_ref(_sink->queue); + gst_object_ref(_sink->mux); + gst_object_ref(_sink->filesink); - gst_bin_add_many(GST_BIN(_pipeline), sink->queue, sink->mux, sink->filesink, NULL); - gst_element_link_many(sink->queue, sink->mux, sink->filesink, NULL); + gst_bin_add_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); - gst_element_sync_state_with_parent(sink->queue); - gst_element_sync_state_with_parent(sink->mux); - gst_element_sync_state_with_parent(sink->filesink); + gst_element_sync_state_with_parent(_sink->queue); + gst_element_sync_state_with_parent(_sink->mux); + gst_element_sync_state_with_parent(_sink->filesink); - GstPad* sinkpad = gst_element_get_static_pad(sink->queue, "sink"); - gst_pad_link(sink->teepad, sinkpad); + GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); + gst_pad_link(_sink->teepad, sinkpad); gst_object_unref(sinkpad); _recording = true; emit recordingChanged(); } -void VideoReceiver::_stopRecording(void) +void VideoReceiver::stopRecording(void) { // exit immediately if we are not recording if(_pipeline == NULL || !_recording) { return; } - gst_pad_add_probe(sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCB, sink, NULL); + gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCB, _sink, NULL); _recording = false; emit recordingChanged(); @@ -246,6 +265,16 @@ void VideoReceiver::_timeout() } #endif + +// When we finish our pipeline will look like this: +// +// +-->queue-->decoder-->_videosink +// | +// datasource-->demux-->parser-->tee +// +// ^ +// | +// +-Here we will later link elements for recording void VideoReceiver::start() { #if defined(QGC_GST_STREAMING) @@ -281,10 +310,6 @@ void VideoReceiver::start() GstPad* teeSrc1 = NULL; // tee source pad 1 GstPad* q1Sink = NULL; // queue1 sink pad - // /queue1---decoder---_videosink - //datasource---demux---parser---tee - // \queue2---matroskamux---filesink - do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_pipeline_new()"; @@ -331,7 +356,7 @@ void VideoReceiver::start() break; } - if((tee = gst_element_factory_make("tee", "stream-file-tee")) == NULL) { + if((_tee = gst_element_factory_make("tee", "stream-file-tee")) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('tee')"; break; } @@ -341,7 +366,7 @@ void VideoReceiver::start() break; } - gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, tee, queue1, decoder, _videoSink, NULL); + gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, _tee, queue1, decoder, _videoSink, NULL); // if(isUdp) { // res = gst_element_link_many(dataSource, demux, parser, decoder, tee, _videoSink, NULL); @@ -350,7 +375,7 @@ void VideoReceiver::start() // } // Link the pipeline in front of the tee - if(!gst_element_link_many(dataSource, demux, parser, tee, NULL)) { + if(!gst_element_link_many(dataSource, demux, parser, _tee, NULL)) { qCritical() << "Unable to link datasource and tee."; break; } @@ -362,7 +387,7 @@ void VideoReceiver::start() } // Link the queues to the tee - teeSrc1 = gst_element_get_request_pad(tee, "src_%u"); + teeSrc1 = gst_element_get_request_pad(_tee, "src_%u"); q1Sink = gst_element_get_static_pad(queue1, "sink"); // Link the tee to queue1 @@ -418,8 +443,8 @@ void VideoReceiver::start() dataSource = NULL; } - if (tee != NULL) { - gst_object_unref(tee); + if (_tee != NULL) { + gst_object_unref(_tee); dataSource = NULL; } @@ -445,8 +470,9 @@ void VideoReceiver::EOS() { void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) + qDebug() << "stop()"; if (_pipeline != NULL) { - qCritical() << "stopping pipeline"; + qDebug() << "Stopping pipeline"; gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); _pipeline = NULL; diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 4477b5109..276a031d7 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -29,13 +29,13 @@ class VideoReceiver : public QObject { Q_OBJECT public: - Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) + Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); #if defined(QGC_GST_STREAMING) - void setVideoSink(GstElement* sink); + void setVideoSink(GstElement* _sink); #endif bool recording() { return _recording; } @@ -44,12 +44,12 @@ signals: void recordingChanged(); public slots: - void start (); - void EOS (); - void stop (); - void setUri (const QString& uri); - void _stopRecording(); - void _startRecording(); + void start (); + void EOS (); + void stop (); + void setUri (const QString& uri); + void stopRecording (); + void startRecording (); private slots: #if defined(QGC_GST_STREAMING) @@ -60,34 +60,33 @@ private slots: private: #if defined(QGC_GST_STREAMING) + typedef struct { - GstPad* teepad; - GstElement* queue; - GstElement* mux; - GstElement* filesink; - gboolean removing; + GstPad* teepad; + GstElement* queue; + GstElement* mux; + GstElement* filesink; + gboolean removing; } Sink; - static Sink* sink; + bool _recording; + static Sink* _sink; + static GstElement* _tee; void _onBusMessage(GstMessage* message); static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer user_data); static gboolean _eosCB(GstBus* bus, GstMessage* message, gpointer user_data); static GstPadProbeReturn _unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); - bool _recording; - static GstElement* tee; - #endif QString _uri; #if defined(QGC_GST_STREAMING) - static GstElement* _pipeline; - static GstElement* _pipeline2; - - GstElement* _videoSink; + static GstElement* _pipeline; + static GstElement* _pipeline2; + GstElement* _videoSink; #endif //-- Wait for Video Server to show up before starting diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index d2bfaa8a2..46b8940f9 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -494,7 +494,8 @@ Item { Item { anchors.top: parent.top anchors.bottom: parent.bottom - width: height + width: height + visible: QGroundControl.videoManager.videoRunning Rectangle { anchors.top: parent.top @@ -506,7 +507,7 @@ Item { MouseArea { anchors.fill: parent - onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver._stopRecording() : QGroundControl.videoManager.videoReceiver._startRecording() + onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() } } -- GitLab From 0f9149b1bf39e69f3a5c5e24a7fa34a74fa93be1 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 00:34:14 -0500 Subject: [PATCH 185/398] Fix repeated calls to restart VideoReceiver --- src/FlightDisplay/VideoManager.cc | 4 +++- src/VideoStreaming/VideoReceiver.h | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index d920acc4d..85437d3bd 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -236,7 +236,9 @@ void VideoManager::_updateTimer() } else { - if(_videoSurface && _videoSurface->lastFrame()) { +// if(_videoSurface && _videoSurface->lastFrame()) { + if(_videoSurface && _videoReceiver->streaming()) { + qDebug() << _videoSurface->lastFrame(); if(!_videoRunning) { _videoRunning = true; diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 276a031d7..2a791fe35 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -30,6 +30,7 @@ class VideoReceiver : public QObject Q_OBJECT public: Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) + Q_PROPERTY(bool streaming READ streaming NOTIFY streamingChanged) explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); @@ -39,9 +40,11 @@ public: #endif bool recording() { return _recording; } + bool streaming() { return GST_STATE(_pipeline) == GST_STATE_PLAYING; } signals: void recordingChanged(); + void streamingChanged(); public slots: void start (); @@ -71,6 +74,7 @@ private: } Sink; bool _recording; + bool _streaming; static Sink* _sink; static GstElement* _tee; -- GitLab From ab0863556ee978c43598d23cfab16aa5a144e465 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 00:51:33 -0500 Subject: [PATCH 186/398] Change VideoReceiver destructor to close video file correctly --- src/VideoStreaming/VideoReceiver.cc | 19 +++++++------------ src/VideoStreaming/VideoReceiver.h | 1 - 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 1f3b0fe1a..64202b852 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -180,12 +180,12 @@ VideoReceiver::VideoReceiver(QObject* parent) VideoReceiver::~VideoReceiver() { #if defined(QGC_GST_STREAMING) -// stop(); -// setVideoSink(NULL); -// if(_socket) { -// delete _socket; -// } - EOS(); + stopRecording(); + stop(); + setVideoSink(NULL); + if(_socket) { + delete _socket; + } #endif } @@ -463,14 +463,9 @@ void VideoReceiver::start() #endif } -void VideoReceiver::EOS() { - gst_element_send_event(_pipeline, gst_event_new_eos()); -} - void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) - qDebug() << "stop()"; if (_pipeline != NULL) { qDebug() << "Stopping pipeline"; gst_element_set_state(_pipeline, GST_STATE_NULL); @@ -495,7 +490,7 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: qDebug() << "Got EOS"; - //stop(); + stop(); break; case GST_MESSAGE_ERROR: do { diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 2a791fe35..847faf749 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -48,7 +48,6 @@ signals: public slots: void start (); - void EOS (); void stop (); void setUri (const QString& uri); void stopRecording (); -- GitLab From ac4080a1375db947623779bf50e1b57c4f3c8bda Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 01:07:29 -0500 Subject: [PATCH 187/398] Add vertical separator for video recording button --- src/VideoStreaming/VideoReceiver.cc | 2 +- src/ui/toolbar/MainToolBarIndicators.qml | 53 ++++++++++++++---------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 64202b852..e382d8ed3 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -70,7 +70,7 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g return GST_PAD_PROBE_OK; // Also unlinks and unrefs - gst_bin_remove_many(GST_BIN (_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_bin_remove_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); // Give tee its pad back gst_element_release_request_pad(_tee, _sink->teepad); diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index 46b8940f9..d6d700294 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -489,28 +489,6 @@ Item { } } - //------------------------------------------------------------------------- - //-- Video Recording - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - visible: QGroundControl.videoManager.videoRunning - - Rectangle { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height - color: colorRed - } - - MouseArea { - anchors.fill: parent - onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() - } - } - //------------------------------------------------------------------------- //-- Mode Selector QGCLabel { @@ -563,6 +541,37 @@ Item { onClicked: flightModesMenu.popup() } } // QGCLabel - Flight mode selector + + Rectangle { +// anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + anchors.top: parent.top + anchors.bottom: parent.bottom + width: 1 + color: qgcPal.text + visible: QGroundControl.videoManager.videoRunning + } + + //------------------------------------------------------------------------- + //-- Video Recording + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + visible: QGroundControl.videoManager.videoRunning + + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height + color: colorRed + } + + MouseArea { + anchors.fill: parent + onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() + } + } } // Row - Vehicle indicators Image { -- GitLab From 05c8fde3c14e08b205456024c207ed2c2fe13164 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 12:28:34 -0500 Subject: [PATCH 188/398] Add button in general settings to select location to save video files --- src/FlightDisplay/VideoManager.cc | 14 ++++++++++++ src/FlightDisplay/VideoManager.h | 5 +++++ src/VideoStreaming/VideoReceiver.cc | 17 ++++++++++++-- src/VideoStreaming/VideoReceiver.h | 12 +++++----- src/ui/preferences/GeneralSettings.qml | 31 ++++++++++++++++++++++++++ 5 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 85437d3bd..943ff18d1 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -27,6 +27,7 @@ static const char* kVideoSourceKey = "VideoSource"; static const char* kVideoUDPPortKey = "VideoUDPPort"; static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; +static const char* kVideoSavePathKey = "VideoSaveDir"; #if defined(QGC_GST_STREAMING) static const char* kUDPStream = "UDP Video Stream"; static const char* kRTSPStream = "RTSP Video Stream"; @@ -80,6 +81,7 @@ VideoManager::setToolbox(QGCToolbox *toolbox) setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL } + setVideoSavePath(settings.value(kVideoSavePathKey, QDir::homePath()).toString()); #endif _init = true; #if defined(QGC_GST_STREAMING) @@ -186,6 +188,17 @@ VideoManager::setRtspURL(QString url) */ } +void +VideoManager::setVideoSavePath(QString path) +{ + _videoSavePath = path; + QSettings settings; + settings.setValue(kVideoSavePathKey, path); + if(_videoReceiver) + _videoReceiver->setVideoSavePath(_videoSavePath); + emit videoSavePathChanged(); +} + //----------------------------------------------------------------------------- QStringList VideoManager::videoSourceList() @@ -265,6 +278,7 @@ void VideoManager::_updateVideo() _videoReceiver->setUri(QStringLiteral("udp://0.0.0.0:%1").arg(_udpPort)); else _videoReceiver->setUri(_rtspURL); + _videoReceiver->setVideoSavePath(_videoSavePath); #endif _videoReceiver->start(); } diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index 2ff5cfac6..1e1cbbe7b 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -37,6 +37,7 @@ public: Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) + Q_PROPERTY(QString videoSavePath READ videoSavePath WRITE setVideoSavePath NOTIFY videoSavePathChanged) Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) @@ -49,6 +50,7 @@ public: QStringList videoSourceList (); quint16 udpPort () { return _udpPort; } QString rtspURL () { return _rtspURL; } + QString videoSavePath () { return _videoSavePath; } #if defined(QGC_DISABLE_UVC) bool uvcEnabled () { return false; } @@ -59,6 +61,7 @@ public: void setVideoSource (QString vSource); void setUdpPort (quint16 port); void setRtspURL (QString url); + void setVideoSavePath (QString path); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); @@ -72,6 +75,7 @@ signals: void videoSourceIDChanged (); void udpPortChanged (); void rtspURLChanged (); + void videoSavePathChanged (); private: void _updateTimer (); @@ -89,6 +93,7 @@ private: QStringList _videoSourceList; quint16 _udpPort; QString _rtspURL; + QString _videoSavePath; bool _init; }; diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index e382d8ed3..e0385816e 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -19,6 +19,7 @@ #include #include #include +#include VideoReceiver::Sink* VideoReceiver::_sink = NULL; GstElement* VideoReceiver::_pipeline = NULL; @@ -127,8 +128,14 @@ void VideoReceiver::startRecording(void) _sink->filesink = gst_element_factory_make("filesink", NULL); _sink->removing = false; - QString filename = QDir::homePath() + "/" + QDateTime::currentDateTime().toString() + ".mkv"; - g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(filename), NULL); + QString fileName; + if(QSysInfo::WindowsVersion != QSysInfo::WV_None) { + fileName = _path + "\\QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; + } else { + fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; + } + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); + qDebug() << "New video file:" << fileName; gst_object_ref(_sink->queue); gst_object_ref(_sink->mux); @@ -482,6 +489,12 @@ void VideoReceiver::setUri(const QString & uri) _uri = uri; } +void VideoReceiver::setVideoSavePath(const QString & path) +{ + _path = path; + qDebug() << "New Path:" << _path; +} + #if defined(QGC_GST_STREAMING) void VideoReceiver::_onBusMessage(GstMessage* msg) { diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 847faf749..27c78a531 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -47,11 +47,12 @@ signals: void streamingChanged(); public slots: - void start (); - void stop (); - void setUri (const QString& uri); - void stopRecording (); - void startRecording (); + void start (); + void stop (); + void setUri (const QString& uri); + void setVideoSavePath (const QString& path); + void stopRecording (); + void startRecording (); private slots: #if defined(QGC_GST_STREAMING) @@ -85,6 +86,7 @@ private: #endif QString _uri; + QString _path; #if defined(QGC_GST_STREAMING) static GstElement* _pipeline; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index d22c60515..ba381f8f7 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -41,6 +41,18 @@ QGCView { QGCPalette { id: qgcPal } + FileDialog { + id: fileDialog + title: "Choose a location to save video files." + folder: shortcuts.home + selectFolder: true + onAccepted: { + var path = fileDialog.fileUrl.toString(); + path = path.replace(/^(file:\/{2})/,""); + QGroundControl.videoManager.videoSavePath = path + } + } + QGCViewPanel { id: panel anchors.fill: parent @@ -499,6 +511,25 @@ QGCView { } } } + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.videoManager.isGStreamer + QGCLabel { + anchors.baseline: pathField.baseline + text: qsTr("Save Path:") + width: _labelWidth + } + QGCTextField { + id: pathField + width: _editFieldWidth + readOnly: true + text: QGroundControl.videoManager.videoSavePath + } + Button { + text: "Browse" + onClicked: fileDialog.visible = true + } + } } } -- GitLab From 782c92c4a7e25ab60f9ab63ea58e1fa6c1035d3e Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 15:37:33 -0500 Subject: [PATCH 189/398] Add Camera image to recording button --- qgcresources.qrc | 1 + src/ui/toolbar/Images/CameraIcon.svg | 44 ++++++++++++++++++++++++ src/ui/toolbar/MainToolBarIndicators.qml | 11 ++++++ 3 files changed, 56 insertions(+) create mode 100644 src/ui/toolbar/Images/CameraIcon.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 5c714bbed..bb2e8de56 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -156,6 +156,7 @@ src/ui/toolbar/Images/Signal100.svg src/ui/toolbar/Images/TelemRSSI.svg src/ui/toolbar/Images/Yield.svg + src/ui/toolbar/Images/CameraIcon.svg src/MissionManager/CogWheel.svg src/AutoPilotPlugins/Common/Images/StationMode.svg src/AutoPilotPlugins/Common/Images/APMode.svg diff --git a/src/ui/toolbar/Images/CameraIcon.svg b/src/ui/toolbar/Images/CameraIcon.svg new file mode 100644 index 000000000..1509445bd --- /dev/null +++ b/src/ui/toolbar/Images/CameraIcon.svg @@ -0,0 +1,44 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index d6d700294..c52975b94 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -567,6 +567,17 @@ Item { color: colorRed } + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: height * 0.625 + sourceSize.width: width + source: "/qmlimages/CameraIcon.svg" + fillMode: Image.PreserveAspectFit + color: colorWhite + } + MouseArea { anchors.fill: parent onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() -- GitLab From afca96f8c058344689aa06808f7e93baa90eaa52 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 15:03:36 -0500 Subject: [PATCH 190/398] Move recording button to flight view --- src/FlightDisplay/FlightDisplayView.qml | 36 ++++++++++++++++ src/FlightDisplay/VideoManager.cc | 3 +- src/VideoStreaming/VideoReceiver.cc | 54 +++++++++++++----------- src/ui/toolbar/MainToolBarIndicators.qml | 42 ------------------ 4 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index be355f7a4..b2da710a7 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -260,6 +260,42 @@ QGCView { visible: singleVehicleView.checked } + // Button to start/stop video recording + Item { + z: _flightVideoPipControl.z + 1 + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + anchors.bottom: _flightVideo.bottom + anchors.right: _flightVideo.right + height: ScreenTools.defaultFontPixelHeight * 2 + width: height + visible: QGroundControl.videoManager.videoRunning + opacity: 0.75 + + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height + color: "red" + } + + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: height * 0.625 + sourceSize.width: width + source: "/qmlimages/CameraIcon.svg" + fillMode: Image.PreserveAspectFit + color: "white" + } + + MouseArea { + anchors.fill: parent + onClicked: QGroundControl.videoManager.videoReceiver.recording ? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() + } + } + MultiVehicleList { anchors.margins: _margins anchors.top: singleMultiSelector.bottom diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 943ff18d1..3c64da7b3 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -27,7 +27,7 @@ static const char* kVideoSourceKey = "VideoSource"; static const char* kVideoUDPPortKey = "VideoUDPPort"; static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; -static const char* kVideoSavePathKey = "VideoSaveDir"; +static const char* kVideoSavePathKey = "VideoSavePath"; #if defined(QGC_GST_STREAMING) static const char* kUDPStream = "UDP Video Stream"; static const char* kRTSPStream = "RTSP Video Stream"; @@ -249,7 +249,6 @@ void VideoManager::_updateTimer() } else { -// if(_videoSurface && _videoSurface->lastFrame()) { if(_videoSurface && _videoReceiver->streaming()) { qDebug() << _videoSurface->lastFrame(); if(!_videoRunning) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index e0385816e..bf192ab0e 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -29,6 +29,7 @@ GstElement* VideoReceiver::_tee = NULL; // -EOS has appeared on the bus of the temporary pipeline // -At this point all of the recoring elements have been flushed, and the video file has been finalized // -Now we can remove the temporary pipeline and its elements +#if defined(QGC_GST_STREAMING) gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_data) { Q_UNUSED(bus); @@ -55,11 +56,13 @@ gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_d return true; } +#endif // -Unlink the recording branch from the tee in the main pipeline // -Create a second temporary pipeline, and place the recording branch elements into that pipeline // -Setup watch and handler for EOS event on the temporary pipeline's bus // -Send an EOS event at the beginning of that pipeline and set up a callback for +#if defined(QGC_GST_STREAMING) GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { Q_UNUSED(pad); @@ -101,6 +104,7 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g return GST_PAD_PROBE_REMOVE; } +#endif // When we finish our pipeline will look like this: // @@ -115,16 +119,16 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g // +--------------------------------------+ void VideoReceiver::startRecording(void) { +#if defined(QGC_GST_STREAMING) // exit immediately if we are already recording if(_pipeline == NULL || _recording) { return; } - _sink = g_new0(Sink, 1); - - _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); - _sink->queue = gst_element_factory_make("queue", NULL); - _sink->mux = gst_element_factory_make("matroskamux", NULL); + _sink = g_new0(Sink, 1); + _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); + _sink->queue = gst_element_factory_make("queue", NULL); + _sink->mux = gst_element_factory_make("matroskamux", NULL); _sink->filesink = gst_element_factory_make("filesink", NULL); _sink->removing = false; @@ -154,10 +158,12 @@ void VideoReceiver::startRecording(void) _recording = true; emit recordingChanged(); +#endif } void VideoReceiver::stopRecording(void) { +#if defined(QGC_GST_STREAMING) // exit immediately if we are not recording if(_pipeline == NULL || !_recording) { return; @@ -167,6 +173,7 @@ void VideoReceiver::stopRecording(void) _recording = false; emit recordingChanged(); +#endif } VideoReceiver::VideoReceiver(QObject* parent) @@ -211,16 +218,16 @@ void VideoReceiver::setVideoSink(GstElement* sink) #endif #if defined(QGC_GST_STREAMING) -static void newPadCB(GstElement * element, GstPad* pad, gpointer data) +static void newPadCB(GstElement* element, GstPad* pad, gpointer data) { - gchar *name; + gchar* name; name = gst_pad_get_name(pad); g_print("A new pad %s was created\n", name); - GstCaps * p_caps = gst_pad_get_pad_template_caps (pad); - gchar * description = gst_caps_to_string(p_caps); + GstCaps* p_caps = gst_pad_get_pad_template_caps (pad); + gchar* description = gst_caps_to_string(p_caps); qDebug() << p_caps << ", " << description; g_free(description); - GstElement * p_rtph264depay = GST_ELEMENT(data); + GstElement* p_rtph264depay = GST_ELEMENT(data); if(gst_element_link_pads(element, name, p_rtph264depay, "sink") == false) qCritical() << "newPadCB : failed to link elements\n"; g_free(name); @@ -272,7 +279,6 @@ void VideoReceiver::_timeout() } #endif - // When we finish our pipeline will look like this: // // +-->queue-->decoder-->_videosink @@ -313,7 +319,7 @@ void VideoReceiver::start() GstElement* decoder = NULL; GstElement* queue1 = NULL; - // Pads to link queues and tee + // Pads to link queue and tee GstPad* teeSrc1 = NULL; // tee source pad 1 GstPad* q1Sink = NULL; // queue1 sink pad @@ -375,16 +381,17 @@ void VideoReceiver::start() gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, _tee, queue1, decoder, _videoSink, NULL); -// if(isUdp) { -// res = gst_element_link_many(dataSource, demux, parser, decoder, tee, _videoSink, NULL); -// } else { -// res = gst_element_link_many(demux, parser, decoder, tee, _videoSink, NULL); -// } - - // Link the pipeline in front of the tee - if(!gst_element_link_many(dataSource, demux, parser, _tee, NULL)) { - qCritical() << "Unable to link datasource and tee."; - break; + if(isUdp) { + // Link the pipeline in front of the tee + if(!gst_element_link_many(dataSource, demux, parser, _tee, NULL)) { + qCritical() << "Unable to link datasource and tee."; + break; + } + } else { + if(!gst_element_link_many(demux, parser, _tee, NULL)) { + qCritical() << "Unable to link datasource and tee."; + break; + } } // Link the videostream to queue1 @@ -498,11 +505,8 @@ void VideoReceiver::setVideoSavePath(const QString & path) #if defined(QGC_GST_STREAMING) void VideoReceiver::_onBusMessage(GstMessage* msg) { - //qDebug() << "Got bus message"; - switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: - qDebug() << "Got EOS"; stop(); break; case GST_MESSAGE_ERROR: diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index c52975b94..73c339325 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -541,48 +541,6 @@ Item { onClicked: flightModesMenu.popup() } } // QGCLabel - Flight mode selector - - Rectangle { -// anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.top: parent.top - anchors.bottom: parent.bottom - width: 1 - color: qgcPal.text - visible: QGroundControl.videoManager.videoRunning - } - - //------------------------------------------------------------------------- - //-- Video Recording - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - visible: QGroundControl.videoManager.videoRunning - - Rectangle { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height - color: colorRed - } - - QGCColoredImage { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - width: height * 0.625 - sourceSize.width: width - source: "/qmlimages/CameraIcon.svg" - fillMode: Image.PreserveAspectFit - color: colorWhite - } - - MouseArea { - anchors.fill: parent - onClicked: QGroundControl.videoManager.videoReceiver.recording? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() - } - } } // Row - Vehicle indicators Image { -- GitLab From 8115753bd47c9ec9706bb38c8cd48cb709cea7e3 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 21:16:20 -0500 Subject: [PATCH 191/398] Construct pipeline in fewer steps --- src/VideoStreaming/VideoReceiver.cc | 48 +++++++---------------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index bf192ab0e..55e12df29 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -316,12 +316,8 @@ void VideoReceiver::start() GstCaps* caps = NULL; GstElement* demux = NULL; GstElement* parser = NULL; + GstElement* queue = NULL; GstElement* decoder = NULL; - GstElement* queue1 = NULL; - - // Pads to link queue and tee - GstPad* teeSrc1 = NULL; // tee source pad 1 - GstPad* q1Sink = NULL; // queue1 sink pad do { if ((_pipeline = gst_pipeline_new("receiver")) == NULL) { @@ -369,53 +365,32 @@ void VideoReceiver::start() break; } - if((_tee = gst_element_factory_make("tee", "stream-file-tee")) == NULL) { + if((_tee = gst_element_factory_make("tee", NULL)) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('tee')"; break; } - if((queue1 = gst_element_factory_make("queue", NULL)) == NULL) { + if((queue = gst_element_factory_make("queue", NULL)) == NULL) { qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('queue1')"; break; } - gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, _tee, queue1, decoder, _videoSink, NULL); + gst_bin_add_many(GST_BIN(_pipeline), dataSource, demux, parser, _tee, queue, decoder, _videoSink, NULL); if(isUdp) { // Link the pipeline in front of the tee - if(!gst_element_link_many(dataSource, demux, parser, _tee, NULL)) { - qCritical() << "Unable to link datasource and tee."; + if(!gst_element_link_many(dataSource, demux, parser, _tee, queue, decoder, _videoSink, NULL)) { + qCritical() << "Unable to link elements."; break; } } else { - if(!gst_element_link_many(demux, parser, _tee, NULL)) { - qCritical() << "Unable to link datasource and tee."; + if(!gst_element_link_many(demux, parser, _tee, queue, decoder, _videoSink, NULL)) { + qCritical() << "Unable to link elements."; break; } } - // Link the videostream to queue1 - if(!gst_element_link_many(queue1, decoder, _videoSink, NULL)) { - qCritical() << "Unable to link queue1 and videosink."; - break; - } - - // Link the queues to the tee - teeSrc1 = gst_element_get_request_pad(_tee, "src_%u"); - q1Sink = gst_element_get_static_pad(queue1, "sink"); - - // Link the tee to queue1 - if (gst_pad_link(teeSrc1, q1Sink) != GST_PAD_LINK_OK ){ - qCritical() << "Tee for queue1 could not be linked.\n"; - break; - } - - gst_object_unref(teeSrc1); - gst_object_unref(q1Sink); - - teeSrc1 = q1Sink = NULL; - queue1 = NULL; - dataSource = demux = parser = decoder = NULL; + dataSource = demux = parser = queue = decoder = NULL; GstBus* bus = NULL; @@ -462,8 +437,8 @@ void VideoReceiver::start() dataSource = NULL; } - if (queue1 != NULL) { - gst_object_unref(queue1); + if (queue != NULL) { + gst_object_unref(queue); dataSource = NULL; } @@ -472,7 +447,6 @@ void VideoReceiver::start() _pipeline = NULL; } } - qDebug() << "Video Receiver started."; #endif } -- GitLab From 9d8875ca567ba03bbc7e3d9be9e7438742ac08a9 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 23:34:52 -0500 Subject: [PATCH 192/398] Make _streaming flag non-gstreamer safe --- src/VideoStreaming/VideoReceiver.cc | 5 +++++ src/VideoStreaming/VideoReceiver.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 55e12df29..1f79dc5de 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -180,6 +180,7 @@ VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) #if defined(QGC_GST_STREAMING) , _recording(false) + , _streaming(false) , _videoSink(NULL) , _socket(NULL) , _serverPresent(false) @@ -460,6 +461,7 @@ void VideoReceiver::stop() gst_object_unref(_pipeline); _pipeline = NULL; _serverPresent = false; + _streaming = false; } #endif } @@ -494,6 +496,9 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) } while(0); stop(); break; + case GST_MESSAGE_STATE_CHANGED: + _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; + break; default: break; } diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 27c78a531..869044d4e 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -40,7 +40,7 @@ public: #endif bool recording() { return _recording; } - bool streaming() { return GST_STATE(_pipeline) == GST_STATE_PLAYING; } + bool streaming() { return _streaming; } signals: void recordingChanged(); -- GitLab From 9214f87663f18e327b58117f7467f5e82828d7b3 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 23:37:14 -0500 Subject: [PATCH 193/398] Always stop recording video when receiver is stopped --- src/VideoStreaming/VideoReceiver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 1f79dc5de..3e1ec6a67 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -195,7 +195,6 @@ VideoReceiver::VideoReceiver(QObject* parent) VideoReceiver::~VideoReceiver() { #if defined(QGC_GST_STREAMING) - stopRecording(); stop(); setVideoSink(NULL); if(_socket) { @@ -457,6 +456,7 @@ void VideoReceiver::stop() #if defined(QGC_GST_STREAMING) if (_pipeline != NULL) { qDebug() << "Stopping pipeline"; + stopRecording(); gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); _pipeline = NULL; -- GitLab From 38ede33671127f6fd048de43b15de40bd514fdb6 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 23:40:43 -0500 Subject: [PATCH 194/398] VideoReceiver flags don't need gstreamer --- src/VideoStreaming/VideoReceiver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 3e1ec6a67..d9b36b569 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -178,9 +178,9 @@ void VideoReceiver::stopRecording(void) VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) -#if defined(QGC_GST_STREAMING) , _recording(false) , _streaming(false) +#if defined(QGC_GST_STREAMING) , _videoSink(NULL) , _socket(NULL) , _serverPresent(false) -- GitLab From 1efd240d2d5cc6edb27c9564e763934054405ec3 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 4 Jan 2017 23:49:53 -0500 Subject: [PATCH 195/398] Add logging category for VideoReceiver --- src/VideoStreaming/VideoReceiver.cc | 18 ++++++++++-------- src/VideoStreaming/VideoReceiver.h | 3 +++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index d9b36b569..a4267d5c0 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -21,6 +21,8 @@ #include #include +QGC_LOGGING_CATEGORY(VideoReceiverLog, "VideoReceiverLog") + VideoReceiver::Sink* VideoReceiver::_sink = NULL; GstElement* VideoReceiver::_pipeline = NULL; GstElement* VideoReceiver::_pipeline2 = NULL; @@ -94,7 +96,7 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g gst_object_unref(bus); if(gst_element_set_state(_pipeline2, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qDebug() << "problem starting pipeline2"; + qCDebug(VideoReceiverLog) << "problem starting pipeline2"; } // Send EOS at the beginning of the pipeline @@ -139,7 +141,7 @@ void VideoReceiver::startRecording(void) fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; } g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); - qDebug() << "New video file:" << fileName; + qCDebug(VideoReceiverLog) << "New video file:" << fileName; gst_object_ref(_sink->queue); gst_object_ref(_sink->mux); @@ -169,8 +171,8 @@ void VideoReceiver::stopRecording(void) return; } + // Wait for data block before unlinking gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCB, _sink, NULL); - _recording = false; emit recordingChanged(); #endif @@ -225,7 +227,7 @@ static void newPadCB(GstElement* element, GstPad* pad, gpointer data) g_print("A new pad %s was created\n", name); GstCaps* p_caps = gst_pad_get_pad_template_caps (pad); gchar* description = gst_caps_to_string(p_caps); - qDebug() << p_caps << ", " << description; + qCDebug(VideoReceiverLog) << p_caps << ", " << description; g_free(description); GstElement* p_rtph264depay = GST_ELEMENT(data); if(gst_element_link_pads(element, name, p_rtph264depay, "sink") == false) @@ -273,7 +275,7 @@ void VideoReceiver::_timeout() _socket = new QTcpSocket; connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); - //qDebug() << "Trying to connect to:" << url.host() << url.port(); + //qCDebug(VideoReceiverLog) << "Trying to connect to:" << url.host() << url.port(); _socket->connectToHost(url.host(), url.port()); _timer.start(5000); } @@ -447,7 +449,7 @@ void VideoReceiver::start() _pipeline = NULL; } } - qDebug() << "Video Receiver started."; + qCDebug(VideoReceiverLog) << "Video Receiver started."; #endif } @@ -455,7 +457,7 @@ void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) if (_pipeline != NULL) { - qDebug() << "Stopping pipeline"; + qCDebug(VideoReceiverLog) << "Stopping pipeline"; stopRecording(); gst_element_set_state(_pipeline, GST_STATE_NULL); gst_object_unref(_pipeline); @@ -475,7 +477,7 @@ void VideoReceiver::setUri(const QString & uri) void VideoReceiver::setVideoSavePath(const QString & path) { _path = path; - qDebug() << "New Path:" << _path; + qCDebug(VideoReceiverLog) << "New Path:" << _path; } #if defined(QGC_GST_STREAMING) diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 869044d4e..77a9af33d 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -17,6 +17,7 @@ #ifndef VIDEORECEIVER_H #define VIDEORECEIVER_H +#include "QGCLoggingCategory.h" #include #include #include @@ -25,6 +26,8 @@ #include #endif +Q_DECLARE_LOGGING_CATEGORY(VideoReceiverLog) + class VideoReceiver : public QObject { Q_OBJECT -- GitLab From 4fb068fccc6f354ba86878e647b2731ff74bc9d6 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Thu, 5 Jan 2017 11:13:21 -0500 Subject: [PATCH 196/398] Make VideoReceiver members non-static --- src/QGCToolbox.h | 2 +- src/VideoStreaming/VideoReceiver.cc | 60 ++++++++++++++++++----------- src/VideoStreaming/VideoReceiver.h | 18 +++++---- 3 files changed, 49 insertions(+), 31 deletions(-) diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index dc6ab6d7f..717bae81a 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -79,7 +79,7 @@ private: MAVLinkProtocol* _mavlinkProtocol; MissionCommandTree* _missionCommandTree; MultiVehicleManager* _multiVehicleManager; - QGCMapEngineManager* _mapEngineManager; + QGCMapEngineManager* _mapEngineManager; UASMessageHandler* _uasMessageHandler; FollowMe* _followMe; QGCPositionManager* _qgcPositionManager; diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index a4267d5c0..a6d4f07cc 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -23,20 +23,13 @@ QGC_LOGGING_CATEGORY(VideoReceiverLog, "VideoReceiverLog") -VideoReceiver::Sink* VideoReceiver::_sink = NULL; -GstElement* VideoReceiver::_pipeline = NULL; -GstElement* VideoReceiver::_pipeline2 = NULL; -GstElement* VideoReceiver::_tee = NULL; - // -EOS has appeared on the bus of the temporary pipeline // -At this point all of the recoring elements have been flushed, and the video file has been finalized // -Now we can remove the temporary pipeline and its elements #if defined(QGC_GST_STREAMING) -gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_data) +void VideoReceiver::_eosCB(GstMessage* message) { - Q_UNUSED(bus); - Q_UNUSED(message); - Q_UNUSED(user_data); + Q_UNUSED(message) gst_bin_remove(GST_BIN(_pipeline2), _sink->queue); gst_bin_remove(GST_BIN(_pipeline2), _sink->mux); @@ -56,7 +49,8 @@ gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_d delete _sink; _sink = NULL; - return true; + _recording = false; + emit recordingChanged(); } #endif @@ -65,15 +59,9 @@ gboolean VideoReceiver::_eosCB(GstBus* bus, GstMessage* message, gpointer user_d // -Setup watch and handler for EOS event on the temporary pipeline's bus // -Send an EOS event at the beginning of that pipeline and set up a callback for #if defined(QGC_GST_STREAMING) -GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) { - Q_UNUSED(pad); - Q_UNUSED(info); - Q_UNUSED(user_data); - - // We will only execute once - if(!g_atomic_int_compare_and_exchange(&_sink->removing, FALSE, TRUE)) - return GST_PAD_PROBE_OK; + Q_UNUSED(info) // Also unlinks and unrefs gst_bin_remove_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); @@ -92,7 +80,7 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g // Add watch for EOS event GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline2)); gst_bus_add_signal_watch(bus); - g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCB), NULL); + g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); gst_object_unref(bus); if(gst_element_set_state(_pipeline2, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { @@ -104,8 +92,33 @@ GstPadProbeReturn VideoReceiver::_unlinkCB(GstPad* pad, GstPadProbeInfo* info, g gst_pad_send_event(sinkpad, gst_event_new_eos()); gst_object_unref(sinkpad); +} +#endif + +#if defined(QGC_GST_STREAMING) +gboolean VideoReceiver::_eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data) +{ + Q_UNUSED(bus) + Q_ASSERT(message != NULL && user_data != NULL); + VideoReceiver* pThis = (VideoReceiver*)user_data; + pThis->_eosCB(message); + return FALSE; +} +#endif + +#if defined(QGC_GST_STREAMING) +GstPadProbeReturn VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +{ + Q_UNUSED(pad); + Q_ASSERT(info != NULL && user_data != NULL); + VideoReceiver* pThis = (VideoReceiver*)user_data; + // We will only execute once + if(!g_atomic_int_compare_and_exchange(&pThis->_sink->removing, FALSE, TRUE)) + return GST_PAD_PROBE_REMOVE; + pThis->_unlinkCB(info); return GST_PAD_PROBE_REMOVE; } + #endif // When we finish our pipeline will look like this: @@ -140,6 +153,7 @@ void VideoReceiver::startRecording(void) } else { fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; } + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); qCDebug(VideoReceiverLog) << "New video file:" << fileName; @@ -172,9 +186,7 @@ void VideoReceiver::stopRecording(void) } // Wait for data block before unlinking - gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCB, _sink, NULL); - _recording = false; - emit recordingChanged(); + gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCallBack, this, NULL); #endif } @@ -183,6 +195,10 @@ VideoReceiver::VideoReceiver(QObject* parent) , _recording(false) , _streaming(false) #if defined(QGC_GST_STREAMING) + , _sink(NULL) + , _tee(NULL) + , _pipeline(NULL) + , _pipeline2(NULL) , _videoSink(NULL) , _socket(NULL) , _serverPresent(false) diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 77a9af33d..bb06fe424 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -78,13 +78,15 @@ private: bool _recording; bool _streaming; - static Sink* _sink; - static GstElement* _tee; + Sink* _sink; + GstElement* _tee; void _onBusMessage(GstMessage* message); - static gboolean _onBusMessage(GstBus* bus, GstMessage* msg, gpointer user_data); - static gboolean _eosCB(GstBus* bus, GstMessage* message, gpointer user_data); - static GstPadProbeReturn _unlinkCB(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); + void _eosCB(GstMessage* message); + void _unlinkCB(GstPadProbeInfo* info); + static gboolean _onBusMessage(GstBus* bus, GstMessage* message, gpointer user_data); + static gboolean _eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data); + static GstPadProbeReturn _unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); #endif @@ -92,9 +94,9 @@ private: QString _path; #if defined(QGC_GST_STREAMING) - static GstElement* _pipeline; - static GstElement* _pipeline2; - GstElement* _videoSink; + GstElement* _pipeline; + GstElement* _pipeline2; + GstElement* _videoSink; #endif //-- Wait for Video Server to show up before starting -- GitLab From 62a4ba7729633cf7ae22ae924dde946a17a83bc2 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 15:05:35 -0500 Subject: [PATCH 197/398] Always shutdown streaming pipeline correctly, and call VideoReceiver start/stop appropriately --- src/FlightDisplay/VideoManager.cc | 55 ++++++++++--------- src/VideoStreaming/VideoReceiver.cc | 84 +++++++++++++++++++---------- src/VideoStreaming/VideoReceiver.h | 8 ++- 3 files changed, 89 insertions(+), 58 deletions(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 3c64da7b3..5dc6b63c8 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -224,38 +224,37 @@ VideoManager::videoSourceList() void VideoManager::_updateTimer() { #if defined(QGC_GST_STREAMING) - if(_videoRunning) - { - time_t elapsed = 0; - if(_videoSurface) - { - elapsed = time(0) - _videoSurface->lastFrame(); - } - if(elapsed > 2 && _videoSurface) - { - _videoRunning = false; - _videoSurface->setLastFrame(0); - emit videoRunningChanged(); - if(_videoReceiver) { - if(isGStreamer()) { - //-- Stop it - _videoReceiver->stop(); - QThread::msleep(100); - //-- And start over - _videoReceiver->start(); - } - } + if(_videoReceiver && _videoSurface) { + if(_videoReceiver->stopping() || _videoReceiver->starting()) { + return; } - } - else - { - if(_videoSurface && _videoReceiver->streaming()) { - qDebug() << _videoSurface->lastFrame(); - if(!_videoRunning) - { + + if(_videoReceiver->streaming()) { + if(!_videoRunning) { + _videoSurface->setLastFrame(0); _videoRunning = true; emit videoRunningChanged(); } + } else { + if(_videoRunning) { + _videoRunning = false; + emit videoRunningChanged(); + } + } + + if(_videoRunning) { + time_t elapsed = 0; + time_t lastFrame = _videoSurface->lastFrame(); + if(lastFrame != 0) { + elapsed = time(0) - _videoSurface->lastFrame(); + } + if(elapsed > 2 && _videoSurface) { + _videoReceiver->stop(); + } + } else { + if(!_videoReceiver->running()) { + _videoReceiver->start(); + } } } #endif diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index a6d4f07cc..9cba31820 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -31,12 +31,12 @@ void VideoReceiver::_eosCB(GstMessage* message) { Q_UNUSED(message) - gst_bin_remove(GST_BIN(_pipeline2), _sink->queue); - gst_bin_remove(GST_BIN(_pipeline2), _sink->mux); - gst_bin_remove(GST_BIN(_pipeline2), _sink->filesink); + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->queue); + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->mux); + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->filesink); - gst_element_set_state(_pipeline2, GST_STATE_NULL); - gst_object_unref(_pipeline2); + gst_element_set_state(_pipelineStopRec, GST_STATE_NULL); + gst_object_unref(_pipelineStopRec); gst_element_set_state(_sink->filesink, GST_STATE_NULL); gst_element_set_state(_sink->mux, GST_STATE_NULL); @@ -51,6 +51,7 @@ void VideoReceiver::_eosCB(GstMessage* message) _recording = false; emit recordingChanged(); + qCDebug(VideoReceiverLog) << "Recording Stopped"; } #endif @@ -71,27 +72,27 @@ void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) gst_object_unref(_sink->teepad); // Create temporary pipeline - _pipeline2 = gst_pipeline_new("pipe2"); + _pipelineStopRec = gst_pipeline_new("pipeStopRec"); // Put our elements from the recording branch into the temporary pipeline - gst_bin_add_many(GST_BIN(_pipeline2), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_bin_add_many(GST_BIN(_pipelineStopRec), _sink->queue, _sink->mux, _sink->filesink, NULL); gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); // Add watch for EOS event - GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline2)); + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); gst_bus_add_signal_watch(bus); g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); gst_object_unref(bus); - if(gst_element_set_state(_pipeline2, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qCDebug(VideoReceiverLog) << "problem starting pipeline2"; + if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qCDebug(VideoReceiverLog) << "problem starting _pipelineStopRec"; } // Send EOS at the beginning of the pipeline GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); gst_pad_send_event(sinkpad, gst_event_new_eos()); gst_object_unref(sinkpad); - + qCDebug(VideoReceiverLog) << "Recording branch unlinked"; } #endif @@ -135,8 +136,10 @@ GstPadProbeReturn VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* i void VideoReceiver::startRecording(void) { #if defined(QGC_GST_STREAMING) + qCDebug(VideoReceiverLog) << "startRecording()"; // exit immediately if we are already recording if(_pipeline == NULL || _recording) { + qCDebug(VideoReceiverLog) << "Already recording!"; return; } @@ -174,17 +177,19 @@ void VideoReceiver::startRecording(void) _recording = true; emit recordingChanged(); + qCDebug(VideoReceiverLog) << "Recording started"; #endif } void VideoReceiver::stopRecording(void) { #if defined(QGC_GST_STREAMING) + qCDebug(VideoReceiverLog) << "stopRecording()"; // exit immediately if we are not recording if(_pipeline == NULL || !_recording) { + qCDebug(VideoReceiverLog) << "Not recording!"; return; } - // Wait for data block before unlinking gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCallBack, this, NULL); #endif @@ -192,13 +197,16 @@ void VideoReceiver::stopRecording(void) VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) + , _running(false) , _recording(false) , _streaming(false) + , _starting(false) + , _stopping(false) #if defined(QGC_GST_STREAMING) , _sink(NULL) , _tee(NULL) , _pipeline(NULL) - , _pipeline2(NULL) + , _pipelineStopRec(NULL) , _videoSink(NULL) , _socket(NULL) , _serverPresent(false) @@ -214,7 +222,6 @@ VideoReceiver::~VideoReceiver() { #if defined(QGC_GST_STREAMING) stop(); - setVideoSink(NULL); if(_socket) { delete _socket; } @@ -309,6 +316,8 @@ void VideoReceiver::_timeout() void VideoReceiver::start() { #if defined(QGC_GST_STREAMING) + qCDebug(VideoReceiverLog) << "start()"; + if (_uri.isEmpty()) { qCritical() << "VideoReceiver::start() failed because URI is not specified"; return; @@ -317,10 +326,14 @@ void VideoReceiver::start() qCritical() << "VideoReceiver::start() failed because video sink is not set"; return; } + if(_running) { + qCDebug(VideoReceiverLog) << "Already running!"; + return; + } - bool isUdp = _uri.contains("udp://"); + _starting = true; - stop(); + bool isUdp = _uri.contains("udp://"); //-- For RTSP, check to see if server is there first if(!_serverPresent && !isUdp) { @@ -334,7 +347,7 @@ void VideoReceiver::start() GstCaps* caps = NULL; GstElement* demux = NULL; GstElement* parser = NULL; - GstElement* queue = NULL; + GstElement* queue = NULL; GstElement* decoder = NULL; do { @@ -389,7 +402,7 @@ void VideoReceiver::start() } if((queue = gst_element_factory_make("queue", NULL)) == NULL) { - qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('queue1')"; + qCritical() << "VideoReceiver::start() failed. Error with gst_element_factory_make('queue')"; break; } @@ -465,28 +478,30 @@ void VideoReceiver::start() _pipeline = NULL; } } - qCDebug(VideoReceiverLog) << "Video Receiver started."; + _starting = false; + _running = true; + qCDebug(VideoReceiverLog) << "Running"; #endif } void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) - if (_pipeline != NULL) { - qCDebug(VideoReceiverLog) << "Stopping pipeline"; - stopRecording(); - gst_element_set_state(_pipeline, GST_STATE_NULL); - gst_object_unref(_pipeline); - _pipeline = NULL; - _serverPresent = false; - _streaming = false; + qCDebug(VideoReceiverLog) << "stop()"; + if (_pipeline != NULL && !_stopping) { + qCDebug(VideoReceiverLog) << "Stopping _pipeline"; + gst_element_send_event(_pipeline, gst_event_new_eos()); + _stopping = true; + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); + GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS); + gst_object_unref(bus); + _onBusMessage(message); } #endif } void VideoReceiver::setUri(const QString & uri) { - stop(); _uri = uri; } @@ -501,7 +516,18 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) { switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: - stop(); + gst_element_set_state(_pipeline, GST_STATE_NULL); + gst_bin_remove(GST_BIN(_pipeline), _videoSink); + gst_object_unref(_pipeline); + _pipeline = NULL; + _serverPresent = false; + _streaming = false; + _recording = false; + _stopping = false; + _running = false; + emit recordingChanged(); + emit streamingChanged(); + qCDebug(VideoReceiverLog) << "Stopped"; break; case GST_MESSAGE_ERROR: do { diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index bb06fe424..082625ffd 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -42,8 +42,11 @@ public: void setVideoSink(GstElement* _sink); #endif + bool running() { return _running; } bool recording() { return _recording; } bool streaming() { return _streaming; } + bool starting() { return _starting; } + bool stopping() { return _stopping; } signals: void recordingChanged(); @@ -76,8 +79,11 @@ private: gboolean removing; } Sink; + bool _running; bool _recording; bool _streaming; + bool _starting; + bool _stopping; Sink* _sink; GstElement* _tee; @@ -95,7 +101,7 @@ private: #if defined(QGC_GST_STREAMING) GstElement* _pipeline; - GstElement* _pipeline2; + GstElement* _pipelineStopRec; GstElement* _videoSink; #endif -- GitLab From 7f1e858c93befd3d4ae3dfa738c9540cad740748 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Thu, 5 Jan 2017 19:49:56 -0500 Subject: [PATCH 198/398] Rearrange function definitions in VideoReceiver.cc --- src/VideoStreaming/VideoReceiver.cc | 352 ++++++++++++++-------------- src/VideoStreaming/VideoReceiver.h | 2 - 2 files changed, 180 insertions(+), 174 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 9cba31820..927dbe05e 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -23,178 +23,6 @@ QGC_LOGGING_CATEGORY(VideoReceiverLog, "VideoReceiverLog") -// -EOS has appeared on the bus of the temporary pipeline -// -At this point all of the recoring elements have been flushed, and the video file has been finalized -// -Now we can remove the temporary pipeline and its elements -#if defined(QGC_GST_STREAMING) -void VideoReceiver::_eosCB(GstMessage* message) -{ - Q_UNUSED(message) - - gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->queue); - gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->mux); - gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->filesink); - - gst_element_set_state(_pipelineStopRec, GST_STATE_NULL); - gst_object_unref(_pipelineStopRec); - - gst_element_set_state(_sink->filesink, GST_STATE_NULL); - gst_element_set_state(_sink->mux, GST_STATE_NULL); - gst_element_set_state(_sink->queue, GST_STATE_NULL); - - gst_object_unref(_sink->queue); - gst_object_unref(_sink->mux); - gst_object_unref(_sink->filesink); - - delete _sink; - _sink = NULL; - - _recording = false; - emit recordingChanged(); - qCDebug(VideoReceiverLog) << "Recording Stopped"; -} -#endif - -// -Unlink the recording branch from the tee in the main pipeline -// -Create a second temporary pipeline, and place the recording branch elements into that pipeline -// -Setup watch and handler for EOS event on the temporary pipeline's bus -// -Send an EOS event at the beginning of that pipeline and set up a callback for -#if defined(QGC_GST_STREAMING) -void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) -{ - Q_UNUSED(info) - - // Also unlinks and unrefs - gst_bin_remove_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); - - // Give tee its pad back - gst_element_release_request_pad(_tee, _sink->teepad); - gst_object_unref(_sink->teepad); - - // Create temporary pipeline - _pipelineStopRec = gst_pipeline_new("pipeStopRec"); - - // Put our elements from the recording branch into the temporary pipeline - gst_bin_add_many(GST_BIN(_pipelineStopRec), _sink->queue, _sink->mux, _sink->filesink, NULL); - gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); - - // Add watch for EOS event - GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); - gst_bus_add_signal_watch(bus); - g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); - gst_object_unref(bus); - - if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - qCDebug(VideoReceiverLog) << "problem starting _pipelineStopRec"; - } - - // Send EOS at the beginning of the pipeline - GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); - gst_pad_send_event(sinkpad, gst_event_new_eos()); - gst_object_unref(sinkpad); - qCDebug(VideoReceiverLog) << "Recording branch unlinked"; -} -#endif - -#if defined(QGC_GST_STREAMING) -gboolean VideoReceiver::_eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data) -{ - Q_UNUSED(bus) - Q_ASSERT(message != NULL && user_data != NULL); - VideoReceiver* pThis = (VideoReceiver*)user_data; - pThis->_eosCB(message); - return FALSE; -} -#endif - -#if defined(QGC_GST_STREAMING) -GstPadProbeReturn VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) -{ - Q_UNUSED(pad); - Q_ASSERT(info != NULL && user_data != NULL); - VideoReceiver* pThis = (VideoReceiver*)user_data; - // We will only execute once - if(!g_atomic_int_compare_and_exchange(&pThis->_sink->removing, FALSE, TRUE)) - return GST_PAD_PROBE_REMOVE; - pThis->_unlinkCB(info); - return GST_PAD_PROBE_REMOVE; -} - -#endif - -// When we finish our pipeline will look like this: -// -// +-->queue-->decoder-->_videosink -// | -// datasource-->demux-->parser-->tee -// | -// | +--------------_sink-------------------+ -// | | | -// we are adding these elements-> +->teepad-->queue-->matroskamux-->_filesink | -// | | -// +--------------------------------------+ -void VideoReceiver::startRecording(void) -{ -#if defined(QGC_GST_STREAMING) - qCDebug(VideoReceiverLog) << "startRecording()"; - // exit immediately if we are already recording - if(_pipeline == NULL || _recording) { - qCDebug(VideoReceiverLog) << "Already recording!"; - return; - } - - _sink = g_new0(Sink, 1); - _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); - _sink->queue = gst_element_factory_make("queue", NULL); - _sink->mux = gst_element_factory_make("matroskamux", NULL); - _sink->filesink = gst_element_factory_make("filesink", NULL); - _sink->removing = false; - - QString fileName; - if(QSysInfo::WindowsVersion != QSysInfo::WV_None) { - fileName = _path + "\\QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; - } else { - fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; - } - - g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); - qCDebug(VideoReceiverLog) << "New video file:" << fileName; - - gst_object_ref(_sink->queue); - gst_object_ref(_sink->mux); - gst_object_ref(_sink->filesink); - - gst_bin_add_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); - gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); - - gst_element_sync_state_with_parent(_sink->queue); - gst_element_sync_state_with_parent(_sink->mux); - gst_element_sync_state_with_parent(_sink->filesink); - - GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); - gst_pad_link(_sink->teepad, sinkpad); - gst_object_unref(sinkpad); - - _recording = true; - emit recordingChanged(); - qCDebug(VideoReceiverLog) << "Recording started"; -#endif -} - -void VideoReceiver::stopRecording(void) -{ -#if defined(QGC_GST_STREAMING) - qCDebug(VideoReceiverLog) << "stopRecording()"; - // exit immediately if we are not recording - if(_pipeline == NULL || !_recording) { - qCDebug(VideoReceiverLog) << "Not recording!"; - return; - } - // Wait for data block before unlinking - gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCallBack, this, NULL); -#endif -} - VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) , _running(false) @@ -559,3 +387,183 @@ gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer dat return TRUE; } #endif + +// When we finish our pipeline will look like this: +// +// +-->queue-->decoder-->_videosink +// | +// datasource-->demux-->parser-->tee +// | +// | +--------------_sink-------------------+ +// | | | +// we are adding these elements-> +->teepad-->queue-->matroskamux-->_filesink | +// | | +// +--------------------------------------+ +void VideoReceiver::startRecording(void) +{ +#if defined(QGC_GST_STREAMING) + qCDebug(VideoReceiverLog) << "startRecording()"; + // exit immediately if we are already recording + if(_pipeline == NULL || _recording) { + qCDebug(VideoReceiverLog) << "Already recording!"; + return; + } + + _sink = g_new0(Sink, 1); + _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); + _sink->queue = gst_element_factory_make("queue", NULL); + _sink->mux = gst_element_factory_make("matroskamux", NULL); + _sink->filesink = gst_element_factory_make("filesink", NULL); + _sink->removing = false; + + if(!_sink->teepad || !_sink->queue || !_sink->mux || !_sink->filesink) { + qCritical() << "VideoReceiver::startRecording() failed to make _sink elements"; + return; + } + + QString fileName; + if(QSysInfo::WindowsVersion != QSysInfo::WV_None) { + fileName = _path + "\\QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; + } else { + fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; + } + + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); + qCDebug(VideoReceiverLog) << "New video file:" << fileName; + + gst_object_ref(_sink->queue); + gst_object_ref(_sink->mux); + gst_object_ref(_sink->filesink); + + gst_bin_add_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); + + gst_element_sync_state_with_parent(_sink->queue); + gst_element_sync_state_with_parent(_sink->mux); + gst_element_sync_state_with_parent(_sink->filesink); + + GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); + gst_pad_link(_sink->teepad, sinkpad); + gst_object_unref(sinkpad); + + _recording = true; + emit recordingChanged(); + qCDebug(VideoReceiverLog) << "Recording started"; +#endif +} + +void VideoReceiver::stopRecording(void) +{ +#if defined(QGC_GST_STREAMING) + qCDebug(VideoReceiverLog) << "stopRecording()"; + // exit immediately if we are not recording + if(_pipeline == NULL || !_recording) { + qCDebug(VideoReceiverLog) << "Not recording!"; + return; + } + // Wait for data block before unlinking + gst_pad_add_probe(_sink->teepad, GST_PAD_PROBE_TYPE_IDLE, _unlinkCallBack, this, NULL); +#endif +} + +// This is only installed on the transient _pipelineStopRec in order +// to finalize a video file. It is not used for the main _pipeline. +// -EOS has appeared on the bus of the temporary pipeline +// -At this point all of the recoring elements have been flushed, and the video file has been finalized +// -Now we can remove the temporary pipeline and its elements +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_eosCB(GstMessage* message) +{ + Q_UNUSED(message) + + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->queue); + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->mux); + gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->filesink); + + gst_element_set_state(_pipelineStopRec, GST_STATE_NULL); + gst_object_unref(_pipelineStopRec); + + gst_element_set_state(_sink->filesink, GST_STATE_NULL); + gst_element_set_state(_sink->mux, GST_STATE_NULL); + gst_element_set_state(_sink->queue, GST_STATE_NULL); + + gst_object_unref(_sink->queue); + gst_object_unref(_sink->mux); + gst_object_unref(_sink->filesink); + + delete _sink; + _sink = NULL; + + _recording = false; + emit recordingChanged(); + qCDebug(VideoReceiverLog) << "Recording Stopped"; +} +#endif + +// -Unlink the recording branch from the tee in the main _pipeline +// -Create a second temporary pipeline, and place the recording branch elements into that pipeline +// -Setup watch and handler for EOS event on the temporary pipeline's bus +// -Send an EOS event at the beginning of that pipeline +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) +{ + Q_UNUSED(info) + + // Also unlinks and unrefs + gst_bin_remove_many(GST_BIN(_pipeline), _sink->queue, _sink->mux, _sink->filesink, NULL); + + // Give tee its pad back + gst_element_release_request_pad(_tee, _sink->teepad); + gst_object_unref(_sink->teepad); + + // Create temporary pipeline + _pipelineStopRec = gst_pipeline_new("pipeStopRec"); + + // Put our elements from the recording branch into the temporary pipeline + gst_bin_add_many(GST_BIN(_pipelineStopRec), _sink->queue, _sink->mux, _sink->filesink, NULL); + gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); + + // Add watch for EOS event + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); + gst_object_unref(bus); + + if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qCDebug(VideoReceiverLog) << "problem starting _pipelineStopRec"; + } + + // Send EOS at the beginning of the pipeline + GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); + gst_pad_send_event(sinkpad, gst_event_new_eos()); + gst_object_unref(sinkpad); + qCDebug(VideoReceiverLog) << "Recording branch unlinked"; +} +#endif + +// This is only installed on the transient _pipelineStopRec in order +// to finalize a video file. It is not used for the main _pipeline. +#if defined(QGC_GST_STREAMING) +gboolean VideoReceiver::_eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data) +{ + Q_UNUSED(bus) + Q_ASSERT(message != NULL && user_data != NULL); + VideoReceiver* pThis = (VideoReceiver*)user_data; + pThis->_eosCB(message); + return FALSE; +} +#endif + +#if defined(QGC_GST_STREAMING) +GstPadProbeReturn VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +{ + Q_UNUSED(pad); + Q_ASSERT(info != NULL && user_data != NULL); + VideoReceiver* pThis = (VideoReceiver*)user_data; + // We will only execute once + if(!g_atomic_int_compare_and_exchange(&pThis->_sink->removing, FALSE, TRUE)) + return GST_PAD_PROBE_REMOVE; + pThis->_unlinkCB(info); + return GST_PAD_PROBE_REMOVE; +} +#endif diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 082625ffd..8de7bb873 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -69,7 +69,6 @@ private slots: private: #if defined(QGC_GST_STREAMING) - typedef struct { GstPad* teepad; @@ -93,7 +92,6 @@ private: static gboolean _onBusMessage(GstBus* bus, GstMessage* message, gpointer user_data); static gboolean _eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data); static GstPadProbeReturn _unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); - #endif QString _uri; -- GitLab From 9df70ac5db3ed00ac325e4fd2114b9e5f9a17155 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 15:06:38 -0500 Subject: [PATCH 199/398] Prevent hang on close while waiting for EOS --- src/VideoStreaming/VideoReceiver.cc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 927dbe05e..d05164350 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -321,7 +321,7 @@ void VideoReceiver::stop() gst_element_send_event(_pipeline, gst_event_new_eos()); _stopping = true; GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); - GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_EOS); + GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS|GST_MESSAGE_ERROR)); gst_object_unref(bus); _onBusMessage(message); } @@ -343,6 +343,16 @@ void VideoReceiver::setVideoSavePath(const QString & path) void VideoReceiver::_onBusMessage(GstMessage* msg) { switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_ERROR: + do { + gchar* debug; + GError* error; + gst_message_parse_error(msg, &error, &debug); + g_free(debug); + qCritical() << error->message; + g_error_free(error); + } while(0); + // No break! case GST_MESSAGE_EOS: gst_element_set_state(_pipeline, GST_STATE_NULL); gst_bin_remove(GST_BIN(_pipeline), _videoSink); @@ -357,17 +367,6 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) emit streamingChanged(); qCDebug(VideoReceiverLog) << "Stopped"; break; - case GST_MESSAGE_ERROR: - do { - gchar* debug; - GError* error; - gst_message_parse_error(msg, &error, &debug); - g_free(debug); - qCritical() << error->message; - g_error_free(error); - } while(0); - stop(); - break; case GST_MESSAGE_STATE_CHANGED: _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; break; -- GitLab From c9885e2307278fef6a6864edbc260b879934a523 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 13:24:17 -0500 Subject: [PATCH 200/398] Pop messages off gstreamer bus manually And fix broken file path to save video on windows --- src/FlightDisplay/VideoManager.cc | 6 ++ src/FlightDisplay/VideoManager.h | 35 ++++++------ src/VideoStreaming/VideoReceiver.cc | 77 +++++++++++++++++++------- src/VideoStreaming/VideoReceiver.h | 2 + src/ui/preferences/GeneralSettings.qml | 6 +- 5 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 5dc6b63c8..e778dafeb 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -11,6 +11,7 @@ #include #include #include +#include #ifndef QGC_DISABLE_UVC #include @@ -188,6 +189,11 @@ VideoManager::setRtspURL(QString url) */ } +void +VideoManager::setVideoSavePathByUrl(QUrl url) { + setVideoSavePath(url.toLocalFile()); +} + void VideoManager::setVideoSavePath(QString path) { diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index 1e1cbbe7b..c9a5e6291 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -13,6 +13,7 @@ #include #include +#include #include "QGCLoggingCategory.h" #include "VideoSurface.h" @@ -29,18 +30,20 @@ public: VideoManager (QGCApplication* app); ~VideoManager (); - Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) - Q_PROPERTY(bool isGStreamer READ isGStreamer NOTIFY isGStreamerChanged) - Q_PROPERTY(QString videoSourceID READ videoSourceID NOTIFY videoSourceIDChanged) - Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) - Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) - Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) - Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) - Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) - Q_PROPERTY(QString videoSavePath READ videoSavePath WRITE setVideoSavePath NOTIFY videoSavePathChanged) - Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) - Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) - Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) + Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) + Q_PROPERTY(bool isGStreamer READ isGStreamer NOTIFY isGStreamerChanged) + Q_PROPERTY(QString videoSourceID READ videoSourceID NOTIFY videoSourceIDChanged) + Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) + Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) + Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) + Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) + Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) + Q_PROPERTY(QString videoSavePath READ videoSavePath NOTIFY videoSavePathChanged) + Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) + Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) + Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) + + Q_INVOKABLE void setVideoSavePathByUrl (QUrl url); bool hasVideo (); bool isGStreamer (); @@ -58,10 +61,10 @@ public: bool uvcEnabled (); #endif - void setVideoSource (QString vSource); - void setUdpPort (quint16 port); - void setRtspURL (QString url); - void setVideoSavePath (QString path); + void setVideoSource (QString vSource); + void setUdpPort (quint16 port); + void setRtspURL (QString url); + void setVideoSavePath (QString path); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index d05164350..e3e31cdf0 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -43,6 +43,7 @@ VideoReceiver::VideoReceiver(QObject* parent) #if defined(QGC_GST_STREAMING) _timer.setSingleShot(true); connect(&_timer, &QTimer::timeout, this, &VideoReceiver::_timeout); + connect(&_busCheckTimer, &QTimer::timeout, this, &VideoReceiver::_busCheck); #endif } @@ -132,6 +133,33 @@ void VideoReceiver::_timeout() } #endif +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_busCheck() { + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); + + GstMessage* message; + + while((message = gst_bus_pop(bus)) != NULL) { + _onBusMessage(message); + gst_message_unref(message); + } + + gst_object_unref(bus); + + if(_pipelineStopRec == NULL) { + return; + } + + bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); + if((message = gst_bus_pop_filtered(bus, GST_MESSAGE_EOS)) != NULL) { + _eosCB(message); + gst_message_unref(message); + } + + gst_object_unref(bus); +} +#endif + // When we finish our pipeline will look like this: // // +-->queue-->decoder-->_videosink @@ -251,13 +279,15 @@ void VideoReceiver::start() dataSource = demux = parser = queue = decoder = NULL; - GstBus* bus = NULL; +// GstBus* bus = NULL; - if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { - gst_bus_add_watch(bus, _onBusMessage, this); - gst_object_unref(bus); - bus = NULL; - } +// if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { +// gst_bus_add_watch(bus, _onBusMessage, this); +// gst_object_unref(bus); +// bus = NULL; +// } + // Workaround for above watch on Windows + _busCheckTimer.start(0); running = gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE; @@ -317,6 +347,7 @@ void VideoReceiver::stop() #if defined(QGC_GST_STREAMING) qCDebug(VideoReceiverLog) << "stop()"; if (_pipeline != NULL && !_stopping) { + _busCheckTimer.stop(); qCDebug(VideoReceiverLog) << "Stopping _pipeline"; gst_element_send_event(_pipeline, gst_event_new_eos()); _stopping = true; @@ -324,6 +355,7 @@ void VideoReceiver::stop() GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS|GST_MESSAGE_ERROR)); gst_object_unref(bus); _onBusMessage(message); + gst_message_unref(message); } #endif } @@ -369,6 +401,7 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) break; case GST_MESSAGE_STATE_CHANGED: _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; + qCDebug(VideoReceiverLog) << "State changed, _streaming:" << _streaming; break; default: break; @@ -408,7 +441,12 @@ void VideoReceiver::startRecording(void) return; } - _sink = g_new0(Sink, 1); + if(_path.isEmpty()) { + qWarning() << "VideoReceiver::startRecording Empty Path!"; + return; + } + + _sink = new Sink(); _sink->teepad = gst_element_get_request_pad(_tee, "src_%u"); _sink->queue = gst_element_factory_make("queue", NULL); _sink->mux = gst_element_factory_make("matroskamux", NULL); @@ -420,15 +458,11 @@ void VideoReceiver::startRecording(void) return; } - QString fileName; - if(QSysInfo::WindowsVersion != QSysInfo::WV_None) { - fileName = _path + "\\QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; - } else { - fileName = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh:mm:ss") + ".mkv"; - } + QString videoFile; + videoFile = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") + ".mkv"; - g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(fileName), NULL); - qCDebug(VideoReceiverLog) << "New video file:" << fileName; + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(videoFile), NULL); + qCDebug(VideoReceiverLog) << "New video file:" << videoFile; gst_object_ref(_sink->queue); gst_object_ref(_sink->mux); @@ -481,6 +515,7 @@ void VideoReceiver::_eosCB(GstMessage* message) gst_element_set_state(_pipelineStopRec, GST_STATE_NULL); gst_object_unref(_pipelineStopRec); + _pipelineStopRec = NULL; gst_element_set_state(_sink->filesink, GST_STATE_NULL); gst_element_set_state(_sink->mux, GST_STATE_NULL); @@ -492,8 +527,8 @@ void VideoReceiver::_eosCB(GstMessage* message) delete _sink; _sink = NULL; - _recording = false; + emit recordingChanged(); qCDebug(VideoReceiverLog) << "Recording Stopped"; } @@ -523,10 +558,12 @@ void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); // Add watch for EOS event - GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); - gst_bus_add_signal_watch(bus); - g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); - gst_object_unref(bus); +// GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); +// gst_bus_add_signal_watch(bus); +// g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); +// gst_object_unref(bus); + + // Above watch is handled by _busCheck now if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { qCDebug(VideoReceiverLog) << "problem starting _pipelineStopRec"; diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 8de7bb873..ad4437256 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -62,6 +62,7 @@ public slots: private slots: #if defined(QGC_GST_STREAMING) + void _busCheck (); void _timeout (); void _connected (); void _socketError (QAbstractSocket::SocketError socketError); @@ -85,6 +86,7 @@ private: bool _stopping; Sink* _sink; GstElement* _tee; + QTimer _busCheckTimer; void _onBusMessage(GstMessage* message); void _eosCB(GstMessage* message); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index ba381f8f7..1fe8d5377 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -46,11 +46,7 @@ QGCView { title: "Choose a location to save video files." folder: shortcuts.home selectFolder: true - onAccepted: { - var path = fileDialog.fileUrl.toString(); - path = path.replace(/^(file:\/{2})/,""); - QGroundControl.videoManager.videoSavePath = path - } + onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) } QGCViewPanel { -- GitLab From ab08550a2c5d7e620b56fbf4b9f5c29efbbb6198 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 14:30:59 -0500 Subject: [PATCH 201/398] Set running flag to false if pipeline fails to change state --- src/VideoStreaming/VideoReceiver.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index e3e31cdf0..37a423f0a 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -335,10 +335,13 @@ void VideoReceiver::start() gst_object_unref(_pipeline); _pipeline = NULL; } + + _running = false; + } else { + _running = true; + qCDebug(VideoReceiverLog) << "Running"; } _starting = false; - _running = true; - qCDebug(VideoReceiverLog) << "Running"; #endif } -- GitLab From 9cfb973c78bcbf98bcc067780ac44c0ff6a26ab2 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 13 Jan 2017 16:07:42 -0500 Subject: [PATCH 202/398] Remove unused streamingChanged signal --- src/VideoStreaming/VideoReceiver.cc | 1 - src/VideoStreaming/VideoReceiver.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 37a423f0a..c3ffa5ab7 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -399,7 +399,6 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) _stopping = false; _running = false; emit recordingChanged(); - emit streamingChanged(); qCDebug(VideoReceiverLog) << "Stopped"; break; case GST_MESSAGE_STATE_CHANGED: diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index ad4437256..8a257cd79 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -33,7 +33,6 @@ class VideoReceiver : public QObject Q_OBJECT public: Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) - Q_PROPERTY(bool streaming READ streaming NOTIFY streamingChanged) explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); @@ -50,7 +49,6 @@ public: signals: void recordingChanged(); - void streamingChanged(); public slots: void start (); -- GitLab From 4a256484253a2de98a622fc9dad99afcbfdc8400 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 13 Jan 2017 14:45:22 -0500 Subject: [PATCH 203/398] Condition SDL counts so we don't pass negative numbers to new Should address #4425 --- src/Joystick/JoystickSDL.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index c23448026..854ac4407 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -49,7 +49,7 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl SDL_JoystickClose(sdlJoystick); qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController; - ret[name] = new JoystickSDL(name, axisCount, buttonCount, hatCount, i, isGameController, _multiVehicleManager); + ret[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); } else { qCDebug(JoystickLog) << "\tSkipping duplicate" << name; } -- GitLab From 1897e73a4d26752a5fd2278966391e804f541677 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 13 Jan 2017 13:58:33 -0800 Subject: [PATCH 204/398] AutoConnect list dried from json file --- qgroundcontrol.qrc | 1 + src/VehicleSetup/Bootloader.h | 18 +- src/VehicleSetup/FirmwareUpgrade.qml | 4 +- src/VehicleSetup/FirmwareUpgradeController.cc | 91 +----- src/VehicleSetup/FirmwareUpgradeController.h | 10 +- src/VehicleSetup/PX4FirmwareUpgradeThread.cc | 15 +- src/VehicleSetup/PX4FirmwareUpgradeThread.h | 8 +- src/comm/LinkManager.cc | 47 +-- src/comm/QGCSerialPortInfo.cc | 309 +++++++++++++----- src/comm/QGCSerialPortInfo.h | 59 +++- src/comm/USBBoardInfo.json | 50 +++ 11 files changed, 368 insertions(+), 244 deletions(-) create mode 100644 src/comm/USBBoardInfo.json diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index b50a6e7fb..4b7b89390 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -167,6 +167,7 @@ src/QmlControls/QGroundControlQmlGlobal.json src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/Survey.FactMetaData.json + src/comm/USBBoardInfo.json src/comm/APMArduCopterMockLink.params diff --git a/src/VehicleSetup/Bootloader.h b/src/VehicleSetup/Bootloader.h index 522f528b8..9703f44e0 100644 --- a/src/VehicleSetup/Bootloader.h +++ b/src/VehicleSetup/Bootloader.h @@ -61,15 +61,15 @@ public: bool reboot(QextSerialPort* port); // Supported bootloader board ids - static const int boardIDPX4FMUV1 = 5; ///< PX4 V1 board, as from USB PID - static const int boardIDPX4FMUV2 = 9; ///< PX4 V2 board, as from USB PID - static const int boardIDPX4FMUV4 = 11; ///< PX4 V4 board, as from USB PID - static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID - static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board, as from USB PID - static const int boardID3DRRadio = 78; ///< 3DR Radio. This is an arbitrary value unrelated to the PID - static const int boardIDMINDPXFMUV2 = 88; ///< MindPX V2 board, as from USB PID - static const int boardIDTAPV1 = 64; ///< TAP V1 board, as from USB PID - static const int boardIDASCV1 = 65; ///< ASC V1 board, as from USB PID + static const int boardIDPX4FMUV1 = 5; ///< PX4 V1 board, as from USB PID + static const int boardIDPX4FMUV2 = 9; ///< PX4 V2 board, as from USB PID + static const int boardIDPX4FMUV4 = 11; ///< PX4 V4 board, as from USB PID + static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID + static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board, as from USB PID + static const int boardID3DRRadio = 78; ///< 3DR Radio. This is an arbitrary value unrelated to the PID + static const int boardIDMINDPXFMUV2 = 88; ///< MindPX V2 board, as from USB PID + static const int boardIDTAPV1 = 64; ///< TAP V1 board, as from USB PID + static const int boardIDASCV1 = 65; ///< ASC V1 board, as from USB PID signals: /// @brief Signals progress indicator for long running bootloader utility routines diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 55df89348..8ab41ccf2 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -97,7 +97,7 @@ QGCView { } else { // We end up here when we detect a board plugged in after we've started upgrade statusTextArea.append(highlightPrefix + qsTr("Found device") + highlightSuffix + ": " + controller.boardType) - if (controller.boardType == "Pixhawk" || controller.boardType == "AeroCore" || controller.boardType == "PX4 Flow" || controller.boardType == "PX4 FMU V1" || controller.boardType == "MindPX" || controller.boardType == "TAP V1" || controller.boardType == "ASC V1") { + if (controller.pixhawkBoard || controller.px4FlowBoard) { showDialog(pixhawkFirmwareSelectDialogComponent, title, qgcView.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel) } } @@ -124,7 +124,7 @@ QGCView { anchors.fill: parent property bool showFirmwareTypeSelection: _advanced.checked - property bool px4Flow: controller.boardType == "PX4 Flow" + property bool px4Flow: controller.px4FlowBoard function updatePX4VersionDisplay() { var versionString = "" diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index ef24f1de4..0105b80aa 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -116,43 +116,14 @@ void FirmwareUpgradeController::cancel(void) _threadController->cancel(); } -void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType) +void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPortInfo& info, int boardType, QString boardName) { _foundBoardInfo = info; _foundBoardType = (QGCSerialPortInfo::BoardType_t)boardType; + _foundBoardTypeName = boardName; + _startFlashWhenBootloaderFound = false; - switch (boardType) { - case QGCSerialPortInfo::BoardTypePX4FMUV1: - _foundBoardTypeName = "PX4 FMU V1"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypePX4FMUV2: - case QGCSerialPortInfo::BoardTypePX4FMUV4: - _foundBoardTypeName = "Pixhawk"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeAeroCore: - _foundBoardTypeName = "AeroCore"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeMINDPXFMUV2: - _foundBoardTypeName = "MindPX"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeTAPV1: - _foundBoardTypeName = "TAP V1"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeASCV1: - _foundBoardTypeName = "ASC V1"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypePX4Flow: - _foundBoardTypeName = "PX4 Flow"; - _startFlashWhenBootloaderFound = false; - break; - case QGCSerialPortInfo::BoardTypeSikRadio: - _foundBoardTypeName = "SiK Radio"; + if (_foundBoardType == QGCSerialPortInfo::BoardTypeSiKRadio) { if (!firstAttempt) { // Radio always flashes latest firmware, so we can start right away without // any further user input. @@ -161,12 +132,10 @@ void FirmwareUpgradeController::_foundBoard(bool firstAttempt, const QSerialPort StableFirmware, DefaultVehicleFirmware); } - break; } - qCDebug(FirmwareUpgradeLog) << _foundBoardType; + qCDebug(FirmwareUpgradeLog) << _foundBoardType << _foundBoardTypeName; emit boardFound(); - _loadAPMVersions(_foundBoardType); } @@ -197,6 +166,8 @@ void FirmwareUpgradeController::_foundBootloader(int bootloaderVersion, int boar if (_startFlashWhenBootloaderFound) { flash(_startFlashWhenBootloaderFoundFirmwareIdentity); } + + _loadAPMVersions(_bootloaderBoardID); } @@ -463,48 +434,6 @@ QHash* FirmwareUpgradeCo } } -QHash* FirmwareUpgradeController::_firmwareHashForBoardType(QGCSerialPortInfo::BoardType_t boardType) -{ - int boardId = Bootloader::boardIDPX4FMUV2; - - switch (boardType) { - case QGCSerialPortInfo::BoardTypePX4FMUV1: - boardId = Bootloader::boardIDPX4FMUV1; - break; - case QGCSerialPortInfo::BoardTypePX4FMUV2: - boardId = Bootloader::boardIDPX4FMUV2; - break; - case QGCSerialPortInfo::BoardTypePX4FMUV4: - boardId = Bootloader::boardIDPX4FMUV4; - break; - case QGCSerialPortInfo::BoardTypeAeroCore: - boardId = Bootloader::boardIDAeroCore; - break; - case QGCSerialPortInfo::BoardTypeMINDPXFMUV2: - boardId = Bootloader::boardIDMINDPXFMUV2; - break; - case QGCSerialPortInfo::BoardTypeTAPV1: - boardId = Bootloader::boardIDTAPV1; - break; - case QGCSerialPortInfo::BoardTypeASCV1: - boardId = Bootloader::boardIDASCV1; - break; - case QGCSerialPortInfo::BoardTypePX4Flow: - boardId = Bootloader::boardIDPX4Flow; - break; - case QGCSerialPortInfo::BoardTypeSikRadio: - boardId = Bootloader::boardID3DRRadio; - break; - default: - qWarning() << "Internal error: invalid board type for flashing" << boardType; - boardId = Bootloader::boardIDPX4FMUV2; - break; - } - - return _firmwareHashForBoardId(boardId); -} - - /// @brief Prompts the user to select a firmware file if needed and moves the state machine to the next state. void FirmwareUpgradeController::_getFirmwareFile(FirmwareIdentifier firmwareId) { @@ -697,11 +626,11 @@ void FirmwareUpgradeController::_eraseComplete(void) _eraseTimer.stop(); } -void FirmwareUpgradeController::_loadAPMVersions(QGCSerialPortInfo::BoardType_t boardType) +void FirmwareUpgradeController::_loadAPMVersions(uint32_t bootloaderBoardID) { _apmVersionMap.clear(); - QHash* prgFirmware = _firmwareHashForBoardType(boardType); + QHash* prgFirmware = _firmwareHashForBoardId(bootloaderBoardID); foreach (FirmwareIdentifier firmwareId, prgFirmware->keys()) { if (firmwareId.autopilotStackType == AutoPilotStackAPM) { @@ -740,7 +669,7 @@ void FirmwareUpgradeController::_apmVersionDownloadFinished(QString remoteFile, // In order to determine the firmware and vehicle type for this file we find the matching entry in the firmware list - QHash* prgFirmware = _firmwareHashForBoardType(_foundBoardType); + QHash* prgFirmware = _firmwareHashForBoardId(_bootloaderBoardID); QString remotePath = QFileInfo(remoteFile).path(); foreach (FirmwareIdentifier firmwareId, prgFirmware->keys()) { diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index 29b4f7d24..b39fdab30 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -96,6 +96,8 @@ public: Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound) Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) Q_PROPERTY(QString boardType MEMBER _foundBoardTypeName NOTIFY boardFound) + Q_PROPERTY(bool pixhawkBoard READ pixhawkBoard NOTIFY boardFound) + Q_PROPERTY(bool px4FlowBoard READ px4FlowBoard NOTIFY boardFound) Q_PROPERTY(FirmwareType_t selectedFirmwareType READ selectedFirmwareType WRITE setSelectedFirmwareType NOTIFY selectedFirmwareTypeChanged) Q_PROPERTY(QStringList apmAvailableVersions READ apmAvailableVersions NOTIFY apmAvailableVersionsChanged) Q_PROPERTY(QString px4StableVersion READ px4StableVersion NOTIFY px4StableVersionChanged) @@ -142,6 +144,9 @@ public: QString px4StableVersion(void) { return _px4StableVersion; } QString px4BetaVersion(void) { return _px4BetaVersion; } + bool pixhawkBoard(void) const { return _foundBoardType == QGCSerialPortInfo::BoardTypePixhawk; } + bool px4FlowBoard(void) const { return _foundBoardType == QGCSerialPortInfo::BoardTypePX4Flow; } + signals: void boardFound(void); void noBoardFound(void); @@ -158,7 +163,7 @@ private slots: void _firmwareDownloadProgress(qint64 curr, qint64 total); void _firmwareDownloadFinished(QString remoteFile, QString localFile); void _firmwareDownloadError(QString errorMsg); - void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int boardType); + void _foundBoard(bool firstAttempt, const QSerialPortInfo& portInfo, int boardType, QString boardName); void _noBoardFound(void); void _boardGone(); void _foundBootloader(int bootloaderVersion, int boardID, int flashSize); @@ -180,9 +185,8 @@ private: void _downloadFirmware(void); void _appendStatusLog(const QString& text, bool critical = false); void _errorCancel(const QString& msg); - void _loadAPMVersions(QGCSerialPortInfo::BoardType_t boardType); + void _loadAPMVersions(uint32_t bootloaderBoardID); QHash* _firmwareHashForBoardId(int boardId); - QHash* _firmwareHashForBoardType(QGCSerialPortInfo::BoardType_t boardType); void _determinePX4StableVersion(void); QString _portName; diff --git a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc index 532ca38df..28797e149 100644 --- a/src/VehicleSetup/PX4FirmwareUpgradeThread.cc +++ b/src/VehicleSetup/PX4FirmwareUpgradeThread.cc @@ -86,14 +86,15 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void) QGCSerialPortInfo portInfo; QGCSerialPortInfo::BoardType_t boardType; + QString boardName; - if (_findBoardFromPorts(portInfo, boardType)) { + if (_findBoardFromPorts(portInfo, boardType, boardName)) { if (!_foundBoard) { _foundBoard = true; _foundBoardPortInfo = portInfo; - emit foundBoard(_findBoardFirstAttempt, portInfo, boardType); + emit foundBoard(_findBoardFirstAttempt, portInfo, boardType, boardName); if (!_findBoardFirstAttempt) { - if (boardType == QGCSerialPortInfo::BoardTypeSikRadio) { + if (boardType == QGCSerialPortInfo::BoardTypeSiKRadio) { _3drRadioForceBootloader(portInfo); return; } else { @@ -116,18 +117,20 @@ void PX4FirmwareUpgradeThreadWorker::_findBoardOnce(void) _timerRetry->start(); } -bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType) +bool PX4FirmwareUpgradeThreadWorker::_findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType, QString& boardName) { foreach (QGCSerialPortInfo info, QGCSerialPortInfo::availablePorts()) { + info.getBoardInfo(boardType, boardName); + qCDebug(FirmwareUpgradeVerboseLog) << "Serial Port --------------"; - qCDebug(FirmwareUpgradeVerboseLog) << "\tboard type" << info.boardType(); + qCDebug(FirmwareUpgradeVerboseLog) << "\tboard type" << boardType; + qCDebug(FirmwareUpgradeVerboseLog) << "\tboard name" << boardName; qCDebug(FirmwareUpgradeVerboseLog) << "\tport name:" << info.portName(); qCDebug(FirmwareUpgradeVerboseLog) << "\tdescription:" << info.description(); qCDebug(FirmwareUpgradeVerboseLog) << "\tsystem location:" << info.systemLocation(); qCDebug(FirmwareUpgradeVerboseLog) << "\tvendor ID:" << info.vendorIdentifier(); qCDebug(FirmwareUpgradeVerboseLog) << "\tproduct ID:" << info.productIdentifier(); - boardType = info.boardType(); if (info.canFlash()) { portInfo = info; return true; diff --git a/src/VehicleSetup/PX4FirmwareUpgradeThread.h b/src/VehicleSetup/PX4FirmwareUpgradeThread.h index cff613019..018249cbc 100644 --- a/src/VehicleSetup/PX4FirmwareUpgradeThread.h +++ b/src/VehicleSetup/PX4FirmwareUpgradeThread.h @@ -43,7 +43,7 @@ public: signals: void updateProgress(int curr, int total); - void foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type); + void foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString boardName); void noBoardFound(void); void boardGone(void); void foundBootloader(int bootloaderVersion, int boardID, int flashSize); @@ -64,7 +64,7 @@ private slots: void _cancel(void); private: - bool _findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType); + bool _findBoardFromPorts(QGCSerialPortInfo& portInfo, QGCSerialPortInfo::BoardType_t& boardType, QString& boardName); bool _findBootloader(const QGCSerialPortInfo& portInfo, bool radioMode, bool errorOnNotFound); void _3drRadioForceBootloader(const QGCSerialPortInfo& portInfo); bool _erase(void); @@ -107,7 +107,7 @@ public: signals: /// @brief Emitted by the find board process when it finds a board. - void foundBoard(bool firstAttempt, const QGCSerialPortInfo &portInfo, int boardType); + void foundBoard(bool firstAttempt, const QGCSerialPortInfo &portInfo, int boardType, QString boardName); void noBoardFound(void); @@ -142,7 +142,7 @@ signals: void _cancel(void); private slots: - void _foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type) { emit foundBoard(firstAttempt, portInfo, type); } + void _foundBoard(bool firstAttempt, const QGCSerialPortInfo& portInfo, int type, QString name) { emit foundBoard(firstAttempt, portInfo, type, name); } void _noBoardFound(void) { emit noBoardFound(); } void _boardGone(void) { emit boardGone(); } void _foundBootloader(int bootloaderVersion, int boardID, int flashSize) { emit foundBootloader(bootloaderVersion, boardID, flashSize); } diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index dc6c49652..68d31c11f 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -524,9 +524,10 @@ void LinkManager::_updateAutoConnectLinks(void) // Save port name currentPorts << portInfo.systemLocation(); - QGCSerialPortInfo::BoardType_t boardType = portInfo.boardType(); + QGCSerialPortInfo::BoardType_t boardType; + QString boardName; - if (boardType != QGCSerialPortInfo::BoardTypeUnknown) { + if (portInfo.getBoardInfo(boardType, boardName)) { if (portInfo.isBootloader()) { // Don't connect to bootloader qCDebug(LinkManagerLog) << "Waiting for bootloader to finish" << portInfo.systemLocation(); @@ -547,51 +548,25 @@ void LinkManager::_updateAutoConnectLinks(void) _autoconnectWaitList.remove(portInfo.systemLocation()); switch (boardType) { - case QGCSerialPortInfo::BoardTypePX4FMUV1: - case QGCSerialPortInfo::BoardTypePX4FMUV2: - case QGCSerialPortInfo::BoardTypePX4FMUV4: + case QGCSerialPortInfo::BoardTypePixhawk: if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(tr("Pixhawk on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); - pSerialConfig->setUsbDirect(true); - } - break; - case QGCSerialPortInfo::BoardTypeAeroCore: - if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(tr("AeroCore on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); - pSerialConfig->setUsbDirect(true); - } - break; - case QGCSerialPortInfo::BoardTypeMINDPXFMUV2: - if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(tr("MindPX on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); - pSerialConfig->setUsbDirect(true); - } - break; - case QGCSerialPortInfo::BoardTypeTAPV1: - if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(tr("TAP on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); - pSerialConfig->setUsbDirect(true); - } - break; - case QGCSerialPortInfo::BoardTypeASCV1: - if (_autoconnectPixhawk) { - pSerialConfig = new SerialConfiguration(tr("ASC on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypePX4Flow: if (_autoconnectPX4Flow) { - pSerialConfig = new SerialConfiguration(tr("PX4Flow on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; - case QGCSerialPortInfo::BoardTypeSikRadio: + case QGCSerialPortInfo::BoardTypeSiKRadio: if (_autoconnect3DRRadio) { - pSerialConfig = new SerialConfiguration(tr("SiK Radio on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; - case QGCSerialPortInfo::BoardTypeLibrePilot: + case QGCSerialPortInfo::BoardTypeOpenPilot: if (_autoconnectLibrePilot) { - pSerialConfig = new SerialConfiguration(tr("LibrePilot on %1 (AutoConnect)").arg(portInfo.portName().trimmed())); + pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; #ifndef __mobile__ @@ -609,7 +584,7 @@ void LinkManager::_updateAutoConnectLinks(void) if (pSerialConfig) { qCDebug(LinkManagerLog) << "New auto-connect port added: " << pSerialConfig->name() << portInfo.systemLocation(); - pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardTypeSikRadio ? 57600 : 115200); + pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardTypeSiKRadio ? 57600 : 115200); pSerialConfig->setDynamic(true); pSerialConfig->setPortName(portInfo.systemLocation()); _sharedAutoconnectConfigurations.append(SharedLinkConfigurationPointer(pSerialConfig)); diff --git a/src/comm/QGCSerialPortInfo.cc b/src/comm/QGCSerialPortInfo.cc index c8c0b79c5..eec24e61d 100644 --- a/src/comm/QGCSerialPortInfo.cc +++ b/src/comm/QGCSerialPortInfo.cc @@ -9,33 +9,37 @@ #include "QGCSerialPortInfo.h" +#include "JsonHelper.h" + +#include +#include +#include +#include QGC_LOGGING_CATEGORY(QGCSerialPortInfoLog, "QGCSerialPortInfoLog") -static const struct VIDPIDMapInfo_s { - int vendorId; - int productId; - QGCSerialPortInfo::BoardType_t boardType; - const char * boardString; -} s_rgVIDPIDMappings[] = { - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::pixhawkFMUV4ProductId, QGCSerialPortInfo::BoardTypePX4FMUV4, "Found PX4 FMU V4" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::pixhawkFMUV2ProductId, QGCSerialPortInfo::BoardTypePX4FMUV2, "Found PX4 FMU V2" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::pixhawkFMUV2OldBootloaderProductId, QGCSerialPortInfo::BoardTypePX4FMUV2, "Found PX4 FMU V2"}, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::pixhawkFMUV1ProductId, QGCSerialPortInfo::BoardTypePX4FMUV1, "Found PX4 FMU V1" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::px4FlowProductId, QGCSerialPortInfo::BoardTypePX4Flow, "Found PX4 Flow" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::AeroCoreProductId, QGCSerialPortInfo::BoardTypeAeroCore, "Found AeroCore" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::MindPXFMUV2ProductId, QGCSerialPortInfo::BoardTypeMINDPXFMUV2,"Found MindPX FMU V2" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::TAPV1ProductId, QGCSerialPortInfo::BoardTypeTAPV1, "Found TAP V1" }, - { QGCSerialPortInfo::px4VendorId, QGCSerialPortInfo::ASCV1ProductId, QGCSerialPortInfo::BoardTypeASCV1, "Found ASC V1" }, - { QGCSerialPortInfo::threeDRRadioVendorId, QGCSerialPortInfo::threeDRRadioProductId, QGCSerialPortInfo::BoardTypeSikRadio, "Found SiK Radio" }, - { QGCSerialPortInfo::siLabsRadioVendorId, QGCSerialPortInfo::siLabsRadioProductId, QGCSerialPortInfo::BoardTypeSikRadio, "Found SiK Radio" }, - { QGCSerialPortInfo::ubloxRTKVendorId, QGCSerialPortInfo::ubloxRTKProductId, QGCSerialPortInfo::BoardTypeRTKGPS, "Found RTK GPS" }, - { QGCSerialPortInfo::openpilotVendorId, QGCSerialPortInfo::revolutionProductId, QGCSerialPortInfo::BoardTypeLibrePilot, "Found OP Revolution" }, - { QGCSerialPortInfo::openpilotVendorId, QGCSerialPortInfo::oplinkProductId, QGCSerialPortInfo::BoardTypeLibrePilot, "Found OP OPLink" }, - { QGCSerialPortInfo::openpilotVendorId, QGCSerialPortInfo::sparky2ProductId, QGCSerialPortInfo::BoardTypeLibrePilot, "Found TL Sparky2" }, - { QGCSerialPortInfo::openpilotVendorId, QGCSerialPortInfo::CC3DProductId, QGCSerialPortInfo::BoardTypeLibrePilot, "Found OP CC3D" }, +bool QGCSerialPortInfo::_jsonLoaded = false; +const char* QGCSerialPortInfo::_jsonFileTypeValue = "USBBoardInfo"; +const char* QGCSerialPortInfo::_jsonBoardInfoKey = "boardInfo"; +const char* QGCSerialPortInfo::_jsonBoardFallbackKey = "boardFallback"; +const char* QGCSerialPortInfo::_jsonVendorIDKey = "vendorID"; +const char* QGCSerialPortInfo::_jsonProductIDKey = "productID"; +const char* QGCSerialPortInfo::_jsonBoardClassKey = "boardClass"; +const char* QGCSerialPortInfo::_jsonNameKey = "name"; +const char* QGCSerialPortInfo::_jsonRegExpKey = "regExp"; +const char* QGCSerialPortInfo::_jsonAndroidOnlyKey = "androidOnly"; + +const QGCSerialPortInfo::BoardClassString2BoardType_t QGCSerialPortInfo::_rgBoardClass2BoardType[] = { + { "Pixhawk", QGCSerialPortInfo::BoardTypePixhawk }, + { "PX4 Flow", QGCSerialPortInfo::BoardTypePX4Flow }, + { "RTK GPS", QGCSerialPortInfo::BoardTypeRTKGPS }, + { "SiK Radio", QGCSerialPortInfo::BoardTypeSiKRadio }, + { "OpenPilot", QGCSerialPortInfo::BoardTypeOpenPilot }, }; +QList QGCSerialPortInfo::_boardInfoList; +QList QGCSerialPortInfo::_boardFallbackList; + QGCSerialPortInfo::QGCSerialPortInfo(void) : QSerialPortInfo() { @@ -48,67 +52,198 @@ QGCSerialPortInfo::QGCSerialPortInfo(const QSerialPort & port) : } -QGCSerialPortInfo::BoardType_t QGCSerialPortInfo::boardType(void) const +void QGCSerialPortInfo::_loadJsonData(void) { - if (isNull()) { - return BoardTypeUnknown; + if (_jsonLoaded) { + return; + } + _jsonLoaded = true; + + QFile file(QStringLiteral(":/json/USBBoardInfo.json")); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning() << "Unable to open board info json:" << file.errorString(); + return; + } + + QByteArray bytes = file.readAll(); + + QJsonParseError jsonParseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(bytes, &jsonParseError)); + if (jsonParseError.error != QJsonParseError::NoError) { + qWarning() << "Unable to parse board info json:" << jsonParseError.errorString(); + return; + } + QJsonObject json = jsonDoc.object(); + + int fileVersion; + QString errorString; + if (!JsonHelper::validateQGCJsonFile(json, + _jsonFileTypeValue, // expected file type + 1, // minimum supported version + 1, // maximum supported version + fileVersion, + errorString)) { + qWarning() << errorString; + return; + } + + // Validate root object keys + QList rootKeyInfoList = { + { _jsonBoardInfoKey, QJsonValue::Array, true }, + { _jsonBoardFallbackKey, QJsonValue::Array, true }, + }; + if (!JsonHelper::validateKeys(json, rootKeyInfoList, errorString)) { + qWarning() << errorString; + return; + } + + // Load board info used to detect known board from vendor/product id + + QList boardKeyInfoList = { + { _jsonVendorIDKey, QJsonValue::Double, true }, + { _jsonProductIDKey, QJsonValue::Double, true }, + { _jsonBoardClassKey, QJsonValue::String, true }, + { _jsonNameKey, QJsonValue::String, true }, + }; + + QJsonArray rgBoardInfo = json[_jsonBoardInfoKey].toArray(); + for (int i=0; i fallbackKeyInfoList = { + { _jsonRegExpKey, QJsonValue::String, true }, + { _jsonBoardClassKey, QJsonValue::String, true }, + { _jsonAndroidOnlyKey, QJsonValue::Bool, false }, + }; - if (vendorIdentifier() == pIDMap->vendorId && productIdentifier() == pIDMap->productId) { - boardType = pIDMap->boardType; - qCDebug(QGCSerialPortInfoLog) << pIDMap->boardString; + QJsonArray rgBoardFallback = json[_jsonBoardFallbackKey].toArray(); + for (int i=0; i QGCSerialPortInfo::availablePorts(void) { QList list; @@ -120,34 +255,34 @@ QList QGCSerialPortInfo::availablePorts(void) return list; } -bool QGCSerialPortInfo::boardTypePixhawk(void) const -{ - BoardType_t boardType = this->boardType(); - - return boardType == BoardTypePX4FMUV1 || boardType == BoardTypePX4FMUV2 - || boardType == BoardTypePX4FMUV4 || boardType == BoardTypeAeroCore - || boardType == BoardTypeMINDPXFMUV2 || boardType == BoardTypeTAPV1 - || boardType == BoardTypeASCV1; -} - bool QGCSerialPortInfo::isBootloader(void) const { - // FIXME: Check SerialLink bootloade detect code which is different - return boardTypePixhawk() && description().contains("BL"); + BoardType_t boardType; + QString name; + + if (getBoardInfo(boardType, name)) { + // FIXME: Check SerialLink bootloade detect code which is different + return boardType == BoardTypePixhawk && description().contains("BL"); + } else { + return false; + } } bool QGCSerialPortInfo::canFlash(void) { - BoardType_t boardType = this->boardType(); - switch(boardType){ - case QGCSerialPortInfo::BoardTypeUnknown: - case QGCSerialPortInfo::BoardTypeRTKGPS: - case QGCSerialPortInfo::BoardTypeLibrePilot: - return false; - default: - return true; + BoardType_t boardType; + QString name; + if (getBoardInfo(boardType, name)) { + switch(boardType){ + case QGCSerialPortInfo::BoardTypePixhawk: + case QGCSerialPortInfo::BoardTypePX4Flow: + case QGCSerialPortInfo::BoardTypeSiKRadio: + return true; + default: + return false; + } + } else { + return false; } - - } diff --git a/src/comm/QGCSerialPortInfo.h b/src/comm/QGCSerialPortInfo.h index 9609a4fa1..51f272845 100644 --- a/src/comm/QGCSerialPortInfo.h +++ b/src/comm/QGCSerialPortInfo.h @@ -27,25 +27,19 @@ class QGCSerialPortInfo : public QSerialPortInfo { public: typedef enum { - BoardTypePX4FMUV1, - BoardTypePX4FMUV2, - BoardTypePX4FMUV4, + BoardTypePixhawk, + BoardTypeSiKRadio, BoardTypePX4Flow, - BoardTypeSikRadio, - BoardTypeAeroCore, + BoardTypeOpenPilot, BoardTypeRTKGPS, - BoardTypeMINDPXFMUV2, - BoardTypeTAPV1, - BoardTypeASCV1, - BoardTypeLibrePilot, BoardTypeUnknown } BoardType_t; // Vendor and products ids for the boards we care about - static const int px4VendorId = 9900; ///< Vendor ID for Pixhawk board (V2 and V1) and PX4 Flow - - static const int pixhawkFMUV4ProductId = 18; ///< Product ID for Pixhawk V2 board + static const int px4VendorId = 9900; ///< Vendor ID for all Pixhawk boards and PX4 Flow + static const int pixhawkFMUV4ProductId = 18; ///< Product ID for Pixhawk V4 board + static const int pixhawkFMUV4ProProductId = 19; ///< Product ID for Pixhawk V4 Pro board static const int pixhawkFMUV2ProductId = 17; ///< Product ID for Pixhawk V2 board static const int pixhawkFMUV2OldBootloaderProductId = 22; ///< Product ID for Bootloader on older Pixhawk V2 boards static const int pixhawkFMUV1ProductId = 16; ///< Product ID for PX4 FMU V1 board @@ -79,18 +73,51 @@ public: /// Override of QSerialPortInfo::availablePorts static QList availablePorts(void); - BoardType_t boardType(void) const; + bool getBoardInfo(BoardType_t& boardType, QString& name) const; /// @return true: we can flash this board type bool canFlash(void); - /// @return true: board is a Pixhawk board - bool boardTypePixhawk(void) const; - /// @return true: Board is currently in bootloader bool isBootloader(void) const; private: + typedef struct { + const char* classString; + BoardType_t boardType; + } BoardClassString2BoardType_t; + + typedef struct { + int vendorId; + int productId; + BoardType_t boardType; + QString name; + } BoardInfo_t; + + typedef struct { + QString regExp; + BoardType_t boardType; + bool androidOnly; + } BoardFallback_t; + + static void _loadJsonData(void); + static BoardType_t _boardClassStringToType(const QString& boardClass); + static QString _boardTypeToString(BoardType_t boardType); + + static bool _jsonLoaded; + static const char* _jsonFileTypeValue; + static const char* _jsonBoardInfoKey; + static const char* _jsonBoardFallbackKey; + static const char* _jsonVendorIDKey; + static const char* _jsonProductIDKey; + static const char* _jsonBoardClassKey; + static const char* _jsonNameKey; + static const char* _jsonRegExpKey; + static const char* _jsonAndroidOnlyKey; + + static const BoardClassString2BoardType_t _rgBoardClass2BoardType[BoardTypeUnknown]; + static QList _boardInfoList; + static QList _boardFallbackList; }; #endif diff --git a/src/comm/USBBoardInfo.json b/src/comm/USBBoardInfo.json new file mode 100644 index 000000000..1050c8986 --- /dev/null +++ b/src/comm/USBBoardInfo.json @@ -0,0 +1,50 @@ +{ + "comment": "AutoConnect usb board info", + + "version": 1, + "fileType": "USBBoardInfo", + "groundStation": "QGroundControl", + + "boardInfo": [ + { "vendorID": 9900, "productID": 16, "boardClass": "Pixhawk", "name": "PX4 FMU V1" }, + { "vendorID": 9900, "productID": 17, "boardClass": "Pixhawk", "name": "PX4 FMU V2" }, + { "vendorID": 9900, "productID": 18, "boardClass": "Pixhawk", "name": "PX4 FMU V4" }, + { "vendorID": 9900, "productID": 22, "boardClass": "Pixhawk", "name": "PX4 FMU V2", "comment": "Bootloader on older Pixhawk V2 boards" }, + { "vendorID": 9900, "productID": 4097, "boardClass": "Pixhawk", "name": "AeroCore" }, + { "vendorID": 9900, "productID": 48, "boardClass": "Pixhawk", "name": "MindPX FMU V2" }, + { "vendorID": 9900, "productID": 64, "boardClass": "Pixhawk", "name": "TAP V1" }, + { "vendorID": 9900, "productID": 65, "boardClass": "Pixhawk", "name": "ASC V1" }, + + { "vendorID": 9900, "productID": 21, "boardClass": "PX4 Flow", "name": "PX4 Flow" }, + + { "vendorID": 1027, "productID": 24597, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "3DR Radio" }, + { "vendorID": 4292, "productID": 600000, "boardClass": "SiK Radio", "name": "SiK Radio", "comment": "SILabs Radio" }, + + { "vendorID": 546, "productID": 424, "boardClass": "RTK GPS", "name": "RTK GPS", "comment": "Ublox RTK GPS" }, + + { "vendorID": 8352, "productID": 16732, "boardClass": "OpenPilot", "name": "OpenPilot OPLink" }, + { "vendorID": 8352, "productID": 16733, "boardClass": "OpenPilot", "name": "OpenPilot CC3D" }, + { "vendorID": 8352, "productID": 16734, "boardClass": "OpenPilot", "name": "OpenPilot Revolution" }, + { "vendorID": 8352, "productID": 16848, "boardClass": "OpenPilot", "name": "Taulabs Sparky2" } + ], + + "boardFallback": [ + { "regExp": "^PX4 FMU v4.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL FMU v4.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 FMU v2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL FMU v2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 FMU v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL FMU v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^MindPX FMU v2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^MindPX FMU v2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^MindPX BL FMU v2.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 TAP v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL TAP v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 ASC v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 BL ASC v1.x$", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 FMU", "boardClass": "Pixhawk" }, + { "regExp": "PX4.*Flow", "boardClass": "PX4 Flow" }, + { "regExp": "^FT231X USB UART$", "boardClass": "SiK Radio" }, + { "regExp": "USB UART$", "boardClass": "SiK Radio", "androidOnly": true, "comment": "Very broad fallback, too dangerous for non-android" } + ] +} -- GitLab From 9b3ee917d9add75eab2f2b817409bd25a719b54c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 13 Jan 2017 14:26:18 -0800 Subject: [PATCH 205/398] Make compiler happy --- src/comm/QGCSerialPortInfo.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/comm/QGCSerialPortInfo.cc b/src/comm/QGCSerialPortInfo.cc index eec24e61d..5e50529b1 100644 --- a/src/comm/QGCSerialPortInfo.cc +++ b/src/comm/QGCSerialPortInfo.cc @@ -227,6 +227,8 @@ bool QGCSerialPortInfo::getBoardInfo(QGCSerialPortInfo::BoardType_t& boardType, QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType) { + QString unknown = QObject::tr("Unknown"); + switch (boardType) { case BoardTypePixhawk: return QObject::tr("Pixhawk"); @@ -239,8 +241,10 @@ QString QGCSerialPortInfo::_boardTypeToString(BoardType_t boardType) case BoardTypeRTKGPS: return QObject::tr("RTK GPS"); case BoardTypeUnknown: - return QObject::tr("Unknown"); + return unknown; } + + return unknown; } -- GitLab From 5de37fe2fffa8759982cfc71ab11206359b5ab07 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 13 Jan 2017 19:05:44 -0800 Subject: [PATCH 206/398] Remove old code with bad reference --- src/ui/preferences/GeneralSettings.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index d22c60515..43fdeae5b 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -34,8 +34,6 @@ QGCView { property Fact _percentRemainingAnnounce: QGroundControl.batteryPercentRemainingAnnounce property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 - property bool _showCruiseSpeed: offlineVehicleCombo.currentText != "Multicopter" - property bool _showHoverSpeed: offlineVehicleCombo.currentText != "FixedWing" readonly property string _requiresRestart: qsTr("(Requires Restart)") -- GitLab From eb72c2a08db7e5318e397d66eb6c7c7f578bc1d5 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Sun, 15 Jan 2017 23:07:04 -0500 Subject: [PATCH 207/398] Fix gstreamer bus message handling for Windows --- src/VideoStreaming/VideoReceiver.cc | 79 +++++++++++------------------ src/VideoStreaming/VideoReceiver.h | 9 ++-- 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index c3ffa5ab7..e24f3b368 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -43,7 +43,8 @@ VideoReceiver::VideoReceiver(QObject* parent) #if defined(QGC_GST_STREAMING) _timer.setSingleShot(true); connect(&_timer, &QTimer::timeout, this, &VideoReceiver::_timeout); - connect(&_busCheckTimer, &QTimer::timeout, this, &VideoReceiver::_busCheck); + connect(this, &VideoReceiver::recordingEOSReceived, this, &VideoReceiver::_eosCB); + connect(this, &VideoReceiver::busMessage, this, &VideoReceiver::_handleBusMessage); #endif } @@ -92,7 +93,6 @@ static void newPadCB(GstElement* element, GstPad* pad, gpointer data) void VideoReceiver::_connected() { //-- Server showed up. Now we start the stream. - _timer.stop(); delete _socket; _socket = NULL; _serverPresent = true; @@ -133,33 +133,6 @@ void VideoReceiver::_timeout() } #endif -#if defined(QGC_GST_STREAMING) -void VideoReceiver::_busCheck() { - GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); - - GstMessage* message; - - while((message = gst_bus_pop(bus)) != NULL) { - _onBusMessage(message); - gst_message_unref(message); - } - - gst_object_unref(bus); - - if(_pipelineStopRec == NULL) { - return; - } - - bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); - if((message = gst_bus_pop_filtered(bus, GST_MESSAGE_EOS)) != NULL) { - _eosCB(message); - gst_message_unref(message); - } - - gst_object_unref(bus); -} -#endif - // When we finish our pipeline will look like this: // // +-->queue-->decoder-->_videosink @@ -279,15 +252,14 @@ void VideoReceiver::start() dataSource = demux = parser = queue = decoder = NULL; -// GstBus* bus = NULL; + GstBus* bus = NULL; -// if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { -// gst_bus_add_watch(bus, _onBusMessage, this); -// gst_object_unref(bus); -// bus = NULL; -// } - // Workaround for above watch on Windows - _busCheckTimer.start(0); + if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { + gst_bus_enable_sync_message_emission(bus); + g_signal_connect(bus, "sync-message", G_CALLBACK(_onBusMessage), this); + gst_object_unref(bus); + bus = NULL; + } running = gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE; @@ -350,14 +322,13 @@ void VideoReceiver::stop() #if defined(QGC_GST_STREAMING) qCDebug(VideoReceiverLog) << "stop()"; if (_pipeline != NULL && !_stopping) { - _busCheckTimer.stop(); qCDebug(VideoReceiverLog) << "Stopping _pipeline"; gst_element_send_event(_pipeline, gst_event_new_eos()); _stopping = true; GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS|GST_MESSAGE_ERROR)); gst_object_unref(bus); - _onBusMessage(message); + _handleBusMessage(message); gst_message_unref(message); } #endif @@ -375,7 +346,7 @@ void VideoReceiver::setVideoSavePath(const QString & path) } #if defined(QGC_GST_STREAMING) -void VideoReceiver::_onBusMessage(GstMessage* msg) +void VideoReceiver::_handleBusMessage(GstMessage* msg) { switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_ERROR: @@ -389,6 +360,13 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) } while(0); // No break! case GST_MESSAGE_EOS: + { + GstBus* bus = NULL; + if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { + gst_bus_disable_sync_message_emission(bus); + gst_object_unref(bus); + bus = NULL; + } gst_element_set_state(_pipeline, GST_STATE_NULL); gst_bin_remove(GST_BIN(_pipeline), _videoSink); gst_object_unref(_pipeline); @@ -400,6 +378,7 @@ void VideoReceiver::_onBusMessage(GstMessage* msg) _running = false; emit recordingChanged(); qCDebug(VideoReceiverLog) << "Stopped"; + } break; case GST_MESSAGE_STATE_CHANGED: _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; @@ -417,7 +396,7 @@ gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer dat Q_UNUSED(bus) Q_ASSERT(msg != NULL && data != NULL); VideoReceiver* pThis = (VideoReceiver*)data; - pThis->_onBusMessage(msg); + pThis->busMessage(msg); return TRUE; } #endif @@ -559,13 +538,11 @@ void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) gst_bin_add_many(GST_BIN(_pipelineStopRec), _sink->queue, _sink->mux, _sink->filesink, NULL); gst_element_link_many(_sink->queue, _sink->mux, _sink->filesink, NULL); - // Add watch for EOS event -// GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); -// gst_bus_add_signal_watch(bus); -// g_signal_connect(bus, "message::eos", G_CALLBACK(_eosCallBack), this); -// gst_object_unref(bus); - - // Above watch is handled by _busCheck now + // Add handler for EOS event + GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); + gst_bus_enable_sync_message_emission(bus); + g_signal_connect(bus, "sync-message", G_CALLBACK(_eosCallBack), this); + gst_object_unref(bus); if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { qCDebug(VideoReceiverLog) << "problem starting _pipelineStopRec"; @@ -586,8 +563,10 @@ gboolean VideoReceiver::_eosCallBack(GstBus* bus, GstMessage* message, gpointer { Q_UNUSED(bus) Q_ASSERT(message != NULL && user_data != NULL); - VideoReceiver* pThis = (VideoReceiver*)user_data; - pThis->_eosCB(message); + if(GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) { + VideoReceiver* pThis = (VideoReceiver*)user_data; + pThis->recordingEOSReceived(message); + } return FALSE; } #endif diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 8a257cd79..555b3ec8f 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -49,6 +49,8 @@ public: signals: void recordingChanged(); + void recordingEOSReceived(GstMessage* message); + void busMessage(GstMessage* message); public slots: void start (); @@ -58,9 +60,10 @@ public slots: void stopRecording (); void startRecording (); + private slots: #if defined(QGC_GST_STREAMING) - void _busCheck (); + void _eosCB(GstMessage* message); void _timeout (); void _connected (); void _socketError (QAbstractSocket::SocketError socketError); @@ -84,10 +87,8 @@ private: bool _stopping; Sink* _sink; GstElement* _tee; - QTimer _busCheckTimer; - void _onBusMessage(GstMessage* message); - void _eosCB(GstMessage* message); + void _handleBusMessage(GstMessage* message); void _unlinkCB(GstPadProbeInfo* info); static gboolean _onBusMessage(GstBus* bus, GstMessage* message, gpointer user_data); static gboolean _eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data); -- GitLab From b67ca0e323f1a850640befb702aa6a712c103d1a Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 17 Jan 2017 13:13:26 -0500 Subject: [PATCH 208/398] Include QDir for VideoManager --- src/FlightDisplay/VideoManager.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index e778dafeb..3eab2d874 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -12,6 +12,7 @@ #include #include #include +#include #ifndef QGC_DISABLE_UVC #include -- GitLab From 4ce68a44b134c5f3631a3c8cde33bdc1249752d3 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 17 Jan 2017 11:33:15 -0500 Subject: [PATCH 209/398] Handle all message processing in _onBusMessage And don't pass along message pointers --- src/VideoStreaming/VideoReceiver.cc | 148 +++++++++++++++------------- src/VideoStreaming/VideoReceiver.h | 18 ++-- 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index e24f3b368..b19646374 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -43,8 +43,9 @@ VideoReceiver::VideoReceiver(QObject* parent) #if defined(QGC_GST_STREAMING) _timer.setSingleShot(true); connect(&_timer, &QTimer::timeout, this, &VideoReceiver::_timeout); - connect(this, &VideoReceiver::recordingEOSReceived, this, &VideoReceiver::_eosCB); - connect(this, &VideoReceiver::busMessage, this, &VideoReceiver::_handleBusMessage); + connect(this, &VideoReceiver::msgErrorReceived, this, &VideoReceiver::_handleError); + connect(this, &VideoReceiver::msgEOSReceived, this, &VideoReceiver::_handleEOS); + connect(this, &VideoReceiver::msgStateChangedReceived, this, &VideoReceiver::_handleStateChanged); #endif } @@ -328,7 +329,12 @@ void VideoReceiver::stop() GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline)); GstMessage* message = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS|GST_MESSAGE_ERROR)); gst_object_unref(bus); - _handleBusMessage(message); + if(GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { + _shutdownPipeline(); + qCritical() << "Error stopping pipeline!"; + } else if(GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) { + _handleEOS(); + } gst_message_unref(message); } #endif @@ -345,58 +351,83 @@ void VideoReceiver::setVideoSavePath(const QString & path) qCDebug(VideoReceiverLog) << "New Path:" << _path; } +void VideoReceiver::_shutdownPipeline() { + GstBus* bus = NULL; + if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { + gst_bus_disable_sync_message_emission(bus); + gst_object_unref(bus); + bus = NULL; + } + gst_element_set_state(_pipeline, GST_STATE_NULL); + gst_bin_remove(GST_BIN(_pipeline), _videoSink); + gst_object_unref(_pipeline); + _pipeline = NULL; + delete _sink; + _sink = NULL; + _serverPresent = false; + _streaming = false; + _recording = false; + _stopping = false; + _running = false; + emit recordingChanged(); +} + #if defined(QGC_GST_STREAMING) -void VideoReceiver::_handleBusMessage(GstMessage* msg) -{ - switch (GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_ERROR: - do { - gchar* debug; - GError* error; - gst_message_parse_error(msg, &error, &debug); - g_free(debug); - qCritical() << error->message; - g_error_free(error); - } while(0); - // No break! - case GST_MESSAGE_EOS: - { - GstBus* bus = NULL; - if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { - gst_bus_disable_sync_message_emission(bus); - gst_object_unref(bus); - bus = NULL; - } - gst_element_set_state(_pipeline, GST_STATE_NULL); - gst_bin_remove(GST_BIN(_pipeline), _videoSink); - gst_object_unref(_pipeline); - _pipeline = NULL; - _serverPresent = false; - _streaming = false; - _recording = false; - _stopping = false; - _running = false; - emit recordingChanged(); +void VideoReceiver::_handleError() { + qCDebug(VideoReceiverLog) << "Gstreamer error!"; + _shutdownPipeline(); +} +#endif + +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_handleEOS() { + if(_stopping) { + _shutdownPipeline(); qCDebug(VideoReceiverLog) << "Stopped"; - } - break; - case GST_MESSAGE_STATE_CHANGED: - _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; - qCDebug(VideoReceiverLog) << "State changed, _streaming:" << _streaming; - break; - default: - break; + } else if(_recording && _sink->removing) { + _shutdownRecordingBranch(); + } else { + qCritical() << "VideoReceiver: Unexpected EOS!"; + _shutdownPipeline(); } } #endif +#if defined(QGC_GST_STREAMING) +void VideoReceiver::_handleStateChanged() { + _streaming = GST_STATE(_pipeline) == GST_STATE_PLAYING; + qCDebug(VideoReceiverLog) << "State changed, _streaming:" << _streaming; +} +#endif + #if defined(QGC_GST_STREAMING) gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer data) { Q_UNUSED(bus) Q_ASSERT(msg != NULL && data != NULL); VideoReceiver* pThis = (VideoReceiver*)data; - pThis->busMessage(msg); + + switch(GST_MESSAGE_TYPE(msg)) { + case(GST_MESSAGE_ERROR): { + gchar* debug; + GError* error; + gst_message_parse_error(msg, &error, &debug); + g_free(debug); + qCritical() << error->message; + g_error_free(error); + pThis->msgErrorReceived(); + } + break; + case(GST_MESSAGE_EOS): + pThis->msgEOSReceived(); + break; + case(GST_MESSAGE_STATE_CHANGED): + pThis->msgStateChangedReceived(); + break; + default: + break; + } + return TRUE; } #endif @@ -486,10 +517,8 @@ void VideoReceiver::stopRecording(void) // -At this point all of the recoring elements have been flushed, and the video file has been finalized // -Now we can remove the temporary pipeline and its elements #if defined(QGC_GST_STREAMING) -void VideoReceiver::_eosCB(GstMessage* message) +void VideoReceiver::_shutdownRecordingBranch() { - Q_UNUSED(message) - gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->queue); gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->mux); gst_bin_remove(GST_BIN(_pipelineStopRec), _sink->filesink); @@ -520,7 +549,7 @@ void VideoReceiver::_eosCB(GstMessage* message) // -Setup watch and handler for EOS event on the temporary pipeline's bus // -Send an EOS event at the beginning of that pipeline #if defined(QGC_GST_STREAMING) -void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) +void VideoReceiver::_detachRecordingBranch(GstPadProbeInfo* info) { Q_UNUSED(info) @@ -541,7 +570,7 @@ void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) // Add handler for EOS event GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(_pipelineStopRec)); gst_bus_enable_sync_message_emission(bus); - g_signal_connect(bus, "sync-message", G_CALLBACK(_eosCallBack), this); + g_signal_connect(bus, "sync-message", G_CALLBACK(_onBusMessage), this); gst_object_unref(bus); if(gst_element_set_state(_pipelineStopRec, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { @@ -556,31 +585,16 @@ void VideoReceiver::_unlinkCB(GstPadProbeInfo* info) } #endif -// This is only installed on the transient _pipelineStopRec in order -// to finalize a video file. It is not used for the main _pipeline. -#if defined(QGC_GST_STREAMING) -gboolean VideoReceiver::_eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data) -{ - Q_UNUSED(bus) - Q_ASSERT(message != NULL && user_data != NULL); - if(GST_MESSAGE_TYPE(message) == GST_MESSAGE_EOS) { - VideoReceiver* pThis = (VideoReceiver*)user_data; - pThis->recordingEOSReceived(message); - } - return FALSE; -} -#endif - #if defined(QGC_GST_STREAMING) GstPadProbeReturn VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) { Q_UNUSED(pad); Q_ASSERT(info != NULL && user_data != NULL); VideoReceiver* pThis = (VideoReceiver*)user_data; - // We will only execute once - if(!g_atomic_int_compare_and_exchange(&pThis->_sink->removing, FALSE, TRUE)) - return GST_PAD_PROBE_REMOVE; - pThis->_unlinkCB(info); + // We will only act once + if(g_atomic_int_compare_and_exchange(&pThis->_sink->removing, FALSE, TRUE)) + pThis->_detachRecordingBranch(info); + return GST_PAD_PROBE_REMOVE; } #endif diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 555b3ec8f..b3a1a9db3 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -49,8 +49,11 @@ public: signals: void recordingChanged(); - void recordingEOSReceived(GstMessage* message); - void busMessage(GstMessage* message); +#if defined(QGC_GST_STREAMING) + void msgErrorReceived(); + void msgEOSReceived(); + void msgStateChangedReceived(); +#endif public slots: void start (); @@ -63,10 +66,12 @@ public slots: private slots: #if defined(QGC_GST_STREAMING) - void _eosCB(GstMessage* message); void _timeout (); void _connected (); void _socketError (QAbstractSocket::SocketError socketError); + void _handleError(); + void _handleEOS(); + void _handleStateChanged(); #endif private: @@ -88,11 +93,12 @@ private: Sink* _sink; GstElement* _tee; - void _handleBusMessage(GstMessage* message); - void _unlinkCB(GstPadProbeInfo* info); static gboolean _onBusMessage(GstBus* bus, GstMessage* message, gpointer user_data); - static gboolean _eosCallBack(GstBus* bus, GstMessage* message, gpointer user_data); static GstPadProbeReturn _unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user_data); + void _detachRecordingBranch(GstPadProbeInfo* info); + void _shutdownRecordingBranch(); + void _shutdownPipeline(); + #endif QString _uri; -- GitLab From a457abc9c2ef1054f03a194f0c04d61ffa089024 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 17 Jan 2017 15:50:06 -0500 Subject: [PATCH 210/398] Don't declare status accessors if there is no gstreamer support --- src/VideoStreaming/VideoReceiver.cc | 2 +- src/VideoStreaming/VideoReceiver.h | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index b19646374..20aa8713b 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -25,12 +25,12 @@ QGC_LOGGING_CATEGORY(VideoReceiverLog, "VideoReceiverLog") VideoReceiver::VideoReceiver(QObject* parent) : QObject(parent) +#if defined(QGC_GST_STREAMING) , _running(false) , _recording(false) , _streaming(false) , _starting(false) , _stopping(false) -#if defined(QGC_GST_STREAMING) , _sink(NULL) , _tee(NULL) , _pipeline(NULL) diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index b3a1a9db3..9ec600fed 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -32,24 +32,27 @@ class VideoReceiver : public QObject { Q_OBJECT public: +#if defined(QGC_GST_STREAMING) Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) +#endif explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); #if defined(QGC_GST_STREAMING) - void setVideoSink(GstElement* _sink); -#endif + void setVideoSink(GstElement* sink); bool running() { return _running; } bool recording() { return _recording; } bool streaming() { return _streaming; } bool starting() { return _starting; } bool stopping() { return _stopping; } +#endif + signals: - void recordingChanged(); #if defined(QGC_GST_STREAMING) + void recordingChanged(); void msgErrorReceived(); void msgEOSReceived(); void msgStateChangedReceived(); -- GitLab From dbf1dc04534f35d4573799430f6592dfb91b6d68 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 17 Jan 2017 16:05:00 -0500 Subject: [PATCH 211/398] Don't define _shutdownPipeline if there is no gstreamer support --- src/VideoStreaming/VideoReceiver.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 20aa8713b..c4f698d6a 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -351,6 +351,7 @@ void VideoReceiver::setVideoSavePath(const QString & path) qCDebug(VideoReceiverLog) << "New Path:" << _path; } +#if defined(QGC_GST_STREAMING) void VideoReceiver::_shutdownPipeline() { GstBus* bus = NULL; if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { @@ -371,6 +372,7 @@ void VideoReceiver::_shutdownPipeline() { _running = false; emit recordingChanged(); } +#endif #if defined(QGC_GST_STREAMING) void VideoReceiver::_handleError() { -- GitLab From 5ce9d168c0d448009059377733d9d6e98d00fb2a Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 17 Jan 2017 17:24:14 -0500 Subject: [PATCH 212/398] FlightDisplayView: check if we have valid VideoReceiver --- src/FlightDisplay/FlightDisplayView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index b2da710a7..e46cd5da1 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -275,7 +275,7 @@ QGCView { anchors.top: parent.top anchors.bottom: parent.bottom width: height - radius: QGroundControl.videoManager.videoReceiver.recording ? 0 : height + radius: QGroundControl.videoManager.videoReceiver && QGroundControl.videoManager.videoReceiver.recording ? 0 : height color: "red" } @@ -292,7 +292,7 @@ QGCView { MouseArea { anchors.fill: parent - onClicked: QGroundControl.videoManager.videoReceiver.recording ? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() + onClicked: QGroundControl.videoManager.videoReceiver && QGroundControl.videoManager.videoReceiver.recording ? QGroundControl.videoManager.videoReceiver.stopRecording() : QGroundControl.videoManager.videoReceiver.startRecording() } } -- GitLab From c4d334783f2965d06646ad5600dc736b35ea2597 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Wed, 18 Jan 2017 15:05:46 -0500 Subject: [PATCH 213/398] SDL: warn with SDL error string when joystick feature detection fails --- src/Joystick/JoystickSDL.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index 854ac4407..57ac14405 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -41,9 +41,13 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl hatCount = 0; } else { isGameController = false; + SDL_ClearError(); axisCount = SDL_JoystickNumAxes(sdlJoystick); buttonCount = SDL_JoystickNumButtons(sdlJoystick); hatCount = SDL_JoystickNumHats(sdlJoystick); + if (axisCount < 0 || buttonCount < 0 || hatCount < 0) { + qCWarning(JoystickLog) << "\t libsdl error parsing joystick features:" << SDL_GetError(); + } } SDL_JoystickClose(sdlJoystick); -- GitLab From 9570aecca62a1e19c7fab8c3830bd15b98cf3114 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Thu, 19 Jan 2017 12:18:33 -0500 Subject: [PATCH 214/398] SDL: handle joystick open failure and log error message --- src/Joystick/JoystickSDL.cc | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index 57ac14405..c99c4c6bf 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -33,7 +33,7 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl int axisCount, buttonCount, hatCount; bool isGameController; - SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i); + if (SDL_IsGameController(i)) { isGameController = true; axisCount = SDL_CONTROLLER_AXIS_MAX; @@ -41,17 +41,21 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl hatCount = 0; } else { isGameController = false; - SDL_ClearError(); - axisCount = SDL_JoystickNumAxes(sdlJoystick); - buttonCount = SDL_JoystickNumButtons(sdlJoystick); - hatCount = SDL_JoystickNumHats(sdlJoystick); - if (axisCount < 0 || buttonCount < 0 || hatCount < 0) { - qCWarning(JoystickLog) << "\t libsdl error parsing joystick features:" << SDL_GetError(); + if (SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i)) { + SDL_ClearError(); + axisCount = SDL_JoystickNumAxes(sdlJoystick); + buttonCount = SDL_JoystickNumButtons(sdlJoystick); + hatCount = SDL_JoystickNumHats(sdlJoystick); + if (axisCount < 0 || buttonCount < 0 || hatCount < 0) { + qCWarning(JoystickLog) << "\t libsdl error parsing joystick features:" << SDL_GetError(); + } + SDL_JoystickClose(sdlJoystick); + } else { + qCWarning(JoystickLog) << "\t libsdl failed opening joystick" << qPrintable(name) << "error:" << SDL_GetError(); + continue; } } - SDL_JoystickClose(sdlJoystick); - qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController; ret[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); } else { -- GitLab From 3363879c547c79744f2d259513feda88e83c603e Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Thu, 19 Jan 2017 12:37:15 -0500 Subject: [PATCH 215/398] Defer joystick probe until after MainWindow is created --- src/Joystick/JoystickManager.cc | 3 +++ src/Joystick/JoystickManager.h | 3 +++ src/QGCApplication.cc | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 1ef0fc607..a8d3f6f93 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -53,7 +53,10 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) _multiVehicleManager = _toolbox->multiVehicleManager(); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +} +void JoystickManager::discoverJoysticks() +{ #ifdef __sdljoystick__ _name2JoystickMap = JoystickSDL::discover(_multiVehicleManager); #elif defined(__android__) diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index 26cf0093f..8460b752f 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -48,6 +48,9 @@ public: // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); +public slots: + void discoverJoysticks(); + signals: void activeJoystickChanged(Joystick* joystick); void activeJoystickNameChanged(const QString& name); diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 8e2295509..d436f6832 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -429,6 +429,9 @@ bool QGCApplication::_initForNormalAppBoot(void) // Load known link configurations toolbox()->linkManager()->loadLinkConfigurationList(); + // Probe for joysticks - TODO: manage on a timer or use events to deal with hotplug + toolbox()->joystickManager()->discoverJoysticks(); + if (_settingsUpgraded) { settings.clear(); settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION); -- GitLab From 3907cd090e8df5d15eb2d10486df695f75217809 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Thu, 19 Jan 2017 16:21:01 -0500 Subject: [PATCH 216/398] Explicitly init SDL joystick to work around upstream bug --- src/Joystick/JoystickSDL.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index c99c4c6bf..303c3005c 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -15,7 +15,7 @@ JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, in QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { static QMap ret; - if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_NOPARACHUTE) < 0) { + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) { qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError(); return ret; } -- GitLab From d90dd924756d9b98a4c42ace0a6c6c84c8e33d08 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 19 Jan 2017 19:16:22 -0700 Subject: [PATCH 217/398] MultiVehicle List supports flight mode change --- qgroundcontrol.qrc | 1 + src/FlightDisplay/MultiVehicleList.qml | 8 +-- src/QmlControls/FlightModeMenu.qml | 66 +++++++++++++++++++ .../QGroundControl.Controls.qmldir | 1 + 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/QmlControls/FlightModeMenu.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 4b7b89390..fc3446c49 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -48,6 +48,7 @@ src/QmlControls/ExclusiveGroupItem.qml src/QmlControls/FactSliderPanel.qml src/QmlControls/FlightModeDropdown.qml + src/QmlControls/FlightModeMenu.qml src/QmlControls/GuidedBar.qml src/QmlControls/IndicatorButton.qml src/QmlControls/JoystickThumbPad.qml diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index fbb78e729..c59f876b8 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -77,10 +77,10 @@ QGCListView { color: _textColor } - QGCLabel { - text: _vehicle.flightMode - font.pointSize: ScreenTools.largeFontPointSize - color: _textColor + FlightModeMenu { + font.pointSize: ScreenTools.largeFontPointSize + color: _textColor + activeVehicle: _vehicle } } diff --git a/src/QmlControls/FlightModeMenu.qml b/src/QmlControls/FlightModeMenu.qml new file mode 100644 index 000000000..2f7794913 --- /dev/null +++ b/src/QmlControls/FlightModeMenu.qml @@ -0,0 +1,66 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +// Label control whichs pop up a flight mode change menu when clicked +QGCLabel { + id: flightModeMenuLabel + text: activeVehicle ? activeVehicle.flightMode : qsTr("N/A", "No data to display") + + property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + Menu { + id: flightModesMenu + } + + Component { + id: flightModeMenuItemComponent + + MenuItem { + onTriggered: activeVehicle.flightMode = text + } + } + + property var flightModesMenuItems: [] + + function updateFlightModesMenu() { + if (activeVehicle && activeVehicle.flightModeSetAvailable) { + // Remove old menu items + for (var i = 0; i < flightModesMenuItems.length; i++) { + flightModesMenu.removeItem(flightModesMenuItems[i]) + } + flightModesMenuItems.length = 0 + // Add new items + for (var i = 0; i < activeVehicle.flightModes.length; i++) { + var menuItem = flightModeMenuItemComponent.createObject(null, { "text": activeVehicle.flightModes[i] }) + flightModesMenuItems.push(menuItem) + flightModesMenu.insertItem(i, menuItem) + } + } + } + + Component.onCompleted: flightModeMenuLabel.updateFlightModesMenu() + + Connections { + target: QGroundControl.multiVehicleManager + onActiveVehicleChanged: flightModeMenuLabel.updateFlightModesMenu() + } + + MouseArea { + visible: activeVehicle && activeVehicle.flightModeSetAvailable + anchors.fill: parent + onClicked: flightModesMenu.popup() + } +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index f1a1caf12..c33e30fea 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -7,6 +7,7 @@ DropButton 1.0 DropButton.qml ExclusiveGroupItem 1.0 ExclusiveGroupItem.qml FactSliderPanel 1.0 FactSliderPanel.qml FlightModeDropdown 1.0 FlightModeDropdown.qml +FlightModeMenu 1.0 FlightModeMenu.qml GuidedBar 1.0 GuidedBar.qml IndicatorButton 1.0 IndicatorButton.qml JoystickThumbPad 1.0 JoystickThumbPad.qml -- GitLab From 85009f4401a17f8314d30871554ea5202789320d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 19 Jan 2017 19:16:32 -0700 Subject: [PATCH 218/398] Mission AutoLoad support --- src/MissionManager/GeoFenceController.cc | 2 +- src/MissionManager/MissionController.cc | 138 ++++++++------- src/MissionManager/MissionController.h | 25 ++- src/MissionManager/MissionLoader.h | 193 +++++++++++++++++++++ src/MissionManager/RallyPointController.cc | 2 +- src/QmlControls/QGroundControlQmlGlobal.cc | 13 ++ src/QmlControls/QGroundControlQmlGlobal.h | 10 +- src/Vehicle/Vehicle.cc | 21 ++- src/ui/preferences/GeneralSettings.qml | 27 +++ 9 files changed, 354 insertions(+), 77 deletions(-) create mode 100644 src/MissionManager/MissionLoader.h diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index ae28afba1..7dbd479f2 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -214,7 +214,7 @@ void GeoFenceController::loadFromFile(const QString& filename) QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - errorString = file.errorString(); + errorString = file.errorString() + QStringLiteral(" ") + filename; } else { QJsonDocument jsonDoc; QByteArray bytes = file.readAll(); diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 38355adc3..71d2b6fff 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -87,7 +87,7 @@ void MissionController::_init(void) { // We start with an empty mission _visualItems = new QmlObjectListModel(this); - _addPlannedHomePosition(_visualItems, false /* addToCenter */); + _addPlannedHomePosition(_activeVehicle, _visualItems, false /* addToCenter */); _initAllVisualItems(); } @@ -113,11 +113,11 @@ void MissionController::_newMissionItemsAvailableFromVehicle(void) _deinitAllVisualItems(); - _visualItems->deleteListAndContents(); + _visualItems->deleteLater(); _visualItems = newControllerMissionItems; if (!_activeVehicle->firmwarePlugin()->sendHomePositionToVehicle() || _visualItems->count() == 0) { - _addPlannedHomePosition(_visualItems,true /* addToCenter */); + _addPlannedHomePosition(_activeVehicle, _visualItems,true /* addToCenter */); } _missionItemsRequested = false; @@ -139,12 +139,18 @@ void MissionController::loadFromVehicle(void) void MissionController::sendToVehicle(void) { - if (_activeVehicle) { + sendItemsToVehicle(_activeVehicle, _visualItems); + _visualItems->setDirty(false); +} + +void MissionController::sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems) +{ + if (vehicle) { // Convert to MissionItems so we can send to vehicle QList missionItems; - for (int i=0; i<_visualItems->count(); i++) { - VisualMissionItem* visualItem = qobject_cast(_visualItems->get(i)); + for (int i=0; icount(); i++) { + VisualMissionItem* visualItem = qobject_cast(visualMissionItems->get(i)); if (visualItem->isSimpleItem()) { missionItems.append(new MissionItem(qobject_cast(visualItem)->missionItem())); } else { @@ -153,15 +159,14 @@ void MissionController::sendToVehicle(void) for (int j=0; jcount(); j++) { missionItems.append(new MissionItem(*qobject_cast(complexMissionItems->get(j)))); } - delete complexMissionItems; + complexMissionItems->deleteLater(); } } - _activeVehicle->missionManager()->writeMissionItems(missionItems); - _visualItems->setDirty(false); + vehicle->missionManager()->writeMissionItems(missionItems); for (int i=0; ideleteLater(); } } } @@ -216,7 +221,7 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) int MissionController::insertComplexMissionItem(QGeoCoordinate coordinate, int i) { int sequenceNumber = _nextSequenceNumber(); - SurveyMissionItem* newItem = new SurveyMissionItem(_activeVehicle, this); + SurveyMissionItem* newItem = new SurveyMissionItem(_activeVehicle, _visualItems); newItem->setSequenceNumber(sequenceNumber); newItem->setCoordinate(coordinate); _initVisualItem(newItem); @@ -260,15 +265,15 @@ void MissionController::removeAll(void) { if (_visualItems) { _deinitAllVisualItems(); - _visualItems->deleteListAndContents(); + _visualItems->deleteLater(); _visualItems = new QmlObjectListModel(this); - _addPlannedHomePosition(_visualItems, false /* addToCenter */); + _addPlannedHomePosition(_activeVehicle, _visualItems, false /* addToCenter */); _initAllVisualItems(); _visualItems->setDirty(true); } } -bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) +bool MissionController::_loadJsonMissionFile(Vehicle* vehicle, const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) { QJsonParseError jsonParseError; QJsonDocument jsonDoc(QJsonDocument::fromJson(bytes, &jsonParseError)); @@ -295,13 +300,13 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL } if (fileVersion == 1) { - return _loadJsonMissionFileV1(json, visualItems, complexItems, errorString); + return _loadJsonMissionFileV1(vehicle, json, visualItems, complexItems, errorString); } else { - return _loadJsonMissionFileV2(json, visualItems, complexItems, errorString); + return _loadJsonMissionFileV2(vehicle, json, visualItems, complexItems, errorString); } } -bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) +bool MissionController::_loadJsonMissionFileV1(Vehicle* vehicle, const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) { // Validate root object keys QList rootKeyInfoList = { @@ -325,7 +330,7 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec return false; } - SurveyMissionItem* item = new SurveyMissionItem(_activeVehicle, this); + SurveyMissionItem* item = new SurveyMissionItem(vehicle, visualItems); const QJsonObject itemObject = itemValue.toObject(); if (item->load(itemObject, itemObject["id"].toInt(), errorString)) { complexItems->append(item); @@ -368,7 +373,7 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec } const QJsonObject itemObject = itemValue.toObject(); - SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* item = new SimpleMissionItem(vehicle, visualItems); if (item->load(itemObject, itemObject["id"].toInt(), errorString)) { qCDebug(MissionControllerLog) << "Json load: adding simple item expectedSequence:actualSequence" << nextSequenceNumber << item->sequenceNumber(); visualItems->append(item); @@ -381,7 +386,7 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec } while (nextSimpleItemIndex < itemArray.count() || nextComplexItemIndex < complexItems->count()); if (json.contains(_jsonPlannedHomePositionKey)) { - SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* item = new SimpleMissionItem(vehicle, visualItems); if (item->load(json[_jsonPlannedHomePositionKey].toObject(), 0, errorString)) { visualItems->insert(0, item); @@ -389,13 +394,13 @@ bool MissionController::_loadJsonMissionFileV1(const QJsonObject& json, QmlObjec return false; } } else { - _addPlannedHomePosition(visualItems, true /* addToCenter */); + _addPlannedHomePosition(vehicle, visualItems, true /* addToCenter */); } return true; } -bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) +bool MissionController::_loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString) { // Validate root object keys QList rootKeyInfoList = { @@ -417,7 +422,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec if (!JsonHelper::loadGeoCoordinate(json[_jsonPlannedHomePositionKey], true /* altitudeRequired */, homeCoordinate, errorString)) { return false; } - if (json.contains(_jsonVehicleTypeKey) && _activeVehicle->isOfflineEditingVehicle()) { + if (json.contains(_jsonVehicleTypeKey) && vehicle->isOfflineEditingVehicle()) { QGroundControlQmlGlobal::offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); } if (json.contains(_jsonCruiseSpeedKey)) { @@ -427,7 +432,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec QGroundControlQmlGlobal::offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); } - SimpleMissionItem* homeItem = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* homeItem = new SimpleMissionItem(vehicle, visualItems); homeItem->setCoordinate(homeCoordinate); visualItems->insert(0, homeItem); qCDebug(MissionControllerLog) << "plannedHomePosition" << homeCoordinate; @@ -457,7 +462,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec if (itemType == VisualMissionItem::jsonTypeSimpleItemValue) { qCDebug(MissionControllerLog) << "Loading MISSION_ITEM: nextSequenceNumber" << nextSequenceNumber; - SimpleMissionItem* simpleItem = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* simpleItem = new SimpleMissionItem(vehicle, visualItems); if (simpleItem->load(itemObject, nextSequenceNumber++, errorString)) { visualItems->append(simpleItem); } else { @@ -474,7 +479,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec if (complexItemType == SurveyMissionItem::jsonComplexItemTypeValue) { qCDebug(MissionControllerLog) << "Loading Survey: nextSequenceNumber" << nextSequenceNumber; - SurveyMissionItem* surveyItem = new SurveyMissionItem(_activeVehicle, this); + SurveyMissionItem* surveyItem = new SurveyMissionItem(vehicle, visualItems); if (!surveyItem->load(itemObject, nextSequenceNumber++, errorString)) { return false; } @@ -519,7 +524,7 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec return true; } -bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString) +bool MissionController::_loadTextMissionFile(Vehicle* vehicle, QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString) { bool addPlannedHomePosition = false; @@ -540,7 +545,7 @@ bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListM if (versionOk) { while (!stream.atEnd()) { - SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* item = new SimpleMissionItem(vehicle, visualItems); if (item->load(stream)) { visualItems->append(item); @@ -555,7 +560,7 @@ bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListM } if (addPlannedHomePosition || visualItems->count() == 0) { - _addPlannedHomePosition(visualItems, true /* addToCenter */); + _addPlannedHomePosition(vehicle, visualItems, true /* addToCenter */); // Update sequence numbers in DO_JUMP commands to take into account added home position in index 0 for (int i=1; icount(); i++) { @@ -571,19 +576,49 @@ bool MissionController::_loadTextMissionFile(QTextStream& stream, QmlObjectListM void MissionController::loadFromFile(const QString& filename) { + QmlObjectListModel* newVisualItems = NULL; + QmlObjectListModel* newComplexItems = NULL; + + if (!loadItemsFromFile(_activeVehicle, filename, &newVisualItems, &newComplexItems)) { + return; + } + + if (_visualItems) { + _deinitAllVisualItems(); + _visualItems->deleteLater(); + } + if (_complexItems) { + _complexItems->deleteLater(); + } + + _visualItems = newVisualItems; + _complexItems = newComplexItems; + + if (_visualItems->count() == 0) { + _addPlannedHomePosition(_activeVehicle, _visualItems, true /* addToCenter */); + } + + _initAllVisualItems(); +} + +bool MissionController::loadItemsFromFile(Vehicle* vehicle, const QString& filename, QmlObjectListModel** visualItems, QmlObjectListModel** complexItems) +{ + *visualItems = NULL; + *complexItems = NULL; + QString errorString; if (filename.isEmpty()) { - return; + return false; } - QmlObjectListModel* newVisualItems = new QmlObjectListModel(this); - QmlObjectListModel* newComplexItems = new QmlObjectListModel(this); + *visualItems = new QmlObjectListModel(); + *complexItems = new QmlObjectListModel(); QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - errorString = file.errorString(); + errorString = file.errorString() + QStringLiteral(" ") + filename; } else { QByteArray bytes = file.readAll(); QTextStream stream(&bytes); @@ -591,42 +626,21 @@ void MissionController::loadFromFile(const QString& filename) QString firstLine = stream.readLine(); if (firstLine.contains(QRegExp("QGC.*WPL"))) { stream.seek(0); - _loadTextMissionFile(stream, newVisualItems, errorString); + _loadTextMissionFile(vehicle, stream, *visualItems, errorString); } else { - _loadJsonMissionFile(bytes, newVisualItems, newComplexItems, errorString); + _loadJsonMissionFile(vehicle, bytes, *visualItems, *complexItems, errorString); } } if (!errorString.isEmpty()) { - for (int i=0; icount(); i++) { - newVisualItems->get(i)->deleteLater(); - } - for (int i=0; icount(); i++) { - newComplexItems->get(i)->deleteLater(); - } - delete newVisualItems; - delete newComplexItems; + (*visualItems)->deleteLater(); + (*complexItems)->deleteLater(); qgcApp()->showMessage(errorString); - return; - } - - if (_visualItems) { - _deinitAllVisualItems(); - _visualItems->deleteListAndContents(); - } - if (_complexItems) { - _complexItems->deleteLater(); - } - - _visualItems = newVisualItems; - _complexItems = newComplexItems; - - if (_visualItems->count() == 0) { - _addPlannedHomePosition(_visualItems, true /* addToCenter */); + return false; } - _initAllVisualItems(); + return true; } void MissionController::loadFromFilePicker(void) @@ -1394,11 +1408,11 @@ double MissionController::_normalizeLon(double lon) } /// Add the home position item to the front of the list -void MissionController::_addPlannedHomePosition(QmlObjectListModel* visualItems, bool addToCenter) +void MissionController::_addPlannedHomePosition(Vehicle* vehicle, QmlObjectListModel* visualItems, bool addToCenter) { bool homePositionSet = false; - SimpleMissionItem* homeItem = new SimpleMissionItem(_activeVehicle, this); + SimpleMissionItem* homeItem = new SimpleMissionItem(vehicle, visualItems); visualItems->insert(0, homeItem); if (visualItems->count() > 1 && addToCenter) { diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index bac54c891..5a341ad19 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -59,6 +59,17 @@ public: /// @return Sequence number for new item Q_INVOKABLE int insertComplexMissionItem(QGeoCoordinate coordinate, int i); + /// Loads the mission items from the specified file + /// @param[in] vehicle Vehicle we are loading items for + /// @param[in] filename File to load from + /// @param[out] visualItems Visual items loaded, returns NULL if error + /// @param[out] complexItems Complex items loaded, returns NULL if error + /// @return success/fail + static bool loadItemsFromFile(Vehicle* vehicle, const QString& filename, QmlObjectListModel** visualItems, QmlObjectListModel** complexItems); + + /// Sends the mission items to the specified vehicle + static void sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* visualMissionItems); + // Overrides from PlanElementController void start (bool editMode) final; void startStaticActiveVehicle (Vehicle* vehicle) final; @@ -135,13 +146,13 @@ private: static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem); bool _findLastAltitude(double* lastAltitude, MAV_FRAME* frame); bool _findLastAcceptanceRadius(double* lastAcceptanceRadius); - void _addPlannedHomePosition(QmlObjectListModel* visualItems, bool addToCenter); - double _normalizeLat(double lat); - double _normalizeLon(double lon); - bool _loadJsonMissionFile(const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); - bool _loadJsonMissionFileV1(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); - bool _loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); - bool _loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); + static double _normalizeLat(double lat); + static double _normalizeLon(double lon); + static void _addPlannedHomePosition(Vehicle* vehicle, QmlObjectListModel* visualItems, bool addToCenter); + static bool _loadJsonMissionFile(Vehicle* vehicle, const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + static bool _loadJsonMissionFileV1(Vehicle* vehicle, const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + static bool _loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + static bool _loadTextMissionFile(Vehicle* vehicle, QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); int _nextSequenceNumber(void); void _setMissionDistance(double missionDistance); void _setMissionTime(double missionTime); diff --git a/src/MissionManager/MissionLoader.h b/src/MissionManager/MissionLoader.h new file mode 100644 index 000000000..36360cbc4 --- /dev/null +++ b/src/MissionManager/MissionLoader.h @@ -0,0 +1,193 @@ +/**************************************************************************** + * + * (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 MissionController_H +#define MissionController_H + +#include "PlanElementController.h" +#include "QmlObjectListModel.h" +#include "Vehicle.h" +#include "QGCLoggingCategory.h" +#include "MavlinkQmlSingleton.h" +#include "VisualMissionItem.h" + +#include + +class CoordinateVector; + +Q_DECLARE_LOGGING_CATEGORY(MissionControllerLog) + +typedef QPair VisualItemPair; +typedef QHash CoordVectHashTable; +class MissionController : public PlanElementController +{ + Q_OBJECT + +public: + MissionController(QObject* parent = NULL); + ~MissionController(); + + Q_PROPERTY(QGeoCoordinate plannedHomePosition READ plannedHomePosition NOTIFY plannedHomePositionChanged) + Q_PROPERTY(QmlObjectListModel* visualItems READ visualItems NOTIFY visualItemsChanged) + Q_PROPERTY(QmlObjectListModel* complexVisualItems READ complexVisualItems NOTIFY complexVisualItemsChanged) + Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) + + Q_PROPERTY(double missionDistance READ missionDistance NOTIFY missionDistanceChanged) + Q_PROPERTY(double missionTime READ missionTime NOTIFY missionTimeChanged) + Q_PROPERTY(double missionHoverDistance READ missionHoverDistance NOTIFY missionHoverDistanceChanged) + Q_PROPERTY(double missionCruiseDistance READ missionCruiseDistance NOTIFY missionCruiseDistanceChanged) + Q_PROPERTY(double missionHoverTime READ missionHoverTime NOTIFY missionHoverTimeChanged) + Q_PROPERTY(double missionCruiseTime READ missionCruiseTime NOTIFY missionCruiseTimeChanged) + Q_PROPERTY(double missionMaxTelemetry READ missionMaxTelemetry NOTIFY missionMaxTelemetryChanged) + + Q_INVOKABLE void removeMissionItem(int index); + + /// Add a new simple mission item to the list + /// @param i: index to insert at + /// @return Sequence number for new item + Q_INVOKABLE int insertSimpleMissionItem(QGeoCoordinate coordinate, int i); + + /// Add a new complex mission item to the list + /// @param i: index to insert at + /// @return Sequence number for new item + Q_INVOKABLE int insertComplexMissionItem(QGeoCoordinate coordinate, int i); + + // Overrides from PlanElementController + void start (bool editMode) final; + void startStaticActiveVehicle (Vehicle* vehicle) final; + void loadFromVehicle (void) final; + void sendToVehicle (void) final; + void loadFromFilePicker (void) final; + void loadFromFile (const QString& filename) final; + void saveToFilePicker (void) final; + void saveToFile (const QString& filename) final; + void removeAll (void) final; + bool syncInProgress (void) const final; + bool dirty (void) const final; + void setDirty (bool dirty) final; + + QString fileExtension(void) const final; + + // Property accessors + + QGeoCoordinate plannedHomePosition (void); + QmlObjectListModel* visualItems (void) { return _visualItems; } + QmlObjectListModel* complexVisualItems (void) { return _complexItems; } + QmlObjectListModel* waypointLines (void) { return &_waypointLines; } + + double missionDistance (void) const { return _missionDistance; } + double missionTime (void) const { return _missionTime; } + double missionHoverDistance (void) const { return _missionHoverDistance; } + double missionHoverTime (void) const { return _missionHoverTime; } + double missionCruiseDistance (void) const { return _missionCruiseDistance; } + double missionCruiseTime (void) const { return _missionCruiseTime; } + double missionMaxTelemetry (void) const { return _missionMaxTelemetry; } + double cruiseSpeed (void) const; + double hoverSpeed (void) const; + + static void createVisualItemsFromFile(const QString& filename); + +signals: + void plannedHomePositionChanged(QGeoCoordinate plannedHomePosition); + void visualItemsChanged(void); + void complexVisualItemsChanged(void); + void waypointLinesChanged(void); + void newItemsFromVehicle(void); + void missionDistanceChanged(double missionDistance); + void missionTimeChanged(void); + void missionHoverDistanceChanged(double missionHoverDistance); + void missionHoverTimeChanged(void); + void missionCruiseDistanceChanged(double missionCruiseDistance); + void missionCruiseTimeChanged(void); + void missionMaxTelemetryChanged(double missionMaxTelemetry); + void cruiseDistanceChanged(double cruiseDistance); + void hoverDistanceChanged(double hoverDistance); + void cruiseSpeedChanged(double cruiseSpeed); + void hoverSpeedChanged(double hoverSpeed); + +private slots: + void _newMissionItemsAvailableFromVehicle(); + void _itemCommandChanged(void); + void _activeVehicleHomePositionAvailableChanged(bool homePositionAvailable); + void _activeVehicleHomePositionChanged(const QGeoCoordinate& homePosition); + void _inProgressChanged(bool inProgress); + void _currentMissionItemChanged(int sequenceNumber); + void _recalcWaypointLines(void); + void _recalcAltitudeRangeBearing(void); + void _homeCoordinateChanged(void); + +private: + void _init(void); + void _recalcSequence(void); + void _recalcChildItems(void); + void _recalcAll(void); + void _initAllVisualItems(void); + void _deinitAllVisualItems(void); + void _initVisualItem(VisualMissionItem* item); + void _deinitVisualItem(VisualMissionItem* item); + void _setupActiveVehicle(Vehicle* activeVehicle, bool forceLoadFromVehicle); + static void _calcPrevWaypointValues(double homeAlt, VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference); + static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem); + bool _findLastAltitude(double* lastAltitude, MAV_FRAME* frame); + bool _findLastAcceptanceRadius(double* lastAcceptanceRadius); + void _addPlannedHomePosition(QmlObjectListModel* visualItems, bool addToCenter); + double _normalizeLat(double lat); + double _normalizeLon(double lon); + bool _loadJsonMissionFile(const QByteArray& bytes, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + bool _loadJsonMissionFileV1(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + bool _loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QmlObjectListModel* complexItems, QString& errorString); + bool _loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); + int _nextSequenceNumber(void); + void _setMissionDistance(double missionDistance); + void _setMissionTime(double missionTime); + void _setMissionHoverDistance(double missionHoverDistance); + void _setMissionHoverTime(double missionHoverTime); + void _setMissionCruiseDistance(double missionCruiseDistance); + void _setMissionCruiseTime(double missionCruiseTime); + void _setMissionMaxTelemetry(double missionMaxTelemetry); + + // Overrides from PlanElementController + void _activeVehicleBeingRemoved(void) final; + void _activeVehicleSet(void) final; + +private: + QmlObjectListModel* _visualItems; + QmlObjectListModel* _complexItems; + QmlObjectListModel _waypointLines; + CoordVectHashTable _linesTable; + bool _firstItemsFromVehicle; + bool _missionItemsRequested; + bool _queuedSend; + double _missionDistance; + double _missionTime; + double _missionHoverDistance; + double _missionHoverTime; + double _missionCruiseDistance; + double _missionCruiseTime; + double _missionMaxTelemetry; + + static const char* _settingsGroup; + static const char* _jsonFileTypeValue; + static const char* _jsonFirmwareTypeKey; + static const char* _jsonVehicleTypeKey; + static const char* _jsonCruiseSpeedKey; + static const char* _jsonHoverSpeedKey; + static const char* _jsonItemsKey; + static const char* _jsonPlannedHomePositionKey; + static const char* _jsonParamsKey; + + // Deprecated V1 format keys + static const char* _jsonMavAutopilotKey; + static const char* _jsonComplexItemsKey; + + static const int _missionFileVersion; +}; + +#endif diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index f6b6091ba..a1a7126a0 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -111,7 +111,7 @@ void RallyPointController::loadFromFile(const QString& filename) QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - errorString = file.errorString(); + errorString = file.errorString() + QStringLiteral(" ") + filename; } else { QJsonDocument jsonDoc; QByteArray bytes = file.readAll(); diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index c93c0e5d5..759d6c853 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -33,6 +33,7 @@ SettingsFact* QGroundControlQmlGlobal::_batteryPercentRemainingAnnounceFact = const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; +const char* QGroundControlQmlGlobal::_missionAutoLoadDirKey = "MissionAutoLoadDir"; QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) : QGCTool(app) @@ -353,3 +354,15 @@ QMap& QGroundControlQmlGlobal::nameToMetaDataMap(void) { return map; } + +QString QGroundControlQmlGlobal::missionAutoLoadDir(void) +{ + QSettings settings; + return settings.value(_missionAutoLoadDirKey).toString(); +} + +void QGroundControlQmlGlobal::setMissionAutoLoadDir(const QString& missionAutoLoadDir) +{ + QSettings settings; + settings.setValue(_missionAutoLoadDirKey, missionAutoLoadDir); +} diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 97e3a3394..757573e81 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -84,6 +84,7 @@ public: Q_PROPERTY(bool isSaveLogPromptNotArmed READ isSaveLogPromptNotArmed WRITE setIsSaveLogPromptNotArmed NOTIFY isSaveLogPromptNotArmedChanged) Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) + Q_PROPERTY(QString missionAutoLoadDir READ missionAutoLoadDir WRITE setMissionAutoLoadDir NOTIFY missionAutoLoadDirChanged STORED false) //------------------------------------------------------------------------- // MavLink Protocol @@ -148,10 +149,10 @@ public: Q_INVOKABLE QStringList loggingCategories(void) const { return QGCLoggingCategoryRegister::instance()->registeredCategories(); } /// Turns on/off logging for the specified category. State is saved in app settings. - Q_INVOKABLE void setCategoryLoggingOn(const QString& category, bool enable) { QGCLoggingCategoryRegister::instance()->setCategoryLoggingOn(category, enable); }; + Q_INVOKABLE void setCategoryLoggingOn(const QString& category, bool enable) { QGCLoggingCategoryRegister::instance()->setCategoryLoggingOn(category, enable); } /// Returns true if logging is turned on for the specified category. - Q_INVOKABLE bool categoryLoggingOn(const QString& category) { return QGCLoggingCategoryRegister::instance()->categoryLoggingOn(category); }; + Q_INVOKABLE bool categoryLoggingOn(const QString& category) { return QGCLoggingCategoryRegister::instance()->categoryLoggingOn(category); } /// Updates the logging filter rules after settings have changed Q_INVOKABLE void updateLoggingFilterRules(void) { QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(QString()); } @@ -211,6 +212,9 @@ public: QString qgcVersion(void) const { return qgcApp()->applicationVersion(); } + static QString missionAutoLoadDir(void); + static void setMissionAutoLoadDir(const QString& missionAutoLoadDir); + // Overrides from QGCTool virtual void setToolbox(QGCToolbox* toolbox); @@ -226,6 +230,7 @@ signals: void mavlinkSystemIDChanged (int id); void flightMapPositionChanged (QGeoCoordinate flightMapPosition); void flightMapZoomChanged (double flightMapZoom); + void missionAutoLoadDirChanged (QString missionAutoLoadDir); private: static SettingsFact* _createSettingsFact(const QString& name); @@ -261,6 +266,7 @@ private: static const char* _virtualTabletJoystickKey; static const char* _baseFontPointSizeKey; + static const char* _missionAutoLoadDirKey; }; #endif diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 1ce413258..10c7deee4 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -16,6 +16,7 @@ #include "UAS.h" #include "JoystickManager.h" #include "MissionManager.h" +#include "MissionController.h" #include "GeoFenceManager.h" #include "RallyPointManager.h" #include "CoordinateVector.h" @@ -153,7 +154,7 @@ Vehicle::Vehicle(LinkInterface* link, connect(this, &Vehicle::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged); _commonInit(); - _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this); + _autopilotPlugin = _firmwarePlugin->autopilotPlugin(this); // connect this vehicle to the follow me handle manager connect(this, &Vehicle::flightModeChanged,qgcApp()->toolbox()->followMe(), &FollowMe::followMeHandleManager); @@ -294,6 +295,7 @@ void Vehicle::_commonInit(void) connect(_missionManager, &MissionManager::error, this, &Vehicle::_missionManagerError); _parameterManager = new ParameterManager(this); + connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady); // GeoFenceManager needs to access ParameterManager so make sure to create after _geoFenceManager = _firmwarePlugin->newGeoFenceManager(this); @@ -1518,7 +1520,18 @@ void Vehicle::_parametersReady(bool parametersReady) { if (parametersReady && !_missionManagerInitialRequestSent) { _missionManagerInitialRequestSent = true; - _missionManager->requestMissionItems(); + QString missionAutoLoadDirPath = QGroundControlQmlGlobal::missionAutoLoadDir(); + if (missionAutoLoadDirPath.isEmpty()) { + _missionManager->requestMissionItems(); + } else { + QmlObjectListModel* visualItems = NULL; + QmlObjectListModel* complexItems = NULL; + QDir missionAutoLoadDir(missionAutoLoadDirPath); + QString autoloadFilename = missionAutoLoadDir.absoluteFilePath(tr("AutoLoad%1.mission").arg(_id)); + if (MissionController::loadItemsFromFile(this, autoloadFilename, &visualItems, &complexItems)) { + MissionController::sendItemsToVehicle(this, visualItems); + } + } } if (parametersReady) { @@ -2032,7 +2045,7 @@ void Vehicle::motorTest(int motor, int percent, int timeoutSecs) void Vehicle::_newMissionItemsAvailable(void) { - // After the initial mission request complets we ask for the geofence + // After the initial mission request completes we ask for the geofence if (!_geoFenceManagerInitialRequestSent) { _geoFenceManagerInitialRequestSent = true; _geoFenceManager->loadFromVehicle(); @@ -2041,7 +2054,7 @@ void Vehicle::_newMissionItemsAvailable(void) void Vehicle::_newGeoFenceAvailable(void) { - // After the initial mission request complets we ask for the geofence + // After geofence request completes we ask for the rally points if (!_rallyPointManagerInitialRequestSent) { _rallyPointManagerInitialRequestSent = true; _rallyPointManager->loadFromVehicle(); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 43fdeae5b..1eacfab91 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -286,6 +286,33 @@ QGCView { visible: QGroundControl.corePlugin.options.enableVirtualJoystick } //----------------------------------------------------------------- + //-- AutoLoad + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCCheckBox { + id: autoLoadCheckbox + anchors.verticalCenter: parent.verticalCenter + text: qsTr("AutoLoad mission directory:") + checked: QGroundControl.missionAutoLoadDir != "" + + onClicked: { + autoLoadDir.enabled = checked + if (!checked) { + QGroundControl.missionAutoLoadDir = "" + autoLoadDir.text = "" + } + } + } + QGCTextField { + id: autoLoadDir + width: _editFieldWidth + enabled: autoLoadCheckbox.checked + anchors.verticalCenter: parent.verticalCenter + text: QGroundControl.missionAutoLoadDir + onEditingFinished: QGroundControl.missionAutoLoadDir = text + } + } + //----------------------------------------------------------------- //-- Map Providers Row { /* -- GitLab From 1ccdcacb08f9eb87f13ca90d84573d31ff9f7d76 Mon Sep 17 00:00:00 2001 From: Andreas Bircher Date: Sun, 22 Jan 2017 14:48:27 +0100 Subject: [PATCH 219/398] increase accuracy of geotags --- src/AnalyzeView/ExifParser.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/AnalyzeView/ExifParser.cc b/src/AnalyzeView/ExifParser.cc index 4e4087ffa..2b4ccf063 100644 --- a/src/AnalyzeView/ExifParser.cc +++ b/src/AnalyzeView/ExifParser.cc @@ -178,17 +178,17 @@ bool ExifParser::write(QByteArray &buf, QGeoCoordinate coordinate) // Filling up the additional information that does not fit into the fields gpsData.readable.extendedData.gpsLat[0] = abs(static_cast(coordinate.latitude())); gpsData.readable.extendedData.gpsLat[1] = 1; - gpsData.readable.extendedData.gpsLat[2] = static_cast((fabs(coordinate.latitude()) - floor(fabs(coordinate.latitude()))) * 60000.0); - gpsData.readable.extendedData.gpsLat[3] = 1000; - gpsData.readable.extendedData.gpsLat[4] = 0; - gpsData.readable.extendedData.gpsLat[5] = 1; + gpsData.readable.extendedData.gpsLat[2] = static_cast((fabs(coordinate.latitude()) - floor(fabs(coordinate.latitude()))) * 60.0); + gpsData.readable.extendedData.gpsLat[3] = 1; + gpsData.readable.extendedData.gpsLat[4] = static_cast((fabs(coordinate.latitude()) * 60.0 - floor(fabs(coordinate.latitude()) * 60.0)) * 60000.0); + gpsData.readable.extendedData.gpsLat[5] = 1000; gpsData.readable.extendedData.gpsLon[0] = abs(static_cast(coordinate.longitude())); gpsData.readable.extendedData.gpsLon[1] = 1; - gpsData.readable.extendedData.gpsLon[2] = static_cast((fabs(coordinate.longitude()) - floor(fabs(coordinate.longitude()))) * 60000.0); - gpsData.readable.extendedData.gpsLon[3] = 1000; - gpsData.readable.extendedData.gpsLon[4] = 0; - gpsData.readable.extendedData.gpsLon[5] = 1; + gpsData.readable.extendedData.gpsLon[2] = static_cast((fabs(coordinate.longitude()) - floor(fabs(coordinate.longitude()))) * 60.0); + gpsData.readable.extendedData.gpsLon[3] = 1; + gpsData.readable.extendedData.gpsLon[4] = static_cast((fabs(coordinate.longitude()) * 60.0 - floor(fabs(coordinate.longitude()) * 60.0)) * 60000.0); + gpsData.readable.extendedData.gpsLon[5] = 1000; gpsData.readable.extendedData.gpsAlt[0] = coordinate.altitude() * 100; gpsData.readable.extendedData.gpsAlt[1] = 100; -- GitLab From a206a372610a55d5b4881a96d7f25404bce5d472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beat=20K=C3=BCng?= Date: Tue, 24 Jan 2017 16:22:36 +0100 Subject: [PATCH 220/398] MAVLinkLogManager: fix use of wrong variable previously the start log button was always disabled, even for PX4. Fixes regression from 9d413108f8116a5ba049f41d94929789f799a509 --- src/Vehicle/MAVLinkLogManager.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Vehicle/MAVLinkLogManager.cc b/src/Vehicle/MAVLinkLogManager.cc index c792e0724..8a1a64c37 100644 --- a/src/Vehicle/MAVLinkLogManager.cc +++ b/src/Vehicle/MAVLinkLogManager.cc @@ -836,7 +836,7 @@ MAVLinkLogManager::_activeVehicleChanged(Vehicle* vehicle) emit canStartLogChanged(); } // Connect new system - if(_vehicle && _vehicle->px4Firmware()) { + if(vehicle && vehicle->px4Firmware()) { _vehicle = vehicle; connect(_vehicle, &Vehicle::armedChanged, this, &MAVLinkLogManager::_armedChanged); connect(_vehicle, &Vehicle::mavlinkLogData, this, &MAVLinkLogManager::_mavlinkLogData); -- GitLab From 2b6517c0f4ab34f8d31bf1330188bea10a7239c4 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 24 Jan 2017 10:53:15 -0500 Subject: [PATCH 221/398] Optimize SQL set creation. --- src/QtLocationPlugin/QGCTileCacheWorker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.cpp b/src/QtLocationPlugin/QGCTileCacheWorker.cpp index 91ec4d160..391042684 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.cpp +++ b/src/QtLocationPlugin/QGCTileCacheWorker.cpp @@ -454,6 +454,7 @@ QGCCacheWorker::_createTileSet(QGCMapTask *mtask) task->tileSet()->setId(setID); //-- Prepare Download List quint64 tileCount = 0; + _db->transaction(); for(int z = task->tileSet()->minZoom(); z <= task->tileSet()->maxZoom(); z++) { QGCTileSet set = QGCMapEngine::getTileCount(z, task->tileSet()->topleftLon(), task->tileSet()->topleftLat(), @@ -493,6 +494,7 @@ QGCCacheWorker::_createTileSet(QGCMapTask *mtask) } } } + _db->commit(); //-- Done _updateSetTotals(task->tileSet()); task->setTileSetSaved(); -- GitLab From 6822b461bde2886918fc6469a65ff30d48aa11d5 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 24 Jan 2017 11:51:39 -0500 Subject: [PATCH 222/398] Allow TX mode selection for joystick calibration --- qgcresources.qrc | 55 ++++++++++++++---- .../joystick/{ => mode1}/joystickCenter.png | Bin .../joystickPitchDown.png} | Bin .../joystickPitchUp.png} | Bin .../joystick/{ => mode1}/joystickRollLeft.png | Bin .../{ => mode1}/joystickRollRight.png | Bin .../joystickThrottleDown.png} | Bin .../joystickThrottleUp.png} | Bin .../joystick/{ => mode1}/joystickYawLeft.png | Bin .../joystick/{ => mode1}/joystickYawRight.png | Bin .../joystick/mode2/joystickCenter.png | Bin 0 -> 22277 bytes .../joystick/mode2/joystickPitchDown.png | Bin 0 -> 22001 bytes .../joystick/mode2/joystickPitchUp.png | Bin 0 -> 22147 bytes .../joystick/mode2/joystickRollLeft.png | Bin 0 -> 22000 bytes .../joystick/mode2/joystickRollRight.png | Bin 0 -> 22029 bytes .../joystick/mode2/joystickThrottleDown.png | Bin 0 -> 21948 bytes .../joystick/mode2/joystickThrottleUp.png | Bin 0 -> 22057 bytes .../joystick/mode2/joystickYawLeft.png | Bin 0 -> 21913 bytes .../joystick/mode2/joystickYawRight.png | Bin 0 -> 22009 bytes .../joystick/mode3/joystickCenter.png | Bin 0 -> 22277 bytes .../joystick/mode3/joystickPitchDown.png | Bin 0 -> 21948 bytes .../joystick/mode3/joystickPitchUp.png | Bin 0 -> 22057 bytes .../joystick/mode3/joystickRollLeft.png | Bin 0 -> 21913 bytes .../joystick/mode3/joystickRollRight.png | Bin 0 -> 22009 bytes .../joystick/mode3/joystickThrottleDown.png | Bin 0 -> 22001 bytes .../joystick/mode3/joystickThrottleUp.png | Bin 0 -> 22147 bytes .../joystick/mode3/joystickYawLeft.png | Bin 0 -> 22000 bytes .../joystick/mode3/joystickYawRight.png | Bin 0 -> 22029 bytes .../joystick/mode4/joystickCenter.png | Bin 0 -> 22277 bytes .../joystick/mode4/joystickPitchDown.png | Bin 0 -> 22001 bytes .../joystick/mode4/joystickPitchUp.png | Bin 0 -> 22147 bytes .../joystick/mode4/joystickRollLeft.png | Bin 0 -> 21913 bytes .../joystick/mode4/joystickRollRight.png | Bin 0 -> 22009 bytes .../joystick/mode4/joystickThrottleDown.png | Bin 0 -> 21948 bytes .../joystick/mode4/joystickThrottleUp.png | Bin 0 -> 22057 bytes .../joystick/mode4/joystickYawLeft.png | Bin 0 -> 22000 bytes .../joystick/mode4/joystickYawRight.png | Bin 0 -> 22029 bytes src/VehicleSetup/JoystickConfig.qml | 49 ++++++++++++++-- src/VehicleSetup/JoystickConfigController.cc | 37 +++++++++++- src/VehicleSetup/JoystickConfigController.h | 9 +++ 40 files changed, 132 insertions(+), 18 deletions(-) rename resources/calibration/joystick/{ => mode1}/joystickCenter.png (100%) rename resources/calibration/joystick/{joystickThrottleDown.png => mode1/joystickPitchDown.png} (100%) rename resources/calibration/joystick/{joystickThrottleUp.png => mode1/joystickPitchUp.png} (100%) rename resources/calibration/joystick/{ => mode1}/joystickRollLeft.png (100%) rename resources/calibration/joystick/{ => mode1}/joystickRollRight.png (100%) rename resources/calibration/joystick/{joystickPitchDown.png => mode1/joystickThrottleDown.png} (100%) rename resources/calibration/joystick/{joystickPitchUp.png => mode1/joystickThrottleUp.png} (100%) rename resources/calibration/joystick/{ => mode1}/joystickYawLeft.png (100%) rename resources/calibration/joystick/{ => mode1}/joystickYawRight.png (100%) create mode 100644 resources/calibration/joystick/mode2/joystickCenter.png create mode 100644 resources/calibration/joystick/mode2/joystickPitchDown.png create mode 100644 resources/calibration/joystick/mode2/joystickPitchUp.png create mode 100644 resources/calibration/joystick/mode2/joystickRollLeft.png create mode 100644 resources/calibration/joystick/mode2/joystickRollRight.png create mode 100644 resources/calibration/joystick/mode2/joystickThrottleDown.png create mode 100644 resources/calibration/joystick/mode2/joystickThrottleUp.png create mode 100644 resources/calibration/joystick/mode2/joystickYawLeft.png create mode 100644 resources/calibration/joystick/mode2/joystickYawRight.png create mode 100644 resources/calibration/joystick/mode3/joystickCenter.png create mode 100644 resources/calibration/joystick/mode3/joystickPitchDown.png create mode 100644 resources/calibration/joystick/mode3/joystickPitchUp.png create mode 100644 resources/calibration/joystick/mode3/joystickRollLeft.png create mode 100644 resources/calibration/joystick/mode3/joystickRollRight.png create mode 100644 resources/calibration/joystick/mode3/joystickThrottleDown.png create mode 100644 resources/calibration/joystick/mode3/joystickThrottleUp.png create mode 100644 resources/calibration/joystick/mode3/joystickYawLeft.png create mode 100644 resources/calibration/joystick/mode3/joystickYawRight.png create mode 100644 resources/calibration/joystick/mode4/joystickCenter.png create mode 100644 resources/calibration/joystick/mode4/joystickPitchDown.png create mode 100644 resources/calibration/joystick/mode4/joystickPitchUp.png create mode 100644 resources/calibration/joystick/mode4/joystickRollLeft.png create mode 100644 resources/calibration/joystick/mode4/joystickRollRight.png create mode 100644 resources/calibration/joystick/mode4/joystickThrottleDown.png create mode 100644 resources/calibration/joystick/mode4/joystickThrottleUp.png create mode 100644 resources/calibration/joystick/mode4/joystickYawLeft.png create mode 100644 resources/calibration/joystick/mode4/joystickYawRight.png diff --git a/qgcresources.qrc b/qgcresources.qrc index 5c714bbed..3d4d9f301 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -253,17 +253,6 @@ resources/calibration/mode2/radioThrottleDown.png resources/calibration/mode2/radioSwitchMinMax.png - - resources/calibration/joystick/joystickCenter.png - resources/calibration/joystick/joystickRollLeft.png - resources/calibration/joystick/joystickRollRight.png - resources/calibration/joystick/joystickPitchUp.png - resources/calibration/joystick/joystickPitchDown.png - resources/calibration/joystick/joystickYawLeft.png - resources/calibration/joystick/joystickYawRight.png - resources/calibration/joystick/joystickThrottleUp.png - resources/calibration/joystick/joystickThrottleDown.png - resources/SDL_GameControllerDB/gamecontrollerdb.txt @@ -277,4 +266,48 @@ resources/opengl/buglist.json + + resources/calibration/joystick/mode1/joystickCenter.png + resources/calibration/joystick/mode1/joystickPitchDown.png + resources/calibration/joystick/mode1/joystickPitchUp.png + resources/calibration/joystick/mode1/joystickRollLeft.png + resources/calibration/joystick/mode1/joystickRollRight.png + resources/calibration/joystick/mode1/joystickThrottleDown.png + resources/calibration/joystick/mode1/joystickThrottleUp.png + resources/calibration/joystick/mode1/joystickYawLeft.png + resources/calibration/joystick/mode1/joystickYawRight.png + + + resources/calibration/joystick/mode2/joystickCenter.png + resources/calibration/joystick/mode2/joystickPitchDown.png + resources/calibration/joystick/mode2/joystickPitchUp.png + resources/calibration/joystick/mode2/joystickRollLeft.png + resources/calibration/joystick/mode2/joystickRollRight.png + resources/calibration/joystick/mode2/joystickThrottleDown.png + resources/calibration/joystick/mode2/joystickThrottleUp.png + resources/calibration/joystick/mode2/joystickYawLeft.png + resources/calibration/joystick/mode2/joystickYawRight.png + + + resources/calibration/joystick/mode3/joystickCenter.png + resources/calibration/joystick/mode3/joystickPitchDown.png + resources/calibration/joystick/mode3/joystickPitchUp.png + resources/calibration/joystick/mode3/joystickRollLeft.png + resources/calibration/joystick/mode3/joystickRollRight.png + resources/calibration/joystick/mode3/joystickThrottleDown.png + resources/calibration/joystick/mode3/joystickThrottleUp.png + resources/calibration/joystick/mode3/joystickYawLeft.png + resources/calibration/joystick/mode3/joystickYawRight.png + + + resources/calibration/joystick/mode4/joystickCenter.png + resources/calibration/joystick/mode4/joystickPitchDown.png + resources/calibration/joystick/mode4/joystickPitchUp.png + resources/calibration/joystick/mode4/joystickRollLeft.png + resources/calibration/joystick/mode4/joystickRollRight.png + resources/calibration/joystick/mode4/joystickThrottleDown.png + resources/calibration/joystick/mode4/joystickThrottleUp.png + resources/calibration/joystick/mode4/joystickYawLeft.png + resources/calibration/joystick/mode4/joystickYawRight.png + diff --git a/resources/calibration/joystick/joystickCenter.png b/resources/calibration/joystick/mode1/joystickCenter.png similarity index 100% rename from resources/calibration/joystick/joystickCenter.png rename to resources/calibration/joystick/mode1/joystickCenter.png diff --git a/resources/calibration/joystick/joystickThrottleDown.png b/resources/calibration/joystick/mode1/joystickPitchDown.png similarity index 100% rename from resources/calibration/joystick/joystickThrottleDown.png rename to resources/calibration/joystick/mode1/joystickPitchDown.png diff --git a/resources/calibration/joystick/joystickThrottleUp.png b/resources/calibration/joystick/mode1/joystickPitchUp.png similarity index 100% rename from resources/calibration/joystick/joystickThrottleUp.png rename to resources/calibration/joystick/mode1/joystickPitchUp.png diff --git a/resources/calibration/joystick/joystickRollLeft.png b/resources/calibration/joystick/mode1/joystickRollLeft.png similarity index 100% rename from resources/calibration/joystick/joystickRollLeft.png rename to resources/calibration/joystick/mode1/joystickRollLeft.png diff --git a/resources/calibration/joystick/joystickRollRight.png b/resources/calibration/joystick/mode1/joystickRollRight.png similarity index 100% rename from resources/calibration/joystick/joystickRollRight.png rename to resources/calibration/joystick/mode1/joystickRollRight.png diff --git a/resources/calibration/joystick/joystickPitchDown.png b/resources/calibration/joystick/mode1/joystickThrottleDown.png similarity index 100% rename from resources/calibration/joystick/joystickPitchDown.png rename to resources/calibration/joystick/mode1/joystickThrottleDown.png diff --git a/resources/calibration/joystick/joystickPitchUp.png b/resources/calibration/joystick/mode1/joystickThrottleUp.png similarity index 100% rename from resources/calibration/joystick/joystickPitchUp.png rename to resources/calibration/joystick/mode1/joystickThrottleUp.png diff --git a/resources/calibration/joystick/joystickYawLeft.png b/resources/calibration/joystick/mode1/joystickYawLeft.png similarity index 100% rename from resources/calibration/joystick/joystickYawLeft.png rename to resources/calibration/joystick/mode1/joystickYawLeft.png diff --git a/resources/calibration/joystick/joystickYawRight.png b/resources/calibration/joystick/mode1/joystickYawRight.png similarity index 100% rename from resources/calibration/joystick/joystickYawRight.png rename to resources/calibration/joystick/mode1/joystickYawRight.png diff --git a/resources/calibration/joystick/mode2/joystickCenter.png b/resources/calibration/joystick/mode2/joystickCenter.png new file mode 100644 index 0000000000000000000000000000000000000000..556321ec4b1c3142dc40acaf8068b4fb57e923a5 GIT binary patch literal 22277 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickPitchDown.png b/resources/calibration/joystick/mode2/joystickPitchDown.png new file mode 100644 index 0000000000000000000000000000000000000000..6e895b1a5a50dac35d977dd496a830cd26b1a5d8 GIT binary patch literal 22001 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 a;@`Jgzj!aB@(j#bQCPEcru5UEiT?*X!VI7Q literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickPitchUp.png b/resources/calibration/joystick/mode2/joystickPitchUp.png new file mode 100644 index 0000000000000000000000000000000000000000..ad9d7c03fa5dc49d5889eedf2551f900381c11fc GIT binary patch literal 22147 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 Pi)ArJS)4C7bh-OKXp%vB literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickRollLeft.png b/resources/calibration/joystick/mode2/joystickRollLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..fddc7dfb4217051af0ca1b51897f82ef7cd2c810 GIT binary patch literal 22000 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 UxRLuUc(bzU=@@AjXx@DCfBV%I_W%F@ literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickRollRight.png b/resources/calibration/joystick/mode2/joystickRollRight.png new file mode 100644 index 0000000000000000000000000000000000000000..9556eed1e7e62df8c0ffe5e0206ad03db6c6f943 GIT binary patch literal 22029 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 KME;NSo&N%yy9fIK literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickThrottleUp.png b/resources/calibration/joystick/mode2/joystickThrottleUp.png new file mode 100644 index 0000000000000000000000000000000000000000..223199991fc90272b1fae1ba6f28799b31681ca2 GIT binary patch literal 22057 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickYawLeft.png b/resources/calibration/joystick/mode2/joystickYawLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..788a09c0b8c504db6f6c1068dd408cf8f39d7554 GIT binary patch literal 21913 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode2/joystickYawRight.png b/resources/calibration/joystick/mode2/joystickYawRight.png new file mode 100644 index 0000000000000000000000000000000000000000..381108825e10adacbbc729dd9ea1ea22b8665f87 GIT binary patch literal 22009 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 Kr;79(|M!2_TWxOu literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickCenter.png b/resources/calibration/joystick/mode3/joystickCenter.png new file mode 100644 index 0000000000000000000000000000000000000000..556321ec4b1c3142dc40acaf8068b4fb57e923a5 GIT binary patch literal 22277 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickPitchDown.png b/resources/calibration/joystick/mode3/joystickPitchDown.png new file mode 100644 index 0000000000000000000000000000000000000000..7436477b0acd29f42758d77df5978cb955785d6a GIT binary patch literal 21948 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 KME;NSo&N%yy9fIK literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickPitchUp.png b/resources/calibration/joystick/mode3/joystickPitchUp.png new file mode 100644 index 0000000000000000000000000000000000000000..223199991fc90272b1fae1ba6f28799b31681ca2 GIT binary patch literal 22057 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickRollLeft.png b/resources/calibration/joystick/mode3/joystickRollLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..788a09c0b8c504db6f6c1068dd408cf8f39d7554 GIT binary patch literal 21913 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickRollRight.png b/resources/calibration/joystick/mode3/joystickRollRight.png new file mode 100644 index 0000000000000000000000000000000000000000..381108825e10adacbbc729dd9ea1ea22b8665f87 GIT binary patch literal 22009 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 Kr;79(|M!2_TWxOu literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickThrottleDown.png b/resources/calibration/joystick/mode3/joystickThrottleDown.png new file mode 100644 index 0000000000000000000000000000000000000000..6e895b1a5a50dac35d977dd496a830cd26b1a5d8 GIT binary patch literal 22001 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 a;@`Jgzj!aB@(j#bQCPEcru5UEiT?*X!VI7Q literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickThrottleUp.png b/resources/calibration/joystick/mode3/joystickThrottleUp.png new file mode 100644 index 0000000000000000000000000000000000000000..ad9d7c03fa5dc49d5889eedf2551f900381c11fc GIT binary patch literal 22147 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 Pi)ArJS)4C7bh-OKXp%vB literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickYawLeft.png b/resources/calibration/joystick/mode3/joystickYawLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..fddc7dfb4217051af0ca1b51897f82ef7cd2c810 GIT binary patch literal 22000 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 UxRLuUc(bzU=@@AjXx@DCfBV%I_W%F@ literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode3/joystickYawRight.png b/resources/calibration/joystick/mode3/joystickYawRight.png new file mode 100644 index 0000000000000000000000000000000000000000..9556eed1e7e62df8c0ffe5e0206ad03db6c6f943 GIT binary patch literal 22029 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickPitchDown.png b/resources/calibration/joystick/mode4/joystickPitchDown.png new file mode 100644 index 0000000000000000000000000000000000000000..6e895b1a5a50dac35d977dd496a830cd26b1a5d8 GIT binary patch literal 22001 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 a;@`Jgzj!aB@(j#bQCPEcru5UEiT?*X!VI7Q literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickPitchUp.png b/resources/calibration/joystick/mode4/joystickPitchUp.png new file mode 100644 index 0000000000000000000000000000000000000000..ad9d7c03fa5dc49d5889eedf2551f900381c11fc GIT binary patch literal 22147 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 Pi)ArJS)4C7bh-OKXp%vB literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickRollLeft.png b/resources/calibration/joystick/mode4/joystickRollLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..788a09c0b8c504db6f6c1068dd408cf8f39d7554 GIT binary patch literal 21913 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickRollRight.png b/resources/calibration/joystick/mode4/joystickRollRight.png new file mode 100644 index 0000000000000000000000000000000000000000..381108825e10adacbbc729dd9ea1ea22b8665f87 GIT binary patch literal 22009 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 Kr;79(|M!2_TWxOu literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickThrottleDown.png b/resources/calibration/joystick/mode4/joystickThrottleDown.png new file mode 100644 index 0000000000000000000000000000000000000000..7436477b0acd29f42758d77df5978cb955785d6a GIT binary patch literal 21948 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 KME;NSo&N%yy9fIK literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickThrottleUp.png b/resources/calibration/joystick/mode4/joystickThrottleUp.png new file mode 100644 index 0000000000000000000000000000000000000000..223199991fc90272b1fae1ba6f28799b31681ca2 GIT binary patch literal 22057 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 literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickYawLeft.png b/resources/calibration/joystick/mode4/joystickYawLeft.png new file mode 100644 index 0000000000000000000000000000000000000000..fddc7dfb4217051af0ca1b51897f82ef7cd2c810 GIT binary patch literal 22000 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 UxRLuUc(bzU=@@AjXx@DCfBV%I_W%F@ literal 0 HcmV?d00001 diff --git a/resources/calibration/joystick/mode4/joystickYawRight.png b/resources/calibration/joystick/mode4/joystickYawRight.png new file mode 100644 index 0000000000000000000000000000000000000000..9556eed1e7e62df8c0ffe5e0206ad03db6c6f943 GIT binary patch literal 22029 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 zZoddzG3image); + } + } +} + void JoystickConfigController::_signalAllAttiudeValueChanges(void) { emit rollAxisMappedChanged(rollAxisMapped()); diff --git a/src/VehicleSetup/JoystickConfigController.h b/src/VehicleSetup/JoystickConfigController.h index 9b0a8c879..ba6ee8960 100644 --- a/src/VehicleSetup/JoystickConfigController.h +++ b/src/VehicleSetup/JoystickConfigController.h @@ -69,6 +69,7 @@ public: Q_PROPERTY(bool deadbandToggle READ getDeadbandToggle WRITE setDeadbandToggle NOTIFY deadbandToggled) + Q_PROPERTY(int transmitterMode READ transmitterMode WRITE setTransmitterMode NOTIFY transmitterModeChanged) Q_PROPERTY(QString imageHelp MEMBER _imageHelp NOTIFY imageHelpChanged) Q_INVOKABLE void cancelButtonClicked(void); @@ -100,6 +101,9 @@ public: void setDeadbandToggle(bool); int axisCount(void); + + int transmitterMode(void) { return _transmitterMode; } + void setTransmitterMode(int mode); signals: void axisValueChanged(int axis, int value); @@ -127,6 +131,7 @@ signals: void deadbandToggled(bool value); void imageHelpChanged(QString source); + void transmitterModeChanged(int mode); // @brief Signalled when in unit test mode and a message box should be displayed by the next button void nextButtonMessageBoxDisplayed(void); @@ -172,6 +177,7 @@ private: Joystick* _activeJoystick; + int _transmitterMode; int _currentStep; ///< Current step of state machine const struct stateMachineEntry* _getStateMachineEntry(int step); @@ -211,7 +217,10 @@ private: // Member variables + static const char* _imageFileMode1Dir; static const char* _imageFileMode2Dir; + static const char* _imageFileMode3Dir; + static const char* _imageFileMode4Dir; static const char* _imageFilePrefix; static const char* _imageCenter; static const char* _imageThrottleUp; -- GitLab From a84be5e64fc4e1cb8b39a4410a41d54fedd94bd8 Mon Sep 17 00:00:00 2001 From: Otavio Pontes Date: Thu, 19 Jan 2017 16:39:50 -0800 Subject: [PATCH 223/398] Fix crash by invalid socket deletion Calling delete on a socket callback is not safe and this can crash QGroundControl. Using deleteLater method from socket to perfom deletion. --- src/VideoStreaming/VideoReceiver.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 594a028f0..8631f174c 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -80,7 +80,7 @@ void VideoReceiver::_connected() { //-- Server showed up. Now we start the stream. _timer.stop(); - delete _socket; + _socket->deleteLater(); _socket = NULL; _serverPresent = true; start(); @@ -91,7 +91,7 @@ void VideoReceiver::_connected() void VideoReceiver::_socketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); - delete _socket; + _socket->deleteLater(); _socket = NULL; //-- Try again in 5 seconds _timer.start(5000); -- GitLab From 21099441fc93b0a7371e747f1013ae907c39d4b7 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 24 Jan 2017 15:45:23 -0500 Subject: [PATCH 224/398] Allow changing/saving joystick TX mode --- src/Joystick/Joystick.cc | 61 +++++++++++++++++++- src/Joystick/Joystick.h | 8 +++ src/VehicleSetup/JoystickConfigController.cc | 9 ++- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 17ce67b47..19700e9fa 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -25,6 +25,7 @@ const char* Joystick::_throttleModeSettingsKey = "ThrottleMode"; const char* Joystick::_exponentialSettingsKey = "Exponential"; const char* Joystick::_accumulatorSettingsKey = "Accumulator"; const char* Joystick::_deadbandSettingsKey = "Deadband"; +const char* Joystick::_txModeSettingsKey = "TXMode"; const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = { "RollAxis", @@ -33,6 +34,8 @@ const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = { "ThrottleAxis" }; +int Joystick::_transmitterMode = 2; + Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager) : _exitThread(false) , _name(name) @@ -81,6 +84,9 @@ void Joystick::_loadSettings(void) QSettings settings; settings.beginGroup(_settingsGroup); + + _transmitterMode = settings.value(_txModeSettingsKey, 2).toInt(); + settings.beginGroup(_name); bool badSettings = false; @@ -136,6 +142,10 @@ void Joystick::_loadSettings(void) qCDebug(JoystickLog) << "_loadSettings function:axis:badsettings" << function << functionAxis << badSettings; } + // FunctionAxis mappings are always stored in TX mode 2 + // Remap to stored TX mode in settings + _remapAxes(2, _transmitterMode, _rgFunctionAxis); + for (int button=0; button<_totalButtonCount; button++) { _rgButtonActions << settings.value(QString(_buttonActionSettingsKey).arg(button), QString()).toString(); qCDebug(JoystickLog) << "_loadSettings button:action" << button << _rgButtonActions[button]; @@ -152,6 +162,11 @@ void Joystick::_saveSettings(void) QSettings settings; settings.beginGroup(_settingsGroup); + + // Transmitter mode is static + // Save the mode we are using + settings.setValue(_txModeSettingsKey, _transmitterMode); + settings.beginGroup(_name); settings.setValue(_calibratedSettingsKey, _calibrated); @@ -160,7 +175,7 @@ void Joystick::_saveSettings(void) settings.setValue(_deadbandSettingsKey, _deadband); settings.setValue(_throttleModeSettingsKey, _throttleMode); - qCDebug(JoystickLog) << "_saveSettings calibrated:throttlemode:deadband" << _calibrated << _throttleMode << _deadband; + qCDebug(JoystickLog) << "_saveSettings calibrated:throttlemode:deadband:txmode" << _calibrated << _throttleMode << _deadband << _transmitterMode; QString minTpl ("Axis%1Min"); QString maxTpl ("Axis%1Max"); @@ -187,8 +202,13 @@ void Joystick::_saveSettings(void) << calibration->deadband; } + // Always save function Axis mappings in TX Mode 2 + // Write mode 2 mappings without changing mapping currently in use + int temp[maxFunction]; + _remapAxes(_transmitterMode, 2, temp); + for (int function=0; function 0 && mode <= 4) { + _remapAxes(_transmitterMode, mode, _rgFunctionAxis); + _transmitterMode = mode; + _saveSettings(); + } else { + qCWarning(JoystickLog) << "Invalid mode:" << mode; + } +} + /// Adjust the raw axis value to the -1:1 range given calibration information float Joystick::_adjustRange(int value, Calibration_t calibration, bool withDeadbands) { @@ -471,6 +527,7 @@ void Joystick::setFunctionAxis(AxisFunction_t function, int axis) _calibrated = true; _rgFunctionAxis[function] = axis; + _saveSettings(); emit calibratedChanged(_calibrated); } diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index c9915eae2..bef89146f 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -101,6 +101,9 @@ public: bool deadband(void); void setDeadband(bool accu); + void setTXMode(int mode); + int getTXMode(void) { return _transmitterMode; } + typedef enum { CalibrationModeOff, // Not calibrating CalibrationModeMonitor, // Monitors are active, continue to send to vehicle if already polling @@ -157,6 +160,9 @@ private: virtual int _getAxis(int i) = 0; virtual uint8_t _getHat(int hat,int i) = 0; + int _mapFunctionMode(int mode, int function); + void _remapAxes(int currentMode, int newMode, int (&newMapping)[maxFunction]); + // Override from QThread virtual void run(void); @@ -172,6 +178,7 @@ protected: int _hatButtonCount; int _totalButtonCount; + static int _transmitterMode; CalibrationMode_t _calibrationMode; int* _rgAxisValues; @@ -203,6 +210,7 @@ private: static const char* _exponentialSettingsKey; static const char* _accumulatorSettingsKey; static const char* _deadbandSettingsKey; + static const char* _txModeSettingsKey; }; #endif diff --git a/src/VehicleSetup/JoystickConfigController.cc b/src/VehicleSetup/JoystickConfigController.cc index a8e6c754d..13bdd5641 100644 --- a/src/VehicleSetup/JoystickConfigController.cc +++ b/src/VehicleSetup/JoystickConfigController.cc @@ -499,6 +499,8 @@ void JoystickConfigController::_setInternalCalibrationValuesFromSettings(void) _rgFunctionAxisMapping[function] = paramAxis; _rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function; } + + _transmitterMode = joystick->getTXMode(); _signalAllAttiudeValueChanges(); } @@ -806,11 +808,14 @@ bool JoystickConfigController::throttleAxisReversed(void) void JoystickConfigController::setTransmitterMode(int mode) { - if (mode == 1 || mode == 2 || mode == 3 || mode == 4) { + if (mode > 0 && mode <= 4) { _transmitterMode = mode; if (_currentStep != -1) { const stateMachineEntry* state = _getStateMachineEntry(_currentStep); _setHelpImage(state->image); + } else { + _activeJoystick->setTXMode(mode); + _setInternalCalibrationValuesFromSettings(); } } } @@ -831,6 +836,8 @@ void JoystickConfigController::_signalAllAttiudeValueChanges(void) emit pitchAxisDeadbandChanged(pitchAxisDeadband()); emit yawAxisDeadbandChanged(yawAxisDeadband()); emit throttleAxisDeadbandChanged(throttleAxisDeadband()); + + emit transmitterModeChanged(_transmitterMode); } void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) -- GitLab From f8a07f3c165b2eb20e5fd3fe0796d0122568f759 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 24 Jan 2017 17:06:50 -0500 Subject: [PATCH 225/398] Disable joystick TX mode selection during calibration --- src/VehicleSetup/JoystickConfig.qml | 4 ++++ src/VehicleSetup/JoystickConfigController.cc | 4 +++- src/VehicleSetup/JoystickConfigController.h | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index 2e243a7ef..ce68595db 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -659,6 +659,7 @@ SetupPage { exclusiveGroup: modeGroup text: "1" checked: controller.transmitterMode == 1 + enabled: !controller.calibrating onClicked: controller.transmitterMode = 1 } @@ -667,6 +668,7 @@ SetupPage { exclusiveGroup: modeGroup text: "2" checked: controller.transmitterMode == 2 + enabled: !controller.calibrating onClicked: controller.transmitterMode = 2 } @@ -675,6 +677,7 @@ SetupPage { exclusiveGroup: modeGroup text: "3" checked: controller.transmitterMode == 3 + enabled: !controller.calibrating onClicked: controller.transmitterMode = 3 } @@ -683,6 +686,7 @@ SetupPage { exclusiveGroup: modeGroup text: "4" checked: controller.transmitterMode == 4 + enabled: !controller.calibrating onClicked: controller.transmitterMode = 4 } diff --git a/src/VehicleSetup/JoystickConfigController.cc b/src/VehicleSetup/JoystickConfigController.cc index 13bdd5641..4af0546dd 100644 --- a/src/VehicleSetup/JoystickConfigController.cc +++ b/src/VehicleSetup/JoystickConfigController.cc @@ -597,6 +597,7 @@ void JoystickConfigController::_startCalibration(void) _currentStep = 0; _setupCurrentState(); + emit calibratingChanged(); } /// @brief Cancels the calibration process, setting things back to initial state. @@ -615,6 +616,7 @@ void JoystickConfigController::_stopCalibration(void) _skipButton->setEnabled(false); _setHelpImage(_imageCenter); + emit calibratingChanged(); } /// @brief Saves the current axis values, so that we can detect when the use moves an input. @@ -810,7 +812,7 @@ void JoystickConfigController::setTransmitterMode(int mode) { if (mode > 0 && mode <= 4) { _transmitterMode = mode; - if (_currentStep != -1) { + if (_currentStep != -1) { // This should never be true, mode selection is disabled during calibration const stateMachineEntry* state = _getStateMachineEntry(_currentStep); _setHelpImage(state->image); } else { diff --git a/src/VehicleSetup/JoystickConfigController.h b/src/VehicleSetup/JoystickConfigController.h index ba6ee8960..c0c709c72 100644 --- a/src/VehicleSetup/JoystickConfigController.h +++ b/src/VehicleSetup/JoystickConfigController.h @@ -71,6 +71,7 @@ public: Q_PROPERTY(int transmitterMode READ transmitterMode WRITE setTransmitterMode NOTIFY transmitterModeChanged) Q_PROPERTY(QString imageHelp MEMBER _imageHelp NOTIFY imageHelpChanged) + Q_PROPERTY(bool calibrating READ calibrating NOTIFY calibratingChanged) Q_INVOKABLE void cancelButtonClicked(void); Q_INVOKABLE void skipButtonClicked(void); @@ -104,6 +105,8 @@ public: int transmitterMode(void) { return _transmitterMode; } void setTransmitterMode(int mode); + + bool calibrating(void) { return _currentStep != -1; } signals: void axisValueChanged(int axis, int value); @@ -132,6 +135,7 @@ signals: void imageHelpChanged(QString source); void transmitterModeChanged(int mode); + void calibratingChanged(void); // @brief Signalled when in unit test mode and a message box should be displayed by the next button void nextButtonMessageBoxDisplayed(void); -- GitLab From 0b2af266b85f8193f15441c808810cd353dbb984 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 24 Jan 2017 18:16:17 -0500 Subject: [PATCH 226/398] Setup gamecontrollers with default calibration --- src/Joystick/Joystick.cc | 32 ++++++++++++++++++++++++++++- src/Joystick/Joystick.h | 26 +++++++++++++++-------- src/Joystick/JoystickSDL.cc | 1 + src/Joystick/JoystickSDL.h | 5 ++++- src/VehicleSetup/JoystickConfig.qml | 1 + 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 19700e9fa..2b217ae48 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -79,6 +79,36 @@ Joystick::~Joystick() delete _rgButtonValues; } +void Joystick::_setDefaultCalibration(void) { + QSettings settings; + settings.beginGroup(_settingsGroup); + settings.beginGroup(_name); + _calibrated = settings.value(_calibratedSettingsKey, false).toBool(); + + // Only set default calibrations if we do not have a calibration for this gamecontroller + if(_calibrated) return; + + for(int axis = 0; axis < _axisCount; axis++) { + Joystick::Calibration_t calibration; + _rgCalibration[axis] = calibration; + } + + _rgCalibration[1].reversed = true; + _rgCalibration[3].reversed = true; + + for(int function = 0; function < maxFunction; function++) { + _rgFunctionAxis[function] = function; + } + + _exponential = false; + _accumulator = false; + _deadband = false; + _throttleMode = ThrottleModeCenterZero; + _calibrated = true; + + _saveSettings(); +} + void Joystick::_loadSettings(void) { QSettings settings; @@ -102,7 +132,7 @@ void Joystick::_loadSettings(void) _throttleMode = (ThrottleMode_t)settings.value(_throttleModeSettingsKey, ThrottleModeCenterZero).toInt(&convertOk); badSettings |= !convertOk; - qCDebug(JoystickLog) << "_loadSettings calibrated:throttlemode:exponential:deadband:badsettings" << _calibrated << _throttleMode << _exponential << _deadband << badSettings; + qCDebug(JoystickLog) << "_loadSettings calibrated:txmode:throttlemode:exponential:deadband:badsettings" << _calibrated << _transmitterMode << _throttleMode << _exponential << _deadband << badSettings; QString minTpl ("Axis%1Min"); QString maxTpl ("Axis%1Max"); diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index bef89146f..521f8866a 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -30,12 +30,18 @@ public: ~Joystick(); - typedef struct { + typedef struct Calibration_t { int min; int max; int center; int deadband; bool reversed; + Calibration_t() + : min(-32767) + , max(32767) + , center(0) + , deadband(0) + , reversed(false) {} } Calibration_t; typedef enum { @@ -68,7 +74,8 @@ public: Q_PROPERTY(int throttleMode READ throttleMode WRITE setThrottleMode NOTIFY throttleModeChanged) Q_PROPERTY(bool exponential READ exponential WRITE setExponential NOTIFY exponentialChanged) Q_PROPERTY(bool accumulator READ accumulator WRITE setAccumulator NOTIFY accumulatorChanged) - + Q_PROPERTY(bool requiresCalibration READ requiresCalibration CONSTANT) + // Property accessors int axisCount(void) { return _axisCount; } @@ -89,6 +96,8 @@ public: QString name(void) { return _name; } + virtual bool requiresCalibration(void) { return true; } + int throttleMode(void); void setThrottleMode(int mode); @@ -144,12 +153,13 @@ signals: void buttonActionTriggered(int action); protected: - void _saveSettings(void); - void _loadSettings(void); - float _adjustRange(int value, Calibration_t calibration, bool withDeadbands); - void _buttonAction(const QString& action); - bool _validAxis(int axis); - bool _validButton(int button); + void _setDefaultCalibration(void); + void _saveSettings(void); + void _loadSettings(void); + float _adjustRange(int value, Calibration_t calibration, bool withDeadbands); + void _buttonAction(const QString& action); + bool _validAxis(int axis); + bool _validButton(int button); private: virtual bool _open() = 0; diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index 303c3005c..59798e268 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -10,6 +10,7 @@ JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, in , _isGameController(isGameController) , _index(index) { + if(_isGameController) _setDefaultCalibration(); } QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { diff --git a/src/Joystick/JoystickSDL.h b/src/Joystick/JoystickSDL.h index 2c4a9ca6a..36d966e28 100644 --- a/src/Joystick/JoystickSDL.h +++ b/src/Joystick/JoystickSDL.h @@ -12,7 +12,10 @@ class JoystickSDL : public Joystick public: JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager); - static QMap discover(MultiVehicleManager* _multiVehicleManager); + static QMap discover(MultiVehicleManager* _multiVehicleManager); + + // This can be uncommented to hide the calibration buttons for gamecontrollers in the future + // bool requiresCalibration(void) final { return !_isGameController; } private: static void _loadGameControllerMappings(); diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index ce68595db..a204c16c1 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -302,6 +302,7 @@ SetupPage { // Command Buttons Row { spacing: 10 + visible: _activeJoystick.requiresCalibration QGCButton { id: skipButton -- GitLab From bffe803cc3bb84751ae6f511cd5f199fe440aaad Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 24 Jan 2017 20:50:55 -0500 Subject: [PATCH 227/398] Add firmware-specific joystick TX mode settings --- .../APM/ArduSubFirmwarePlugin.h | 2 + src/FirmwarePlugin/FirmwarePlugin.cc | 5 ++ src/FirmwarePlugin/FirmwarePlugin.h | 4 ++ src/Joystick/Joystick.cc | 64 +++++++++++++++---- src/Joystick/Joystick.h | 8 +++ 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index c79c2f186..d9911f703 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -69,6 +69,8 @@ public: // Overrides from FirmwarePlugin int manualControlReservedButtonCount(void); + int defaultJoystickTXMode(void) final { return 3; } + bool supportsThrottleModeCenterZero(void); bool supportsManualControl(void); diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index eb69d9a71..42aa28387 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -106,6 +106,11 @@ int FirmwarePlugin::manualControlReservedButtonCount(void) return -1; } +int FirmwarePlugin::defaultJoystickTXMode(void) +{ + return 2; +} + bool FirmwarePlugin::supportsThrottleModeCenterZero(void) { // By default, this is supported diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 096ae7151..3e8ededb9 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -142,6 +142,10 @@ public: /// @return -1: reserver all buttons, >0 number of buttons to reserve virtual int manualControlReservedButtonCount(void); + /// Default tx mode to apply to joystick axes + /// TX modes are as outlined here: http://www.rc-airplane-world.com/rc-transmitter-modes.html + virtual int defaultJoystickTXMode(void); + /// Returns true if the vehicle and firmware supports the use of a throttle joystick that /// is zero when centered. Typically not supported on vehicles that have bidirectional /// throttle. diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 2b217ae48..2d08bf6c1 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -18,14 +18,19 @@ QGC_LOGGING_CATEGORY(JoystickLog, "JoystickLog") QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog") -const char* Joystick::_settingsGroup = "Joysticks"; -const char* Joystick::_calibratedSettingsKey = "Calibrated1"; // Increment number to force recalibration -const char* Joystick::_buttonActionSettingsKey = "ButtonActionName%1"; -const char* Joystick::_throttleModeSettingsKey = "ThrottleMode"; -const char* Joystick::_exponentialSettingsKey = "Exponential"; -const char* Joystick::_accumulatorSettingsKey = "Accumulator"; -const char* Joystick::_deadbandSettingsKey = "Deadband"; -const char* Joystick::_txModeSettingsKey = "TXMode"; +const char* Joystick::_settingsGroup = "Joysticks"; +const char* Joystick::_calibratedSettingsKey = "Calibrated1"; // Increment number to force recalibration +const char* Joystick::_buttonActionSettingsKey = "ButtonActionName%1"; +const char* Joystick::_throttleModeSettingsKey = "ThrottleMode"; +const char* Joystick::_exponentialSettingsKey = "Exponential"; +const char* Joystick::_accumulatorSettingsKey = "Accumulator"; +const char* Joystick::_deadbandSettingsKey = "Deadband"; +const char* Joystick::_txModeSettingsKey = NULL; +const char* Joystick::_fixedWingTXModeSettingsKey = "TXMode_FixedWing"; +const char* Joystick::_multiRotorTXModeSettingsKey = "TXMode_MultiRotor"; +const char* Joystick::_roverTXModeSettingsKey = "TXMode_Rover"; +const char* Joystick::_vtolTXModeSettingsKey = "TXMode_VTOL"; +const char* Joystick::_submarineTXModeSettingsKey = "TXMode_Submarine"; const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = { "RollAxis", @@ -70,6 +75,8 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC } _loadSettings(); + + connect(_multiVehicleManager, &MultiVehicleManager::activeVehicleChanged, this, &Joystick::_activeVehicleChanged); } Joystick::~Joystick() @@ -96,9 +103,11 @@ void Joystick::_setDefaultCalibration(void) { _rgCalibration[1].reversed = true; _rgCalibration[3].reversed = true; - for(int function = 0; function < maxFunction; function++) { - _rgFunctionAxis[function] = function; - } + // Default TX Mode 2 axis assignments for gamecontrollers + _rgFunctionAxis[rollFunction] = 2; + _rgFunctionAxis[pitchFunction] = 3; + _rgFunctionAxis[yawFunction] = 0; + _rgFunctionAxis[throttleFunction] = 1; _exponential = false; _accumulator = false; @@ -109,13 +118,41 @@ void Joystick::_setDefaultCalibration(void) { _saveSettings(); } +void Joystick::_activeVehicleChanged(Vehicle *activeVehicle) +{ + if(activeVehicle) { + if(activeVehicle->fixedWing()) { + _txModeSettingsKey = _fixedWingTXModeSettingsKey; + } else if(activeVehicle->multiRotor()) { + _txModeSettingsKey = _multiRotorTXModeSettingsKey; + } else if(activeVehicle->rover()) { + _txModeSettingsKey = _roverTXModeSettingsKey; + } else if(activeVehicle->vtol()) { + _txModeSettingsKey = _vtolTXModeSettingsKey; + } else if(activeVehicle->sub()) { + _txModeSettingsKey = _submarineTXModeSettingsKey; + } else { + _txModeSettingsKey = NULL; + qWarning() << "No valid joystick TXmode settings key for selected vehicle"; + return; + } + + QSettings settings; + settings.beginGroup(_settingsGroup); + int mode = settings.value(_txModeSettingsKey, activeVehicle->firmwarePlugin()->defaultJoystickTXMode()).toInt(); + + setTXMode(mode); + } +} + void Joystick::_loadSettings(void) { QSettings settings; settings.beginGroup(_settingsGroup); - _transmitterMode = settings.value(_txModeSettingsKey, 2).toInt(); + if(_txModeSettingsKey) + _transmitterMode = settings.value(_txModeSettingsKey, 2).toInt(); settings.beginGroup(_name); @@ -195,7 +232,8 @@ void Joystick::_saveSettings(void) // Transmitter mode is static // Save the mode we are using - settings.setValue(_txModeSettingsKey, _transmitterMode); + if(_txModeSettingsKey) + settings.setValue(_txModeSettingsKey, _transmitterMode); settings.beginGroup(_name); diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index 521f8866a..0ec6d53ab 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -221,6 +221,14 @@ private: static const char* _accumulatorSettingsKey; static const char* _deadbandSettingsKey; static const char* _txModeSettingsKey; + static const char* _fixedWingTXModeSettingsKey; + static const char* _multiRotorTXModeSettingsKey; + static const char* _roverTXModeSettingsKey; + static const char* _vtolTXModeSettingsKey; + static const char* _submarineTXModeSettingsKey; + +private slots: + void _activeVehicleChanged(Vehicle* activeVehicle); }; #endif -- GitLab From 0dbd417fa6f678c3c660f60b131187e73ee06322 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 20 Jan 2017 16:10:56 -0500 Subject: [PATCH 228/398] Label Roll/Pitch as Lateral/Forward for Sub --- src/VehicleSetup/JoystickConfig.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index a204c16c1..69c36928a 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -176,7 +176,7 @@ SetupPage { QGCLabel { id: rollLabel width: defaultTextWidth * 10 - text: qsTr("Roll") + text: _activeVehicle.sub ? qsTr("Lateral") : qsTr("Roll") } Loader { @@ -208,7 +208,7 @@ SetupPage { QGCLabel { id: pitchLabel width: defaultTextWidth * 10 - text: qsTr("Pitch") + text: _activeVehicle.sub ? qsTr("Forward") : qsTr("Pitch") } Loader { -- GitLab From 3d8e14acd6ffc3aa3a82b59745213208c0bb3513 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Wed, 25 Jan 2017 21:52:09 -0500 Subject: [PATCH 229/398] PX4 FW default landing altitude 0m --- src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json b/src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json index b595e720f..ebbc6516b 100644 --- a/src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json +++ b/src/FirmwarePlugin/PX4/MavCmdInfoFixedWing.json @@ -7,7 +7,13 @@ { "id": 21, "comment": "MAV_CMD_NAV_LAND", - "paramRemove": "1,4" + "paramRemove": "1,4", + "param7": { + "label": "Altitude:", + "units": "m", + "default": 0, + "decimalPlaces": 1 + } }, { "id": 22, -- GitLab From 3b802c0c0fdf3e5835a45cb8809de6c8b2b286be Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Wed, 25 Jan 2017 22:11:47 -0500 Subject: [PATCH 230/398] reboot vehicle from param menu --- src/QmlControls/ParameterEditor.qml | 22 +++++++++++++++++++++ src/QmlControls/ParameterEditorController.h | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index 50c8d837f..45f883553 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -118,6 +118,11 @@ QGCView { onTriggered: controller.clearRCToParam() visible: _showRCToParam } + MenuSeparator { } + MenuItem { + text: qsTr("Reboot Vehicle") + onTriggered: showDialog(rebootVehicleConfirmComponent, qsTr("Reboot Vehicle"), qgcView.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Ok) + } } } @@ -294,4 +299,21 @@ QGCView { } } } + + Component { + id: rebootVehicleConfirmComponent + + QGCViewDialog { + function accept() { + QGroundControl.multiVehicleManager.activeVehicle.rebootVehicle() + hideDialog() + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: qsTr("Select Ok to reboot vehicle.") + } + } + } } // QGCView diff --git a/src/QmlControls/ParameterEditorController.h b/src/QmlControls/ParameterEditorController.h index 74e2d3e05..2c8b275fa 100644 --- a/src/QmlControls/ParameterEditorController.h +++ b/src/QmlControls/ParameterEditorController.h @@ -36,20 +36,20 @@ public: Q_PROPERTY(QmlObjectListModel* parameters MEMBER _parameters CONSTANT) Q_PROPERTY(QVariantList componentIds MEMBER _componentIds CONSTANT) - Q_INVOKABLE QStringList getGroupsForComponent(int componentId); - Q_INVOKABLE QStringList getParametersForGroup(int componentId, QString group); + Q_INVOKABLE QStringList getGroupsForComponent(int componentId); + Q_INVOKABLE QStringList getParametersForGroup(int componentId, QString group); Q_INVOKABLE QStringList searchParametersForComponent(int componentId, const QString& searchText, bool searchInName=true, bool searchInDescriptions=true); - Q_INVOKABLE void clearRCToParam(void); + Q_INVOKABLE void clearRCToParam(void); Q_INVOKABLE void saveToFilePicker(void); Q_INVOKABLE void loadFromFilePicker(void); Q_INVOKABLE void saveToFile(const QString& filename); Q_INVOKABLE void loadFromFile(const QString& filename); Q_INVOKABLE void refresh(void); Q_INVOKABLE void resetAllToDefaults(void); - Q_INVOKABLE void setRCToParam(const QString& paramName); + Q_INVOKABLE void setRCToParam(const QString& paramName); - QList model(void); + QList model(void); signals: void searchTextChanged(QString searchText); -- GitLab From 525f96819a62c66aebf1a287b3a93f49ef12a94b Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Wed, 25 Jan 2017 22:38:49 -0500 Subject: [PATCH 231/398] guided mode altitude slider better maximum --- src/FlightDisplay/FlightDisplayViewWidgets.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index fa01ef116..9e564002b 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -452,8 +452,8 @@ Item { anchors.left: parent.left anchors.right: parent.right orientation: Qt.Vertical - minimumValue: QGroundControl.metersToAppSettingsDistanceUnits(2) - maximumValue: QGroundControl.metersToAppSettingsDistanceUnits((_activeVehicle && _activeVehicle.flying) ? 100 : 10) + minimumValue: QGroundControl.metersToAppSettingsDistanceUnits(0) + maximumValue: QGroundControl.metersToAppSettingsDistanceUnits((_activeVehicle && _activeVehicle.flying) ? Math.round((_activeVehicle.altitudeRelative.value + 100) / 100) * 100 : 10) } } } -- GitLab From 02441ff537261eb0abfc97d92000f3566267e5ce Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 27 Jan 2017 11:35:12 -0500 Subject: [PATCH 232/398] Enable source context in log messages for release builds --- QGCCommon.pri | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGCCommon.pri b/QGCCommon.pri index 2fb1d8016..de0c51665 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -221,7 +221,7 @@ WindowsBuild { # ReleaseBuild { - DEFINES += QT_NO_DEBUG + DEFINES += QT_NO_DEBUG QT_MESSAGELOGCONTEXT CONFIG += force_debug_info # Enable debugging symbols on release builds !iOSBuild { CONFIG += ltcg # Turn on link time code generation -- GitLab From 3237dd415d46d595007cec931134c64e65a94f71 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 27 Jan 2017 11:47:04 -0500 Subject: [PATCH 233/398] Enforce threadsafe shutdown and deletion of MAVLinkDecoder --- src/ui/MAVLinkDecoder.cc | 7 ++++--- src/ui/MAVLinkDecoder.h | 4 +++- src/ui/MainWindow.cc | 8 ++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ui/MAVLinkDecoder.cc b/src/ui/MAVLinkDecoder.cc index 5b02b32b3..ff22edf6b 100644 --- a/src/ui/MAVLinkDecoder.cc +++ b/src/ui/MAVLinkDecoder.cc @@ -3,10 +3,9 @@ #include -MAVLinkDecoder::MAVLinkDecoder(MAVLinkProtocol* protocol, QObject *parent) : - QThread() +MAVLinkDecoder::MAVLinkDecoder(MAVLinkProtocol* protocol) : + QThread(), creationThread(QThread::currentThread()) { - Q_UNUSED(parent); // We're doing it wrong - because the Qt folks got the API wrong: // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ moveToThread(this); @@ -52,6 +51,7 @@ MAVLinkDecoder::MAVLinkDecoder(MAVLinkProtocol* protocol, QObject *parent) : // textMessageFilter.insert(MAVLINK_MSG_ID_HIGHRES_IMU, false); connect(protocol, &MAVLinkProtocol::messageReceived, this, &MAVLinkDecoder::receiveMessage); + connect(this, &MAVLinkDecoder::finish, this, &QThread::quit); start(LowPriority); } @@ -63,6 +63,7 @@ MAVLinkDecoder::MAVLinkDecoder(MAVLinkProtocol* protocol, QObject *parent) : void MAVLinkDecoder::run() { exec(); + moveToThread(creationThread); } void MAVLinkDecoder::receiveMessage(LinkInterface* link,mavlink_message_t message) diff --git a/src/ui/MAVLinkDecoder.h b/src/ui/MAVLinkDecoder.h index eaa190906..ffe761f37 100644 --- a/src/ui/MAVLinkDecoder.h +++ b/src/ui/MAVLinkDecoder.h @@ -8,13 +8,14 @@ class MAVLinkDecoder : public QThread { Q_OBJECT public: - MAVLinkDecoder(MAVLinkProtocol* protocol, QObject *parent = 0); + MAVLinkDecoder(MAVLinkProtocol* protocol); void run(); signals: void textMessageReceived(int uasid, int componentid, int severity, const QString& text); void valueChanged(const int uasId, const QString& name, const QString& unit, const QVariant& value, const quint64 msec); + void finish(); ///< Trigger a thread safe shutdown public slots: /** @brief Receive one message from the protocol and decode it */ @@ -35,6 +36,7 @@ protected: quint64 onboardTimeOffset[cMessageIds]; ///< Offset of onboard time from Unix epoch (of the receiving GCS) qint64 onboardToGCSUnixTimeOffsetAndDelay[cMessageIds]; ///< Offset of onboard time and GCS Unix time quint64 firstOnboardTime[cMessageIds]; ///< First seen onboard time + QThread* creationThread; ///< QThread on which the object is created }; #endif // MAVLINKDECODER_H diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 6d925bc46..5b1fb93b4 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -274,6 +274,11 @@ MainWindow::MainWindow() MainWindow::~MainWindow() { + // Enforce thread-safe shutdown of the mavlink decoder + mavlinkDecoder->finish(); + mavlinkDecoder->wait(1000); + mavlinkDecoder->deleteLater(); + // This needs to happen before we get into the QWidget dtor // otherwise the QML engine reads freed data and tries to // destroy MainWindow a second time. @@ -290,8 +295,7 @@ QString MainWindow::_getWindowGeometryKey() void MainWindow::_buildCommonWidgets(void) { // Add generic MAVLink decoder - // TODO: This is never deleted - mavlinkDecoder = new MAVLinkDecoder(qgcApp()->toolbox()->mavlinkProtocol(), this); + mavlinkDecoder = new MAVLinkDecoder(qgcApp()->toolbox()->mavlinkProtocol()); connect(mavlinkDecoder.data(), &MAVLinkDecoder::valueChanged, this, &MainWindow::valueChanged); // Log player -- GitLab From 400d6d2c269241d53a5df9c59087d65a1aada51d Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Thu, 15 Dec 2016 23:17:10 -0500 Subject: [PATCH 234/398] Joystick Hotplugging --- src/Joystick/Joystick.cc | 16 ++-- src/Joystick/Joystick.h | 5 ++ src/Joystick/JoystickManager.cc | 89 ++++++++++++++++---- src/Joystick/JoystickManager.h | 10 ++- src/Joystick/JoystickSDL.cc | 54 ++++++++++-- src/Joystick/JoystickSDL.h | 4 + src/QGCApplication.cc | 4 +- src/Vehicle/Vehicle.cc | 8 ++ src/Vehicle/Vehicle.h | 2 + src/VehicleSetup/JoystickConfig.qml | 33 +++++++- src/VehicleSetup/JoystickConfigController.cc | 20 +++-- 11 files changed, 202 insertions(+), 43 deletions(-) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 17ce67b47..f86dbab98 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -71,9 +71,9 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC Joystick::~Joystick() { - delete _rgAxisValues; - delete _rgCalibration; - delete _rgButtonValues; + delete[] _rgAxisValues; + delete[] _rgCalibration; + delete[] _rgButtonValues; } void Joystick::_loadSettings(void) @@ -286,7 +286,7 @@ void Joystick::run(void) } } - if (_calibrationMode != CalibrationModeCalibrating) { + if (_calibrationMode != CalibrationModeCalibrating && _calibrated) { int axis = _rgFunctionAxis[rollFunction]; float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband); @@ -406,6 +406,9 @@ void Joystick::startPolling(Vehicle* vehicle) vehicle->setJoystickEnabled(false); } + // Update qml in case of joystick transition + emit calibratedChanged(_calibrated); + // Only connect the new vehicle if it wants joystick data if (vehicle->joystickEnabled()) { _pollingStartedForCalibration = false; @@ -430,14 +433,15 @@ void Joystick::stopPolling(void) if (_activeVehicle && _activeVehicle->joystickEnabled()) { UAS* uas = _activeVehicle->uas(); - + // Neutral attitude controls + // emit manualControl(0, 0, 0, 0.5, 0, _activeVehicle->joystickMode()); disconnect(this, &Joystick::manualControl, uas, &UAS::setExternalControlSetpoint); } // FIXME: **** //disconnect(this, &Joystick::buttonActionTriggered, uas, &UAS::triggerAction); _exitThread = true; - } + } } void Joystick::setCalibration(int axis, Calibration_t& calibration) diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index c9915eae2..8b8b52490 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -88,6 +88,11 @@ public: QVariantList buttonActions(void); QString name(void) { return _name; } + + // Joystick index used by sdl library + // Settable because sdl library remaps indicies after certain events + virtual int index(void) = 0; + virtual void setIndex(int index) = 0; int throttleMode(void); void setThrottleMode(int mode); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index a8d3f6f93..0e3fa6a2d 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -55,10 +55,22 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } -void JoystickManager::discoverJoysticks() +void JoystickManager::init() { + if (JoystickSDL::init()) { + _setActiveJoystickFromSettings(); + connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimer.start(250); + } +} + +void JoystickManager::_setActiveJoystickFromSettings(void) +{ + QMap newMap; + #ifdef __sdljoystick__ - _name2JoystickMap = JoystickSDL::discover(_multiVehicleManager); + // Get the latest joystick mapping + newMap = JoystickSDL::discover(_multiVehicleManager); #elif defined(__android__) /* * Android Joystick not yet supported @@ -66,16 +78,30 @@ void JoystickManager::discoverJoysticks() */ #endif - if (!_name2JoystickMap.count()) { - qCDebug(JoystickManagerLog) << "\tnone found"; - return; + // Check to see if our current mapping contains any joysticks that are not in the new mapping + // If so, those joysticks have been unplugged, and need to be cleaned up + QMap::iterator i; + for (i = _name2JoystickMap.begin(); i != _name2JoystickMap.end(); ++i) { + if (!newMap.contains(i.key())) { + qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key(); + if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { + qCDebug(JoystickManagerLog) << "\twas active"; + _activeJoystick->stopPolling(); + _activeJoystick = NULL; + } + // FIXME: Don't leave this hanging! We need to free the object. + //i.value()->deleteLater(); + } } - _setActiveJoystickFromSettings(); -} + _name2JoystickMap = newMap; + emit availableJoysticksChanged(); -void JoystickManager::_setActiveJoystickFromSettings(void) -{ + if (!_name2JoystickMap.count()) { + setActiveJoystick(NULL); + return; + } + QSettings settings; settings.beginGroup(_settingsGroup); @@ -97,23 +123,30 @@ Joystick* JoystickManager::activeJoystick(void) void JoystickManager::setActiveJoystick(Joystick* joystick) { QSettings settings; - - if (!_name2JoystickMap.contains(joystick->name())) { + + if (joystick != NULL && !_name2JoystickMap.contains(joystick->name())) { qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name(); return; } - + + if (_activeJoystick == joystick) { + return; + } + if (_activeJoystick) { _activeJoystick->stopPolling(); } _activeJoystick = joystick; - settings.beginGroup(_settingsGroup); - settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); - - emit activeJoystickChanged(_activeJoystick); - emit activeJoystickNameChanged(_activeJoystick->name()); + if (_activeJoystick != NULL) { + qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name(); + + settings.beginGroup(_settingsGroup); + settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); + emit activeJoystickChanged(_activeJoystick); + emit activeJoystickNameChanged(_activeJoystick->name()); + } } QVariantList JoystickManager::joysticks(void) @@ -146,3 +179,25 @@ void JoystickManager::setActiveJoystickName(const QString& name) setActiveJoystick(_name2JoystickMap[name]); } + +void JoystickManager::_updateAvailableJoysticks(void) +{ + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + qCDebug(JoystickManagerLog) << "SDL ERROR:" << SDL_GetError(); + break; + case SDL_JOYDEVICEADDED: + qCDebug(JoystickManagerLog) << "Joystick added:" << event.jdevice.which; + _setActiveJoystickFromSettings(); + break; + case SDL_JOYDEVICEREMOVED: + qCDebug(JoystickManagerLog) << "Joystick removed:" << event.jdevice.which; + _setActiveJoystickFromSettings(); + break; + default: + break; + } + } +} diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index 8460b752f..98a3d09ee 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -29,8 +29,8 @@ public: ~JoystickManager(); /// List of available joysticks - Q_PROPERTY(QVariantList joysticks READ joysticks CONSTANT) - Q_PROPERTY(QStringList joystickNames READ joystickNames CONSTANT) + Q_PROPERTY(QVariantList joysticks READ joysticks NOTIFY availableJoysticksChanged) + Q_PROPERTY(QStringList joystickNames READ joystickNames NOTIFY availableJoysticksChanged) /// Active joystick Q_PROPERTY(Joystick* activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged) @@ -49,13 +49,15 @@ public: virtual void setToolbox(QGCToolbox *toolbox); public slots: - void discoverJoysticks(); + void init(); signals: void activeJoystickChanged(Joystick* joystick); void activeJoystickNameChanged(const QString& name); + void availableJoysticksChanged(void); private slots: + void _updateAvailableJoysticks(void); private: void _setActiveJoystickFromSettings(void); @@ -67,6 +69,8 @@ private: static const char * _settingsGroup; static const char * _settingsKeyActiveJoystick; + + QTimer _joystickCheckTimer; }; #endif diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index 303c3005c..4e074e923 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -12,15 +12,21 @@ JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, in { } -QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { - static QMap ret; - +bool JoystickSDL::init(void) { if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) { + SDL_JoystickEventState(SDL_ENABLE); qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError(); - return ret; + return false; } _loadGameControllerMappings(); + return true; +} + +QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { + static QMap ret; + + QMap newRet; // Load available joysticks @@ -29,6 +35,7 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl for (int i=0; i JoystickSDL::discover(MultiVehicleManager* _multiVehicl } qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount << "hats:" << hatCount << "isGC:" << isGameController; - ret[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); + newRet[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); } else { + newRet[name] = ret[name]; + if (newRet[name]->index() != i) { + newRet[name]->setIndex(i); // This joystick index has been remapped by SDL + } + // Anything left in ret after we exit the loop has been removed (unplugged) and needs to be cleaned up. + // We will handle that in JoystickManager in case the removed joystick was in use. + ret.remove(name); qCDebug(JoystickLog) << "\tSkipping duplicate" << name; } } + + if (!newRet.count()) { + qCDebug(JoystickLog) << "\tnone found"; + } + + ret = newRet; return ret; } @@ -93,11 +113,32 @@ bool JoystickSDL::_open(void) { return false; } + qCDebug(JoystickLog) << "Opened joystick at" << sdlJoystick; + return true; } void JoystickSDL::_close(void) { - SDL_JoystickClose(sdlJoystick); + if (sdlJoystick == NULL) { + qCDebug(JoystickLog) << "Attempt to close null joystick!"; + return; + } + + qCDebug(JoystickLog) << "Closing" << SDL_JoystickName(sdlJoystick) << "at" << sdlJoystick; + + // We get a segfault if we try to close a joystick that has been detached + if (SDL_JoystickGetAttached(sdlJoystick) == SDL_FALSE) { + qCDebug(JoystickLog) << "\tJoystick is not attached!"; + } else { + + if (SDL_JoystickInstanceID(sdlJoystick) != -1) { + qCDebug(JoystickLog) << "\tID:" << SDL_JoystickInstanceID(sdlJoystick); + SDL_JoystickClose(sdlJoystick); + } + } + + sdlJoystick = NULL; + sdlController = NULL; } bool JoystickSDL::_update(void) @@ -131,4 +172,3 @@ uint8_t JoystickSDL::_getHat(int hat,int i) { } return 0; } - diff --git a/src/Joystick/JoystickSDL.h b/src/Joystick/JoystickSDL.h index 2c4a9ca6a..e8111a79b 100644 --- a/src/Joystick/JoystickSDL.h +++ b/src/Joystick/JoystickSDL.h @@ -13,6 +13,10 @@ public: JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager); static QMap discover(MultiVehicleManager* _multiVehicleManager); + static bool init(void); + + int index(void) { return _index; } + void setIndex(int index) { _index = index; } private: static void _loadGameControllerMappings(); diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index d436f6832..6a7de14ab 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -429,8 +429,8 @@ bool QGCApplication::_initForNormalAppBoot(void) // Load known link configurations toolbox()->linkManager()->loadLinkConfigurationList(); - // Probe for joysticks - TODO: manage on a timer or use events to deal with hotplug - toolbox()->joystickManager()->discoverJoysticks(); + // Probe for joysticks + toolbox()->joystickManager()->init(); if (_settingsUpgraded) { settings.clear(); diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 10c7deee4..6c7df9c29 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -141,6 +141,8 @@ Vehicle::Vehicle(LinkInterface* link, { _addLink(link); + connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_activeJoystickChanged); + _mavlink = qgcApp()->toolbox()->mavlinkProtocol(); connect(_mavlink, &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived); @@ -1295,6 +1297,12 @@ QStringList Vehicle::joystickModes(void) return list; } +void Vehicle::_activeJoystickChanged(void) +{ + _loadSettings(); + _startJoystick(true); +} + bool Vehicle::joystickEnabled(void) { return _joystickEnabled; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index c9d2c0114..c291ea210 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -690,6 +690,8 @@ private slots: void _newGeoFenceAvailable(void); void _sendMavCommandAgain(void); + void _activeJoystickChanged(void); + private: bool _containsLink(LinkInterface* link); void _addLink(LinkInterface* link); diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index 503c6be53..58069ffa7 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -27,6 +27,16 @@ SetupPage { pageName: qsTr("Joystick") pageDescription: qsTr("Joystick Setup is used to configure a calibrate joysticks.") + Connections { + target: joystickManager + onAvailableJoysticksChanged: { + if( joystickManager.joysticks.length == 0 ) { + summaryButton.checked = true + setupView.showSummaryPanel() + } + } + } + Component { id: pageComponent @@ -358,11 +368,20 @@ SetupPage { QGCCheckBox { - enabled: checked || _activeJoystick.calibrated + id: enabledCheckBox + enabled: _activeJoystick.calibrated text: _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)") checked: _activeVehicle.joystickEnabled onClicked: _activeVehicle.joystickEnabled = checked + + Connections { + target: joystickManager + + onActiveJoystickChanged: { + enabledCheckBox.checked = Qt.binding(function() { return _activeJoystick.calibrated && _activeVehicle.joystickEnabled }) + } + } } Row { @@ -390,6 +409,18 @@ SetupPage { joystickCombo.currentIndex = index } } + + Connections { + target: joystickManager + onAvailableJoysticksChanged: { + var index = joystickCombo.find(joystickManager.activeJoystickName) + if (index == -1) { + console.warn(qsTr("Active joystick name not in combo"), joystickManager.activeJoystickName) + } else { + joystickCombo.currentIndex = index + } + } + } } } diff --git a/src/VehicleSetup/JoystickConfigController.cc b/src/VehicleSetup/JoystickConfigController.cc index 505478bd7..8111896d9 100644 --- a/src/VehicleSetup/JoystickConfigController.cc +++ b/src/VehicleSetup/JoystickConfigController.cc @@ -69,7 +69,9 @@ void JoystickConfigController::start(void) JoystickConfigController::~JoystickConfigController() { - _activeJoystick->stopCalibrationMode(Joystick::CalibrationModeMonitor); + if(_activeJoystick) { + _activeJoystick->stopCalibrationMode(Joystick::CalibrationModeMonitor); + } } /// @brief Returns the state machine entry for the specified state. @@ -491,9 +493,10 @@ void JoystickConfigController::_setInternalCalibrationValuesFromSettings(void) int paramAxis; paramAxis = joystick->getFunctionAxis((Joystick::AxisFunction_t)function); - - _rgFunctionAxisMapping[function] = paramAxis; - _rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function; + if(paramAxis >= 0) { + _rgFunctionAxisMapping[function] = paramAxis; + _rgAxisInfo[paramAxis].function = (Joystick::AxisFunction_t)function; + } } _signalAllAttiudeValueChanges(); @@ -809,9 +812,11 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) if (_activeJoystick) { joystickTransition = true; disconnect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged); - delete _rgAxisInfo; - delete _axisValueSave; - delete _axisRawValue; + // This will reset _rgFunctionAxis values to -1 to prevent out-of-bounds accesses + _resetInternalCalibrationValues(); + delete[] _rgAxisInfo; + delete[] _axisValueSave; + delete[] _axisRawValue; _axisCount = 0; _activeJoystick = NULL; } @@ -826,6 +831,7 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) _rgAxisInfo = new struct AxisInfo[_axisCount]; _axisValueSave = new int[_axisCount]; _axisRawValue = new int[_axisCount]; + _setInternalCalibrationValuesFromSettings(); connect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged); } } -- GitLab From a44be8b74adf7e8b92ae72be13521c0b81aa748e Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 27 Jan 2017 17:31:24 -0500 Subject: [PATCH 235/398] Fix non-SDL builds --- src/Joystick/JoystickManager.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 0e3fa6a2d..9b55ccea4 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -57,11 +57,17 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) void JoystickManager::init() { +#ifdef __sdljoystick__ if (JoystickSDL::init()) { _setActiveJoystickFromSettings(); connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); _joystickCheckTimer.start(250); } +#elif defined(__android__) + /* + * Android Joystick not yet supported + */ +#endif } void JoystickManager::_setActiveJoystickFromSettings(void) @@ -182,6 +188,7 @@ void JoystickManager::setActiveJoystickName(const QString& name) void JoystickManager::_updateAvailableJoysticks(void) { +#ifdef __sdljoystick__ SDL_Event event; while (SDL_PollEvent(&event)) { switch(event.type) { @@ -200,4 +207,9 @@ void JoystickManager::_updateAvailableJoysticks(void) break; } } +#elif defined(__android__) + /* + * Android Joystick not yet supported + */ +#endif } -- GitLab From 501c0fe301f51269913c20766646b11ede6b22a4 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 27 Jan 2017 18:18:21 -0500 Subject: [PATCH 236/398] Close SDL joystick threads after joystick is unplugged --- src/Joystick/JoystickManager.cc | 12 +++++------ src/VehicleSetup/JoystickConfig.qml | 32 ++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 9b55ccea4..97a0b10cc 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -92,11 +92,10 @@ void JoystickManager::_setActiveJoystickFromSettings(void) qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key(); if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { qCDebug(JoystickManagerLog) << "\twas active"; - _activeJoystick->stopPolling(); - _activeJoystick = NULL; + setActiveJoystick(NULL); } - // FIXME: Don't leave this hanging! We need to free the object. - //i.value()->deleteLater(); + i.value()->wait(1000); + i.value()->deleteLater(); } } @@ -150,9 +149,10 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) settings.beginGroup(_settingsGroup); settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); - emit activeJoystickChanged(_activeJoystick); - emit activeJoystickNameChanged(_activeJoystick->name()); } + + emit activeJoystickChanged(_activeJoystick); + emit activeJoystickNameChanged(_activeJoystick?_activeJoystick->name():""); } QVariantList JoystickManager::joysticks(void) diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index 58069ffa7..0b5c3f459 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -369,8 +369,8 @@ SetupPage { QGCCheckBox { id: enabledCheckBox - enabled: _activeJoystick.calibrated - text: _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)") + enabled: _activeJoystick ? _activeJoystick.calibrated : false + text: _activeJoystick ? _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)") : "" checked: _activeVehicle.joystickEnabled onClicked: _activeVehicle.joystickEnabled = checked @@ -379,7 +379,9 @@ SetupPage { target: joystickManager onActiveJoystickChanged: { + if(_activeJoystick) { enabledCheckBox.checked = Qt.binding(function() { return _activeJoystick.calibrated && _activeVehicle.joystickEnabled }) + } } } } @@ -414,9 +416,7 @@ SetupPage { target: joystickManager onAvailableJoysticksChanged: { var index = joystickCombo.find(joystickManager.activeJoystickName) - if (index == -1) { - console.warn(qsTr("Active joystick name not in combo"), joystickManager.activeJoystickName) - } else { + if (index >= 0) { joystickCombo.currentIndex = index } } @@ -433,7 +433,7 @@ SetupPage { QGCRadioButton { exclusiveGroup: throttleModeExclusiveGroup text: qsTr("Center stick is zero throttle") - checked: _activeJoystick.throttleMode == 0 + checked: _activeJoystick ? _activeJoystick.throttleMode == 0 : false onClicked: _activeJoystick.throttleMode = 0 } @@ -442,11 +442,11 @@ SetupPage { x: 20 width: parent.width spacing: ScreenTools.defaultFontPixelWidth - visible: _activeJoystick.throttleMode == 0 + visible: _activeJoystick ? _activeJoystick.throttleMode == 0 : false QGCCheckBox { id: accumulator - checked: _activeJoystick.accumulator + checked: _activeJoystick ? _activeJoystick.accumulator : false text: qsTr("Spring loaded throttle smoothing") onClicked: _activeJoystick.accumulator = checked @@ -456,7 +456,7 @@ SetupPage { QGCRadioButton { exclusiveGroup: throttleModeExclusiveGroup text: qsTr("Full down stick is zero throttle") - checked: _activeJoystick.throttleMode == 1 + checked: _activeJoystick ? _activeJoystick.throttleMode == 1 : false onClicked: _activeJoystick.throttleMode = 1 } @@ -467,7 +467,7 @@ SetupPage { QGCCheckBox { id: exponential - checked: _activeJoystick.exponential + checked: _activeJoystick ? _activeJoystick.exponential : false text: qsTr("Use exponential curve on roll, pitch, yaw") onClicked: _activeJoystick.exponential = checked @@ -556,7 +556,7 @@ SetupPage { Repeater { id: buttonActionRepeater - model: _activeJoystick.totalButtonCount + model: _activeJoystick ? _activeJoystick.totalButtonCount : 0 Row { spacing: ScreenTools.defaultFontPixelWidth @@ -566,7 +566,7 @@ SetupPage { QGCCheckBox { anchors.verticalCenter: parent.verticalCenter - checked: _activeJoystick.buttonActions[modelData] != "" + checked: _activeJoystick ? _activeJoystick.buttonActions[modelData] != "" : false onClicked: _activeJoystick.setButtonAction(modelData, checked ? buttonActionCombo.textAt(buttonActionCombo.currentIndex) : "") } @@ -592,7 +592,7 @@ SetupPage { QGCComboBox { id: buttonActionCombo width: ScreenTools.defaultFontPixelWidth * 20 - model: _activeJoystick.actions + model: _activeJoystick ? _activeJoystick.actions : 0 onActivated: _activeJoystick.setButtonAction(modelData, textAt(index)) Component.onCompleted: currentIndex = find(_activeJoystick.buttonActions[modelData]) @@ -623,7 +623,7 @@ SetupPage { Repeater { id: jsButtonActionRepeater - model: _activeJoystick.totalButtonCount + model: _activeJoystick ? _activeJoystick.totalButtonCount : 0 Row { spacing: ScreenTools.defaultFontPixelWidth @@ -704,7 +704,7 @@ SetupPage { Repeater { id: axisMonitorRepeater - model: _activeJoystick.axisCount + model: _activeJoystick ? _activeJoystick.axisCount : 0 width: parent.width Row { @@ -756,7 +756,7 @@ SetupPage { Repeater { id: buttonMonitorRepeater - model: _activeJoystick.totalButtonCount + model: _activeJoystick ? _activeJoystick.totalButtonCount : 0 Rectangle { width: ScreenTools.defaultFontPixelHeight * 1.2 -- GitLab From d5d238e525d345568b39bab918a474efb7ff6974 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 27 Jan 2017 20:45:06 -0500 Subject: [PATCH 237/398] Always stopPolling joystick before stopping thread --- src/Joystick/JoystickManager.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 97a0b10cc..eb9685bf6 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -84,16 +84,18 @@ void JoystickManager::_setActiveJoystickFromSettings(void) */ #endif + if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { + qCDebug(JoystickManagerLog) << "Active joystick removed"; + setActiveJoystick(NULL); + } + // Check to see if our current mapping contains any joysticks that are not in the new mapping // If so, those joysticks have been unplugged, and need to be cleaned up QMap::iterator i; for (i = _name2JoystickMap.begin(); i != _name2JoystickMap.end(); ++i) { if (!newMap.contains(i.key())) { qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key(); - if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { - qCDebug(JoystickManagerLog) << "\twas active"; - setActiveJoystick(NULL); - } + i.value()->stopPolling(); i.value()->wait(1000); i.value()->deleteLater(); } -- GitLab From 6156e5a0fc5a012a574e94cfff7a552623cf9d00 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 28 Jan 2017 22:45:43 -0500 Subject: [PATCH 238/398] Add MAV_AUTOPILOT_GENERIC only once. --- src/FirmwarePlugin/FirmwarePluginManager.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/FirmwarePlugin/FirmwarePluginManager.cc b/src/FirmwarePlugin/FirmwarePluginManager.cc index b2ec571cd..4cbe2b34f 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.cc +++ b/src/FirmwarePlugin/FirmwarePluginManager.cc @@ -30,14 +30,11 @@ QList FirmwarePluginManager::knownFirmwareTypes(void) { if (_knownFirmwareTypes.isEmpty()) { QList factoryList = FirmwarePluginFactoryRegister::instance()->pluginFactories(); - - for (int i=0; iknownFirmwareTypes()); } + _knownFirmwareTypes.append(MAV_AUTOPILOT_GENERIC); } - - _knownFirmwareTypes.append(MAV_AUTOPILOT_GENERIC); - return _knownFirmwareTypes; } -- GitLab From a286b108baed3a983bc5bc92dec24db205da03b3 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 28 Jan 2017 22:49:19 -0500 Subject: [PATCH 239/398] Fix size and rendering of mission item hamburger button. --- src/MissionEditor/MissionItemEditor.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index 7761d9660..c59674d8f 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -18,8 +18,8 @@ Rectangle { color: _currentItem ? qgcPal.buttonHighlight : qgcPal.windowShade radius: _radius - property var missionItem ///< MissionItem associated with this editor - property bool readOnly ///< true: read only view, false: full editing view + property var missionItem ///< MissionItem associated with this editor + property bool readOnly ///< true: read only view, false: full editing view signal clicked signal remove @@ -33,6 +33,7 @@ Rectangle { readonly property real _editFieldWidth: Math.min(width - _margin * 2, ScreenTools.defaultFontPixelWidth * 12) readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _hamburgerSize: commandPicker.height * 0.75 QGCPalette { id: qgcPal @@ -59,9 +60,9 @@ Rectangle { anchors.rightMargin: ScreenTools.defaultFontPixelWidth anchors.right: parent.right anchors.verticalCenter: commandPicker.verticalCenter - width: commandPicker.height - height: commandPicker.height - sourceSize.height: height + width: _hamburgerSize + height: _hamburgerSize + sourceSize.height: _hamburgerSize source: "qrc:/qmlimages/Hamburger.svg" visible: missionItem.isCurrentItem && missionItem.sequenceNumber != 0 -- GitLab From f46746ca30a9ca71ee840b7f8a8de9f61d3a676d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 28 Jan 2017 22:53:20 -0500 Subject: [PATCH 240/398] Add message telling QGC is waiting for a vehicle --- src/ui/toolbar/MainToolBar.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index b1b8ed899..dee03d8f5 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -212,6 +212,16 @@ Rectangle { Layout.fillWidth: true visible: _activeVehicle } + + QGCLabel { + id: waitForVehicle + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Waiting For Vehicle Connection") + font.pointSize: ScreenTools.mediumFontPointSize + font.family: ScreenTools.demiboldFontFamily + color: colorRed + visible: !_activeVehicle + } } // Progress bar -- GitLab From 5b5278c36c210418562857c0c0b1301f35d5d834 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 30 Jan 2017 15:41:04 -0500 Subject: [PATCH 241/398] UI Tweaks --- src/MissionEditor/MissionEditor.qml | 2 +- src/MissionEditor/MissionItemEditor.qml | 3 +- src/MissionEditor/MissionSettingsEditor.qml | 39 +++++++++++++------ src/QGCPalette.cc | 4 +- src/QmlControls/QGCButton.qml | 5 ++- src/QmlControls/QGCComboBox.qml | 1 + src/QmlControls/QGroundControlQmlGlobal.cc | 8 ++++ src/QmlControls/QGroundControlQmlGlobal.h | 4 ++ src/VideoStreaming/VideoStreaming.cc | 2 +- .../gstqtvideosink/utils/glutils.h | 2 +- 10 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index b05d49231..e71b23026 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -37,7 +37,7 @@ QGCView { readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5 readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle readonly property real _rightPanelWidth: Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30) - readonly property real _rightPanelOpacity: 0.8 + readonly property real _rightPanelOpacity: 1 readonly property int _toolButtonCount: 6 readonly property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) readonly property var _defaultVehicleCoordinate: QtPositioning.coordinate(37.803784, -122.462276) diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index c59674d8f..e9f4797ff 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -55,7 +55,7 @@ Rectangle { color: _outerTextColor } - Image { + QGCColoredImage { id: hamburger anchors.rightMargin: ScreenTools.defaultFontPixelWidth anchors.right: parent.right @@ -65,6 +65,7 @@ Rectangle { sourceSize.height: _hamburgerSize source: "qrc:/qmlimages/Hamburger.svg" visible: missionItem.isCurrentItem && missionItem.sequenceNumber != 0 + color: qgcPal.windowShade MouseArea { anchors.fill: parent diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index b6a062924..65311e3ad 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -37,6 +37,7 @@ Rectangle { property bool _showOfflineEditingCombos: _offlineEditing && _noMissionItemsAdded property bool _showCruiseSpeed: !_missionVehicle.multiRotor property bool _showHoverSpeed: _missionVehicle.multiRotor || missionController.vehicle.vtol + property bool _multipleFirmware: QGroundControl.supportedFirmwareCount > 2 readonly property string _firmwareLabel: qsTr("Firmware:") readonly property string _vehicleLabel: qsTr("Vehicle:") @@ -50,7 +51,9 @@ Rectangle { anchors.top: parent.top spacing: _margin - QGCLabel { text: qsTr("Planned Home Position:") } + QGCLabel { + text: qsTr("Planned Home Position") + } Rectangle { anchors.left: parent.left @@ -90,27 +93,25 @@ Rectangle { } } - QGCButton { - text: qsTr("Move Home to map center") - visible: missionItem.homePosition - onClicked: editorRoot.moveHomeToMapCenter() - anchors.horizontalCenter: parent.horizontalCenter - } - QGCLabel { width: parent.width wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - text: qsTr("Note: Planned home position for mission display only. Actual home position set by vehicle at flight time.") + text: qsTr("Actual position set by vehicle at flight time.") + horizontalAlignment: Text.AlignHCenter } - QGCLabel { text: qsTr("Vehicle Info:") } + QGCLabel { + text: qsTr("Vehicle Info:") + visible: _multipleFirmware + } Rectangle { anchors.left: parent.left anchors.right: parent.right height: 1 color: qgcPal.text + visible: _multipleFirmware } GridLayout { @@ -119,6 +120,7 @@ Rectangle { columnSpacing: ScreenTools.defaultFontPixelWidth rowSpacing: columnSpacing columns: 2 + visible: _multipleFirmware QGCLabel { text: _firmwareLabel @@ -187,8 +189,23 @@ Rectangle { width: parent.width wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - text: qsTr("Note: Speeds are planned speeds only for time calculations. Actual vehicle will not be affected.") + visible: _multipleFirmware + text: qsTr("Speeds are only for time calculations. Actual vehicle will not be affected.") + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text } + + QGCButton { + text: qsTr("Set Home To Map Center") + onClicked: editorRoot.moveHomeToMapCenter() + anchors.horizontalCenter: parent.horizontalCenter + } + } // Column } // Item } // Component diff --git a/src/QGCPalette.cc b/src/QGCPalette.cc index 6e6ea6095..6416801bc 100644 --- a/src/QGCPalette.cc +++ b/src/QGCPalette.cc @@ -42,7 +42,7 @@ QColor QGCPalette::_text[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { QColor QGCPalette::_warningText[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { { QColor("#cc0808"), QColor("#cc0808") }, - { QColor("#e4e428"), QColor("#e4e428") } + { QColor("0xed, 0xd4, 0x69"), QColor("0xed, 0xd4, 0x69") } }; QColor QGCPalette::_button[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { @@ -57,7 +57,7 @@ QColor QGCPalette::_buttonText[QGCPalette::_cThemes][QGCPalette::_cColorGroups] QColor QGCPalette::_buttonHighlight[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { { QColor("#e4e4e4"), QColor("#33b5e5") }, - { QColor(0x58, 0x58, 0x58), QColor(237, 235, 51) }, + { QColor(0x58, 0x58, 0x58), QColor(0xed, 0xd4, 0x69) }, }; QColor QGCPalette::_buttonHighlightText[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { diff --git a/src/QmlControls/QGCButton.qml b/src/QmlControls/QGCButton.qml index 1417af567..5cff7f1df 100644 --- a/src/QmlControls/QGCButton.qml +++ b/src/QmlControls/QGCButton.qml @@ -50,10 +50,10 @@ Button { style: ButtonStyle { /*! The padding between the background and the label components. */ padding { - top: __padding + top: __padding * 0.5 left: __padding right: control.menu !== null ? Math.round(ScreenTools.defaultFontPixelHeight) : __padding - bottom: __padding + bottom: __padding * 0.5 } /*! This defines the background of the button. */ @@ -66,6 +66,7 @@ Button { anchors.fill: parent border.width: _showBorder ? 1: 0 border.color: _qgcPal.buttonText + radius: 3 color: _showHighlight ? control._qgcPal.buttonHighlight : (primary ? control._qgcPal.primaryButton : control._qgcPal.button) diff --git a/src/QmlControls/QGCComboBox.qml b/src/QmlControls/QGCComboBox.qml index c8f24b13b..4e2150add 100644 --- a/src/QmlControls/QGCComboBox.qml +++ b/src/QmlControls/QGCComboBox.qml @@ -24,6 +24,7 @@ ComboBox { Rectangle { anchors.fill: parent color: _showHighlight ? control._qgcPal.buttonHighlight : control._qgcPal.button + radius: 3 border.width: _showBorder ? 1: 0 border.color: control._qgcPal.buttonText } diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 759d6c853..19f0c8ced 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -46,6 +46,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _videoManager(NULL) , _mavlinkLogManager(NULL) , _corePlugin(NULL) + , _firmwarePluginManager(NULL) , _virtualTabletJoystick(false) , _baseFontPointSize(0.0) { @@ -74,6 +75,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _videoManager = toolbox->videoManager(); _mavlinkLogManager = toolbox->mavlinkLogManager(); _corePlugin = toolbox->corePlugin(); + _firmwarePluginManager = toolbox->firmwarePluginManager(); } void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) @@ -337,6 +339,12 @@ Fact* QGroundControlQmlGlobal::batteryPercentRemainingAnnounce(void) return _batteryPercentRemainingAnnounceFact; } +int QGroundControlQmlGlobal::supportedFirmwareCount() +{ + return _firmwarePluginManager->knownFirmwareTypes().count(); +} + + bool QGroundControlQmlGlobal::linesIntersect(QPointF line1A, QPointF line1B, QPointF line2A, QPointF line2B) { QPointF intersectPoint; diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 757573e81..47a46e647 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -99,6 +99,7 @@ public: Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) + Q_PROPERTY(int supportedFirmwareCount READ supportedFirmwareCount CONSTANT) Q_PROPERTY(QGeoCoordinate lastKnownHomePosition READ lastKnownHomePosition CONSTANT) Q_PROPERTY(QGeoCoordinate flightMapPosition MEMBER _flightMapPosition NOTIFY flightMapPositionChanged) @@ -196,6 +197,8 @@ public: static Fact* speedUnits (void); static Fact* batteryPercentRemainingAnnounce(void); + int supportedFirmwareCount (); + void setIsDarkStyle (bool dark); void setIsAudioMuted (bool muted); void setIsSaveLogPrompt (bool prompt); @@ -245,6 +248,7 @@ private: VideoManager* _videoManager; MAVLinkLogManager* _mavlinkLogManager; QGCCorePlugin* _corePlugin; + FirmwarePluginManager* _firmwarePluginManager; bool _virtualTabletJoystick; qreal _baseFontPointSize; diff --git a/src/VideoStreaming/VideoStreaming.cc b/src/VideoStreaming/VideoStreaming.cc index f70ddd60b..48c0527d9 100644 --- a/src/VideoStreaming/VideoStreaming.cc +++ b/src/VideoStreaming/VideoStreaming.cc @@ -131,7 +131,7 @@ void initializeVideoStreaming(int &argc, char* argv[]) // Our own plugin GST_PLUGIN_STATIC_REGISTER(QGC_VIDEOSINK_PLUGIN); // The static plugins we use - #if defined(__mobile__) + #if defined(__mobile__) && !defined(__macos__) GST_PLUGIN_STATIC_REGISTER(coreelements); GST_PLUGIN_STATIC_REGISTER(libav); GST_PLUGIN_STATIC_REGISTER(rtp); diff --git a/src/VideoStreaming/gstqtvideosink/utils/glutils.h b/src/VideoStreaming/gstqtvideosink/utils/glutils.h index 002b1aa54..f7de1882b 100644 --- a/src/VideoStreaming/gstqtvideosink/utils/glutils.h +++ b/src/VideoStreaming/gstqtvideosink/utils/glutils.h @@ -17,7 +17,7 @@ #ifndef GLUTILS_H #define GLUTILS_H -#ifdef __mobile__ +#if defined(__mobile__) && !defined(__macos__) #include #define getQOpenGLFunctions() QOpenGLContext::currentContext()->functions() #define QOpenGLFunctionsDef QOpenGLFunctions -- GitLab From e74046c97d166bd453c5675e566f9b186f9fdb9d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 30 Jan 2017 19:21:22 -0500 Subject: [PATCH 242/398] Remove unused code --- src/ui/toolbar/MainToolBarController.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h index 19fb78e0b..c8f42b284 100644 --- a/src/ui/toolbar/MainToolBarController.h +++ b/src/ui/toolbar/MainToolBarController.h @@ -37,7 +37,6 @@ public: MainToolBarController(QObject* parent = NULL); ~MainToolBarController(); - Q_PROPERTY(double height MEMBER _toolbarHeight NOTIFY heightChanged) Q_PROPERTY(int telemetryRRSSI READ telemetryRRSSI NOTIFY telemetryRRSSIChanged) Q_PROPERTY(int telemetryLRSSI READ telemetryLRSSI NOTIFY telemetryLRSSIChanged) Q_PROPERTY(unsigned int telemetryRXErrors READ telemetryRXErrors NOTIFY telemetryRXErrorsChanged) @@ -82,8 +81,6 @@ private: uint32_t _telemetryLNoise; uint32_t _telemetryRNoise; - double _toolbarHeight; - QStringList _toolbarMessageQueue; QMutex _toolbarMessageQueueMutex; }; -- GitLab From a241a1b23006decba3eed2125182a93e8de75e6d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 30 Jan 2017 20:33:26 -0500 Subject: [PATCH 243/398] Allow plugin to define default font size, toolbar height and option to display/hide plan view selector. --- src/MissionEditor/MissionEditor.qml | 3 ++- src/QmlControls/ScreenTools.qml | 8 ++++++-- src/api/QGCOptions.h | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index e71b23026..f2eee50d5 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -554,6 +554,7 @@ QGCView { anchors.leftMargin: parent.width - _rightPanelWidth anchors.left: parent.left spacing: _horizontalMargin + visible: QGroundControl.corePlugin.options.enablePlanViewSelector readonly property real _buttonRadius: ScreenTools.defaultFontPixelHeight * 0.75 @@ -615,7 +616,7 @@ QGCView { Item { id: missionItemEditor anchors.topMargin: _margin - anchors.top: planElementSelectorRow.bottom + anchors.top: planElementSelectorRow.visible ? planElementSelectorRow.bottom : planElementSelectorRow.top anchors.bottom: parent.bottom anchors.right: parent.right width: _rightPanelWidth diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 84bb0a099..964559273 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -47,7 +47,7 @@ Item { property real largeFontPointSize: 10 property real availableHeight: 0 - property real toolbarHeight: defaultFontPixelHeight * 3 + property real toolbarHeight: 0 readonly property real smallFontPointRatio: 0.75 readonly property real mediumFontPointRatio: 1.25 @@ -93,6 +93,7 @@ Item { smallFontPointSize = defaultFontPointSize * _screenTools.smallFontPointRatio mediumFontPointSize = defaultFontPointSize * _screenTools.mediumFontPointRatio largeFontPointSize = defaultFontPointSize * _screenTools.largeFontPointRatio + toolbarHeight = defaultFontPixelHeight * 3 * QGroundControl.corePlugin.options.toolbarHeightMultiplier } Text { @@ -107,7 +108,10 @@ Item { property real fontWidth: contentWidth property real fontHeight: contentHeight Component.onCompleted: { - var baseSize = QGroundControl.baseFontPointSize; + var baseSize = QGroundControl.corePlugin.options.defaultFontPointSize + if(baseSize == 0.0) { + baseSize = QGroundControl.baseFontPointSize; + } //-- If this is the first time (not saved in settings) if(baseSize < 6 || baseSize > 48) { //-- Init base size base on the platform diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index e1e913512..1845d3abb 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -29,6 +29,9 @@ public: Q_PROPERTY(bool definesVideo READ definesVideo CONSTANT) Q_PROPERTY(uint16_t videoUDPPort READ videoUDPPort CONSTANT) Q_PROPERTY(QString videoRSTPUrl READ videoRSTPUrl CONSTANT) + Q_PROPERTY(double toolbarHeightMultiplier READ toolbarHeightMultiplier CONSTANT) + Q_PROPERTY(double defaultFontPointSize READ defaultFontPointSize CONSTANT) + Q_PROPERTY(bool enablePlanViewSelector READ enablePlanViewSelector CONSTANT) //! Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? /*! @@ -65,4 +68,19 @@ public: @return RTSP url to use. Return "" to disable RTSP. */ virtual QString videoRSTPUrl () { return QString(); } + //! Main ToolBar Multiplier. + /*! + @return Factor to use when computing toolbar height + */ + virtual double toolbarHeightMultiplier () { return 1.0; } + //! Application wide default font point size + /*! + @return Font size or 0.0 to use computed size. + */ + virtual double defaultFontPointSize () { return 0.0; } + //! Enable Plan View Selector (Mission, Fence or Rally) + /*! + @return True or false + */ + virtual bool enablePlanViewSelector () { return true; } }; -- GitLab From 170606b4547ea9853f9b245b2146acea74726dd9 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 30 Jan 2017 21:09:40 -0500 Subject: [PATCH 244/398] Disable font size control when fixed size is defined. --- src/ui/preferences/GeneralSettings.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 1eacfab91..0e7ce90c8 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -145,6 +145,7 @@ QGCView { //----------------------------------------------------------------- //-- Base UI Font Point Size Row { + visible: QGroundControl.corePlugin.options.defaultFontPointSize < 1.0 spacing: ScreenTools.defaultFontPixelWidth QGCLabel { id: baseFontLabel -- GitLab From 9d3153fad798a13a33563c50e1c45a3f67ce9ffe Mon Sep 17 00:00:00 2001 From: Anton Matosov Date: Fri, 20 Jan 2017 23:02:54 -0800 Subject: [PATCH 245/398] Add mouse area to the FactSliderPanel in order to avoid unwanted value changes while scrolling --- src/QmlControls/FactSliderPanel.qml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/QmlControls/FactSliderPanel.qml b/src/QmlControls/FactSliderPanel.qml index e900e8382..2d1fc417e 100644 --- a/src/QmlControls/FactSliderPanel.qml +++ b/src/QmlControls/FactSliderPanel.qml @@ -12,6 +12,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 @@ -110,6 +111,25 @@ Column { onValueChanged: { if (_loadComplete) { fact.value = value + } + } + + activeFocusOnPress: true + + MultiPointTouchArea { + anchors.fill: parent + + minimumTouchPoints: 1 + maximumTouchPoints: 1 + mouseEnabled: false + } + + // Block wheel events + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + onWheel: { + wheel.accepted = true } } } // Slider -- GitLab From c333553d85d684c82032f5d40c6670c2da7a9878 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 31 Jan 2017 09:31:55 -0500 Subject: [PATCH 246/398] Check for valid pipeline before shutting down --- src/VideoStreaming/VideoReceiver.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 5d8914929..65dc1a16c 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -354,6 +354,10 @@ void VideoReceiver::setVideoSavePath(const QString & path) #if defined(QGC_GST_STREAMING) void VideoReceiver::_shutdownPipeline() { + if(!_pipeline) { + qCDebug(VideoReceiverLog) << "No pipeline"; + return; + } GstBus* bus = NULL; if ((bus = gst_pipeline_get_bus(GST_PIPELINE(_pipeline))) != NULL) { gst_bus_disable_sync_message_emission(bus); -- GitLab From be4a4f87fb2435f21a5d904b90273d317f3c5bea Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 31 Jan 2017 10:05:58 -0500 Subject: [PATCH 247/398] Fix whitespace --- src/Joystick/JoystickManager.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index eb9685bf6..a36f4a648 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -48,7 +48,7 @@ JoystickManager::~JoystickManager() { void JoystickManager::setToolbox(QGCToolbox *toolbox) { - QGCTool::setToolbox(toolbox); + QGCTool::setToolbox(toolbox); _multiVehicleManager = _toolbox->multiVehicleManager(); @@ -108,7 +108,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) setActiveJoystick(NULL); return; } - + QSettings settings; settings.beginGroup(_settingsGroup); @@ -193,7 +193,7 @@ void JoystickManager::_updateAvailableJoysticks(void) #ifdef __sdljoystick__ SDL_Event event; while (SDL_PollEvent(&event)) { - switch(event.type) { + switch(event.type) { case SDL_QUIT: qCDebug(JoystickManagerLog) << "SDL ERROR:" << SDL_GetError(); break; @@ -205,7 +205,7 @@ void JoystickManager::_updateAvailableJoysticks(void) qCDebug(JoystickManagerLog) << "Joystick removed:" << event.jdevice.which; _setActiveJoystickFromSettings(); break; - default: + default: break; } } -- GitLab From 35a762608b8d813637a2ab829a51dd0b8ec466ec Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 31 Jan 2017 13:50:24 -0500 Subject: [PATCH 248/398] Set mobile flag based on the actual define. --- src/QmlControls/ScreenToolsController.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/QmlControls/ScreenToolsController.h b/src/QmlControls/ScreenToolsController.h index 47d7dcb01..86e5ee247 100644 --- a/src/QmlControls/ScreenToolsController.h +++ b/src/QmlControls/ScreenToolsController.h @@ -42,34 +42,35 @@ public: Q_INVOKABLE int mouseX(void) { return QCursor::pos().x(); } Q_INVOKABLE int mouseY(void) { return QCursor::pos().y(); } +#if defined(__mobile__) + bool isMobile () { return true; } +#else + bool isMobile () { return qgcApp()->fakeMobile(); } +#endif + #if defined (__android__) bool isAndroid () { return true; } bool isiOS () { return false; } - bool isMobile () { return true; } bool isLinux () { return false; } bool isMacOS () { return false; } #elif defined(__ios__) bool isAndroid () { return false; } bool isiOS () { return true; } - bool isMobile () { return true; } bool isLinux () { return false; } bool isMacOS () { return false; } #elif defined(__macos__) bool isAndroid () { return false; } bool isiOS () { return false; } - bool isMobile () { return qgcApp()->fakeMobile(); } bool isLinux () { return false; } bool isMacOS () { return true; } #elif defined(Q_OS_LINUX) bool isAndroid () { return false; } bool isiOS () { return false; } - bool isMobile () { return qgcApp()->fakeMobile(); } bool isLinux () { return true; } bool isMacOS () { return false; } #else bool isAndroid () { return false; } bool isiOS () { return false; } - bool isMobile () { return qgcApp()->fakeMobile(); } bool isLinux () { return false; } bool isMacOS () { return false; } #endif -- GitLab From 80d7676fa71a32f4e0eb3a20d330ef3c78b9543d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 31 Jan 2017 20:50:46 -0500 Subject: [PATCH 249/398] Correct check for bad value from parseFloat --- src/AutoPilotPlugins/APM/APMPowerComponent.qml | 4 ++-- src/AutoPilotPlugins/PX4/PowerComponent.qml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMPowerComponent.qml b/src/AutoPilotPlugins/APM/APMPowerComponent.qml index 9a1c6b19e..7cb5437a1 100644 --- a/src/AutoPilotPlugins/APM/APMPowerComponent.qml +++ b/src/AutoPilotPlugins/APM/APMPowerComponent.qml @@ -138,11 +138,11 @@ SetupPage { onClicked: { var measuredVoltageValue = parseFloat(measuredVoltage.text) - if (measuredVoltageValue == 0) { + if (measuredVoltageValue == 0 || isNaN(measuredVoltageValue)) { return } var newVoltageMultiplier = (measuredVoltageValue * battVoltMult.value) / controller.vehicle.battery.voltage.value - if (newVoltageMultiplier != 0) { + if (newVoltageMultiplier > 0) { battVoltMult.value = newVoltageMultiplier } } diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.qml b/src/AutoPilotPlugins/PX4/PowerComponent.qml index 8ccdc9270..5b9108341 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -144,11 +144,11 @@ SetupPage { onClicked: { var measuredVoltageValue = parseFloat(measuredVoltage.text) - if (measuredVoltageValue == 0) { + if (measuredVoltageValue == 0 || isNaN(measuredVoltageValue)) { return } var newVoltageDivider = (measuredVoltageValue * battVoltageDivider.value) / controller.vehicle.battery.voltage.value - if (newVoltageDivider != 0) { + if (newVoltageDivider > 0) { battVoltageDivider.value = newVoltageDivider } } -- GitLab From 2d78d9b625ffbc6a9a48c0d8e0cac59fda410155 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 31 Jan 2017 20:56:05 -0500 Subject: [PATCH 250/398] Remove duplicate resources --- qgroundcontrol.qrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index fc3446c49..99607527a 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -15,7 +15,6 @@ src/AutoPilotPlugins/Common/ESP8266Component.qml src/AutoPilotPlugins/Common/ESP8266ComponentSummary.qml src/VehicleSetup/FirmwareUpgrade.qml - src/FlightDisplay/FlightDisplayView.qml src/FlightDisplay/FlightDisplayViewDummy.qml src/FlightDisplay/FlightDisplayViewUVC.qml src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml @@ -26,7 +25,6 @@ src/ui/preferences/LinkSettings.qml src/AnalyzeView/LogDownloadPage.qml src/ui/preferences/LogReplaySettings.qml - src/ui/toolbar/MainToolBar.qml src/ui/MainWindowHybrid.qml src/ui/MainWindowInner.qml src/ui/MainWindowNative.qml -- GitLab From a13ad01b0a285a593949ea0246bbd83a066f44aa Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 31 Jan 2017 21:19:45 -0500 Subject: [PATCH 251/398] Add new Static fix type --- src/Vehicle/GPSFact.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/GPSFact.json b/src/Vehicle/GPSFact.json index db76da950..9b018b459 100644 --- a/src/Vehicle/GPSFact.json +++ b/src/Vehicle/GPSFact.json @@ -22,8 +22,8 @@ "name": "lock", "shortDescription": "GPS Lock", "type": "uint32", - "enumStrings": "None,None,2D Lock,3D Lock,3D DGPS Lock,3D RTK GPS Lock (float),3D RTK GPS Lock (fixed)", - "enumValues": "0,1,2,3,4,5,6", + "enumStrings": "None,None,2D Lock,3D Lock,3D DGPS Lock,3D RTK GPS Lock (float),3D RTK GPS Lock (fixed),Static (fixed)", + "enumValues": "0,1,2,3,4,5,6,7", "decimalPlaces": 0 }, { -- GitLab From 5766d7df47ba0d3a985162ccd882d29ecc8322fb Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 2 Feb 2017 20:05:15 -0500 Subject: [PATCH 252/398] Allow disabling video recording --- src/FlightDisplay/FlightDisplayView.qml | 2 +- src/FlightDisplay/VideoManager.cc | 23 ++++++++++++++---- src/FlightDisplay/VideoManager.h | 31 +++++++++++++++---------- src/VideoStreaming/VideoReceiver.cc | 11 ++++++--- src/VideoStreaming/VideoStreaming.pri | 9 +++++++ src/ui/preferences/GeneralSettings.qml | 2 +- 6 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index e46cd5da1..ec47cffb2 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -268,7 +268,7 @@ QGCView { anchors.right: _flightVideo.right height: ScreenTools.defaultFontPixelHeight * 2 width: height - visible: QGroundControl.videoManager.videoRunning + visible: QGroundControl.videoManager.videoRunning && QGroundControl.videoManager.recordingEnabled opacity: 0.75 Rectangle { diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 3eab2d874..09a7c3216 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -29,12 +29,15 @@ static const char* kVideoSourceKey = "VideoSource"; static const char* kVideoUDPPortKey = "VideoUDPPort"; static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; -static const char* kVideoSavePathKey = "VideoSavePath"; +static const char* kNoVideo = "No Video Available"; + #if defined(QGC_GST_STREAMING) +#if defined(QGC_ENABLE_VIDEORECORDING) +static const char* kVideoSavePathKey= "VideoSavePath"; +#endif static const char* kUDPStream = "UDP Video Stream"; static const char* kRTSPStream = "RTSP Video Stream"; #endif -static const char* kNoVideo = "No Video Available"; QGC_LOGGING_CATEGORY(VideoManagerLog, "VideoManagerLog") @@ -83,7 +86,9 @@ VideoManager::setToolbox(QGCToolbox *toolbox) setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL } +#if defined(QGC_ENABLE_VIDEORECORDING) setVideoSavePath(settings.value(kVideoSavePathKey, QDir::homePath()).toString()); +#endif #endif _init = true; #if defined(QGC_GST_STREAMING) @@ -192,18 +197,26 @@ VideoManager::setRtspURL(QString url) void VideoManager::setVideoSavePathByUrl(QUrl url) { +#if defined(QGC_ENABLE_VIDEORECORDING) setVideoSavePath(url.toLocalFile()); +#else + Q_UNUSED(url); +#endif } void VideoManager::setVideoSavePath(QString path) { +#if defined(QGC_ENABLE_VIDEORECORDING) _videoSavePath = path; QSettings settings; settings.setValue(kVideoSavePathKey, path); if(_videoReceiver) _videoReceiver->setVideoSavePath(_videoSavePath); emit videoSavePathChanged(); +#else + Q_UNUSED(path); +#endif } //----------------------------------------------------------------------------- @@ -277,14 +290,16 @@ void VideoManager::_updateVideo() delete _videoSurface; _videoSurface = new VideoSurface; _videoReceiver = new VideoReceiver(this); - #if defined(QGC_GST_STREAMING) +#if defined(QGC_GST_STREAMING) _videoReceiver->setVideoSink(_videoSurface->videoSink()); if(_videoSource == kUDPStream) _videoReceiver->setUri(QStringLiteral("udp://0.0.0.0:%1").arg(_udpPort)); else _videoReceiver->setUri(_rtspURL); +#if defined(QGC_ENABLE_VIDEORECORDING) _videoReceiver->setVideoSavePath(_videoSavePath); - #endif +#endif +#endif _videoReceiver->start(); } } diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index c9a5e6291..912e57715 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -30,18 +30,19 @@ public: VideoManager (QGCApplication* app); ~VideoManager (); - Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) - Q_PROPERTY(bool isGStreamer READ isGStreamer NOTIFY isGStreamerChanged) - Q_PROPERTY(QString videoSourceID READ videoSourceID NOTIFY videoSourceIDChanged) - Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) - Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) - Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) - Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) - Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) - Q_PROPERTY(QString videoSavePath READ videoSavePath NOTIFY videoSavePathChanged) - Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) - Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) - Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) + Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) + Q_PROPERTY(bool isGStreamer READ isGStreamer NOTIFY isGStreamerChanged) + Q_PROPERTY(QString videoSourceID READ videoSourceID NOTIFY videoSourceIDChanged) + Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) + Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) + Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) + Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) + Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) + Q_PROPERTY(QString videoSavePath READ videoSavePath NOTIFY videoSavePathChanged) + Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) + Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) + Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) + Q_PROPERTY(bool recordingEnabled READ recordingEnabled CONSTANT) Q_INVOKABLE void setVideoSavePathByUrl (QUrl url); @@ -61,6 +62,12 @@ public: bool uvcEnabled (); #endif +#if defined(QGC_GST_STREAMING) && defined(QGC_ENABLE_VIDEORECORDING) + bool recordingEnabled () { return true; } +#else + bool recordingEnabled () { return false; } +#endif + void setVideoSource (QString vSource); void setUdpPort (quint16 port); void setRtspURL (QString url); diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 65dc1a16c..5d6e27f31 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -346,10 +346,14 @@ void VideoReceiver::setUri(const QString & uri) _uri = uri; } -void VideoReceiver::setVideoSavePath(const QString & path) +void VideoReceiver::setVideoSavePath(const QString& path) { +#if defined(QGC_ENABLE_VIDEORECORDING) _path = path; qCDebug(VideoReceiverLog) << "New Path:" << _path; +#else + Q_UNUSED(path); +#endif } #if defined(QGC_GST_STREAMING) @@ -452,7 +456,8 @@ gboolean VideoReceiver::_onBusMessage(GstBus* bus, GstMessage* msg, gpointer dat // +--------------------------------------+ void VideoReceiver::startRecording(void) { -#if defined(QGC_GST_STREAMING) +#if defined(QGC_GST_STREAMING) && defined(QGC_ENABLE_VIDEORECORDING) + qCDebug(VideoReceiverLog) << "startRecording()"; // exit immediately if we are already recording if(_pipeline == NULL || _recording) { @@ -506,7 +511,7 @@ void VideoReceiver::startRecording(void) void VideoReceiver::stopRecording(void) { -#if defined(QGC_GST_STREAMING) +#if defined(QGC_GST_STREAMING) && defined(QGC_ENABLE_VIDEORECORDING) qCDebug(VideoReceiverLog) << "stopRecording()"; // exit immediately if we are not recording if(_pipeline == NULL || !_recording) { diff --git a/src/VideoStreaming/VideoStreaming.pri b/src/VideoStreaming/VideoStreaming.pri index 762a608f5..fb1e58206 100644 --- a/src/VideoStreaming/VideoStreaming.pri +++ b/src/VideoStreaming/VideoStreaming.pri @@ -120,6 +120,15 @@ VideoEnabled { message("Including support for video streaming") + contains (CONFIG, DISABLE_VIDEORECORDING) { + message("Skipping support for video recording (manual override from command line)") + # Otherwise the user can still disable this feature in the user_config.pri file. + } else:exists($$BASEDIR/user_config.pri):infile($$BASEDIR/user_config.pri, DEFINES, DISABLE_VIDEORECORDING) { + message("Skipping support for video recording (manual override from user_config.pri)") + } else { + DEFINES += QGC_ENABLE_VIDEORECORDING + } + DEFINES += \ QGC_GST_STREAMING \ GST_PLUGIN_BUILD_STATIC \ diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index be64cafd5..ed39910a2 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -535,7 +535,7 @@ QGCView { } Row { spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.videoManager.isGStreamer + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.recordingEnabled QGCLabel { anchors.baseline: pathField.baseline text: qsTr("Save Path:") -- GitLab From 534028a91106dcbc0290328715df40f6e41b1198 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 2 Feb 2017 20:09:08 -0500 Subject: [PATCH 253/398] Make buttons and comboboxes square again. --- src/QmlControls/QGCButton.qml | 2 +- src/QmlControls/QGCComboBox.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QmlControls/QGCButton.qml b/src/QmlControls/QGCButton.qml index 5cff7f1df..5238f025a 100644 --- a/src/QmlControls/QGCButton.qml +++ b/src/QmlControls/QGCButton.qml @@ -66,7 +66,7 @@ Button { anchors.fill: parent border.width: _showBorder ? 1: 0 border.color: _qgcPal.buttonText - radius: 3 + //radius: 3 color: _showHighlight ? control._qgcPal.buttonHighlight : (primary ? control._qgcPal.primaryButton : control._qgcPal.button) diff --git a/src/QmlControls/QGCComboBox.qml b/src/QmlControls/QGCComboBox.qml index 4e2150add..116ffb0e6 100644 --- a/src/QmlControls/QGCComboBox.qml +++ b/src/QmlControls/QGCComboBox.qml @@ -24,7 +24,7 @@ ComboBox { Rectangle { anchors.fill: parent color: _showHighlight ? control._qgcPal.buttonHighlight : control._qgcPal.button - radius: 3 + //radius: 3 border.width: _showBorder ? 1: 0 border.color: control._qgcPal.buttonText } -- GitLab From becb0a6f91a8759873a529067d5ff4876a5c518f Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 3 Feb 2017 09:18:48 -0500 Subject: [PATCH 254/398] Fix wrong QColor argument encoding. --- src/QGCPalette.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QGCPalette.cc b/src/QGCPalette.cc index 6416801bc..2c6a304cf 100644 --- a/src/QGCPalette.cc +++ b/src/QGCPalette.cc @@ -42,7 +42,7 @@ QColor QGCPalette::_text[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { QColor QGCPalette::_warningText[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { { QColor("#cc0808"), QColor("#cc0808") }, - { QColor("0xed, 0xd4, 0x69"), QColor("0xed, 0xd4, 0x69") } + { QColor("#fd5d13"), QColor("#fd5d13") } }; QColor QGCPalette::_button[QGCPalette::_cThemes][QGCPalette::_cColorGroups] = { -- GitLab From 898f386a6a26eef63f4e63ed76225040a7468627 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 3 Feb 2017 10:14:03 -0500 Subject: [PATCH 255/398] Work around using the Clear button while mobile keyboard is up. --- src/QmlControls/ParameterEditor.qml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index 45f883553..61621b69e 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -56,6 +56,17 @@ QGCView { anchors.right: parent.right spacing: ScreenTools.defaultFontPixelWidth + Timer { + id: clearTimer + interval: 100; + running: false; + repeat: false + onTriggered: { + searchText.text = "" + controller.searchText = "" + } + } + QGCLabel { anchors.baseline: clearButton.baseline text: qsTr("Search:") @@ -71,7 +82,12 @@ QGCView { QGCButton { id: clearButton text: qsTr("Clear") - onClicked: searchText.text = "" + onClicked: { + if(ScreenTools.isMobile) { + Qt.inputMethod.hide(); + } + clearTimer.start() + } } } // Row - Header -- GitLab From f30d265c9e33e545c708c65e6143234a29b8260c Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 5 Feb 2017 22:52:40 -0500 Subject: [PATCH 256/398] Tidying up Mission Settings and Survey Settings --- src/MissionEditor/MissionSettingsEditor.qml | 73 +++-- src/MissionEditor/SurveyItemEditor.qml | 294 ++++++++++++-------- 2 files changed, 221 insertions(+), 146 deletions(-) diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index 65311e3ad..a7805187f 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -38,6 +38,7 @@ Rectangle { property bool _showCruiseSpeed: !_missionVehicle.multiRotor property bool _showHoverSpeed: _missionVehicle.multiRotor || missionController.vehicle.vtol property bool _multipleFirmware: QGroundControl.supportedFirmwareCount > 2 + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 16 readonly property string _firmwareLabel: qsTr("Firmware:") readonly property string _vehicleLabel: qsTr("Vehicle:") @@ -53,6 +54,7 @@ Rectangle { QGCLabel { text: qsTr("Planned Home Position") + color: qgcPal.buttonHighlight } Rectangle { @@ -64,31 +66,21 @@ Rectangle { Repeater { model: missionItem.textFieldFacts - - Item { - width: valuesColumn.width - height: textField.height - - QGCLabel { - id: textFieldLabel - anchors.baseline: textField.baseline - text: object.name - } - + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { text: object.name; Layout.fillWidth: true } FactTextField { - id: textField - anchors.right: parent.right - width: _editFieldWidth - showUnits: true - fact: object - visible: !_root.readOnly + Layout.preferredWidth: _fieldWidth + showUnits: true + fact: object + visible: !_root.readOnly } - FactLabel { - anchors.baseline: textFieldLabel.baseline - anchors.right: parent.right - fact: object - visible: _root.readOnly + Layout.preferredWidth: _fieldWidth + fact: object + visible: _root.readOnly } } } @@ -102,7 +94,8 @@ Rectangle { } QGCLabel { - text: qsTr("Vehicle Info:") + text: qsTr("Vehicle Info") + color: qgcPal.buttonHighlight visible: _multipleFirmware } @@ -125,72 +118,93 @@ Rectangle { QGCLabel { text: _firmwareLabel visible: _showOfflineEditingCombos + Layout.fillWidth: true } FactComboBox { - Layout.fillWidth: true fact: QGroundControl.offlineEditingFirmwareType indexModel: false visible: _showOfflineEditingCombos + Layout.preferredWidth: _fieldWidth } QGCLabel { text: _firmwareLabel visible: !_showOfflineEditingCombos + Layout.fillWidth: true } QGCLabel { text: _missionVehicle.firmwareTypeString visible: !_showOfflineEditingCombos + Layout.preferredWidth: _fieldWidth } QGCLabel { text: _vehicleLabel visible: _showOfflineEditingCombos + Layout.fillWidth: true } FactComboBox { id: offlineVehicleCombo - Layout.fillWidth: true fact: QGroundControl.offlineEditingVehicleType indexModel: false visible: _showOfflineEditingCombos + Layout.preferredWidth: _fieldWidth } QGCLabel { text: _vehicleLabel visible: !_showOfflineEditingCombos + Layout.fillWidth: true } QGCLabel { text: _missionVehicle.vehicleTypeString visible: !_showOfflineEditingCombos + Layout.preferredWidth: _fieldWidth } + QGCLabel { Layout.row: 2 text: qsTr("Cruise speed:") visible: _showCruiseSpeed + Layout.fillWidth: true } FactTextField { - Layout.fillWidth: true fact: QGroundControl.offlineEditingCruiseSpeed visible: _showCruiseSpeed + Layout.preferredWidth: _fieldWidth } QGCLabel { Layout.row: 3 text: qsTr("Hover speed:") visible: _showHoverSpeed + Layout.fillWidth: true } FactTextField { - Layout.fillWidth: true fact: QGroundControl.offlineEditingHoverSpeed visible: _showHoverSpeed + Layout.preferredWidth: _fieldWidth } } // GridLayout + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + visible: !_multipleFirmware + QGCLabel { text: qsTr("Hover speed:"); Layout.fillWidth: true } + FactTextField { + Layout.preferredWidth: _fieldWidth + fact: QGroundControl.offlineEditingHoverSpeed + } + } + QGCLabel { width: parent.width wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - visible: _multipleFirmware - text: qsTr("Speeds are only for time calculations. Actual vehicle will not be affected.") + text: qsTr("Speeds are only used for time calculations. Actual vehicle speed will not be affected.") + horizontalAlignment: Text.AlignHCenter } Rectangle { @@ -201,6 +215,7 @@ Rectangle { } QGCButton { + width: parent.width * 0.9 text: qsTr("Set Home To Map Center") onClicked: editorRoot.moveHomeToMapCenter() anchors.horizontalCenter: parent.horizontalCenter diff --git a/src/MissionEditor/SurveyItemEditor.qml b/src/MissionEditor/SurveyItemEditor.qml index b5fb661e2..7d442e427 100644 --- a/src/MissionEditor/SurveyItemEditor.qml +++ b/src/MissionEditor/SurveyItemEditor.qml @@ -22,8 +22,9 @@ Rectangle { //property real availableWidth ///< Width for control //property var missionItem ///< Mission Item for editor - property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _margin: ScreenTools.defaultFontPixelWidth * 0.25 property int _cameraIndex: 1 + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 readonly property int _gridTypeManual: 0 readonly property int _gridTypeCustomCamera: 1 @@ -33,11 +34,11 @@ Rectangle { id: cameraModelList Component.onCompleted: { - cameraModelList.setProperty(_gridTypeCustomCamera, "sensorWidth", missionItem.cameraSensorWidth.rawValue) - cameraModelList.setProperty(_gridTypeCustomCamera, "sensorHeight", missionItem.cameraSensorHeight.rawValue) - cameraModelList.setProperty(_gridTypeCustomCamera, "imageWidth", missionItem.cameraResolutionWidth.rawValue) - cameraModelList.setProperty(_gridTypeCustomCamera, "imageHeight", missionItem.cameraResolutionHeight.rawValue) - cameraModelList.setProperty(_gridTypeCustomCamera, "focalLength", missionItem.cameraFocalLength.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "sensorWidth", missionItem.cameraSensorWidth.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "sensorHeight", missionItem.cameraSensorHeight.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "imageWidth", missionItem.cameraResolutionWidth.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "imageHeight", missionItem.cameraResolutionHeight.rawValue) + cameraModelList.setProperty(_gridTypeCustomCamera, "focalLength", missionItem.cameraFocalLength.rawValue) } ListElement { @@ -46,6 +47,14 @@ Rectangle { ListElement { text: qsTr("Custom Camera Grid") } + ListElement { + text: qsTr("Typhoon H CGO3+") + sensorWidth: 6.264 + sensorHeight: 4.698 + imageWidth: 4000 + imageHeight: 3000 + focalLength: 14 + } ListElement { text: qsTr("Sony ILCE-QX1") //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications sensorWidth: 23.2 //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications @@ -89,16 +98,16 @@ Rectangle { } function recalcFromCameraValues() { - var focalLength = missionItem.cameraFocalLength.rawValue - var sensorWidth = missionItem.cameraSensorWidth.rawValue - var sensorHeight = missionItem.cameraSensorHeight.rawValue - var imageWidth = missionItem.cameraResolutionWidth.rawValue - var imageHeight = missionItem.cameraResolutionHeight.rawValue + var focalLength = missionItem.cameraFocalLength.rawValue + var sensorWidth = missionItem.cameraSensorWidth.rawValue + var sensorHeight = missionItem.cameraSensorHeight.rawValue + var imageWidth = missionItem.cameraResolutionWidth.rawValue + var imageHeight = missionItem.cameraResolutionHeight.rawValue - var altitude = missionItem.gridAltitude.rawValue - var groundResolution = missionItem.groundResolution.rawValue - var frontalOverlap = missionItem.frontalOverlap.rawValue - var sideOverlap = missionItem.sideOverlap.rawValue + var altitude = missionItem.gridAltitude.rawValue + var groundResolution= missionItem.groundResolution.rawValue + var frontalOverlap = missionItem.frontalOverlap.rawValue + var sideOverlap = missionItem.sideOverlap.rawValue if (focalLength <= 0 || sensorWidth <= 0 || sensorHeight <= 0 || imageWidth <= 0 || imageHeight <= 0 || groundResolution <= 0) { return @@ -110,17 +119,17 @@ Rectangle { var cameraTriggerDistance if (missionItem.fixedValueIsAltitude) { - groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength) + groundResolution = (altitude * sensorWidth * 100) / (imageWidth * focalLength) } else { altitude = (imageWidth * groundResolution * focalLength) / (sensorWidth * 100) } if (cameraOrientationLandscape.checked) { - imageSizeSideGround = (imageWidth * groundResolution) / 100 + imageSizeSideGround = (imageWidth * groundResolution) / 100 imageSizeFrontGround = (imageHeight * groundResolution) / 100 } else { - imageSizeSideGround = (imageHeight * groundResolution) / 100 - imageSizeFrontGround = (imageWidth * groundResolution) / 100 + imageSizeSideGround = (imageHeight * groundResolution) / 100 + imageSizeFrontGround = (imageWidth * groundResolution) / 100 } gridSpacing = imageSizeSideGround * ( (100-sideOverlap) / 100 ) @@ -238,25 +247,6 @@ Rectangle { anchors.right: parent.right spacing: _margin - QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.smallFontPointSize - text: gridTypeCombo.currentIndex == 0 ? - qsTr("Create a flight path which covers a polygonal area by specifying all grid parameters.") : - qsTr("Create a flight path which fully covers a polygonal area using camera specifications.") - } - - QGCLabel { text: qsTr("Camera:") } - - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - height: 1 - color: qgcPal.text - } - QGCComboBox { id: gridTypeCombo anchors.left: parent.left @@ -285,17 +275,27 @@ Rectangle { missionItem.manualGrid = false missionItem.camera = gridTypeCombo.textAt(index) _noCameraValueRecalc = true - missionItem.cameraSensorWidth.rawValue = cameraModelList.get(index).sensorWidth - missionItem.cameraSensorHeight.rawValue = cameraModelList.get(index).sensorHeight - missionItem.cameraResolutionWidth.rawValue = cameraModelList.get(index).imageWidth + missionItem.cameraSensorWidth.rawValue = cameraModelList.get(index).sensorWidth + missionItem.cameraSensorHeight.rawValue = cameraModelList.get(index).sensorHeight + missionItem.cameraResolutionWidth.rawValue = cameraModelList.get(index).imageWidth missionItem.cameraResolutionHeight.rawValue = cameraModelList.get(index).imageHeight - missionItem.cameraFocalLength.rawValue = cameraModelList.get(index).focalLength + missionItem.cameraFocalLength.rawValue = cameraModelList.get(index).focalLength _noCameraValueRecalc = false recalcFromCameraValues() } } } + QGCLabel { text: qsTr("Camera"); color: qgcPal.buttonHighlight; visible: gridTypeCombo.currentIndex !== _gridTypeManual} + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + visible: gridTypeCombo.currentIndex !== _gridTypeManual + } + // Camera based grid ui Column { anchors.left: parent.left @@ -305,6 +305,7 @@ Rectangle { Row { spacing: _margin + anchors.horizontalCenter: parent.horizontalCenter QGCRadioButton { id: cameraOrientationLandscape @@ -322,82 +323,104 @@ Rectangle { } Column { + id: custCameraCol anchors.left: parent.left anchors.right: parent.right spacing: _margin - visible: gridTypeCombo.currentIndex == _gridTypeCustomCamera - - GridLayout { - columns: 3 - columnSpacing: _margin - rowSpacing: _margin - - property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10 - - QGCLabel { } - QGCLabel { text: qsTr("Width") } - QGCLabel { text: qsTr("Height") } + visible: gridTypeCombo.currentIndex === _gridTypeCustomCamera + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + Item { Layout.fillWidth: true } + QGCLabel { + Layout.preferredWidth: _root._fieldWidth + text: qsTr("Width") + } + QGCLabel { + Layout.preferredWidth: _root._fieldWidth + text: qsTr("Height") + } + } - QGCLabel { text: qsTr("Sensor:") } + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { text: qsTr("Sensor:"); Layout.fillWidth: true } FactTextField { - Layout.preferredWidth: parent._fieldWidth + Layout.preferredWidth: _root._fieldWidth fact: missionItem.cameraSensorWidth } FactTextField { - Layout.preferredWidth: parent._fieldWidth + Layout.preferredWidth: _root._fieldWidth fact: missionItem.cameraSensorHeight } + } - QGCLabel { text: qsTr("Image:") } + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { text: qsTr("Image:"); Layout.fillWidth: true } FactTextField { - Layout.preferredWidth: parent._fieldWidth + Layout.preferredWidth: _root._fieldWidth fact: missionItem.cameraResolutionWidth } FactTextField { - Layout.preferredWidth: parent._fieldWidth + Layout.preferredWidth: _root._fieldWidth fact: missionItem.cameraResolutionHeight } } - FactTextFieldRow { - spacing: _margin - fact: missionItem.cameraFocalLength + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { + text: missionItem.cameraFocalLength.name + ":" + Layout.fillWidth: true + } + FactTextField { + Layout.preferredWidth: _root._fieldWidth + fact: missionItem.cameraFocalLength + } } - } // Column - custom camera - QGCLabel { text: qsTr("Image Overlap") } + } // Column - custom camera - Row { + RowLayout { + anchors.left: parent.left + anchors.right: parent.right spacing: _margin - - Item { - width: ScreenTools.defaultFontPixelWidth * 2 - height: 1 + Item { Layout.fillWidth: true } + QGCLabel { + Layout.preferredWidth: _root._fieldWidth + text: qsTr("Frontal") } - QGCLabel { - anchors.baseline: frontalOverlapField.baseline - text: qsTr("Frontal:") + Layout.preferredWidth: _root._fieldWidth + text: qsTr("Side") } + } + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { text: qsTr("Overlap:"); Layout.fillWidth: true } FactTextField { - id: frontalOverlapField - width: ScreenTools.defaultFontPixelWidth * 7 - fact: missionItem.frontalOverlap - } - - QGCLabel { - anchors.baseline: frontalOverlapField.baseline - text: qsTr("Side:") + Layout.preferredWidth: _root._fieldWidth + fact: missionItem.frontalOverlap } - FactTextField { - width: frontalOverlapField.width - fact: missionItem.sideOverlap + Layout.preferredWidth: _root._fieldWidth + fact: missionItem.sideOverlap } } - QGCLabel { text: qsTr("Grid:") } + QGCLabel { text: qsTr("Grid"); color: qgcPal.buttonHighlight;} Rectangle { anchors.left: parent.left @@ -406,12 +429,36 @@ Rectangle { color: qgcPal.text } - FactTextFieldGrid { + RowLayout { anchors.left: parent.left anchors.right: parent.right - columnSpacing: _margin - rowSpacing: _margin - factList: [ missionItem.gridAngle, missionItem.turnaroundDist ] + spacing: _margin + QGCLabel { + text: missionItem.gridAngle.name + ":" + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + } + FactTextField { + fact: missionItem.gridAngle + anchors.verticalCenter: parent.verticalCenter + Layout.preferredWidth: _root._fieldWidth + } + } + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + QGCLabel { + text: missionItem.turnaroundDist.name + ":" + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter + } + FactTextField { + fact: missionItem.turnaroundDist + anchors.verticalCenter: parent.verticalCenter + Layout.preferredWidth: _root._fieldWidth + } } QGCLabel { @@ -428,19 +475,20 @@ Rectangle { spacing: _margin QGCRadioButton { - id: fixedAltitudeRadio - anchors.baseline: gridAltitudeField.baseline - text: qsTr("Altitude:") - checked: missionItem.fixedValueIsAltitude - exclusiveGroup: fixedValueGroup - onClicked: missionItem.fixedValueIsAltitude = true + id: fixedAltitudeRadio + text: qsTr("Altitude:") + checked: missionItem.fixedValueIsAltitude + exclusiveGroup: fixedValueGroup + onClicked: missionItem.fixedValueIsAltitude = true + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter } FactTextField { - id: gridAltitudeField - Layout.fillWidth: true - fact: missionItem.gridAltitude - enabled: fixedAltitudeRadio.checked + fact: missionItem.gridAltitude + enabled: fixedAltitudeRadio.checked + Layout.preferredWidth: _root._fieldWidth + anchors.verticalCenter: parent.verticalCenter } } @@ -450,19 +498,20 @@ Rectangle { spacing: _margin QGCRadioButton { - id: fixedGroundResolutionRadio - anchors.baseline: groundResolutionField.baseline - text: qsTr("Ground res:") - checked: !missionItem.fixedValueIsAltitude - exclusiveGroup: fixedValueGroup - onClicked: missionItem.fixedValueIsAltitude = false + id: fixedGroundResolutionRadio + text: qsTr("Ground res:") + checked: !missionItem.fixedValueIsAltitude + exclusiveGroup: fixedValueGroup + onClicked: missionItem.fixedValueIsAltitude = false + Layout.fillWidth: true + anchors.verticalCenter: parent.verticalCenter } FactTextField { - id: groundResolutionField - Layout.fillWidth: true - fact: missionItem.groundResolution - enabled: fixedGroundResolutionRadio.checked + fact: missionItem.groundResolution + enabled: fixedGroundResolutionRadio.checked + Layout.preferredWidth: _root._fieldWidth + anchors.verticalCenter: parent.verticalCenter } } } @@ -474,7 +523,7 @@ Rectangle { spacing: _margin visible: gridTypeCombo.currentIndex == _gridTypeManual - QGCLabel { text: qsTr("Grid:") } + QGCLabel { text: qsTr("Grid"); color: qgcPal.buttonHighlight;} Rectangle { anchors.left: parent.left @@ -486,7 +535,7 @@ Rectangle { FactTextFieldGrid { anchors.left: parent.left anchors.right: parent.right - columnSpacing: _margin + columnSpacing: _margin * 10 rowSpacing: _margin factList: [ missionItem.gridAngle, missionItem.gridSpacing, missionItem.gridAltitude, missionItem.turnaroundDist ] } @@ -498,7 +547,7 @@ Rectangle { onClicked: missionItem.gridAltitudeRelative = checked } - QGCLabel { text: qsTr("Camera:") } + QGCLabel { text: qsTr("Camera"); color: qgcPal.buttonHighlight;} Rectangle { anchors.left: parent.left @@ -529,7 +578,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Polygon:") } + QGCLabel { text: qsTr("Polygon"); color: qgcPal.buttonHighlight;} Rectangle { anchors.left: parent.left @@ -540,8 +589,10 @@ Rectangle { Row { spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter QGCButton { + width: _root.width * 0.45 text: editorMap.polygonDraw.drawingPolygon ? qsTr("Finish Draw") : qsTr("Draw") visible: !editorMap.polygonDraw.adjustingPolygon enabled: ((editorMap.polygonDraw.drawingPolygon && editorMap.polygonDraw.polygonReady) || !editorMap.polygonDraw.drawingPolygon) @@ -556,6 +607,7 @@ Rectangle { } QGCButton { + width: _root.width * 0.4 text: editorMap.polygonDraw.adjustingPolygon ? qsTr("Finish Adjust") : qsTr("Adjust") visible: missionItem.polygonPath.length > 0 && !editorMap.polygonDraw.drawingPolygon @@ -569,7 +621,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Statistics:") } + QGCLabel { text: qsTr("Statistics"); color: qgcPal.buttonHighlight;} Rectangle { anchors.left: parent.left @@ -585,11 +637,19 @@ Rectangle { QGCLabel { text: qsTr("Survey area:") } QGCLabel { text: QGroundControl.squareMetersToAppSettingsAreaUnits(missionItem.coveredArea).toFixed(2) + " " + QGroundControl.appSettingsAreaUnitsString } - QGCLabel { text: qsTr("# shots:") } + QGCLabel { text: qsTr("Photo count:") } QGCLabel { text: missionItem.cameraShots } - QGCLabel { text: qsTr("Shot interval:") } - QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs")} + QGCLabel { text: qsTr("Photo interval:") } + QGCLabel { + text: { + var timeVal = missionItem.timeBetweenShots + if(!isFinite(timeVal) || missionItem.cameraShots === 0) { + return qsTr("N/A") + } + return timeVal.toFixed(1) + " " + qsTr("secs") + } + } } } } -- GitLab From cc18085661146bb815e7f32aed5bc0fcaa467b2f Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Mon, 6 Feb 2017 12:04:26 -0500 Subject: [PATCH 257/398] Joystick: Increment calibration key to force recalibration --- src/Joystick/Joystick.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index 2d08bf6c1..20093ab2c 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -19,7 +19,7 @@ QGC_LOGGING_CATEGORY(JoystickLog, "JoystickLog") QGC_LOGGING_CATEGORY(JoystickValuesLog, "JoystickValuesLog") const char* Joystick::_settingsGroup = "Joysticks"; -const char* Joystick::_calibratedSettingsKey = "Calibrated1"; // Increment number to force recalibration +const char* Joystick::_calibratedSettingsKey = "Calibrated2"; // Increment number to force recalibration const char* Joystick::_buttonActionSettingsKey = "ButtonActionName%1"; const char* Joystick::_throttleModeSettingsKey = "ThrottleMode"; const char* Joystick::_exponentialSettingsKey = "Exponential"; -- GitLab From f38008a136ec073d7d0567d85225d2f0ca35542e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 6 Feb 2017 18:06:24 -0600 Subject: [PATCH 258/398] Don't start Vehicles for things which aren't Vehicles --- src/Vehicle/MultiVehicleManager.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index eae33e12b..6d363948b 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -70,11 +70,22 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType) { - if (_ignoreVehicleIds.contains(vehicleId) || getVehicleById(vehicleId) - || vehicleId == 0) { + if (_ignoreVehicleIds.contains(vehicleId) || getVehicleById(vehicleId) || vehicleId == 0) { return; } + switch (vehicleType) { + case MAV_TYPE_GCS: + case MAV_TYPE_ONBOARD_CONTROLLER: + case MAV_TYPE_GIMBAL: + case MAV_TYPE_ADSB: + // These are not vehicles, so don't create a vehicle for them + return; + default: + // All other MAV_TYPEs create vehicles + break; + } + qCDebug(MultiVehicleManagerLog()) << "Adding new vehicle link:vehicleId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType " << link->getName() << vehicleId -- GitLab From 0c47dfc137221f17a5800b674fdc94a225c92570 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Mon, 6 Feb 2017 11:57:29 +0100 Subject: [PATCH 259/398] Fix log file ending in PX4 log downloader for ulog --- src/AnalyzeView/LogDownloadController.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/AnalyzeView/LogDownloadController.cc b/src/AnalyzeView/LogDownloadController.cc index 38cf2c4b8..a63f3ba88 100644 --- a/src/AnalyzeView/LogDownloadController.cc +++ b/src/AnalyzeView/LogDownloadController.cc @@ -16,6 +16,7 @@ #include "QGCApplication.h" #include "QGCToolbox.h" #include "QGCMapEngine.h" +#include "ParameterManager.h" #include "Vehicle.h" #include "MainWindow.h" @@ -583,8 +584,14 @@ LogDownloadController::_prepareLogDownload() } _downloadData = new LogDownloadData(entry); _downloadData->filename = QString("log_") + QString::number(entry->id()) + "_" + ftime; - if(_vehicle->firmwareType() == MAV_AUTOPILOT_PX4) { - _downloadData->filename += ".px4log"; + if (_vehicle->firmwareType() == MAV_AUTOPILOT_PX4) { + + // This is a stopgap and should be removed once log file types are properly supported by the log download protocol + if (_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, "SYS_LOGGER")->rawValue().toInt() == 0) { + _downloadData->filename += ".px4log"; + } else { + _downloadData->filename += ".ulg"; + } } else { _downloadData->filename += ".bin"; } -- GitLab From b07760406e3700d5d6309da7a0b6609886d32f0d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 7 Feb 2017 13:45:43 -0500 Subject: [PATCH 260/398] Cleaning up some unused icons. --- qgcresources.qrc | 6 ------ src/FlightMap/Images/ArrowHead.svg | 9 --------- src/FlightMap/Images/airplaneOpaque.svg | 10 ---------- src/FlightMap/Images/airplaneOutline.svg | 10 ---------- src/FlightMap/Images/buttonHome.svg | 11 ----------- src/FlightMap/Images/buttonMore.svg | 15 --------------- .../Images/compassInstrumentAirplane.svg | 9 --------- 7 files changed, 70 deletions(-) delete mode 100644 src/FlightMap/Images/ArrowHead.svg delete mode 100644 src/FlightMap/Images/airplaneOpaque.svg delete mode 100644 src/FlightMap/Images/airplaneOutline.svg delete mode 100644 src/FlightMap/Images/buttonHome.svg delete mode 100644 src/FlightMap/Images/buttonMore.svg delete mode 100644 src/FlightMap/Images/compassInstrumentAirplane.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 1c4005bf1..bc66e1716 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -100,9 +100,6 @@ src/FlightMap/Images/attitudeDial.svg src/FlightMap/Images/attitudeInstrument.svg src/FlightMap/Images/attitudePointer.svg - src/FlightMap/Images/buttonHome.svg - src/FlightMap/Images/buttonMore.svg - src/FlightMap/Images/compassInstrumentAirplane.svg src/FlightMap/Images/compassInstrumentArrow.svg src/FlightMap/Images/compassInstrumentDial.svg src/FlightMap/Images/crossHair.svg @@ -113,13 +110,10 @@ src/FlightMap/Images/scale_end.png src/FlightMap/Images/scaleLight.png src/FlightMap/Images/scale_endLight.png - src/FlightMap/Images/airplaneOutline.svg - src/FlightMap/Images/airplaneOpaque.svg src/FlightMap/Images/vehicleArrowOutline.svg src/FlightMap/Images/vehicleArrowOpaque.svg src/FlightMap/Images/ZoomPlus.svg src/FlightMap/Images/ZoomMinus.svg - src/FlightMap/Images/ArrowHead.svg src/FlightMap/Images/Help.svg src/FlightMap/Images/HelpBlack.svg src/FlightMap/Images/MapAddMission.svg diff --git a/src/FlightMap/Images/ArrowHead.svg b/src/FlightMap/Images/ArrowHead.svg deleted file mode 100644 index fd70dad19..000000000 --- a/src/FlightMap/Images/ArrowHead.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/src/FlightMap/Images/airplaneOpaque.svg b/src/FlightMap/Images/airplaneOpaque.svg deleted file mode 100644 index c9ac663f1..000000000 --- a/src/FlightMap/Images/airplaneOpaque.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - -Layer 1 - - diff --git a/src/FlightMap/Images/airplaneOutline.svg b/src/FlightMap/Images/airplaneOutline.svg deleted file mode 100644 index 686381d43..000000000 --- a/src/FlightMap/Images/airplaneOutline.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - -Layer 1 - - diff --git a/src/FlightMap/Images/buttonHome.svg b/src/FlightMap/Images/buttonHome.svg deleted file mode 100644 index 22012e566..000000000 --- a/src/FlightMap/Images/buttonHome.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/src/FlightMap/Images/buttonMore.svg b/src/FlightMap/Images/buttonMore.svg deleted file mode 100644 index f19e27ff1..000000000 --- a/src/FlightMap/Images/buttonMore.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/FlightMap/Images/compassInstrumentAirplane.svg b/src/FlightMap/Images/compassInstrumentAirplane.svg deleted file mode 100644 index 5fdc72864..000000000 --- a/src/FlightMap/Images/compassInstrumentAirplane.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - -- GitLab From 240b37fe11023ada539fad0186e259477956591c Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 7 Feb 2017 15:07:23 -0500 Subject: [PATCH 261/398] Allow FirmwarePlugin to provide vehicle icons. --- src/FirmwarePlugin/FirmwarePlugin.cc | 18 ++++++++++++++ src/FirmwarePlugin/FirmwarePlugin.h | 9 +++++++ .../FlightDisplayViewWidgets.qml | 2 -- src/FlightDisplay/MultiVehicleList.qml | 2 +- src/FlightMap/MapItems/VehicleMapItem.qml | 2 +- src/FlightMap/Widgets/QGCCompassWidget.qml | 15 ++++++------ src/FlightMap/Widgets/QGCInstrumentWidget.qml | 2 +- .../Widgets/QGCInstrumentWidgetAlternate.qml | 3 ++- src/Vehicle/Vehicle.cc | 24 +++++++++++++++++++ src/Vehicle/Vehicle.h | 7 ++++++ 10 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index 42aa28387..fb6d02816 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -302,3 +302,21 @@ QString FirmwarePlugin::takeControlFlightMode(void) { return QString(); } + +QString FirmwarePlugin::vehicleImageOpaque(const Vehicle* vehicle) const +{ + Q_UNUSED(vehicle); + return QStringLiteral("/qmlimages/vehicleArrowOpaque.svg"); +} + +QString FirmwarePlugin::vehicleImageOutline(const Vehicle* vehicle) const +{ + Q_UNUSED(vehicle); + return QStringLiteral("/qmlimages/vehicleArrowOutline.svg"); +} + +QString FirmwarePlugin::vehicleImageCompass(const Vehicle* vehicle) const +{ + Q_UNUSED(vehicle); + return QStringLiteral("/qmlimages/compassInstrumentArrow.svg"); +} diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 3e8ededb9..0c0eb0f77 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -242,6 +242,15 @@ public: /// Return the resource file which contains the brand image for the vehicle. virtual QString brandImage(const Vehicle* vehicle) const { Q_UNUSED(vehicle) return QString(); } + /// Return the resource file which contains the vehicle icon used in the flight view when the view is dark (Satellite for instance) + virtual QString vehicleImageOpaque(const Vehicle* vehicle) const; + + /// Return the resource file which contains the vehicle icon used in the flight view when the view is light (Map for instance) + virtual QString vehicleImageOutline(const Vehicle* vehicle) const; + + /// Return the resource file which contains the vehicle icon used in the compass + virtual QString vehicleImageCompass(const Vehicle* vehicle) const; + // FIXME: Hack workaround for non pluginize FollowMe support static const char* px4FollowMeFlightMode; }; diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 9e564002b..9743c72f3 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -81,7 +81,6 @@ Item { anchors.verticalCenter: parent.verticalCenter visible: !_useAlternateInstruments size: getGadgetWidth() - active: _activeVehicle != null heading: _heading rollAngle: _roll pitchAngle: _pitch @@ -100,7 +99,6 @@ Item { anchors.right: altitudeSlider.visible ? altitudeSlider.left : parent.right visible: _useAlternateInstruments width: ScreenTools.isTinyScreen ? getGadgetWidth() * 1.5 : getGadgetWidth() - active: _activeVehicle != null heading: _heading rollAngle: _roll pitchAngle: _pitch diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index c59f876b8..9278056d6 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -52,8 +52,8 @@ QGCListView { QGCCompassWidget { size: _widgetHeight - active: true heading: _vehicle.heading.rawValue + vehicle: _vehicle } QGCAttitudeWidget { diff --git a/src/FlightMap/MapItems/VehicleMapItem.qml b/src/FlightMap/MapItems/VehicleMapItem.qml index 8940e563d..fa1988a2e 100644 --- a/src/FlightMap/MapItems/VehicleMapItem.qml +++ b/src/FlightMap/MapItems/VehicleMapItem.qml @@ -30,7 +30,7 @@ MapQuickItem { sourceItem: Image { id: vehicleIcon - source: isSatellite ? "/qmlimages/vehicleArrowOpaque.svg" : "/qmlimages/vehicleArrowOutline.svg" + source: isSatellite ? vehicle.vehicleImageOpaque : vehicle.vehicleImageOutline mipmap: true width: size sourceSize.width: size diff --git a/src/FlightMap/Widgets/QGCCompassWidget.qml b/src/FlightMap/Widgets/QGCCompassWidget.qml index 2a3aff1e5..b941fbf91 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -17,15 +17,16 @@ import QtQuick 2.4 import QtGraphicalEffects 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 Item { id: root - property bool active: false ///< true: actively connected to data provider, false: show inactive control - property real heading: 0 property real size: _defaultSize + property real heading: 0 + property var vehicle: null property real _defaultSize: ScreenTools.defaultFontPixelHeight * (10) property real _sizeRatio: ScreenTools.isTinyScreen ? (size / _defaultSize) * 0.5 : size / _defaultSize @@ -48,7 +49,7 @@ Item { Image { id: pointer - source: "/qmlimages/compassInstrumentArrow.svg" + source: vehicle ? vehicle.vehicleImageCompass : "" mipmap: true width: size * 0.75 sourceSize.width: width @@ -78,8 +79,8 @@ Item { color: Qt.rgba(0,0,0,0.65) QGCLabel { - text: active ? heading.toFixed(0) : qsTr("OFF") - font.family: active ? ScreenTools.demiboldFontFamily : ScreenTools.normalFontFamily + text: vehicle ? heading.toFixed(0) : qsTr("OFF") + font.family: vehicle ? ScreenTools.demiboldFontFamily : ScreenTools.normalFontFamily font.pointSize: _fontSize < 8 ? 8 : _fontSize; color: "white" anchors.centerIn: parent diff --git a/src/FlightMap/Widgets/QGCInstrumentWidget.qml b/src/FlightMap/Widgets/QGCInstrumentWidget.qml index a5352549c..879f740c4 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidget.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidget.qml @@ -150,8 +150,8 @@ Item { QGCCompassWidget { id: compass size: parent.width * 0.95 - active: instrumentPanel.active visible: _showCompass + vehicle: _activeVehicle anchors.horizontalCenter: parent.horizontalCenter } } diff --git a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml index ad11cf65b..2ce355663 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml @@ -10,6 +10,7 @@ import QtQuick 2.4 +import QGroundControl 1.0 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.FactSystem 1.0 @@ -68,7 +69,7 @@ Rectangle { anchors.leftMargin: _spacing anchors.left: attitude.right size: _innerRadius * 2 - active: root.active + vehicle: QGroundControl.multiVehicleManager.activeVehicle anchors.verticalCenter: parent.verticalCenter } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 10c7deee4..4876910d6 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2203,6 +2203,30 @@ QString Vehicle::takeControlFlightMode(void) const return _firmwarePlugin->takeControlFlightMode(); } +QString Vehicle::vehicleImageOpaque() const +{ + if(_firmwarePlugin) + return _firmwarePlugin->vehicleImageOpaque(this); + else + return QString(); +} + +QString Vehicle::vehicleImageOutline() const +{ + if(_firmwarePlugin) + return _firmwarePlugin->vehicleImageOutline(this); + else + return QString(); +} + +QString Vehicle::vehicleImageCompass() const +{ + if(_firmwarePlugin) + return _firmwarePlugin->vehicleImageCompass(this); + else + return QString(); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index c9d2c0114..a765f876a 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -264,6 +264,9 @@ public: Q_PROPERTY(QString takeControlFlightMode READ takeControlFlightMode CONSTANT) Q_PROPERTY(QString firmwareTypeString READ firmwareTypeString NOTIFY firmwareTypeChanged) Q_PROPERTY(QString vehicleTypeString READ vehicleTypeString NOTIFY vehicleTypeChanged) + Q_PROPERTY(QString vehicleImageOpaque READ vehicleImageOpaque CONSTANT) + Q_PROPERTY(QString vehicleImageOutline READ vehicleImageOutline CONSTANT) + Q_PROPERTY(QString vehicleImageCompass READ vehicleImageCompass CONSTANT) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -582,6 +585,10 @@ public: /// and destroyed when the vehicle goes away. void setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData); + QString vehicleImageOpaque () const; + QString vehicleImageOutline () const; + QString vehicleImageCompass () const; + public slots: void setLatitude(double latitude); void setLongitude(double longitude); -- GitLab From 358c87229567dc248a29bf7d791f3efaf5eab1bd Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 7 Feb 2017 16:55:21 -0600 Subject: [PATCH 262/398] Fix unit test code path --- src/MissionManager/SurveyMissionItem.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index 38ea5f11b..03a8f4356 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -136,9 +136,13 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) connect(this, &SurveyMissionItem::cameraTriggerChanged, this, &SurveyMissionItem::_cameraTriggerChanged); - connect(&_cameraTriggerDistanceFact, &Fact::valueChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); - connect(_vehicle, &Vehicle::cruiseSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); - connect(_vehicle, &Vehicle::hoverSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + connect(&_cameraTriggerDistanceFact, &Fact::valueChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + + // NULL check since object creation during unit testing passes NULL for vehicle + if (_vehicle) { + connect(_vehicle, &Vehicle::cruiseSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + connect(_vehicle, &Vehicle::hoverSpeedChanged, this, &SurveyMissionItem::timeBetweenShotsChanged); + } } void SurveyMissionItem::_setSurveyDistance(double surveyDistance) -- GitLab From d4ce5a93890bb3097cca8e82781360edba420f8c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 7 Feb 2017 16:55:44 -0600 Subject: [PATCH 263/398] Change unit test for latest Survey changes --- src/MissionManager/ComplexMissionItemTest.cc | 22 +------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/MissionManager/ComplexMissionItemTest.cc b/src/MissionManager/ComplexMissionItemTest.cc index 6d446ded7..a91e37dda 100644 --- a/src/MissionManager/ComplexMissionItemTest.cc +++ b/src/MissionManager/ComplexMissionItemTest.cc @@ -180,26 +180,6 @@ void ComplexMissionItemTest::_testCameraTrigger(void) { QCOMPARE(_complexItem->property("cameraTrigger").toBool(), true); - // Turning on/off camera triggering while there is no grid should trigger: - // cameraTriggerChanged - // dirtyChanged - // lastSequenceNumber should not change - - int lastSeq = _complexItem->lastSequenceNumber(); - - _complexItem->setProperty("cameraTrigger", false); - QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask | cameraTriggerChangedMask)); - QVERIFY(!_multiSpy->pullBoolFromSignalIndex(cameraTriggerChangedIndex)); - QCOMPARE(_complexItem->lastSequenceNumber(), lastSeq); - - _complexItem->setDirty(false); - _multiSpy->clearAllSignals(); - - _complexItem->setProperty("cameraTrigger", true); - QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask | cameraTriggerChangedMask)); - QVERIFY(_multiSpy->pullBoolFromSignalIndex(cameraTriggerChangedIndex)); - QCOMPARE(_complexItem->lastSequenceNumber(), lastSeq); - // Set up a grid for (int i=0; i<3; i++) { @@ -209,7 +189,7 @@ void ComplexMissionItemTest::_testCameraTrigger(void) _complexItem->setDirty(false); _multiSpy->clearAllSignals(); - lastSeq = _complexItem->lastSequenceNumber(); + int lastSeq = _complexItem->lastSequenceNumber(); QVERIFY(lastSeq > 0); // Turning off camera triggering should remove two camera trigger mission items, this should trigger: -- GitLab From 61226f2dd0369f693d038eac6ac84829256e7eb4 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 7 Feb 2017 17:28:06 -0600 Subject: [PATCH 264/398] Use all remaining width for text fields Done by using GridLayout --- src/MissionEditor/SimpleItemEditor.qml | 88 ++++++++++++-------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/src/MissionEditor/SimpleItemEditor.qml b/src/MissionEditor/SimpleItemEditor.qml index 3e7b1e56a..677656d76 100644 --- a/src/MissionEditor/SimpleItemEditor.qml +++ b/src/MissionEditor/SimpleItemEditor.qml @@ -2,6 +2,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 import QGroundControl.ScreenTools 1.0 import QGroundControl.Vehicle 1.0 @@ -42,65 +43,65 @@ Rectangle { width: parent.width wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - text: missionItem.sequenceNumber == 0 ? - qsTr("Planned home position. Actual home position set by Vehicle.") : - (missionItem.rawEdit ? - qsTr("Provides advanced access to all commands/parameters. Be very careful!") : - missionItem.commandDescription) + text: missionItem.rawEdit ? + qsTr("Provides advanced access to all commands/parameters. Be very careful!") : + missionItem.commandDescription } - Repeater { - model: missionItem.comboboxFacts + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columns: 2 - Item { - width: valuesColumn.width - height: comboBoxFact.height + Repeater { + model: missionItem.comboboxFacts QGCLabel { - id: comboBoxLabel - anchors.baseline: comboBoxFact.baseline - text: object.name - visible: object.name != "" + text: object.name + visible: object.name != "" + Layout.column: 0 + Layout.row: index } + } + + Repeater { + model: missionItem.comboboxFacts FactComboBox { - id: comboBoxFact - anchors.right: parent.right - width: comboBoxLabel.visible ? _editFieldWidth : parent.width - indexModel: false - model: object.enumStrings - fact: object + indexModel: false + model: object.enumStrings + fact: object + Layout.column: 1 + Layout.row: index + Layout.fillWidth: true } } } - Repeater { - model: missionItem.textFieldFacts + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columns: 2 - Item { - width: valuesColumn.width - height: textField.height + Repeater { + model: missionItem.textFieldFacts QGCLabel { - id: textFieldLabel - anchors.baseline: textField.baseline - text: object.name + text: object.name + Layout.column: 0 + Layout.row: index } + } - FactTextField { - id: textField - anchors.right: parent.right - width: _editFieldWidth - showUnits: true - fact: object - visible: !_root.readOnly - } + Repeater { + model: missionItem.textFieldFacts - FactLabel { - anchors.baseline: textFieldLabel.baseline - anchors.right: parent.right + FactTextField { + showUnits: true fact: object - visible: _root.readOnly + Layout.column: 1 + Layout.row: index + Layout.fillWidth: true } } } @@ -113,13 +114,6 @@ Rectangle { fact: object } } - - QGCButton { - text: qsTr("Move Home to map center") - visible: missionItem.homePosition - onClicked: editorRoot.moveHomeToMapCenter() - anchors.horizontalCenter: parent.horizontalCenter - } } // Column } // Item } // Component -- GitLab From e4e525363d37d30ace718c7c32b2401208c4f2b4 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Wed, 8 Feb 2017 03:01:36 -0500 Subject: [PATCH 265/398] PX4 hide FOLLOW and OFFBOARD (#4529) --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 3aac9dfa0..814a12ac9 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -64,8 +64,8 @@ static const struct Modes2Name rgModes2Name[] = { { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::_missionFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::_rtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::_followMeFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::_offboardFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::_followMeFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::_offboardFlightMode, true, false, true }, // modes that can't be directly set by the user { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::_landingFlightMode, false, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::_readyFlightMode, false, true, true }, -- GitLab From 23cb5e1b950528e717d701446cb736b0e114e8a3 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Wed, 8 Feb 2017 21:58:57 -0500 Subject: [PATCH 266/398] Dealing with lack of Internet connectivity. --- src/QtLocationPlugin/QGCMapEngine.cpp | 18 +++- src/QtLocationPlugin/QGCMapEngine.h | 4 + src/QtLocationPlugin/QGCMapEngineData.h | 13 ++- src/QtLocationPlugin/QGCMapTileSet.cpp | 11 +- src/QtLocationPlugin/QGCTileCacheWorker.cpp | 39 +++++--- src/QtLocationPlugin/QGCTileCacheWorker.h | 2 + src/QtLocationPlugin/QGeoMapReplyQGC.cpp | 105 ++++++++++++-------- src/QtLocationPlugin/QGeoMapReplyQGC.h | 9 +- src/QtLocationPlugin/QGeoTileFetcherQGC.cpp | 13 +++ src/QtLocationPlugin/QGeoTileFetcherQGC.h | 4 + 10 files changed, 161 insertions(+), 57 deletions(-) diff --git a/src/QtLocationPlugin/QGCMapEngine.cpp b/src/QtLocationPlugin/QGCMapEngine.cpp index 38edfdc74..d23351a11 100644 --- a/src/QtLocationPlugin/QGCMapEngine.cpp +++ b/src/QtLocationPlugin/QGCMapEngine.cpp @@ -130,11 +130,13 @@ QGCMapEngine::QGCMapEngine() , _maxMemCache(0) , _prunning(false) , _cacheWasReset(false) + , _isInternetActive(false) { qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType>(); - connect(&_worker, &QGCCacheWorker::updateTotals, this, &QGCMapEngine::_updateTotals); + connect(&_worker, &QGCCacheWorker::updateTotals, this, &QGCMapEngine::_updateTotals); + connect(&_worker, &QGCCacheWorker::internetStatus, this, &QGCMapEngine::_internetStatus); } //----------------------------------------------------------------------------- @@ -483,5 +485,19 @@ QGCCreateTileSetTask::~QGCCreateTileSetTask() delete _tileSet; } +//----------------------------------------------------------------------------- +void +QGCMapEngine::testInternet() +{ + getQGCMapEngine()->addTask(new QGCTestInternetTask()); +} + +//----------------------------------------------------------------------------- +void +QGCMapEngine::_internetStatus(bool active) +{ + _isInternetActive = active; +} + // Resolution math: https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Resolution_and_Scale diff --git a/src/QtLocationPlugin/QGCMapEngine.h b/src/QtLocationPlugin/QGCMapEngine.h index 8b38d1300..8ecedd980 100644 --- a/src/QtLocationPlugin/QGCMapEngine.h +++ b/src/QtLocationPlugin/QGCMapEngine.h @@ -86,7 +86,9 @@ public: void setMaxMemCache (quint32 size); const QString getCachePath () { return _cachePath; } const QString getCacheFilename () { return _cacheFile; } + void testInternet (); bool wasCacheReset () { return _cacheWasReset; } + bool isInternetActive () { return _isInternetActive; } UrlFactory* urlFactory () { return _urlFactory; } @@ -103,6 +105,7 @@ public: private slots: void _updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); void _pruned (); + void _internetStatus (bool active); signals: void updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); @@ -123,6 +126,7 @@ private: quint32 _maxMemCache; bool _prunning; bool _cacheWasReset; + bool _isInternetActive; }; extern QGCMapEngine* getQGCMapEngine(); diff --git a/src/QtLocationPlugin/QGCMapEngineData.h b/src/QtLocationPlugin/QGCMapEngineData.h index 1e70939ba..5765aa856 100644 --- a/src/QtLocationPlugin/QGCMapEngineData.h +++ b/src/QtLocationPlugin/QGCMapEngineData.h @@ -110,6 +110,7 @@ public: enum TaskType { taskInit, + taskTestInternet, taskCacheTile, taskFetchTile, taskFetchTileSets, @@ -129,7 +130,7 @@ public: virtual TaskType type () { return _type; } - void setError(QString errorString) + void setError(QString errorString = QString()) { emit error(_type, errorString); } @@ -141,6 +142,16 @@ private: TaskType _type; }; +//----------------------------------------------------------------------------- +class QGCTestInternetTask : public QGCMapTask +{ + Q_OBJECT +public: + QGCTestInternetTask() + : QGCMapTask(QGCMapTask::taskTestInternet) + {} +}; + //----------------------------------------------------------------------------- class QGCFetchTileSetTask : public QGCMapTask { diff --git a/src/QtLocationPlugin/QGCMapTileSet.cpp b/src/QtLocationPlugin/QGCMapTileSet.cpp index 71f770f3c..f5c0e1082 100644 --- a/src/QtLocationPlugin/QGCMapTileSet.cpp +++ b/src/QtLocationPlugin/QGCMapTileSet.cpp @@ -232,11 +232,20 @@ void QGCCachedTileSet::_prepareDownload() _tilesToDownload.removeFirst(); QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(tile->type(), tile->x(), tile->y(), tile->z(), _networkManager); request.setAttribute(QNetworkRequest::User, tile->hash()); +#if !defined(__mobile__) + QNetworkProxy proxy = _networkManager->proxy(); + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::DefaultProxy); + _networkManager->setProxy(tProxy); +#endif QNetworkReply* reply = _networkManager->get(request); reply->setParent(0); connect(reply, &QNetworkReply::finished, this, &QGCCachedTileSet::_networkReplyFinished); connect(reply, static_cast(&QNetworkReply::error), this, &QGCCachedTileSet::_networkReplyError); _replies.insert(tile->hash(), reply); +#if !defined(__mobile__) + _networkManager->setProxy(proxy); +#endif delete tile; //-- Refill queue if running low if(!_batchRequested && !_noMoreTiles && _tilesToDownload.count() < (QGCMapEngine::concurrentDownloads(_type) * 10)) { @@ -309,7 +318,7 @@ QGCCachedTileSet::_networkReplyError(QNetworkReply::NetworkError error) if (!reply) { return; } - //-- Upodate error count + //-- Update error count _errorCount++; emit errorCountChanged(); //-- Get tile hash diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.cpp b/src/QtLocationPlugin/QGCTileCacheWorker.cpp index 391042684..254e4c9e2 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.cpp +++ b/src/QtLocationPlugin/QGCTileCacheWorker.cpp @@ -93,20 +93,15 @@ QGCCacheWorker::enqueueTask(QGCMapTask* task) task->deleteLater(); return false; } - if(!_taskQueue.contains(task)) - { - _mutex.lock(); - _taskQueue.enqueue(task); - _mutex.unlock(); - if(this->isRunning()) { - _waitc.wakeAll(); - } else { - this->start(QThread::NormalPriority); - } - return true; + _mutex.lock(); + _taskQueue.enqueue(task); + _mutex.unlock(); + if(this->isRunning()) { + _waitc.wakeAll(); + } else { + this->start(QThread::HighPriority); } - //-- Should never happen - return false; + return true; } //----------------------------------------------------------------------------- @@ -158,6 +153,9 @@ QGCCacheWorker::run() case QGCMapTask::taskReset: _resetCacheDatabase(task); break; + case QGCMapTask::taskTestInternet: + _testInternet(); + break; } task->deleteLater(); //-- Check for update timeout @@ -662,6 +660,7 @@ QGCCacheWorker::_init() qCritical() << "Could not find suitable cache directory."; _failed = true; } + _testInternet(); return _failed; } @@ -749,3 +748,17 @@ QGCCacheWorker::_createDB() } _failed = !_valid; } + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_testInternet() +{ + QTcpSocket socket; + socket.connectToHost("8.8.8.8", 53); + if (socket.waitForConnected(2500)) { + emit internetStatus(true); + return; + } + qWarning() << "No Internet Access"; + emit internetStatus(false); +} diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.h b/src/QtLocationPlugin/QGCTileCacheWorker.h index da63535fa..1b87e1d4f 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.h +++ b/src/QtLocationPlugin/QGCTileCacheWorker.h @@ -59,6 +59,7 @@ private: void _deleteTileSet (QGCMapTask* mtask); void _resetCacheDatabase (QGCMapTask* mtask); void _pruneCache (QGCMapTask* mtask); + void _testInternet (); quint64 _findTile (const QString hash); bool _findTileSetID (const QString name, quint64& setID); @@ -70,6 +71,7 @@ private: signals: void updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); + void internetStatus (bool active); private: QQueue _taskQueue; diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp index 066b12356..c86252266 100644 --- a/src/QtLocationPlugin/QGeoMapReplyQGC.cpp +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.cpp @@ -46,11 +46,14 @@ #include "QGCMapEngine.h" #include "QGeoMapReplyQGC.h" +#include "QGeoTileFetcherQGC.h" #include #include #include +int QGeoTiledMapReplyQGC::_requestCount = 0; + //----------------------------------------------------------------------------- QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager, const QNetworkRequest &request, const QGeoTileSpec &spec, QObject *parent) : QGeoTiledMapReply(spec, parent) @@ -59,15 +62,7 @@ QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager , _networkManager(networkManager) { if(_request.url().isEmpty()) { - if(!_badMapBox.size()) { - QFile b(":/res/notile.png"); - if(b.open(QFile::ReadOnly)) - _badMapBox = b.readAll(); - } - setMapImageData(_badMapBox); - setMapImageFormat("png"); - setFinished(true); - setCached(false); + _returnBadTile(); } else { QGCFetchTileTask* task = getQGCMapEngine()->createFetchTileTask((UrlFactory::MapType)spec.mapId(), spec.x(), spec.y(), spec.zoom()); connect(task, &QGCFetchTileTask::tileFetched, this, &QGeoTiledMapReplyQGC::cacheReply); @@ -78,31 +73,49 @@ QGeoTiledMapReplyQGC::QGeoTiledMapReplyQGC(QNetworkAccessManager *networkManager //----------------------------------------------------------------------------- QGeoTiledMapReplyQGC::~QGeoTiledMapReplyQGC() +{ + _clearReply(); +} + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::_clearReply() { if (_reply) { _reply->deleteLater(); _reply = 0; + _requestCount--; } } + //----------------------------------------------------------------------------- void -QGeoTiledMapReplyQGC::abort() +QGeoTiledMapReplyQGC::_returnBadTile() { - if (_reply) - _reply->abort(); + if(!_badMapBox.size()) { + QFile b(":/res/notile.png"); + if(b.open(QFile::ReadOnly)) + _badMapBox = b.readAll(); + } + setMapImageData(_badMapBox); + setMapImageFormat("png"); + setFinished(true); + setCached(false); } //----------------------------------------------------------------------------- void -QGeoTiledMapReplyQGC::replyDestroyed() +QGeoTiledMapReplyQGC::abort() { - _reply = 0; + if (_reply) + _reply->abort(); } //----------------------------------------------------------------------------- void QGeoTiledMapReplyQGC::networkReplyFinished() { + _timer.stop(); if (!_reply) { return; } @@ -117,51 +130,54 @@ QGeoTiledMapReplyQGC::networkReplyFinished() getQGCMapEngine()->cacheTile((UrlFactory::MapType)tileSpec().mapId(), tileSpec().x(), tileSpec().y(), tileSpec().zoom(), a, format); } setFinished(true); - _reply->deleteLater(); - _reply = 0; + _clearReply(); } //----------------------------------------------------------------------------- void QGeoTiledMapReplyQGC::networkReplyError(QNetworkReply::NetworkError error) { + _timer.stop(); if (!_reply) { return; } if (error != QNetworkReply::OperationCanceledError) { qWarning() << "Fetch tile error:" << _reply->errorString(); } - _reply->deleteLater(); - _reply = 0; - if(!_badTile.size()) { - QFile b(":/res/notile.png"); - if(b.open(QFile::ReadOnly)) - _badTile = b.readAll(); - } - setMapImageData(_badTile); - setMapImageFormat("png"); - setFinished(true); - setCached(false); + _returnBadTile(); + _clearReply(); } //----------------------------------------------------------------------------- void QGeoTiledMapReplyQGC::cacheError(QGCMapTask::TaskType type, QString /*errorString*/) { - if(type != QGCMapTask::taskFetchTile) { - qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; + if(_networkManager->networkAccessible() < QNetworkAccessManager::Accessible || !getQGCMapEngine()->isInternetActive()) { + _returnBadTile(); + } else { + if(type != QGCMapTask::taskFetchTile) { + qWarning() << "QGeoTiledMapReplyQGC::cacheError() for wrong task"; + } + //-- Tile not in cache. Get it off the Internet. +#if !defined(__mobile__) + QNetworkProxy proxy = _networkManager->proxy(); + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::DefaultProxy); + _networkManager->setProxy(tProxy); +#endif + _reply = _networkManager->get(_request); + _reply->setParent(0); + connect(_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); +#if !defined(__mobile__) + _networkManager->setProxy(proxy); +#endif + //- Wait for an answer up to 10 seconds + connect(&_timer, &QTimer::timeout, this, &QGeoTiledMapReplyQGC::timeout); + _timer.setSingleShot(true); + _timer.start(10000); + _requestCount++; } - //-- Tile not in cache. Get it off the Internet. - QNetworkProxy proxy = _networkManager->proxy(); - QNetworkProxy tProxy; - tProxy.setType(QNetworkProxy::DefaultProxy); - _networkManager->setProxy(tProxy); - _reply = _networkManager->get(_request); - _reply->setParent(0); - connect(_reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); - connect(_reply, SIGNAL(destroyed()), this, SLOT(replyDestroyed())); - _networkManager->setProxy(proxy); } //----------------------------------------------------------------------------- @@ -174,3 +190,12 @@ QGeoTiledMapReplyQGC::cacheReply(QGCCacheTile* tile) setCached(true); tile->deleteLater(); } + +//----------------------------------------------------------------------------- +void +QGeoTiledMapReplyQGC::timeout() +{ + if(_reply) { + _reply->abort(); + } +} diff --git a/src/QtLocationPlugin/QGeoMapReplyQGC.h b/src/QtLocationPlugin/QGeoMapReplyQGC.h index b66791090..46c38e529 100644 --- a/src/QtLocationPlugin/QGeoMapReplyQGC.h +++ b/src/QtLocationPlugin/QGeoMapReplyQGC.h @@ -49,6 +49,7 @@ #include #include +#include #include "QGCMapEngineData.h" @@ -61,11 +62,15 @@ public: void abort(); private slots: - void replyDestroyed (); void networkReplyFinished (); void networkReplyError (QNetworkReply::NetworkError error); void cacheReply (QGCCacheTile* tile); void cacheError (QGCMapTask::TaskType type, QString errorString); + void timeout (); + +private: + void _clearReply (); + void _returnBadTile (); private: QNetworkReply* _reply; @@ -73,6 +78,8 @@ private: QNetworkAccessManager* _networkManager; QByteArray _badMapBox; QByteArray _badTile; + QTimer _timer; + static int _requestCount; }; #endif // QGEOMAPREPLYQGC_H diff --git a/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp b/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp index 485154716..1b38673ce 100644 --- a/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp +++ b/src/QtLocationPlugin/QGeoTileFetcherQGC.cpp @@ -57,6 +57,10 @@ QGeoTileFetcherQGC::QGeoTileFetcherQGC(QGeoTiledMappingManagerEngine *parent) : QGeoTileFetcher(parent) , _networkManager(new QNetworkAccessManager(this)) { + //-- Check internet status every 30 seconds or so + connect(&_timer, &QTimer::timeout, this, &QGeoTileFetcherQGC::timeout); + _timer.setSingleShot(false); + _timer.start(30000); } //----------------------------------------------------------------------------- @@ -73,3 +77,12 @@ QGeoTileFetcherQGC::getTileImage(const QGeoTileSpec &spec) QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL((UrlFactory::MapType)spec.mapId(), spec.x(), spec.y(), spec.zoom(), _networkManager); return new QGeoTiledMapReplyQGC(_networkManager, request, spec); } + +//----------------------------------------------------------------------------- +void +QGeoTileFetcherQGC::timeout() +{ + if(!getQGCMapEngine()->isInternetActive()) { + getQGCMapEngine()->testInternet(); + } +} diff --git a/src/QtLocationPlugin/QGeoTileFetcherQGC.h b/src/QtLocationPlugin/QGeoTileFetcherQGC.h index 54f507a7a..9c8663324 100644 --- a/src/QtLocationPlugin/QGeoTileFetcherQGC.h +++ b/src/QtLocationPlugin/QGeoTileFetcherQGC.h @@ -48,6 +48,7 @@ #define QGEOTILEFETCHERQGC_H #include +#include #include "QGCMapUrlEngine.h" class QGeoTiledMappingManagerEngine; @@ -59,10 +60,13 @@ class QGeoTileFetcherQGC : public QGeoTileFetcher public: explicit QGeoTileFetcherQGC (QGeoTiledMappingManagerEngine *parent = 0); ~QGeoTileFetcherQGC(); +public slots: + void timeout (); private: QGeoTiledMapReply* getTileImage (const QGeoTileSpec &spec); private: QNetworkAccessManager* _networkManager; + QTimer _timer; }; #endif // QGEOTILEFETCHERQGC_H -- GitLab From 3345db1e5f8b76b9903c4d128b9c901dea57ddf3 Mon Sep 17 00:00:00 2001 From: Danny Schrader Date: Fri, 10 Feb 2017 15:31:56 -0500 Subject: [PATCH 267/398] added Abort button (for abort landing). button sends MAV_CMD_DO_GO_AROUND. --- src/FirmwarePlugin/FirmwarePlugin.cc | 7 +++++++ src/FirmwarePlugin/FirmwarePlugin.h | 3 +++ src/FlightDisplay/FlightDisplayViewWidgets.qml | 14 ++++++++++++++ src/Vehicle/Vehicle.cc | 10 ++++++++++ src/Vehicle/Vehicle.h | 3 +++ 5 files changed, 37 insertions(+) diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index fb6d02816..f42ed6efd 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -16,6 +16,7 @@ static FirmwarePluginFactoryRegister* _instance = NULL; const char* guided_mode_not_supported_by_vehicle = "Guided mode not supported by Vehicle."; +const char* landing_aborted = "Landing aborted."; const char* FirmwarePlugin::px4FollowMeFlightMode = "Follow Me"; @@ -231,6 +232,12 @@ void FirmwarePlugin::pauseVehicle(Vehicle* vehicle) qgcApp()->showMessage(guided_mode_not_supported_by_vehicle); } +void FirmwarePlugin::abortLanding(Vehicle* vehicle) +{ + Q_UNUSED(vehicle); + qgcApp()->showMessage(landing_aborted); +} + void FirmwarePlugin::guidedModeRTL(Vehicle* vehicle) { // Not supported by generic vehicle diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 0c0eb0f77..f5c27eab0 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -105,6 +105,9 @@ public: /// If not, vehicle will be left in Loiter. virtual void pauseVehicle(Vehicle* vehicle); + /// Command vehicle to abort landing + virtual void abortLanding(Vehicle* vehicle); + /// Command vehicle to return to launch virtual void guidedModeRTL(Vehicle* vehicle); diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 9743c72f3..f60854c4a 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -191,6 +191,7 @@ Item { readonly property int confirmGoTo: 8 readonly property int confirmRetask: 9 readonly property int confirmOrbit: 10 + readonly property int confirmAbort: 11 property int confirmActionCode property real _showMargin: _margins @@ -238,6 +239,9 @@ Item { //-- Center on current flight map position and orbit with a 50m radius (velocity/direction controlled by the RC) //_activeVehicle.guidedModeOrbit(QGroundControl.flightMapPosition, 50.0) break; + case confirmAbort: + _activeVehicle.abortLanding() + break; default: console.warn(qsTr("Internal error: unknown confirmActionCode"), confirmActionCode) } @@ -289,6 +293,9 @@ Item { case confirmOrbit: guidedModeConfirm.confirmText = qsTr("enter orbit mode") break; + case confirmAbort: + guidedModeConfirm.confirmText = qsTr("abort landing") + break; } _guidedModeBar.visible = false guidedModeConfirm.visible = true @@ -356,6 +363,13 @@ Item { onClicked: _guidedModeBar.confirmAction(_guidedModeBar.confirmOrbit) } + QGCButton { + pointSize: _guidedModeBar._fontPointSize + text: qsTr("Abort") + visible: _activeVehicle && _activeVehicle.flying + onClicked: _guidedModeBar.confirmAction(_guidedModeBar.confirmAbort) + } + } // Row } // Column } // Rectangle - Guided mode buttons diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 4876910d6..796886790 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1838,6 +1838,16 @@ void Vehicle::pauseVehicle(void) _firmwarePlugin->pauseVehicle(this); } +void Vehicle::abortLanding(void) +{ + sendMavCommand(defaultComponentId(), + MAV_CMD_DO_GO_AROUND, + true, // show error if fails + 50); + + //_firmwarePlugin->abortLanding(this); +} + bool Vehicle::guidedMode(void) const { return _firmwarePlugin->isGuidedMode(this); diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index a765f876a..9d168eb00 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -355,6 +355,9 @@ public: /// Command vehicle to kill all motors no matter what state Q_INVOKABLE void emergencyStop(void); + /// Command vehicle to abort landing + Q_INVOKABLE void abortLanding(void); + /// Alter the current mission item on the vehicle Q_INVOKABLE void setCurrentMissionSequence(int seq); -- GitLab From 143434f49190aaa417bc8a938d34b35f0ed6b584 Mon Sep 17 00:00:00 2001 From: Phillip Kocmoud Date: Sun, 12 Feb 2017 11:00:58 -0600 Subject: [PATCH 268/398] Add support for the AUAV X2.1 Flight Controller (#4501) * Fix minor spelling error * Added support for the AUAV x2.1 Flight Controller also removed a duplicate entry for MindPX from src/comm/USBBoardInfo.json Adds entries required to correctly identify, download and flash PX4 firmware to the AUAV X2.1 Flight Controller * Update USBBoardInfo.json Removed un-needed fallback entries. This has been tested! --- src/VehicleSetup/Bootloader.h | 1 + src/VehicleSetup/FirmwareUpgradeController.cc | 15 ++++++++++++++- src/VehicleSetup/FirmwareUpgradeController.h | 1 + src/comm/QGCSerialPortInfo.h | 1 + src/comm/USBBoardInfo.json | 2 +- src/ui/preferences/GeneralSettings.qml | 4 ++-- 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/VehicleSetup/Bootloader.h b/src/VehicleSetup/Bootloader.h index 9703f44e0..268132483 100644 --- a/src/VehicleSetup/Bootloader.h +++ b/src/VehicleSetup/Bootloader.h @@ -66,6 +66,7 @@ public: static const int boardIDPX4FMUV4 = 11; ///< PX4 V4 board, as from USB PID static const int boardIDPX4Flow = 6; ///< PX4 Flow board, as from USB PID static const int boardIDAeroCore = 98; ///< Gumstix AeroCore board, as from USB PID + static const int boardIDAUAVX2_1 = 33; ///< AUAV X2.1 board, as from USB PID static const int boardID3DRRadio = 78; ///< 3DR Radio. This is an arbitrary value unrelated to the PID static const int boardIDMINDPXFMUV2 = 88; ///< MindPX V2 board, as from USB PID static const int boardIDTAPV1 = 64; ///< TAP V1 board, as from USB PID diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index f80ac3d45..66d6e2697 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -301,7 +301,12 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v1.px4"}, { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v1.px4"} }; - + //////////////////////////////////// AUAVX2_1 firmwares ////////////////////////////////////////////////// + FirmwareToUrlElement_t rgAUAVX2_1FirmwareArray[] = { + { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/auav-x21_default.px4"}, + { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/auav-x21_default.px4"}, + { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/auav-x21_default.px4"}, + }; //////////////////////////////////// MindPXFMUV2 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgMindPXFMUV2FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/mindpx-v2_default.px4"}, @@ -355,6 +360,12 @@ void FirmwareUpgradeController::_initFirmwareHash() _rgPX4FMUV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } + size = sizeof(rgAUAVX2_1FirmwareArray)/sizeof(rgAUAVX2_1FirmwareArray[0]); + for (int i = 0; i < size; i++) { + const FirmwareToUrlElement_t& element = rgAUAVX2_1FirmwareArray[i]; + _rgAUAVX2_1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); + } + size = sizeof(rgMindPXFMUV2FirmwareArray)/sizeof(rgMindPXFMUV2FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgMindPXFMUV2FirmwareArray[i]; @@ -406,6 +417,8 @@ QHash* FirmwareUpgradeCo return &_rgPX4FMUV4Firmware; case Bootloader::boardIDAeroCore: return &_rgAeroCoreFirmware; + case Bootloader::boardIDAUAVX2_1: + return &_rgAUAVX2_1Firmware; case Bootloader::boardIDMINDPXFMUV2: return &_rgMindPXFMUV2Firmware; case Bootloader::boardIDTAPV1: diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index f4596e31f..683301931 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -198,6 +198,7 @@ private: QHash _rgPX4FMUV2Firmware; QHash _rgAeroCoreFirmware; QHash _rgPX4FMUV1Firmware; + QHash _rgAUAVX2_1Firmware; QHash _rgMindPXFMUV2Firmware; QHash _rgTAPV1Firmware; QHash _rgASCV1Firmware; diff --git a/src/comm/QGCSerialPortInfo.h b/src/comm/QGCSerialPortInfo.h index 51f272845..71398d500 100644 --- a/src/comm/QGCSerialPortInfo.h +++ b/src/comm/QGCSerialPortInfo.h @@ -43,6 +43,7 @@ public: static const int pixhawkFMUV2ProductId = 17; ///< Product ID for Pixhawk V2 board static const int pixhawkFMUV2OldBootloaderProductId = 22; ///< Product ID for Bootloader on older Pixhawk V2 boards static const int pixhawkFMUV1ProductId = 16; ///< Product ID for PX4 FMU V1 board + static const int auavx2_1FMUV2ProductId = 33; ///< Product ID for AUAV X2.1 FMU V2 board static const int AeroCoreProductId = 4097; ///< Product ID for the AeroCore board diff --git a/src/comm/USBBoardInfo.json b/src/comm/USBBoardInfo.json index 1050c8986..424fb6e01 100644 --- a/src/comm/USBBoardInfo.json +++ b/src/comm/USBBoardInfo.json @@ -11,6 +11,7 @@ { "vendorID": 9900, "productID": 18, "boardClass": "Pixhawk", "name": "PX4 FMU V4" }, { "vendorID": 9900, "productID": 22, "boardClass": "Pixhawk", "name": "PX4 FMU V2", "comment": "Bootloader on older Pixhawk V2 boards" }, { "vendorID": 9900, "productID": 4097, "boardClass": "Pixhawk", "name": "AeroCore" }, + { "vendorID": 9900, "productID": 33, "boardClass": "Pixhawk", "name": "AUAV X2.1 FMU V2" }, { "vendorID": 9900, "productID": 48, "boardClass": "Pixhawk", "name": "MindPX FMU V2" }, { "vendorID": 9900, "productID": 64, "boardClass": "Pixhawk", "name": "TAP V1" }, { "vendorID": 9900, "productID": 65, "boardClass": "Pixhawk", "name": "ASC V1" }, @@ -36,7 +37,6 @@ { "regExp": "^PX4 FMU v1.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 BL FMU v1.x$", "boardClass": "Pixhawk" }, { "regExp": "^MindPX FMU v2.x$", "boardClass": "Pixhawk" }, - { "regExp": "^MindPX FMU v2.x$", "boardClass": "Pixhawk" }, { "regExp": "^MindPX BL FMU v2.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 TAP v1.x$", "boardClass": "Pixhawk" }, { "regExp": "^PX4 BL TAP v1.x$", "boardClass": "Pixhawk" }, diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index ed39910a2..59618eafe 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -128,7 +128,7 @@ QGCView { } } //----------------------------------------------------------------- - //-- Miscelanous + //-- Miscellanous Item { width: qgcView.width * 0.8 height: miscLabel.height @@ -136,7 +136,7 @@ QGCView { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { id: miscLabel - text: qsTr("Miscelaneous") + text: qsTr("Miscellaneous") font.family: ScreenTools.demiboldFontFamily } } -- GitLab From d3fcd7ae68925454cec755d6dca94b6aa7593c1c Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Mon, 13 Feb 2017 11:38:43 -0500 Subject: [PATCH 269/398] Don't send EOS on pipeline if not streaming video --- src/VideoStreaming/VideoReceiver.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 5d6e27f31..0a58ff8d0 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -323,7 +323,9 @@ void VideoReceiver::stop() { #if defined(QGC_GST_STREAMING) qCDebug(VideoReceiverLog) << "stop()"; - if (_pipeline != NULL && !_stopping) { + if(!_streaming) { + _shutdownPipeline(); + } else if (_pipeline != NULL && !_stopping) { qCDebug(VideoReceiverLog) << "Stopping _pipeline"; gst_element_send_event(_pipeline, gst_event_new_eos()); _stopping = true; -- GitLab From 8c2f662c4c5a89d4dd999dd2035a561024682bf4 Mon Sep 17 00:00:00 2001 From: Danny Schrader Date: Mon, 13 Feb 2017 13:45:20 -0500 Subject: [PATCH 270/398] Added comment for dummy variable in vehicle.cc. Enabled _firmwarePlugin call that shows 'Landing aborted.' message. --- src/Vehicle/Vehicle.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 796886790..f9399cbc3 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1843,9 +1843,9 @@ void Vehicle::abortLanding(void) sendMavCommand(defaultComponentId(), MAV_CMD_DO_GO_AROUND, true, // show error if fails - 50); + 50); // this is a dummy value that is currently ignored - //_firmwarePlugin->abortLanding(this); + _firmwarePlugin->abortLanding(this); } bool Vehicle::guidedMode(void) const -- GitLab From 842a70736702c18c34286123aee14406e0e4c8d9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 13 Feb 2017 14:27:33 -0800 Subject: [PATCH 271/398] Add NSCameraUsageDescription --- ios/iOS-Info.plist | 162 +++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/ios/iOS-Info.plist b/ios/iOS-Info.plist index 1343e18ee..a7bde7a2a 100644 --- a/ios/iOS-Info.plist +++ b/ios/iOS-Info.plist @@ -2,85 +2,87 @@ - CFBundleDisplayName - QGroundControl - CFBundleExecutable - $(EXECUTABLE_NAME) - NSHumanReadableCopyright - Open Source Flight Systems GmbH - Internal Build - CFBundleIconFile - - CFBundleIdentifier - org.QGroundControl.qgc - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 0.0.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - QGCLaunchScreen - UIRequiresFullScreen - - CFBundleInfoDictionaryVersion - 6.0 - ForAppStore - No - NSLocationUsageDescription - Ground Station Location - NSLocationWhenInUseUsageDescription - Ground Station Location - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - CFBundleIcons - - CFBundlePrimaryIcon - - CFBundleIconFiles - - AppIcon29x29.png - AppIcon29x29@2x.png - AppIcon40x40@2x.png - AppIcon57x57.png - AppIcon57x57@2x.png - AppIcon60x60@2x.png - - - - CFBundleIcons~ipad - - CFBundlePrimaryIcon - - CFBundleIconFiles - - AppIcon29x29.png - AppIcon29x29@2x.png - AppIcon40x40@2x.png - AppIcon57x57.png - AppIcon57x57@2x.png - AppIcon60x60@2x.png - AppIcon29x29~ipad.png - AppIcon29x29@2x~ipad.png - AppIcon40x40~ipad.png - AppIcon40x40@2x~ipad.png - AppIcon50x50~ipad.png - AppIcon50x50@2x~ipad.png - AppIcon72x72~ipad.png - AppIcon72x72@2x~ipad.png - AppIcon76x76~ipad.png - AppIcon76x76@2x~ipad.png - AppIcon83.5x83.5@2x~ipad.png - - - + NSCameraUsageDescription + QGC uses UVC devices for video streaming. + CFBundleDisplayName + QGroundControl + CFBundleExecutable + $(EXECUTABLE_NAME) + NSHumanReadableCopyright + Open Source Flight Systems GmbH - Internal Build + CFBundleIconFile + + CFBundleIdentifier + org.QGroundControl.qgc + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + QGCLaunchScreen + UIRequiresFullScreen + + CFBundleInfoDictionaryVersion + 6.0 + ForAppStore + No + NSLocationUsageDescription + Ground Station Location + NSLocationWhenInUseUsageDescription + Ground Station Location + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleIcons + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + + + + CFBundleIcons~ipad + + CFBundlePrimaryIcon + + CFBundleIconFiles + + AppIcon29x29.png + AppIcon29x29@2x.png + AppIcon40x40@2x.png + AppIcon57x57.png + AppIcon57x57@2x.png + AppIcon60x60@2x.png + AppIcon29x29~ipad.png + AppIcon29x29@2x~ipad.png + AppIcon40x40~ipad.png + AppIcon40x40@2x~ipad.png + AppIcon50x50~ipad.png + AppIcon50x50@2x~ipad.png + AppIcon72x72~ipad.png + AppIcon72x72@2x~ipad.png + AppIcon76x76~ipad.png + AppIcon76x76@2x~ipad.png + AppIcon83.5x83.5@2x~ipad.png + + + -- GitLab From 8de7cfc2c2468436d69aa8914192d8e3c6f7fd33 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 13 Feb 2017 14:27:58 -0800 Subject: [PATCH 272/398] Default to clip = true --- src/QmlControls/QGCFlickable.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QmlControls/QGCFlickable.qml b/src/QmlControls/QGCFlickable.qml index 80a7e0346..c3a7dfdd5 100644 --- a/src/QmlControls/QGCFlickable.qml +++ b/src/QmlControls/QGCFlickable.qml @@ -6,6 +6,7 @@ import QGroundControl.Palette 1.0 Flickable { id: root boundsBehavior: Flickable.StopAtBounds + clip: true property color indicatorColor: qgcPal.text -- GitLab From 5f20c3aca1f8a9c84300d62c96233c39e26fcb93 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 13 Feb 2017 14:28:12 -0800 Subject: [PATCH 273/398] Rework for mobile usability --- src/FactSystem/FactControls/FactTextField.qml | 2 +- src/QmlControls/ParameterEditorDialog.qml | 110 ++++++++---------- src/QmlControls/QGCTextField.qml | 7 +- 3 files changed, 52 insertions(+), 67 deletions(-) diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index bf0b4f956..dc9315a86 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -40,7 +40,7 @@ QGCTextField { } } - onHelpClicked: qgcView.showDialog(helpDialogComponent, qsTr("Value Details"), qgcView.showDialogDefaultWidth, StandardButton.Save) + onHelpClicked: qgcView.showDialog(helpDialogComponent, qsTr("Value Details"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel) Component { diff --git a/src/QmlControls/ParameterEditorDialog.qml b/src/QmlControls/ParameterEditorDialog.qml index 7270ede46..41b8966e3 100644 --- a/src/QmlControls/ParameterEditorDialog.qml +++ b/src/QmlControls/ParameterEditorDialog.qml @@ -7,12 +7,9 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - import QtQuick 2.5 import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.2 import QGroundControl.Controls 1.0 import QGroundControl.Palette 1.0 @@ -29,7 +26,8 @@ QGCViewDialog { property bool validate: false property string validateValue - property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 20 + property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 20 + property bool _longDescriptionAvailable: fact.longDescription != "" ParameterEditorController { id: controller; factPanel: parent } @@ -68,7 +66,6 @@ QGCViewDialog { validationError.text = fact.validate(validateValue, false /* convertOnly */) forceSave.visible = true } - //valueField.forceActiveFocus() } QGCFlickable { @@ -83,28 +80,24 @@ QGCViewDialog { anchors.right: parent.right QGCLabel { + id: validationError width: parent.width wrapMode: Text.WordWrap - visible: fact.shortDescription - text: fact.shortDescription - } - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - visible: fact.longDescription - text: fact.longDescription + color: qgcPal.warningText } - Row { - spacing: defaultTextWidth + RowLayout { + spacing: defaultTextWidth + anchors.left: parent.left + anchors.right: parent.right QGCTextField { - id: valueField - text: validate ? validateValue : fact.valueString - visible: fact.enumStrings.length == 0 || validate - //focus: true - + id: valueField + text: validate ? validateValue : fact.valueString + visible: fact.enumStrings.length == 0 || validate + unitsLabel: fact.units + showUnits: fact.units != "" + Layout.fillWidth: true inputMethodHints: ScreenTools.isiOS ? Qt.ImhNone : // iOS numeric keyboard has not done button, we can't use it Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard @@ -113,7 +106,6 @@ QGCViewDialog { QGCButton { anchors.baseline: valueField.baseline visible: fact.defaultValueAvailable - width: _editFieldWidth text: qsTr("Reset to default") onClicked: { @@ -159,63 +151,55 @@ QGCViewDialog { } QGCLabel { - text: fact.name - visible: fact.componentId > 0 // > 0 means it's a parameter fact + width: parent.width + wrapMode: Text.WordWrap + visible: !longDescriptionLabel.visible + text: fact.shortDescription } - Column { - spacing: defaultTextHeight / 2 - anchors.left: parent.left - anchors.right: parent.right - - Row { - spacing: defaultTextWidth - - QGCLabel { text: qsTr("Units:") } - QGCLabel { text: fact.units ? fact.units : qsTr("none") } - } - - Row { - spacing: defaultTextWidth - visible: !fact.minIsDefaultForType - - QGCLabel { text: qsTr("Minimum value:") } - QGCLabel { text: fact.minString } - } + QGCLabel { + id: longDescriptionLabel + width: parent.width + wrapMode: Text.WordWrap + visible: fact.longDescription != "" + text: fact.longDescription + } - Row { - spacing: defaultTextWidth - visible: !fact.maxIsDefaultForType + Row { + spacing: defaultTextWidth - QGCLabel { text: qsTr("Maximum value:") } - QGCLabel { text: fact.maxString } + QGCLabel { + id: minValueDisplay + text: qsTr("Min: ") + fact.minString + visible: !fact.minIsDefaultForType } - Row { - spacing: defaultTextWidth - - QGCLabel { text: qsTr("Default value:") } - QGCLabel { text: fact.defaultValueAvailable ? fact.defaultValueString : qsTr("none") } + QGCLabel { + text: qsTr("Max: ") + fact.maxString + visible: !fact.maxIsDefaultForType } QGCLabel { - visible: fact.rebootRequired - text: "Reboot required after change" + text: qsTr("Default: ") + fact.defaultValueString + visible: fact.defaultValueAvailable } - } // Column + } QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("Warning: Modifying values while vehicle is in flight can lead to vehicle instability and possible vehicle loss. ") + - qsTr("Make sure you know what you are doing and double-check your values before Save!") + text: qsTr("Parameter name: ") + fact.name + visible: fact.componentId > 0 // > 0 means it's a parameter fact + } + + QGCLabel { + visible: fact.rebootRequired + text: "Reboot required after change" } QGCLabel { - id: validationError width: parent.width wrapMode: Text.WordWrap - color: qsTr("yellow") + text: qsTr("Warning: Modifying values while vehicle is in flight can lead to vehicle instability and possible vehicle loss. ") + + qsTr("Make sure you know what you are doing and double-check your values before Save!") } QGCCheckBox { diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 732bc3561..6542d290b 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -97,9 +97,10 @@ TextField { } MouseArea { - anchors.fill: unitsHelpLayout - enabled: control.activeFocus - onClicked: root.helpClicked() + anchors.margins: ScreenTools.isMobile ? -(ScreenTools.defaultFontPixelWidth * 0.66) : 0 // Larger touch area for mobile + anchors.fill: unitsHelpLayout + enabled: control.activeFocus + onClicked: root.helpClicked() } } -- GitLab From a0a79a7736a8a473050d6feee3e2bd6aa580195d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 13 Feb 2017 17:06:27 -0800 Subject: [PATCH 274/398] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 13a5c0693..cd0d25dd7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,8 @@ QGroundControl builds are supported for OSX, Linux, Windows, iOS and Android. QG ###### Install QT You need to install Qt as described below instead of using pre-built packages from say, a Linux distribution because QGroundControl needs access to private Qt headers. * Download the [Qt installer](http://www.qt.io/download-open-source) - * Make sure to install Qt version **5.5.1** NOT 5.4.x, 5.6.x, 5.7.x, etc. + * Make sure to install Qt version **5.5.1** NOT 5.4.x, 5.6.x, 5.7.x, etc. + * If you don't install the full Qt 5.5 make sure you install Qt Location and Qt Quick Controls. * Ubuntu: Set the downloaded file to executable using:`chmod +x`. Install to default location for use with ./qgroundcontrol-start.sh. If you install Qt to a non-default location you will need to modify qgroundcontrol-start.sh in order to run downloaded builds. * Windows: Default installer not quite correct, use [this](http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-windows-x86-msvc2013-5.5.1.exe) instead -- GitLab From ec091ecc11d8d61755aa3120cb8f2097328cdf86 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 13 Feb 2017 19:14:45 -0800 Subject: [PATCH 275/398] Changed default component id discovery Use component id from heartbeat --- src/FactSystem/ParameterManager.cc | 96 ++++---------------- src/FactSystem/ParameterManager.h | 7 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.h | 1 - src/FirmwarePlugin/FirmwarePlugin.h | 3 - src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 1 - src/MissionManager/GeoFenceController.cc | 2 +- src/QmlControls/ParameterEditorController.cc | 5 +- src/Vehicle/MultiVehicleManager.cc | 7 +- src/Vehicle/MultiVehicleManager.h | 2 +- src/Vehicle/Vehicle.cc | 32 ++++--- src/Vehicle/Vehicle.h | 7 +- src/comm/MAVLinkProtocol.cc | 2 +- src/comm/MAVLinkProtocol.h | 2 +- 13 files changed, 54 insertions(+), 113 deletions(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index a678407b8..3ad3112ec 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -52,7 +52,6 @@ ParameterManager::ParameterManager(Vehicle* vehicle) , _initialLoadComplete(false) , _waitingForDefaultComponent(false) , _saveRequired(false) - , _defaultComponentId(MAV_COMP_ID_ALL) , _parameterSetMajorVersion(-1) , _parameterMetaData(NULL) , _prevWaitingReadParamIndexCount(0) @@ -81,9 +80,6 @@ ParameterManager::ParameterManager(Vehicle* vehicle) connect(_vehicle->uas(), &UASInterface::parameterUpdate, this, &ParameterManager::_parameterUpdate); - _defaultComponentIdParam = vehicle->firmwarePlugin()->getDefaultComponentIdParam(); - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Default component param" << _defaultComponentIdParam; - // Ensure the cache directory exists QFileInfo(QSettings().fileName()).dir().mkdir("ParamCache"); refreshAllParameters(); @@ -163,12 +159,6 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Seeing component for first time - paramcount:" << parameterCount; } - // Determine default component id - if (!_defaultComponentIdParam.isEmpty() && _defaultComponentIdParam == parameterName) { - qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Default component id determined"; - _defaultComponentId = componentId; - } - bool componentParamsComplete = false; if (_waitingReadParamIndexMap[componentId].count() == 1) { // We need to know when we get the last param from a component in order to complete setup @@ -229,13 +219,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _waitingParamTimeoutTimer.start(); qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer: totalWaitingParamCount:" << totalWaitingParamCount; } else { - if (_defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { - // Still waiting for default component id, restart timer - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer (still waiting for default component)"; - _waitingParamTimeoutTimer.start(); - } else { - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; - } + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; } // Update progress bar for waiting reads @@ -306,7 +290,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString fact->_containerSetRawValue(value); if (componentParamsComplete) { - if (componentId == _defaultComponentId) { + if (componentId == _vehicle->defaultComponentId()) { // Add meta data to default component. We need to do this before we setup the group map since group // map requires meta data. _addMetaDataToDefaultComponent(); @@ -337,8 +321,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _prevWaitingReadParamNameCount = waitingReadParamNameCount; _prevWaitingWriteParamNameCount = waitingWriteParamNameCount; - // Don't fail initial load complete if default component isn't found yet. That will be handled in wait timeout check. - _checkInitialLoadComplete(false /* failIfNoDefaultComponent */); + _checkInitialLoadComplete(); qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "_parameterUpdate complete"; } @@ -409,33 +392,11 @@ void ParameterManager::refreshAllParameters(uint8_t componentId) qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Request to refresh all parameters for component ID:" << what; } -void ParameterManager::_determineDefaultComponentId(void) -{ - if (_defaultComponentId == MAV_COMP_ID_ALL) { - // We don't have a default component id yet. That means the plugin can't provide - // the param to trigger off of. Instead we use the most prominent component id in - // the set of parameters. Better than nothing! - - int largestCompParamCount = 0; - foreach(int componentId, _mapParameterName2Variant.keys()) { - int compParamCount = _mapParameterName2Variant[componentId].count(); - if (compParamCount > largestCompParamCount) { - largestCompParamCount = compParamCount; - _defaultComponentId = componentId; - } - } - - if (_defaultComponentId == MAV_COMP_ID_ALL) { - qWarning() << _logVehiclePrefix() << "All parameters missing, unable to determine default component id"; - } - } -} - /// Translates FactSystem::defaultComponentId to real component id if needed int ParameterManager::_actualComponentId(int componentId) { if (componentId == FactSystem::defaultComponentId) { - componentId = _defaultComponentId; + componentId = _vehicle->defaultComponentId(); if (componentId == FactSystem::defaultComponentId) { qWarning() << _logVehiclePrefix() << "Default component id not set"; } @@ -569,18 +530,17 @@ void ParameterManager::_waitingParamTimeout(void) } } - if (!paramsRequested && _defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty() && !_waitingForDefaultComponent) { - // Initial load is complete but we still don't have default component params. Wait one more cycle to see if the - // default component finally shows up. - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer - still don't have default component id"; + if (!paramsRequested && !_waitingForDefaultComponent && !_mapParameterName2Variant.contains(_vehicle->defaultComponentId())) { + // Initial load is complete but we still don't have any default component params. Wait one more cycle to see if the + // any show up. + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer - still don't have default component params"; _waitingParamTimeoutTimer.start(); _waitingForDefaultComponent = true; return; } _waitingForDefaultComponent = false; - // Check for initial load complete success/failure. Fail load if we don't have a default component at this point. - _checkInitialLoadComplete(true /* failIfNoDefaultComponent */); + _checkInitialLoadComplete(); if (!paramsRequested) { foreach(int componentId, _waitingWriteParamNameMap.keys()) { @@ -955,11 +915,6 @@ FactMetaData::ValueType_t ParameterManager::_mavTypeToFactType(MAV_PARAM_TYPE ma void ParameterManager::_addMetaDataToDefaultComponent(void) { - if (_defaultComponentId == MAV_COMP_ID_ALL) { - // We don't know what the default component is so we can't support meta data - return; - } - if (_parameterMetaData) { return; } @@ -974,14 +929,13 @@ void ParameterManager::_addMetaDataToDefaultComponent(void) _parameterMetaData = _vehicle->firmwarePlugin()->loadParameterMetaData(metaDataFile); // Loop over all parameters in default component adding meta data - QVariantMap& factMap = _mapParameterName2Variant[_defaultComponentId]; + QVariantMap& factMap = _mapParameterName2Variant[_vehicle->defaultComponentId()]; foreach (const QString& key, factMap.keys()) { _vehicle->firmwarePlugin()->addMetaDataToFact(_parameterMetaData, factMap[key].value(), _vehicle->vehicleType()); } } -/// @param failIfNoDefaultComponent true: Fails parameter load if no default component but we should have one -void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) +void ParameterManager::_checkInitialLoadComplete(void) { // Already processed? if (_initialLoadComplete) { @@ -995,11 +949,6 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) } } - if (!failIfNoDefaultComponent && _defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { - // We are still waiting for default component to show up - return; - } - // We aren't waiting for any more initial parameter updates, initial parameter loading is complete _initialLoadComplete = true; @@ -1031,23 +980,10 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) if (!qgcApp()->runningUnitTests()) { qCWarning(ParameterManagerLog) << _logVehiclePrefix() << "The following parameter indices could not be loaded after the maximum number of retries: " << indexList; } - } else if (_defaultComponentId == MAV_COMP_ID_ALL && !_defaultComponentIdParam.isEmpty()) { - // Missing default component when we should have one - _missingParameters = true; - QString errorMsg = tr("QGroundControl did not receive parameters from the default component for vehicle %1. " - "This will cause QGroundControl to be unable to display its full user interface. " - "If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. " - "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue.").arg(_vehicle->id()); - qCDebug(ParameterManagerLog) << errorMsg; - qgcApp()->showMessage(errorMsg); - if (!qgcApp()->runningUnitTests()) { - qCWarning(ParameterManagerLog) << _logVehiclePrefix() << "Default component was never found, param:" << _defaultComponentIdParam; - } } // Signal load complete _parametersReady = true; - _determineDefaultComponentId(); _vehicle->autopilotPlugin()->parametersReadyPreChecks(); emit parametersReadyChanged(true); emit missingParametersChanged(_missingParameters); @@ -1056,12 +992,11 @@ void ParameterManager::_checkInitialLoadComplete(bool failIfNoDefaultComponent) void ParameterManager::_initialRequestTimeout(void) { if (!_disableAllRetries && ++_initialRequestRetryCount <= _maxInitialRequestListRetry) { - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Retyring initial parameter request list"; + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Retrying initial parameter request list"; refreshAllParameters(); _initialRequestTimeoutTimer.start(); } else { if (!_vehicle->genericFirmware()) { - // Generic vehicles (like BeBop) may not have any parameters, so don't annoy the user QString errorMsg = tr("Vehicle %1 did not respond to request for parameters. " "This will cause QGroundControl to be unable to display its full user interface.").arg(_vehicle->id()); qCDebug(ParameterManagerLog) << errorMsg; @@ -1282,7 +1217,8 @@ void ParameterManager::_loadOfflineEditingParams(void) QStringList paramData = line.split("\t"); Q_ASSERT(paramData.count() == 5); - _defaultComponentId = paramData.at(1).toInt(); + int defaultComponentId = paramData.at(1).toInt(); + _vehicle->setOfflineEditingDefaultComponentId(defaultComponentId); QString paramName = paramData.at(2); QString valStr = paramData.at(3); MAV_PARAM_TYPE paramType = static_cast(paramData.at(4).toUInt()); @@ -1321,8 +1257,8 @@ void ParameterManager::_loadOfflineEditingParams(void) _parameterSetMajorVersion = paramValue.toInt(); } - Fact* fact = new Fact(_defaultComponentId, paramName, _mavTypeToFactType(paramType), this); - _mapParameterName2Variant[_defaultComponentId][paramName] = QVariant::fromValue(fact); + Fact* fact = new Fact(defaultComponentId, paramName, _mavTypeToFactType(paramType), this); + _mapParameterName2Variant[defaultComponentId][paramName] = QVariant::fromValue(fact); } _addMetaDataToDefaultComponent(); diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 106426277..a2c65c015 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -104,8 +104,6 @@ public: /// If this file is newer than anything in the cache, cache it as the latest version static void cacheMetaDataFile(const QString& metaDataFile, MAV_AUTOPILOT firmwareType); - int defaultComponentId(void) { return _defaultComponentId; } - /// Saves the specified param set to the json object. /// @param componentId Component id which contains params, MAV_COMP_ID_ALL to save all components /// @param paramsToSave List of params names to save, empty to save all for component @@ -139,7 +137,6 @@ protected: private: static QVariant _stringToTypedVariant(const QString& string, FactMetaData::ValueType_t type, bool failOk = false); int _actualComponentId(int componentId); - void _determineDefaultComponentId(void); void _setupGroupMap(void); void _readParameterRaw(int componentId, const QString& paramName, int paramIndex); void _writeParameterRaw(int componentId, const QString& paramName, const QVariant& value); @@ -154,7 +151,7 @@ private: MAV_PARAM_TYPE _factTypeToMavType(FactMetaData::ValueType_t factType); FactMetaData::ValueType_t _mavTypeToFactType(MAV_PARAM_TYPE mavType); void _saveToEEPROM(void); - void _checkInitialLoadComplete(bool failIfNoDefaultComponent); + void _checkInitialLoadComplete(void); /// First mapping is by component id /// Second mapping is parameter name, to Fact* in QVariant @@ -172,8 +169,6 @@ private: bool _initialLoadComplete; ///< true: Initial load of all parameters complete, whether successful or not bool _waitingForDefaultComponent; ///< true: last chance wait for default component params bool _saveRequired; ///< true: _saveToEEPROM should be called - int _defaultComponentId; - QString _defaultComponentIdParam; ///< Parameter which identifies default component QString _versionParam; ///< Parameter which contains parameter set version int _parameterSetMajorVersion; ///< Version for parameter set, -1 if not known QObject* _parameterMetaData; ///< Opaque data from FirmwarePlugin::loadParameterMetaDataCall diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index 6dcfac972..6ea8bc4d2 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -88,7 +88,6 @@ public: void initializeVehicle (Vehicle* vehicle) final; bool sendHomePositionToVehicle (void) final; void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; - QString getDefaultComponentIdParam (void) const final { return QString("SYSID_SW_TYPE"); } QString missionCommandOverrides (MAV_TYPE vehicleType) const; QString getVersionParam (void) final { return QStringLiteral("SYSID_SW_MREV"); } QString internalParameterMetaDataFile (Vehicle* vehicle) final; diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 0c0eb0f77..81c7dd623 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -187,9 +187,6 @@ public: /// false: Do not send first item to vehicle, sequence numbers must be adjusted virtual bool sendHomePositionToVehicle(void); - /// Returns the parameter that is used to identify the default component - virtual QString getDefaultComponentIdParam(void) const { return QString(); } - /// Returns the parameter which is used to identify the version number of parameter set virtual QString getVersionParam(void) { return QString(); } diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index ac1ab600e..718f8b7f4 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -50,7 +50,6 @@ public: void initializeVehicle (Vehicle* vehicle) final; bool sendHomePositionToVehicle (void) final; void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; - QString getDefaultComponentIdParam (void) const final { return QString("SYS_AUTOSTART"); } QString missionCommandOverrides (MAV_TYPE vehicleType) const final; QString getVersionParam (void) final { return QString("SYS_PARAM_VER"); } QString internalParameterMetaDataFile (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } diff --git a/src/MissionManager/GeoFenceController.cc b/src/MissionManager/GeoFenceController.cc index 7dbd479f2..5a00533dc 100644 --- a/src/MissionManager/GeoFenceController.cc +++ b/src/MissionManager/GeoFenceController.cc @@ -279,7 +279,7 @@ void GeoFenceController::saveToFile(const QString& filename) paramNames.append(params[i].value()->name()); } if (paramNames.count() > 0) { - paramMgr->saveToJson(paramMgr->defaultComponentId(), paramNames, fenceFileObject); + paramMgr->saveToJson(_activeVehicle->defaultComponentId(), paramNames, fenceFileObject); } if (breachReturnEnabled()) { diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc index 6fe546c04..6613ef4ed 100644 --- a/src/QmlControls/ParameterEditorController.cc +++ b/src/QmlControls/ParameterEditorController.cc @@ -33,7 +33,10 @@ ParameterEditorController::ParameterEditorController(void) _componentIds += QString("%1").arg(componentId); } - _currentGroup = groupMap[_currentComponentId].keys()[0]; + // Be careful about no parameters + if (groupMap.contains(_currentComponentId) && groupMap[_currentComponentId].size() != 0) { + _currentGroup = groupMap[_currentComponentId].keys()[0]; + } _updateParameters(); connect(this, &ParameterEditorController::searchTextChanged, this, &ParameterEditorController::_updateParameters); diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 6d363948b..fdab658a1 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -68,7 +68,7 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) this); } -void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType) +void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType) { if (_ignoreVehicleIds.contains(vehicleId) || getVehicleById(vehicleId) || vehicleId == 0) { return; @@ -86,9 +86,10 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle break; } - qCDebug(MultiVehicleManagerLog()) << "Adding new vehicle link:vehicleId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType " + qCDebug(MultiVehicleManagerLog()) << "Adding new vehicle link:vehicleId:componentId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType " << link->getName() << vehicleId + << componentId << vehicleMavlinkVersion << vehicleFirmwareType << vehicleType; @@ -107,7 +108,7 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle // return; // } - Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _joystickManager); + Vehicle* vehicle = new Vehicle(link, vehicleId, componentId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _joystickManager); connect(vehicle, &Vehicle::allLinksInactive, this, &MultiVehicleManager::_deleteVehiclePhase1); connect(vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &MultiVehicleManager::_vehicleParametersReadyChanged); diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 8dc19950f..c8d5e3134 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -94,7 +94,7 @@ private slots: void _setActiveVehiclePhase2(void); void _vehicleParametersReadyChanged(bool parametersReady); void _sendGCSHeartbeat(void); - void _vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); + void _vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); private: bool _vehicleExists(int vehicleId); diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 4876910d6..347abce3b 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -58,12 +58,14 @@ const int Vehicle::_lowBatteryAnnounceRepeatMSecs = 30 * 1000; Vehicle::Vehicle(LinkInterface* link, int vehicleId, + int defaultComponentId, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, JoystickManager* joystickManager) : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json") , _id(vehicleId) + , _defaultComponentId(defaultComponentId) , _active(false) , _offlineEditingVehicle(false) , _firmwareType(firmwareType) @@ -211,6 +213,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, QObject* parent) : FactGroup(_vehicleUIUpdateRateMSecs, ":/json/Vehicle/VehicleFact.json", parent) , _id(0) + , _defaultComponentId(MAV_COMP_ID_ALL) , _active(false) , _offlineEditingVehicle(true) , _firmwareType(firmwareType) @@ -1347,7 +1350,7 @@ QGeoCoordinate Vehicle::homePosition(void) void Vehicle::setArmed(bool armed) { // We specifically use COMMAND_LONG:MAV_CMD_COMPONENT_ARM_DISARM since it is supported by more flight stacks. - sendMavCommand(defaultComponentId(), + sendMavCommand(_defaultComponentId, MAV_CMD_COMPONENT_ARM_DISARM, true, // show error if fails armed ? 1.0f : 0.0f); @@ -1428,7 +1431,7 @@ void Vehicle::requestDataStream(MAV_DATA_STREAM stream, uint16_t rate, bool send dataStream.req_message_rate = rate; dataStream.start_stop = 1; // start dataStream.target_system = id(); - dataStream.target_component = defaultComponentId(); + dataStream.target_component = _defaultComponentId; mavlink_msg_request_data_stream_encode_chan(_mavlink->getSystemId(), _mavlink->getComponentId(), @@ -1850,7 +1853,7 @@ void Vehicle::setGuidedMode(bool guidedMode) void Vehicle::emergencyStop(void) { - sendMavCommand(defaultComponentId(), + sendMavCommand(_defaultComponentId, MAV_CMD_COMPONENT_ARM_DISARM, true, // show error if fails 0.0f, @@ -2019,12 +2022,7 @@ QString Vehicle::firmwareVersionTypeString(void) const void Vehicle::rebootVehicle() { - sendMavCommand(defaultComponentId(), MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true, 1.0f); -} - -int Vehicle::defaultComponentId(void) -{ - return _parameterManager->defaultComponentId(); + sendMavCommand(_defaultComponentId, MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN, true, 1.0f); } void Vehicle::setSoloFirmware(bool soloFirmware) @@ -2039,7 +2037,7 @@ void Vehicle::setSoloFirmware(bool soloFirmware) // Temporarily removed, waiting for new command implementation void Vehicle::motorTest(int motor, int percent, int timeoutSecs) { - doCommandLongUnverified(defaultComponentId(), MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); + doCommandLongUnverified(_defaultComponentId, MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); } #endif @@ -2113,6 +2111,14 @@ QStringList Vehicle::unhealthySensors(void) const return sensorList; } +void Vehicle::setOfflineEditingDefaultComponentId(int defaultComponentId) +{ + if (_offlineEditingVehicle) { + _defaultComponentId = defaultComponentId; + } else { + qWarning() << "Call to Vehicle::setOfflineEditingDefaultComponentId on vehicle which is not offline"; + } +} const char* VehicleGPSFactGroup::_hdopFactName = "hdop"; const char* VehicleGPSFactGroup::_vdopFactName = "vdop"; @@ -2141,12 +2147,12 @@ VehicleGPSFactGroup::VehicleGPSFactGroup(QObject* parent) void Vehicle::startMavlinkLog() { - sendMavCommand(defaultComponentId(), MAV_CMD_LOGGING_START, false /* showError */); + sendMavCommand(_defaultComponentId, MAV_CMD_LOGGING_START, false /* showError */); } void Vehicle::stopMavlinkLog() { - sendMavCommand(defaultComponentId(), MAV_CMD_LOGGING_STOP, false /* showError */); + sendMavCommand(_defaultComponentId, MAV_CMD_LOGGING_STOP, false /* showError */); } void Vehicle::_ackMavlinkLogData(uint16_t sequence) @@ -2154,7 +2160,7 @@ void Vehicle::_ackMavlinkLogData(uint16_t sequence) mavlink_message_t msg; mavlink_logging_ack_t ack; ack.sequence = sequence; - ack.target_component = defaultComponentId(); + ack.target_component = _defaultComponentId; ack.target_system = id(); mavlink_msg_logging_ack_encode_chan( _mavlink->getSystemId(), diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index a765f876a..82bc21a1d 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -191,6 +191,7 @@ class Vehicle : public FactGroup public: Vehicle(LinkInterface* link, int vehicleId, + int defaultComponentId, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, @@ -567,7 +568,10 @@ public: bool soloFirmware(void) const { return _soloFirmware; } void setSoloFirmware(bool soloFirmware); - int defaultComponentId(void); + int defaultComponentId(void) { return _defaultComponentId; } + + /// Sets the default component id for an offline editing vehicle + void setOfflineEditingDefaultComponentId(int defaultComponentId); /// @return -1 = Unknown, Number of motors on vehicle int motorCount(void); @@ -736,6 +740,7 @@ private: void _commonInit(void); int _id; ///< Mavlink system id + int _defaultComponentId; bool _active; bool _offlineEditingVehicle; ///< This Vehicle is a "disconnected" vehicle for ui use while offline editing diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 17f1e853c..d32455a9f 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -283,7 +283,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) mavlink_heartbeat_t heartbeat; mavlink_msg_heartbeat_decode(&message, &heartbeat); - emit vehicleHeartbeatInfo(link, message.sysid, heartbeat.mavlink_version, heartbeat.autopilot, heartbeat.type); + emit vehicleHeartbeatInfo(link, message.sysid, message.compid, heartbeat.mavlink_version, heartbeat.autopilot, heartbeat.type); } // Increase receive counter diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 346de17fe..3d1cd1657 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -130,7 +130,7 @@ protected: signals: /// Heartbeat received on link - void vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); + void vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); /** @brief Message received and directly copied via signal */ void messageReceived(LinkInterface* link, mavlink_message_t message); -- GitLab From 5f0bd3d4875db58b590634d5164a4770d0ed8b83 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 13 Feb 2017 23:02:33 -0500 Subject: [PATCH 276/398] Experimenting with Video / Photo commands --- src/uas/UAS.cc | 45 +++++++++++++++++++++++++++++++++++------- src/uas/UAS.h | 4 ++++ src/uas/UASInterface.h | 4 ++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 6be68ccaf..bbbd9acc8 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -1209,15 +1209,46 @@ void UAS::setManual6DOFControlCommands(double x, double y, double z, double roll */ void UAS::pairRX(int rxType, int rxSubType) { - if (!_vehicle) { - return; + if (_vehicle) { + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_IMAGE_START_CAPTURE, // command id + MAV_CMD_START_RX_PAIR, // command id + true, // showError + rxType, + rxSubType); } +} - _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component - MAV_CMD_START_RX_PAIR, // command id - true, // showError - rxType, - rxSubType); +/** +* Order the robot to take a picture (Testing -- Incomplete API) +*/ +void UAS::takePhoto() +{ + if (_vehicle) { + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_IMAGE_START_CAPTURE, // command id + 0, // Duration between two consecutive pictures (in seconds) + 1, // Number of images to capture total - 0 for unlimited capture + 0, // Resolution in megapixels (0.3 for 640x480, 1.3 for 1280x720, etc), set to 0 if param 4/5 are used + 1920, // Resolution horizontal in pixels + 1080); // Resolution horizontal in pixels + } +} + +/** +* Order the robot to toggle video recording (Testing -- Incomplete API) +*/ +void UAS::toggleVideo() +{ + if (_vehicle) { + _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component + MAV_CMD_VIDEO_START_CAPTURE, // command id + 0, // Camera ID (0 for all cameras), 1 for first, 2 for second, etc. + 60, // Frames per second + 0, // Resolution in megapixels (0.3 for 640x480, 1.3 for 1280x720, etc), set to 0 if param 4/5 are used + 1920, // Resolution horizontal in pixels + 1080); // Resolution horizontal in pixels + } } /** diff --git a/src/uas/UAS.h b/src/uas/UAS.h index 4dad4d5fd..4820fb41a 100644 --- a/src/uas/UAS.h +++ b/src/uas/UAS.h @@ -262,6 +262,10 @@ public: public slots: /** @brief Order the robot to pair its receiver **/ void pairRX(int rxType, int rxSubType); + /** @brief Order the robot to take a picture **/ + void takePhoto(); + /** @brief Order the robot to toggle video recording **/ + void toggleVideo(); /** @brief Enable / disable HIL */ #ifndef __mobile__ diff --git a/src/uas/UASInterface.h b/src/uas/UASInterface.h index 8ce2460b9..515fb599b 100644 --- a/src/uas/UASInterface.h +++ b/src/uas/UASInterface.h @@ -137,6 +137,10 @@ public: public slots: /** @brief Order the robot to pair its receiver **/ virtual void pairRX(int rxType, int rxSubType) = 0; + /** @brief Order the robot to take a picture **/ + virtual void takePhoto() {} + /** @brief Order the robot to toggle video recording **/ + virtual void toggleVideo() {} /** @brief Send the full HIL state to the MAV */ #ifndef __mobile__ -- GitLab From 4f5f7a0ac23cd08cb183a6d6cd71499cdf3c27d4 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 13 Feb 2017 23:24:34 -0500 Subject: [PATCH 277/398] Copy/Paste error. --- src/uas/UAS.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index bbbd9acc8..633ba60df 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -1211,7 +1211,6 @@ void UAS::pairRX(int rxType, int rxSubType) { if (_vehicle) { _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component - MAV_CMD_IMAGE_START_CAPTURE, // command id MAV_CMD_START_RX_PAIR, // command id true, // showError rxType, -- GitLab From fedb83612245e3c0bce582fc2385227ac4cacf69 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 13 Feb 2017 23:27:49 -0500 Subject: [PATCH 278/398] Maybe the third time I get this right? --- src/uas/UAS.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 633ba60df..ebe066245 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -1226,6 +1226,7 @@ void UAS::takePhoto() if (_vehicle) { _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component MAV_CMD_IMAGE_START_CAPTURE, // command id + true, // showError 0, // Duration between two consecutive pictures (in seconds) 1, // Number of images to capture total - 0 for unlimited capture 0, // Resolution in megapixels (0.3 for 640x480, 1.3 for 1280x720, etc), set to 0 if param 4/5 are used @@ -1242,6 +1243,7 @@ void UAS::toggleVideo() if (_vehicle) { _vehicle->sendMavCommand(_vehicle->defaultComponentId(), // target component MAV_CMD_VIDEO_START_CAPTURE, // command id + true, // showError 0, // Camera ID (0 for all cameras), 1 for first, 2 for second, etc. 60, // Frames per second 0, // Resolution in megapixels (0.3 for 640x480, 1.3 for 1280x720, etc), set to 0 if param 4/5 are used -- GitLab From bcb69abd0c693aab84fcc4d5bb2d9190008af6ac Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 14 Feb 2017 17:09:09 -0800 Subject: [PATCH 279/398] DoubleValidator causing problems with regional settings Causing data loss --- src/FactSystem/FactControls/FactTextField.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index bf0b4f956..b2e508728 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -20,10 +20,9 @@ QGCTextField { property string _validateString // At this point all Facts are numeric - validator: DoubleValidator {} - inputMethodHints: ScreenTools.isiOS ? - Qt.ImhNone : // iOS numeric keyboard has not done button, we can't use it - Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard + inputMethodHints: ScreenTools.isiOS ? + Qt.ImhNone : // iOS numeric keyboard has not done button, we can't use it + Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard onEditingFinished: { if (typeof qgcView !== 'undefined' && qgcView) { -- GitLab From 3125494433b0db51f092ad7478a3b31930d4e511 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 14 Feb 2017 18:25:06 -0800 Subject: [PATCH 280/398] Fix position of children of complex items --- src/MissionEditor/MissionEditor.qml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index f2eee50d5..889f82968 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -467,6 +467,24 @@ QGCView { missionItem: object sequenceNumber: object.lastSequenceNumber visible: object.specifiesCoordinate + + // These are the non-coordinate child mission items attached to this item + Row { + anchors.top: parent.top + anchors.left: parent.right + + Repeater { + model: !object.isSimpleItem ? object.childItems : 0 + + delegate: MissionItemIndexLabel { + label: object.abbreviation + checked: object.isCurrentItem + z: 2 + + onClicked: setCurrentItem(object.sequenceNumber) + } + } + } } } @@ -514,7 +532,7 @@ QGCView { anchors.left: parent.right Repeater { - model: object.childItems + model: object.isSimpleItem ? object.childItems : 0 delegate: MissionItemIndexLabel { label: object.abbreviation -- GitLab From 17eee4a9e609febb36fe3f97ec903d93801c9b6c Mon Sep 17 00:00:00 2001 From: Alexey Bulatov Date: Tue, 24 Jan 2017 15:53:13 +0300 Subject: [PATCH 281/398] Make virtual method isAutoConnectAllowed to return false --- src/comm/BluetoothLink.h | 1 - src/comm/LinkConfiguration.h | 2 +- src/comm/LogReplayLink.h | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/comm/BluetoothLink.h b/src/comm/BluetoothLink.h index 9e9d318bb..e639ac37d 100644 --- a/src/comm/BluetoothLink.h +++ b/src/comm/BluetoothLink.h @@ -104,7 +104,6 @@ public: void loadSettings (QSettings& settings, const QString& root); void saveSettings (QSettings& settings, const QString& root); void updateSettings (); - bool isAutoConnectAllowed () { return false; } QString settingsURL () { return "BluetoothSettings.qml"; } public slots: diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 42a181bf4..76a4bed12 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -93,7 +93,7 @@ public: * Is Auto Connect allowed for this type? * @return True if this type can be set as an Auto Connect configuration */ - virtual bool isAutoConnectAllowed() { return true; } + virtual bool isAutoConnectAllowed() { return false; } /*! * @brief Connection type diff --git a/src/comm/LogReplayLink.h b/src/comm/LogReplayLink.h index 2b7875626..10b55d8bd 100644 --- a/src/comm/LogReplayLink.h +++ b/src/comm/LogReplayLink.h @@ -40,7 +40,6 @@ public: void loadSettings (QSettings& settings, const QString& root); void saveSettings (QSettings& settings, const QString& root); void updateSettings (); - bool isAutoConnectAllowed () { return false; } QString settingsURL () { return "LogReplaySettings.qml"; } signals: void fileNameChanged(); -- GitLab From 498a8f1c5633a3914029f0ece99bf0981af4f636 Mon Sep 17 00:00:00 2001 From: Alexey Bulatov Date: Tue, 24 Jan 2017 15:57:04 +0300 Subject: [PATCH 282/398] Create connected link if set AutoConnect --- src/QGCApplication.cc | 3 +++ src/comm/LinkManager.cc | 11 +++++++++++ src/comm/LinkManager.h | 2 ++ 3 files changed, 16 insertions(+) diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index d436f6832..baa84cfbe 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -439,6 +439,9 @@ bool QGCApplication::_initForNormalAppBoot(void) "Your saved settings have been reset to defaults."); } + // Connect links with flag AutoconnectLink + toolbox()->linkManager()->startAutoConnectedLinks(); + if (getQGCMapEngine()->wasCacheReset()) { showMessage("The Offline Map Cache database has been upgraded. " "Your old map cache sets have been reset."); diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 68d31c11f..09233d44c 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -990,3 +990,14 @@ QList LinkManager::links(void) return rawLinks; } + +void LinkManager::startAutoConnectedLinks(void) +{ + SharedLinkConfigurationPointer conf; + + for(int i = 0; i < _sharedConfigurations.count(); i++) { + conf = _sharedConfigurations[i]; + if (conf->isAutoConnect()) + createConnectedLink(conf); + } +} diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 9383c38a0..8a2031b86 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -171,6 +171,8 @@ public: SharedLinkConfigurationPointer addConfiguration(LinkConfiguration* config); + void startAutoConnectedLinks(void); + signals: void autoconnectUDPChanged (bool autoconnect); void autoconnectPixhawkChanged (bool autoconnect); -- GitLab From eee84bfffd8d4b5a0493b8b213dca2e9fe65bab9 Mon Sep 17 00:00:00 2001 From: Alexey Bulatov Date: Tue, 24 Jan 2017 15:57:51 +0300 Subject: [PATCH 283/398] Checkbox for auto connect on start --- src/ui/preferences/LinkSettings.qml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ui/preferences/LinkSettings.qml b/src/ui/preferences/LinkSettings.qml index 447982474..cd4d75be0 100644 --- a/src/ui/preferences/LinkSettings.qml +++ b/src/ui/preferences/LinkSettings.qml @@ -271,12 +271,11 @@ Rectangle { height: ScreenTools.defaultFontPixelHeight * 0.5 width: parent.width } - /* - //-- Auto Connect + //-- Auto Connect on Start QGCCheckBox { text: "Automatically Connect on Start" checked: false - enabled: editConfig ? editConfig.autoConnectAllowed : false + visible: editConfig ? editConfig.autoConnectAllowed : false onCheckedChanged: { if(editConfig) { editConfig.autoConnect = checked @@ -287,7 +286,6 @@ Rectangle { checked = editConfig.autoConnect } } - */ Item { height: ScreenTools.defaultFontPixelHeight width: parent.width -- GitLab From 2d75a33c44a3be1194fe00060d86c8ae5e1da0a9 Mon Sep 17 00:00:00 2001 From: Alexey Bulatov Date: Tue, 24 Jan 2017 15:59:07 +0300 Subject: [PATCH 284/398] Allow autoconnect on start for UDP links --- src/comm/UDPLink.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/comm/UDPLink.h b/src/comm/UDPLink.h index df0c58e73..a20efa26e 100644 --- a/src/comm/UDPLink.h +++ b/src/comm/UDPLink.h @@ -131,12 +131,13 @@ public: QStringList hostList () { return _hostList; } /// From LinkConfiguration - LinkType type () { return LinkConfiguration::TypeUdp; } - void copyFrom (LinkConfiguration* source); - void loadSettings (QSettings& settings, const QString& root); - void saveSettings (QSettings& settings, const QString& root); - void updateSettings (); - QString settingsURL () { return "UdpSettings.qml"; } + LinkType type () { return LinkConfiguration::TypeUdp; } + void copyFrom (LinkConfiguration* source); + void loadSettings (QSettings& settings, const QString& root); + void saveSettings (QSettings& settings, const QString& root); + void updateSettings (); + bool isAutoConnectAllowed () { return true; } + QString settingsURL () { return "UdpSettings.qml"; } signals: void localPortChanged (); -- GitLab From 4dab0e1e4c5d2d9bc041de4b711edf12a0384664 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 11:10:19 -0800 Subject: [PATCH 285/398] Handle ArudPilot parameter changes in newer firmwares --- .../APM/APMCameraComponent.qml | 51 +- .../APM/APMLightsComponent.qml | 30 +- .../APM/APMLightsComponentSummary.qml | 26 +- .../APM/APMTuningComponentCopter.qml | 2 +- .../Common/RadioComponentController.cc | 227 +- .../Common/RadioComponentController.h | 19 +- src/FactSystem/FactControls/FactCheckBox.qml | 7 +- .../FactControls/FactPanelController.cc | 3 +- src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 56 +- .../APMParameterFactMetaData.Copter.3.5.xml | 8518 ++++++++++------- .../APMParameterFactMetaData.Plane.3.8.xml | 8496 ++++++++++++++++ .../APMParameterFactMetaData.Rover.3.2.xml | 8496 ++++++++++++++++ src/FirmwarePlugin/APM/APMResources.qrc | 2 + .../APM/ArduCopterFirmwarePlugin.cc | 65 +- .../APM/ArduPlaneFirmwarePlugin.cc | 30 +- .../APM/ArduPlaneFirmwarePlugin.h | 6 + .../APM/ArduRoverFirmwarePlugin.cc | 31 +- .../APM/ArduRoverFirmwarePlugin.h | 8 + 18 files changed, 22488 insertions(+), 3585 deletions(-) create mode 100644 src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml create mode 100644 src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml diff --git a/src/AutoPilotPlugins/APM/APMCameraComponent.qml b/src/AutoPilotPlugins/APM/APMCameraComponent.qml index 6ff44a5ce..0eafaade9 100644 --- a/src/AutoPilotPlugins/APM/APMCameraComponent.qml +++ b/src/AutoPilotPlugins/APM/APMCameraComponent.qml @@ -55,21 +55,23 @@ SetupPage { property Fact _mountAngMinPan: controller.getParameterFact(-1, "MNT_ANGMIN_PAN") property Fact _mountAngMaxPan: controller.getParameterFact(-1, "MNT_ANGMAX_PAN") - property Fact _rc5Function: controller.getParameterFact(-1, "RC5_FUNCTION") - property Fact _rc6Function: controller.getParameterFact(-1, "RC6_FUNCTION") - property Fact _rc7Function: controller.getParameterFact(-1, "RC7_FUNCTION") - property Fact _rc8Function: controller.getParameterFact(-1, "RC8_FUNCTION") - property Fact _rc9Function: controller.getParameterFact(-1, "RC9_FUNCTION") - property Fact _rc10Function: controller.getParameterFact(-1, "RC10_FUNCTION") - property Fact _rc11Function: controller.getParameterFact(-1, "RC11_FUNCTION") - property Fact _rc12Function: controller.getParameterFact(-1, "RC12_FUNCTION") - property Fact _rc13Function: controller.getParameterFact(-1, "RC13_FUNCTION") - property Fact _rc14Function: controller.getParameterFact(-1, "RC14_FUNCTION") + property Fact _rc5Function: controller.getParameterFact(-1, "r.SERVO5_FUNCTION") + property Fact _rc6Function: controller.getParameterFact(-1, "r.SERVO6_FUNCTION") + property Fact _rc7Function: controller.getParameterFact(-1, "r.SERVO7_FUNCTION") + property Fact _rc8Function: controller.getParameterFact(-1, "r.SERVO8_FUNCTION") + property Fact _rc9Function: controller.getParameterFact(-1, "r.SERVO9_FUNCTION") + property Fact _rc10Function: controller.getParameterFact(-1, "r.SERVO10_FUNCTION") + property Fact _rc11Function: controller.getParameterFact(-1, "r.SERVO11_FUNCTION") + property Fact _rc12Function: controller.getParameterFact(-1, "r.SERVO12_FUNCTION") + property Fact _rc13Function: controller.getParameterFact(-1, "r.SERVO13_FUNCTION") + property Fact _rc14Function: controller.getParameterFact(-1, "r.SERVO14_FUNCTION") property bool _tiltEnabled: false property bool _panEnabled: false property bool _rollEnabled: false + property bool _servoReverseIsBool: controller.parameterExists(-1, "RC5_REVERSED") + // Gimbal Settings not available on older firmware property bool _showGimbaLSettings: controller.parameterExists(-1, "MNT_DEFLT_MODE") @@ -95,10 +97,17 @@ SetupPage { loader.gimbalOutIndex = channel - 4 loader.servoPWMMinFact = controller.getParameterFact(-1, rcPrefix + "MIN") loader.servoPWMMaxFact = controller.getParameterFact(-1, rcPrefix + "MAX") - loader.servoReverseFact = controller.getParameterFact(-1, rcPrefix + "REV") + if (controller.parameterExists(-1, "RC5_REVERSED")) { + // Newer firmware parameter + loader.servoReverseFact = controller.getParameterFact(-1, rcPrefix + "REVERSED") + } else { + // Older firmware parameter + loader.servoReverseFact = controller.getParameterFact(-1, rcPrefix + "REV") + } + } - /// Gimbal output channels are stored in RC#_FUNCTION parameters. We need to loop through those + /// Gimbal output channels are stored in SERVO#_FUNCTION parameters. We need to loop through those /// to find them and setup the ui accordindly. function calcGimbalOutValues() { gimbalDirectionTiltLoader.gimbalOutIndex = 0 @@ -108,7 +117,7 @@ SetupPage { _panEnabled = false _rollEnabled = false for (var channel=_firstGimbalOutChannel; channel<=_lastGimbalOutChannel; channel++) { - var functionFact = controller.getParameterFact(-1, "RC" + channel + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + channel + "_FUNCTION") if (functionFact.value == _rcFunctionMountTilt) { _tiltEnabled = true setGimbalSettingsServoInfo(gimbalDirectionTiltLoader, channel) @@ -125,7 +134,7 @@ SetupPage { function setRCFunction(channel, rcFunction) { // First clear any previous settings for this function for (var index=_firstGimbalOutChannel; index<=_lastGimbalOutChannel; index++) { - var functionFact = controller.getParameterFact(-1, "RC" + index + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + index + "_FUNCTION") if (functionFact.value != _rcFunctionDisabled && functionFact.value == rcFunction) { functionFact.value = _rcFunctionDisabled } @@ -133,12 +142,12 @@ SetupPage { // Now set the function into the new channel if (channel != 0) { - var functionFact = controller.getParameterFact(-1, "RC" + channel + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + channel + "_FUNCTION") functionFact.value = rcFunction } } - // Whenever any RC#_FUNCTION parameters chagnes we need to go looking for gimbal output channels again + // Whenever any SERVO#_FUNCTION parameters changes we need to go looking for gimbal output channels again Connections { target: _rc5Function; onValueChanged: calcGimbalOutValues() } Connections { target: _rc6Function; onValueChanged: calcGimbalOutValues() } Connections { target: _rc7Function; onValueChanged: calcGimbalOutValues() } @@ -195,6 +204,7 @@ SetupPage { // property Fact servoPWMMinFact // property Fact servoPWMMaxFact // property Fact servoReverseFact + // property bool servoReverseIsBool // property int rcFunction Item { @@ -234,10 +244,12 @@ SetupPage { anchors.top: mountStabCheckBox.bottom anchors.right: parent.right text: qsTr("Servo reverse") - checkedValue: 1 - uncheckedValue: 0 + checkedValue: _servoReverseIsBool ? 1 : -1 + uncheckedValue: _servoReverseIsBool ? 0 : 1 fact: servoReverseFact enabled: directionEnabled + + property bool _servoReverseIsBool: servoReverseIsBool } QGCLabel { @@ -462,6 +474,7 @@ SetupPage { property Fact servoPWMMinFact: Fact { } property Fact servoPWMMaxFact: Fact { } property Fact servoReverseFact: Fact { } + property bool servoReverseIsBool: _servoReverseIsBool property int rcFunction: _rcFunctionMountTilt } @@ -479,6 +492,7 @@ SetupPage { property Fact servoPWMMinFact: Fact { } property Fact servoPWMMaxFact: Fact { } property Fact servoReverseFact: Fact { } + property bool servoReverseIsBool: _servoReverseIsBool property int rcFunction: _rcFunctionMountRoll } @@ -496,6 +510,7 @@ SetupPage { property Fact servoPWMMinFact: Fact { } property Fact servoPWMMaxFact: Fact { } property Fact servoReverseFact: Fact { } + property bool servoReverseIsBool: _servoReverseIsBool property int rcFunction: _rcFunctionMountPan } diff --git a/src/AutoPilotPlugins/APM/APMLightsComponent.qml b/src/AutoPilotPlugins/APM/APMLightsComponent.qml index 42c76d1a2..aefb1d923 100644 --- a/src/AutoPilotPlugins/APM/APMLightsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMLightsComponent.qml @@ -32,16 +32,16 @@ SetupPage { QGCPalette { id: palette; colorGroupEnabled: true } - property Fact _rc5Function: controller.getParameterFact(-1, "RC5_FUNCTION") - property Fact _rc6Function: controller.getParameterFact(-1, "RC6_FUNCTION") - property Fact _rc7Function: controller.getParameterFact(-1, "RC7_FUNCTION") - property Fact _rc8Function: controller.getParameterFact(-1, "RC8_FUNCTION") - property Fact _rc9Function: controller.getParameterFact(-1, "RC9_FUNCTION") - property Fact _rc10Function: controller.getParameterFact(-1, "RC10_FUNCTION") - property Fact _rc11Function: controller.getParameterFact(-1, "RC11_FUNCTION") - property Fact _rc12Function: controller.getParameterFact(-1, "RC12_FUNCTION") - property Fact _rc13Function: controller.getParameterFact(-1, "RC13_FUNCTION") - property Fact _rc14Function: controller.getParameterFact(-1, "RC14_FUNCTION") + property Fact _rc5Function: controller.getParameterFact(-1, "r.SERVO5_FUNCTION") + property Fact _rc6Function: controller.getParameterFact(-1, "r.SERVO6_FUNCTION") + property Fact _rc7Function: controller.getParameterFact(-1, "r.SERVO7_FUNCTION") + property Fact _rc8Function: controller.getParameterFact(-1, "r.SERVO8_FUNCTION") + property Fact _rc9Function: controller.getParameterFact(-1, "r.SERVO9_FUNCTION") + property Fact _rc10Function: controller.getParameterFact(-1, "r.SERVO10_FUNCTION") + property Fact _rc11Function: controller.getParameterFact(-1, "r.SERVO11_FUNCTION") + property Fact _rc12Function: controller.getParameterFact(-1, "r.SERVO12_FUNCTION") + property Fact _rc13Function: controller.getParameterFact(-1, "r.SERVO13_FUNCTION") + property Fact _rc14Function: controller.getParameterFact(-1, "r.SERVO14_FUNCTION") readonly property real _margins: ScreenTools.defaultFontPixelHeight readonly property int _rcFunctionDisabled: 0 @@ -54,13 +54,13 @@ SetupPage { calcLightOutValues() } - /// Light output channels are stored in RC#_FUNCTION parameters. We need to loop through those + /// Light output channels are stored in SERVO#_FUNCTION parameters. We need to loop through those /// to find them and setup the ui accordindly. function calcLightOutValues() { lightsLoader.lights1OutIndex = 0 lightsLoader.lights2OutIndex = 0 for (var channel=_firstLightsOutChannel; channel<=_lastLightsOutChannel; channel++) { - var functionFact = controller.getParameterFact(-1, "RC" + channel + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + channel + "_FUNCTION") if (functionFact.value == _rcFunctionRCIN9) { lightsLoader.lights1OutIndex = channel - 4 } else if (functionFact.value == _rcFunctionRCIN10) { @@ -72,7 +72,7 @@ SetupPage { function setRCFunction(channel, rcFunction) { // First clear any previous settings for this function for (var index=_firstLightsOutChannel; index<=_lastLightsOutChannel; index++) { - var functionFact = controller.getParameterFact(-1, "RC" + index + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + index + "_FUNCTION") if (functionFact.value != _rcFunctionDisabled && functionFact.value == rcFunction) { functionFact.value = _rcFunctionDisabled } @@ -80,12 +80,12 @@ SetupPage { // Now set the function into the new channel if (channel != 0) { - var functionFact = controller.getParameterFact(-1, "RC" + channel + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + channel + "_FUNCTION") functionFact.value = rcFunction } } - // Whenever any RC#_FUNCTION parameters chagnes we need to go looking for light output channels again + // Whenever any SERVO#_FUNCTION parameters chagnes we need to go looking for light output channels again Connections { target: _rc5Function; onValueChanged: calcLightOutValues() } Connections { target: _rc6Function; onValueChanged: calcLightOutValues() } Connections { target: _rc7Function; onValueChanged: calcLightOutValues() } diff --git a/src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml b/src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml index 3eb129d28..7712df99c 100644 --- a/src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml +++ b/src/AutoPilotPlugins/APM/APMLightsComponentSummary.qml @@ -14,16 +14,16 @@ FactPanel { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } FactPanelController { id: controller; factPanel: panel } - property Fact _rc5Function: controller.getParameterFact(-1, "RC5_FUNCTION") - property Fact _rc6Function: controller.getParameterFact(-1, "RC6_FUNCTION") - property Fact _rc7Function: controller.getParameterFact(-1, "RC7_FUNCTION") - property Fact _rc8Function: controller.getParameterFact(-1, "RC8_FUNCTION") - property Fact _rc9Function: controller.getParameterFact(-1, "RC9_FUNCTION") - property Fact _rc10Function: controller.getParameterFact(-1, "RC10_FUNCTION") - property Fact _rc11Function: controller.getParameterFact(-1, "RC11_FUNCTION") - property Fact _rc12Function: controller.getParameterFact(-1, "RC12_FUNCTION") - property Fact _rc13Function: controller.getParameterFact(-1, "RC13_FUNCTION") - property Fact _rc14Function: controller.getParameterFact(-1, "RC14_FUNCTION") + property Fact _rc5Function: controller.getParameterFact(-1, "r.SERVO5_FUNCTION") + property Fact _rc6Function: controller.getParameterFact(-1, "r.SERVO6_FUNCTION") + property Fact _rc7Function: controller.getParameterFact(-1, "r.SERVO7_FUNCTION") + property Fact _rc8Function: controller.getParameterFact(-1, "r.SERVO8_FUNCTION") + property Fact _rc9Function: controller.getParameterFact(-1, "r.SERVO9_FUNCTION") + property Fact _rc10Function: controller.getParameterFact(-1, "r.SERVO10_FUNCTION") + property Fact _rc11Function: controller.getParameterFact(-1, "r.SERVO11_FUNCTION") + property Fact _rc12Function: controller.getParameterFact(-1, "r.SERVO12_FUNCTION") + property Fact _rc13Function: controller.getParameterFact(-1, "r.SERVO13_FUNCTION") + property Fact _rc14Function: controller.getParameterFact(-1, "r.SERVO14_FUNCTION") readonly property int _rcFunctionRCIN9: 59 readonly property int _rcFunctionRCIN10: 60 @@ -34,13 +34,13 @@ FactPanel { calcLightOutValues() } - /// Light output channels are stored in RC#_FUNCTION parameters. We need to loop through those + /// Light output channels are stored in SERVO#_FUNCTION parameters. We need to loop through those /// to find them and setup the ui accordindly. function calcLightOutValues() { lightsLoader.lights1OutIndex = 0 lightsLoader.lights2OutIndex = 0 for (var channel=_firstLightsOutChannel; channel<=_lastLightsOutChannel; channel++) { - var functionFact = controller.getParameterFact(-1, "RC" + channel + "_FUNCTION") + var functionFact = controller.getParameterFact(-1, "r.SERVO" + channel + "_FUNCTION") if (functionFact.value == _rcFunctionRCIN9) { lightsLoader.lights1OutIndex = channel - 4 } else if (functionFact.value == _rcFunctionRCIN10) { @@ -49,7 +49,7 @@ FactPanel { } } - // Whenever any RC#_FUNCTION parameters chagnes we need to go looking for light output channels again + // Whenever any SERVO#_FUNCTION parameters chagnes we need to go looking for light output channels again Connections { target: _rc5Function; onValueChanged: calcLightOutValues() } Connections { target: _rc6Function; onValueChanged: calcLightOutValues() } Connections { target: _rc7Function; onValueChanged: calcLightOutValues() } diff --git a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml index 2bb09e601..4547fbd3c 100644 --- a/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml +++ b/src/AutoPilotPlugins/APM/APMTuningComponentCopter.qml @@ -84,7 +84,7 @@ SetupPage { calcAutoTuneChannel() } - /// The AutoTune switch is stored in one of the RC#_FUNCTION parameters. We need to loop through those + /// The AutoTune switch is stored in one of the CH#_OPT parameters. We need to loop through those /// to find them and setup the ui accordindly. function calcAutoTuneChannel() { _autoTuneSwitchChannelIndex = 0 diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index 2af400c22..f64f29517 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -18,6 +18,7 @@ #include QGC_LOGGING_CATEGORY(RadioComponentControllerLog, "RadioComponentControllerLog") +QGC_LOGGING_CATEGORY(RadioComponentControllerVerboseLog, "RadioComponentControllerVerboseLog") #ifdef UNITTEST_BUILD // Nasty hack to expose controller to unit test code @@ -26,66 +27,78 @@ RadioComponentController* RadioComponentController::_unitTestController = NULL; const int RadioComponentController::_updateInterval = 150; ///< Interval for timer which updates radio channel widgets const int RadioComponentController::_rcCalPWMCenterPoint = ((RadioComponentController::_rcCalPWMValidMaxValue - RadioComponentController::_rcCalPWMValidMinValue) / 2.0f) + RadioComponentController::_rcCalPWMValidMinValue; -// FIXME: Double check these mins againt 150% throws -const int RadioComponentController::_rcCalPWMValidMinValue = 1300; ///< Largest valid minimum PWM Min range value -const int RadioComponentController::_rcCalPWMValidMaxValue = 1700; ///< Smallest valid maximum PWM Max range value -const int RadioComponentController::_rcCalPWMDefaultMinValue = 1000; ///< Default value for Min if not set -const int RadioComponentController::_rcCalPWMDefaultMaxValue = 2000; ///< Default value for Max if not set -const int RadioComponentController::_rcCalRoughCenterDelta = 50; ///< Delta around center point which is considered to be roughly centered -const int RadioComponentController::_rcCalMoveDelta = 300; ///< Amount of delta past center which is considered stick movement -const int RadioComponentController::_rcCalSettleDelta = 20; ///< Amount of delta which is considered no stick movement -const int RadioComponentController::_rcCalMinDelta = 100; ///< Amount of delta allowed around min value to consider channel at min +const int RadioComponentController::_rcCalPWMValidMinValue = 1300; ///< Largest valid minimum PWM Min range value +const int RadioComponentController::_rcCalPWMValidMaxValue = 1700; ///< Smallest valid maximum PWM Max range value +const int RadioComponentController::_rcCalPWMDefaultMinValue = 1000; ///< Default value for Min if not set +const int RadioComponentController::_rcCalPWMDefaultMaxValue = 2000; ///< Default value for Max if not set +const int RadioComponentController::_rcCalRoughCenterDelta = 50; ///< Delta around center point which is considered to be roughly centered +const int RadioComponentController::_rcCalMoveDelta = 300; ///< Amount of delta past center which is considered stick movement +const int RadioComponentController::_rcCalSettleDelta = 20; ///< Amount of delta which is considered no stick movement +const int RadioComponentController::_rcCalMinDelta = 100; ///< Amount of delta allowed around min value to consider channel at min const int RadioComponentController::_stickDetectSettleMSecs = 500; -const char* RadioComponentController::_imageFilePrefix = "calibration/"; +const char* RadioComponentController::_imageFilePrefix = "calibration/"; const char* RadioComponentController::_imageFileMode1Dir = "mode1/"; const char* RadioComponentController::_imageFileMode2Dir = "mode2/"; -const char* RadioComponentController::_imageCenter = "radioCenter.png"; -const char* RadioComponentController::_imageHome = "radioHome.png"; -const char* RadioComponentController::_imageThrottleUp = "radioThrottleUp.png"; +const char* RadioComponentController::_imageCenter = "radioCenter.png"; +const char* RadioComponentController::_imageHome = "radioHome.png"; +const char* RadioComponentController::_imageThrottleUp = "radioThrottleUp.png"; const char* RadioComponentController::_imageThrottleDown = "radioThrottleDown.png"; -const char* RadioComponentController::_imageYawLeft = "radioYawLeft.png"; -const char* RadioComponentController::_imageYawRight = "radioYawRight.png"; -const char* RadioComponentController::_imageRollLeft = "radioRollLeft.png"; -const char* RadioComponentController::_imageRollRight = "radioRollRight.png"; -const char* RadioComponentController::_imagePitchUp = "radioPitchUp.png"; -const char* RadioComponentController::_imagePitchDown = "radioPitchDown.png"; +const char* RadioComponentController::_imageYawLeft = "radioYawLeft.png"; +const char* RadioComponentController::_imageYawRight = "radioYawRight.png"; +const char* RadioComponentController::_imageRollLeft = "radioRollLeft.png"; +const char* RadioComponentController::_imageRollRight = "radioRollRight.png"; +const char* RadioComponentController::_imagePitchUp = "radioPitchUp.png"; +const char* RadioComponentController::_imagePitchDown = "radioPitchDown.png"; const char* RadioComponentController::_imageSwitchMinMax = "radioSwitchMinMax.png"; -const char* RadioComponentController::_settingsGroup = "RadioCalibration"; +const char* RadioComponentController::_settingsGroup = "RadioCalibration"; const char* RadioComponentController::_settingsKeyTransmitterMode = "TransmitterMode"; +const char* RadioComponentController::_px4RevParamFormat = "RC%1_REV"; +const char* RadioComponentController::_apmNewRevParamFormat = "RC%1_REVERSED"; + const struct RadioComponentController::FunctionInfo RadioComponentController::_rgFunctionInfoPX4[RadioComponentController::rcCalFunctionMax] = { - { "RC_MAP_ROLL" }, - { "RC_MAP_PITCH" }, - { "RC_MAP_YAW" }, - { "RC_MAP_THROTTLE" } +{ "RC_MAP_ROLL" }, +{ "RC_MAP_PITCH" }, +{ "RC_MAP_YAW" }, +{ "RC_MAP_THROTTLE" } }; const struct RadioComponentController::FunctionInfo RadioComponentController::_rgFunctionInfoAPM[RadioComponentController::rcCalFunctionMax] = { - { "RCMAP_ROLL" }, - { "RCMAP_PITCH" }, - { "RCMAP_YAW" }, - { "RCMAP_THROTTLE" } +{ "RCMAP_ROLL" }, +{ "RCMAP_PITCH" }, +{ "RCMAP_YAW" }, +{ "RCMAP_THROTTLE" } }; -RadioComponentController::RadioComponentController(void) : - _currentStep(-1), - _transmitterMode(2), - _chanCount(0), - _rcCalState(rcCalStateChannelWait), - _unitTestMode(false), - _statusText(NULL), - _cancelButton(NULL), - _nextButton(NULL), - _skipButton(NULL) +RadioComponentController::RadioComponentController(void) + : _currentStep(-1) + , _transmitterMode(2) + , _chanCount(0) + , _rcCalState(rcCalStateChannelWait) + , _unitTestMode(false) + , _statusText(NULL) + , _cancelButton(NULL) + , _nextButton(NULL) + , _skipButton(NULL) { #ifdef UNITTEST_BUILD // Nasty hack to expose controller to unit test code _unitTestController = this; #endif + if (parameterExists(FactSystem::defaultComponentId, QStringLiteral("RC1_REVERSED"))) { + // Newer ardupilot firmwares have a different reverse param naming scheme and value scheme + _revParamFormat = _apmNewRevParamFormat; + _revParamIsBool = true; // param value is boolean 0/1 for reversed or not + } else { + // Older ardupilot firmwares share the same naming convention as PX4 + _revParamFormat = _px4RevParamFormat; + _revParamIsBool = false; // paeram value if -1 indicates reversed + } + connect(_vehicle, &Vehicle::rcChannelsChanged, this, &RadioComponentController::_rcChannelsChanged); _loadSettings(); @@ -226,7 +239,7 @@ void RadioComponentController::_rcChannelsChanged(int channelCount, int pwmValue int channelValue = pwmValues[channel]; if (channelValue != -1) { - qCDebug(RadioComponentControllerLog) << "Raw value" << channel << channelValue; + qCDebug(RadioComponentControllerVerboseLog) << "Raw value" << channel << channelValue; _rcRawValue[channel] = channelValue; emit channelRCValueChanged(channel, channelValue); @@ -259,9 +272,12 @@ void RadioComponentController::_rcChannelsChanged(int channelCount, int pwmValue } } else { const stateMachineEntry* state = _getStateMachineEntry(_currentStep); - Q_ASSERT(state); - if (state->rcInputFn) { - (this->*state->rcInputFn)(state->function, channel, channelValue); + if (state) { + if (state->rcInputFn) { + (this->*state->rcInputFn)(state->function, channel, channelValue); + } + } else { + qWarning() << "Internal error: NULL _getStateMachineEntry return"; } } } @@ -283,20 +299,27 @@ void RadioComponentController::nextButtonClicked(void) _startCalibration(); } else { const stateMachineEntry* state = _getStateMachineEntry(_currentStep); - Q_ASSERT(state); - Q_ASSERT(state->nextFn); - (this->*state->nextFn)(); + if (state && state->nextFn) { + (this->*state->nextFn)(); + } else { + qWarning() << "Internal error: NULL _getStateMachineEntry return"; + } } } void RadioComponentController::skipButtonClicked(void) { - Q_ASSERT(_currentStep != -1); + if (_currentStep == -1) { + qWarning() << "Internal error: _currentStep == -1"; + return; + } const stateMachineEntry* state = _getStateMachineEntry(_currentStep); - Q_ASSERT(state); - Q_ASSERT(state->skipFn); - (this->*state->skipFn)(); + if (state && state->skipFn) { + (this->*state->skipFn)(); + } else { + qWarning() << "Internal error: NULL _getStateMachineEntry return"; + } } void RadioComponentController::cancelButtonClicked(void) @@ -581,9 +604,6 @@ void RadioComponentController::_setInternalCalibrationValuesFromParameters(void) QString minTpl("RC%1_MIN"); QString maxTpl("RC%1_MAX"); QString trimTpl("RC%1_TRIM"); - QString revTpl("RC%1_REV"); - - bool convertOk; for (int i = 0; i < _chanMax(); ++i) { struct ChannelInfo* info = &_rgChannelInfo[i]; @@ -601,29 +621,20 @@ void RadioComponentController::_setInternalCalibrationValuesFromParameters(void) Fact* paramFact = getParameterFact(FactSystem::defaultComponentId, trimTpl.arg(i+1)); if (paramFact) { - info->rcTrim = paramFact->rawValue().toInt(&convertOk); - Q_ASSERT(convertOk); + info->rcTrim = paramFact->rawValue().toInt(); } paramFact = getParameterFact(FactSystem::defaultComponentId, minTpl.arg(i+1)); if (paramFact) { - info->rcMin = paramFact->rawValue().toInt(&convertOk); - Q_ASSERT(convertOk); + info->rcMin = paramFact->rawValue().toInt(); } paramFact = getParameterFact(FactSystem::defaultComponentId, maxTpl.arg(i+1)); if (paramFact) { - info->rcMax = getParameterFact(FactSystem::defaultComponentId, maxTpl.arg(i+1))->rawValue().toInt(&convertOk); - Q_ASSERT(convertOk); + info->rcMax = getParameterFact(FactSystem::defaultComponentId, maxTpl.arg(i+1))->rawValue().toInt(); } - paramFact = getParameterFact(FactSystem::defaultComponentId, revTpl.arg(i+1)); - if (paramFact) { - float floatReversed = paramFact->rawValue().toFloat(&convertOk); - Q_ASSERT(convertOk); - Q_ASSERT(floatReversed == 1.0f || floatReversed == -1.0f); - info->reversed = floatReversed == -1.0f; - } + info->reversed = _channelReversedParamValue(i); } for (int i=0; irawValue().toInt(&convertOk); - Q_ASSERT(convertOk); + paramChannel = paramFact->rawValue().toInt(); if (paramChannel != 0) { _rgFunctionChannelMapping[i] = paramChannel - 1; @@ -668,21 +678,21 @@ void RadioComponentController::_validateCalibration(void) info->rcTrim = info->rcMin + ((info->rcMax - info->rcMin) / 2); } else { switch (_rgChannelInfo[chan].function) { - case rcCalFunctionThrottle: - case rcCalFunctionYaw: - case rcCalFunctionRoll: - case rcCalFunctionPitch: - // Make sure trim is within min/max - if (info->rcTrim < info->rcMin) { - info->rcTrim = info->rcMin; - } else if (info->rcTrim > info->rcMax) { - info->rcTrim = info->rcMax; - } - break; - default: - // Non-attitude control channels have calculated trim - info->rcTrim = info->rcMin + ((info->rcMax - info->rcMin) / 2); - break; + case rcCalFunctionThrottle: + case rcCalFunctionYaw: + case rcCalFunctionRoll: + case rcCalFunctionPitch: + // Make sure trim is within min/max + if (info->rcTrim < info->rcMin) { + info->rcTrim = info->rcMin; + } else if (info->rcTrim > info->rcMax) { + info->rcTrim = info->rcMax; + } + break; + default: + // Non-attitude control channels have calculated trim + info->rcTrim = info->rcMin + ((info->rcMax - info->rcMin) / 2); + break; } } @@ -717,7 +727,6 @@ void RadioComponentController::_writeCalibration(void) QString minTpl("RC%1_MIN"); QString maxTpl("RC%1_MAX"); QString trimTpl("RC%1_TRIM"); - QString revTpl("RC%1_REV"); // Note that the rc parameters are all float, so you must cast to float in order to get the right QVariant for (int chan = 0; chan<_chanMax(); chan++) { @@ -746,16 +755,13 @@ void RadioComponentController::_writeCalibration(void) // may affect channel reversing so we can't automatically determine it. This is ok for PX4 given how it uses mixers, but not for ArduPilot. if (_vehicle->px4Firmware() || _vehicle->multiRotor()) { // APM multi-rotor has a backwards interpretation of "reversed" on the Pitch control. So be careful. - float reversedParamValue; + bool reversed; if (_px4Vehicle() || info->function != rcCalFunctionPitch) { - reversedParamValue = info->reversed ? -1.0f : 1.0f; + reversed = info->reversed; } else { - reversedParamValue = info->reversed ? 1.0f : -1.0f; - } - paramFact = getParameterFact(FactSystem::defaultComponentId, revTpl.arg(oneBasedChannel)); - if (paramFact) { - paramFact->setRawValue(reversedParamValue); + reversed = !info->reversed; } + _setChannelReversedParamValue(chan, reversed); } } @@ -797,7 +803,10 @@ void RadioComponentController::_writeCalibration(void) /// @brief Starts the calibration process void RadioComponentController::_startCalibration(void) { - Q_ASSERT(_chanCount >= _chanMinimum); + if (_chanCount < _chanMinimum) { + qWarning() << "Call to RadioComponentController::_startCalibration with _chanCount < _chanMinimum"; + return; + } _resetInternalCalibrationValues(); @@ -853,7 +862,7 @@ void RadioComponentController::_rcCalSave(void) _statusText->setProperty("text", "The current calibration settings are now displayed for each channel on screen.\n\n" - "Click the Next button to upload calibration to board. Click Cancel if you don't want to save these values."); + "Click the Next button to upload calibration to board. Click Cancel if you don't want to save these values."); _nextButton->setEnabled(true); _skipButton->setEnabled(false); @@ -895,7 +904,8 @@ void RadioComponentController::_setHelpImage(const char* imageFile) } else if (_transmitterMode == 2) { file += _imageFileMode2Dir; } else { - Q_ASSERT(false); + qWarning() << "Internal error: Bad _transmitterMode value"; + return; } file += imageFile; @@ -1045,3 +1055,34 @@ int RadioComponentController::_chanMax(void) const { return _px4Vehicle() ? _chanMaxPX4 : _chanMaxAPM; } + +bool RadioComponentController::_channelReversedParamValue(int channel) +{ + Fact* paramFact = getParameterFact(FactSystem::defaultComponentId, _revParamFormat.arg(channel+1)); + if (paramFact) { + if (_revParamIsBool) { + return paramFact->rawValue().toBool(); + } else { + bool convertOk; + float floatReversed = paramFact->rawValue().toFloat(&convertOk); + if (!convertOk) { + floatReversed = 1.0f; + } + return floatReversed == -1.0f; + } + } + + return false; +} + +void RadioComponentController::_setChannelReversedParamValue(int channel, bool reversed) +{ + Fact* paramFact = getParameterFact(FactSystem::defaultComponentId, _revParamFormat.arg(channel+1)); + if (paramFact) { + if (_revParamIsBool) { + paramFact->setRawValue(reversed); + } else { + paramFact->setRawValue(reversed ? -1.0f : 1.0f); + } + } +} diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.h b/src/AutoPilotPlugins/Common/RadioComponentController.h index 2b6062b7c..57f0ff02c 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.h +++ b/src/AutoPilotPlugins/Common/RadioComponentController.h @@ -24,6 +24,7 @@ #include "AutoPilotPlugin.h" Q_DECLARE_LOGGING_CATEGORY(RadioComponentControllerLog) +Q_DECLARE_LOGGING_CATEGORY(RadioComponentControllerVerboseLog) class RadioConfigest; @@ -232,7 +233,10 @@ private: void _signalAllAttiudeValueChanges(void); int _chanMax(void) const; - + + bool _channelReversedParamValue(int channel); + void _setChannelReversedParamValue(int channel, bool reversed); + // @brief Called by unit test code to set the mode to unit testing void _setUnitTestMode(void){ _unitTestMode = true; } @@ -259,20 +263,20 @@ private: int _transmitterMode; ///< 1: transmitter is mode 1, 2: transmitted is mode 2 static const int _updateInterval; ///< Interval for ui update timer - + static const struct FunctionInfo _rgFunctionInfoAPM[rcCalFunctionMax]; ///< Information associated with each function, PX4 firmware static const struct FunctionInfo _rgFunctionInfoPX4[rcCalFunctionMax]; ///< Information associated with each function, APM firmware int _rgFunctionChannelMapping[rcCalFunctionMax]; ///< Maps from rcCalFunctions to channel index. _chanMax indicates channel not set for this function. static const int _attitudeControls = 5; - + int _chanCount; ///< Number of actual rc channels available static const int _chanMaxPX4 = 18; ///< Maximum number of supported rc channels, PX4 Firmware static const int _chanMaxAPM = 14; ///< Maximum number of supported rc channels, APM firmware static const int _chanMaxAny = 18; ///< Maximum number of support rc channels by this implementation static const int _chanMinimum = 5; ///< Minimum numner of channels required to run - + struct ChannelInfo _rgChannelInfo[_chanMaxAny]; ///< Information associated with each rc channel QList _apmPossibleMissingRCChannelParams; ///< List of possible missing RC*_* params for APM stack @@ -292,7 +296,12 @@ private: static const int _rcCalMoveDelta; static const int _rcCalSettleDelta; static const int _rcCalMinDelta; - + + static const char* _px4RevParamFormat; + static const char* _apmNewRevParamFormat; + QString _revParamFormat; + bool _revParamIsBool; + int _rcValueSave[_chanMaxAny]; ///< Saved values prior to detecting channel movement int _rcRawValue[_chanMaxAny]; ///< Current set of raw channel values diff --git a/src/FactSystem/FactControls/FactCheckBox.qml b/src/FactSystem/FactControls/FactCheckBox.qml index 5dc7788a4..06d0eec0f 100644 --- a/src/FactSystem/FactControls/FactCheckBox.qml +++ b/src/FactSystem/FactControls/FactCheckBox.qml @@ -11,12 +11,9 @@ QGCCheckBox { property variant checkedValue: 1 property variant uncheckedValue: 0 - partiallyCheckedEnabled: fact ? fact.value !== checkedValue && fact.value !== uncheckedValue : false - checkedState: fact ? fact.value === checkedValue ? Qt.Checked : (fact.value === uncheckedValue ? Qt.Unchecked : Qt.PartiallyChecked) : false + checkedState: fact ? (fact.value === checkedValue ? Qt.Checked : Qt.Unchecked) : Qt.Unchecked text: qsTr("Label") - onClicked: { - fact.value = checked ? checkedValue : uncheckedValue - } + onClicked: fact.value = checked ? checkedValue : uncheckedValue } diff --git a/src/FactSystem/FactControls/FactPanelController.cc b/src/FactSystem/FactControls/FactPanelController.cc index 7e7759933..b7059d776 100644 --- a/src/FactSystem/FactControls/FactPanelController.cc +++ b/src/FactSystem/FactControls/FactPanelController.cc @@ -127,8 +127,9 @@ Fact* FactPanelController::getParameterFact(int componentId, const QString& name QQmlEngine::setObjectOwnership(fact, QQmlEngine::CppOwnership); return fact; } else { - if(reportMissing) + if (reportMissing) { _reportMissingParameter(componentId, name); + } return NULL; } } diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 54855385b..1a7975490 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -735,6 +735,9 @@ void APMFirmwarePlugin::_artooSocketError(QAbstractSocket::SocketError socketErr QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) { + int majorVersion = vehicle->firmwareMajorVersion(); + int minorVersion = vehicle->firmwareMinorVersion(); + switch (vehicle->vehicleType()) { case MAV_TYPE_QUADROTOR: case MAV_TYPE_HEXAROTOR: @@ -742,22 +745,57 @@ QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) case MAV_TYPE_TRICOPTER: case MAV_TYPE_COAXIAL: case MAV_TYPE_HELICOPTER: - if (vehicle->firmwareMajorVersion() < 3 || (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() <= 3)) { + if (majorVersion < 3) { return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml"); - } else if (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() == 4) { - return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.4.xml"); - } else { - return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml"); + } else if (majorVersion == 3) { + switch (minorVersion) { + case 3: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml"); + case 4: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.4.xml"); + case 5: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml"); + default: + if (minorVersion < 3) { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.3.xml"); + } + } } + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml"); case MAV_TYPE_FIXED_WING: - if (vehicle->firmwareMajorVersion() < 3 || (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() <= 3)) { + if (majorVersion < 3) { return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.3.xml"); - } else { - return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.5.xml"); + } else if (majorVersion == 3) { + switch (minorVersion) { + case 3: + case 4: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.3.xml"); + case 5: + case 6: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.5.xml"); + case 7: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml"); + default: + if (minorVersion < 3) { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.3.xml"); + } + } } + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml"); case MAV_TYPE_GROUND_ROVER: case MAV_TYPE_SURFACE_BOAT: - return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.0.xml"); + if (majorVersion < 3) { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.0.xml"); + } else if (majorVersion == 3) { + switch (minorVersion) { + case 0: + case 1: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.0.xml"); + default: + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml"); + } + } + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml"); case MAV_TYPE_SUBMARINE: return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml"); default: diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml index 1734b74b8..91f87f90f 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml @@ -128,11 +128,6 @@ L1Controller - -0 1 -0.1 -Percent - Automatic @@ -1253,6 +1248,7 @@ Avoidance PrecLoiter Object Avoidance +ArmDisarm @@ -1293,6 +1289,7 @@ Avoidance PrecLoiter Object Avoidance +ArmDisarm @@ -1333,6 +1330,7 @@ Avoidance PrecLoiter Object Avoidance +ArmDisarm @@ -1373,6 +1371,7 @@ Avoidance PrecLoiter Object Avoidance +ArmDisarm @@ -1413,6 +1412,7 @@ Avoidance PrecLoiter Object Avoidance +ArmDisarm @@ -1453,20 +1453,7 @@ Avoidance PrecLoiter Object Avoidance - - - -0:All,1:Baro,2:Compass,3:GPS,4:INS,5:Parameters+Rangefinder,6:RC,7:Voltage - -Disabled -Enabled -Skip Baro -Skip Compass -Skip GPS -Skip INS -Skip Params/Rangefinder -Skip RC -Skip Voltage +ArmDisarm @@ -2151,1002 +2138,799 @@ gravities - - + + -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 +Disabled +Enabled - - -MAVlink1 -MAVLink2 - + +1 100 - - -None -MAVLink1 -MAVLink2 -Frsky D -Frsky SPort -GPS -Alexmos Gimbal Serial -SToRM32 Gimbal Serial -Lidar -FrSky SPort Passthrough (OpenTX) -Lidar360 -Aerotenna uLanding -Pozyx Beacon - + +1 100000 - - -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 - + +-1 16777215 - + -None -MAVLink1 -MAVLink2 -Frsky D -Frsky SPort -GPS -Alexmos Gimbal Serial -SToRM32 Gimbal Serial -Lidar -FrSky SPort Passthrough (OpenTX) -Lidar360 -Aerotenna uLanding -Pozyx Beacon +NoInfo +Light +Small +Large +HighVortexlarge +Heavy +HighlyManuv +Rotocraft +RESERVED +Glider +LightAir +Parachute +UltraLight +RESERVED +UAV +Space +RESERVED +EmergencySurface +ServiceSurface +PointObstacle - + -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 +NO_DATA +L15W23 +L25W28P5 +L25W34 +L35W33 +L35W38 +L45W39P5 +L45W45 +L55W45 +L55W52 +L65W59P5 +L65W67 +L75W72P5 +L75W80 +L85W80 +L85W90 - + -None -MAVLink1 -MAVLink2 -Frsky D -Frsky SPort -GPS -Alexmos Gimbal Serial -SToRM32 Gimbal Serial -Lidar -FrSky SPort Passthrough (OpenTX) -Lidar360 -Aerotenna uLanding -Pozyx Beacon +NoData +Left2m +Left4m +Left6m +Center +Right2m +Right4m +Right6m - + -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 +NO_DATA +AppliedBySensor - + -None -MAVLink1 -MAVLink2 -Frsky D -Frsky SPort -GPS -Alexmos Gimbal Serial -SToRM32 Gimbal Serial -Lidar -FrSky SPort Passthrough (OpenTX) -Lidar360 -Aerotenna uLanding -Pozyx Beacon +Disabled +Rx-Only +Tx-Only +Rx and Tx Enabled - - -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 - + + + - - -None -MAVLink1 -MAVLink2 -Frsky D -Frsky SPort -GPS -Alexmos Gimbal Serial -SToRM32 Gimbal Serial -Lidar -FrSky SPort Passthrough (OpenTX) -Lidar360 -Aerotenna uLanding -Pozyx Beacon - + - - -1200 -2400 -4800 -9600 -19200 -38400 -57600 -111100 -115200 -500000 -921600 -1500000 - + - - - -True -True -1 -pascals + - -True -True -1 -degrees celsius + - -0.1 + + + + + + + meters - - -FirstBaro -2ndBaro -3rdBaro - + +meters - - -Disabled -Bus0 - + +millibar + + + + + + + + + + + + + + +seconds - - + + +0.0 1.0 +.01 + + -None -AUTO -uBlox -MTK -MTK19 -NMEA -SiRF -HIL -SwiftNav -PX4-UAVCAN -SBF -GSOF -QURT -ERB -MAV -NOVA +Disabled +Enabled -True - + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + None -AUTO -uBlox -MTK -MTK19 -NMEA -SiRF -HIL -SwiftNav -PX4-UAVCAN -SBF -GSOF -QURT -ERB -MAV -NOVA +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 -True - - -Portable -Stationary -Pedestrian -Automotive -Sea -Airborne1G -Airborne2G -Airborne4G - + +0.001 0.5 +.01 - + +0 10 +1 + + Disabled -Enabled +Enable EKF2 +Enable EKF3 - + + + -Any -FloatRTK -IntegerRTK +Disabled +THR_MIN PWM when disarmed +0 PWM when disarmed -True - + +0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration -Disabled -Enabled -NoChange +None +All +Barometer +Compass +GPS Lock +INS(INertial Sensors - accels & gyros) +Parameters(unused) +RC Failsafe +Board voltage +Battery Level +Airspeed +LoggingAvailable +Hardware safety switch +GPS configuration - --100 90 -Degrees + +0.25 3.0 +m/s/s - + +0.1 +Volts + + +0.1 +Volts + + + + -send to first GPS -send to 2nd GPS -send to all +None +I2C-MS4525D0 +Analog +I2C-MS5525 - + -None -All -External only +Use +Don't Use - + +0.1 + + +0.1 + + + + + + + + -Disabled -log every sample -log every 5 samples +Disable +Enable -True - -0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + + + -Leave as currently configured -GPS-NoSBAS -GPS+SBAS -Galileo-NoSBAS -Galileo+SBAS -Beidou -GPS+IMES+QZSS+SBAS (Japan Only) -GLONASS -GLONASS+SBAS -GPS+GLONASS+SBAS +Bus0 +Bus1 - + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 -Do not save config -Save config -Save only when needed +Disabled +Slow +Medium +Fast +1000 +Centi-Degrees/Sec/Sec - -0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + -Leave as currently configured -GPS-NoSBAS -GPS+SBAS -Galileo-NoSBAS -Galileo+SBAS -Beidou -GPS+IMES+QZSS+SBAS (Japan Only) -GLONASS -GLONASS+SBAS -GPS+GLONASS+SBAS +Disabled +Enabled - + +0 180000 -Disables automatic configuration -Enable automatic configuration +Disabled +Slow +Medium +Fast +1000 +Centi-Degrees/Sec/Sec - + +0 180000 -10Hz -8Hz -5Hz +Disabled +Slow +Medium +Fast -milliseconds +1000 +Centi-Degrees/Sec/Sec - + -10Hz -8Hz -5Hz +Disabled +Enabled -milliseconds - -m + +3.000 12.000 - -m + +3.000 12.000 - -m + +3.000 6.000 - -m + +0.5 10.0 - -m + +0.08 0.30 +0.005 - -m + +0.01 0.5 +0.01 - - - - -Servo -Relay - + +0 1 +0.01 +Percent - -0 50 -seconds + +0.0 0.02 +0.001 - -1000 2000 -pwm + +1 100 +1 +Hz - -1000 2000 -pwm + +0.08 0.30 +0.005 - -0 1000 -meters + +0.01 0.5 +0.01 - - -Low -High - + +0 1 +0.01 +Percent - -0 10000 -milliseconds + +0.0 0.02 +0.001 - -0 180 -Degrees + +1 100 +1 +Hz - - -Disabled -PX4 AUX1 -PX4 AUX2 -PX4 AUX3 -PX4 AUX4(fast capture) -PX4 AUX5 -PX4 AUX6 - + +0.10 0.50 +0.005 - - -TriggerLow -TriggerHigh - + +0.010 0.05 +0.01 - - - - -Disabled -THR_MIN PWM when disarmed -0 PWM when disarmed - + +0 1 +0.01 +Percent - -0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration - -None -All -Barometer -Compass -GPS Lock -INS(INertial Sensors - accels & gyros) -Parameters(unused) -RC Failsafe -Board voltage -Battery Level -Airspeed -LoggingAvailable -Hardware safety switch -GPS configuration - + +0.000 0.02 +0.001 - -0.25 3.0 -m/s/s + +1 100 +1 +Hz - -0.1 -Volts + +0.1 0.25 - -0.1 -Volts + +0.5 0.9 - - - + +0.5 0.9 + + -Disabled -APM2 A9 pin -APM1 relay -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 +Disabled 1 - - -Disabled -APM2 A9 pin -APM1 relay -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - - - - -Disabled -APM2 A9 pin -APM1 relay -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +0 1000 +Centi-Degrees - - -Disabled -APM2 A9 pin -APM1 relay -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +0.08 0.35 +0.005 - - -Off -On -NoChange - + +0.01 0.6 +0.01 - - - - -Disabled -Enabled - + +0 1 +0.01 - - -First Relay -Second Relay -Third Relay -Fourth Relay -Servo - + +0.001 0.03 +0.001 - -1000 2000 + +1 20 1 -pwm +Hz - -1000 2000 -1 -pwm + +0.08 0.35 +0.005 - -0 32000 -1 -Meters + +0.01 0.6 +0.01 - -0 5000 -1 -Milliseconds + +0 1 +0.01 - - - - -None -Analog -MaxbotixI2C -PulsedLightI2C -PX4-I2C -PX4-PWM -BBB-PRU -LightWareI2C -LightWareSerial -Bebop -MAVLink -uLanding -LeddarOne -MaxbotixSerial - + +0.001 0.03 +0.001 - - -Not Used -APM2-A0 -APM2-A1 -APM2-A2 -APM2-A3 -APM2-A4 -APM2-A5 -APM2-A6 -APM2-A7 -APM2-A8 -APM2-A9 -PX4-airspeed port -Pixhawk-airspeed port -APM1-airspeed port - + +1 20 +1 +Hz - -0.001 -meters/Volt + +0.180 0.60 +0.005 - -0.001 -Volts + +0.01 0.06 +0.01 - - -Linear -Inverted -Hyperbolic - + +0 1 +0.01 - -1 -centimeters + +0.000 0.02 +0.001 - + +1 20 1 -centimeters +Hz - + + + -Not Used -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 +Disabled +Enabled - -1 -milliseconds - - + -No -Yes +Remain in AVOID_ADSB +Resume previous flight mode +RTL +Resume if AUTO else Loiter - -0 32767 -meters + - -0 127 -1 -centimeters + +seconds - -0 127 -1 + +seconds - -m + +meters - -m + +meters - -m + +meters - + +meters + + + + +0:StopAtFence,1:UseProximitySensor None -Analog -MaxbotixI2C -PulsedLightI2C -PX4-I2C -PX4-PWM -BBB-PRU -LightWareI2C -LightWareSerial -Bebop -MAVLink -uLanding -LeddarOne -MaxbotixSerial - - - - -Not Used -APM2-A0 -APM2-A1 -APM2-A2 -APM2-A3 -APM2-A4 -APM2-A5 -APM2-A6 -APM2-A7 -APM2-A8 -APM2-A9 -PX4-airspeed port -Pixhawk-airspeed port -APM1-airspeed port +StopAtFence +UseProximitySensor +All - -0.001 -meters/Volt + +0 4500 - -0.001 -Volts + +3 30 +meters - + + + -Linear -Inverted -Hyperbolic +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell - -1 -centimeters - - -1 -centimeters - - + -Not Used -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 +Disabled +A0 +A1 +Pixhawk +A13 +PX4 - -1 -milliseconds - - + -No -Yes +Disabled +A1 +A2 +Pixhawk +A12 +PX4 - -0 127 -1 -centimeters + - -0 127 -1 + +Amps/Volt - -m + +Volts - -m + +50 +mAh - -m + +1 +Watts - + -None -Analog -APM2-MaxbotixI2C -APM2-PulsedLightI2C -PX4-I2C -PX4-PWM -BBB-PRU -LightWareI2C -LightWareSerial -Bebop -MAVLink -uLanding -LeddarOne -MaxbotixSerial +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell - + -Not Used -APM2-A0 -APM2-A1 -APM2-A2 -APM2-A3 -APM2-A4 -APM2-A5 -APM2-A6 -APM2-A7 -APM2-A8 -APM2-A9 -PX4-airspeed port -Pixhawk-airspeed port -APM1-airspeed port +Disabled +A0 +A1 +Pixhawk +A13 +PX4 - -0.001 -meters/Volt - - -0.001 -Volts - - + -Linear -Inverted -Hyperbolic +Disabled +A1 +A2 +Pixhawk +A12 +PX4 - -1 -centimeters + - -1 -centimeters + +Amps/Volt - - -Not Used -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +Volts - + +50 +mAh + + 1 -milliseconds +Amps - + + + -No -Yes +None +Pozyx - -0 127 -1 -centimeters - - -0 127 -1 + +-90 90 +0.000001 +degrees - -m + +-180 180 +0.000001 +degrees - -m + +0 10000 +1 +meters - -m + +-180 +180 +1 +degrees - + + + -None -Analog -APM2-MaxbotixI2C -APM2-PulsedLightI2C -PX4-I2C -PX4-PWM -BBB-PRU -LightWareI2C -LightWareSerial -Bebop -MAVLink -uLanding -LeddarOne -MaxbotixSerial +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture +True - + -Not Used -APM2-A0 -APM2-A1 -APM2-A2 -APM2-A3 -APM2-A4 -APM2-A5 -APM2-A6 -APM2-A7 -APM2-A8 -APM2-A9 -PX4-airspeed port -Pixhawk-airspeed port -APM1-airspeed port +Disabled +Enabled +Auto +True - -0.001 -meters/Volt + + +Disabled +Enabled +Auto + +True - -0.001 -Volts + + +Disabled +Enabled + +True - + -Linear -Inverted -Hyperbolic +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz +True - -1 -centimeters + +-32767 32768 - -1 -centimeters + + +Disabled +Enabled +Dynamic ID/Update + - + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 -Not Used +Disabled +Enabled + +True + + +-1 80 +degreesC + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + +Disabled +Enabled + + + + +Disabled Pixhawk AUXOUT1 Pixhawk AUXOUT2 Pixhawk AUXOUT3 @@ -3161,504 +2945,226 @@ PX4IO ACC2 - -1 -milliseconds - - + -No -Yes +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 - -0 127 -1 -centimeters - - -0 127 -1 - - -m + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + - -m + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + - -m + +0 3600 - - + + -Disable -Enable +Servo +Relay - -1 + +0 50 +deciseconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 meters - - - + -Disabled -Enabled +Low +High - -1 100 + +0 10000 +milliseconds - -1 100000 + +0 180 +Degrees - --1 16777215 + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + - + -NoInfo -Light -Small -Large -HighVortexlarge -Heavy -HighlyManuv -Rotocraft -RESERVED -Glider -LightAir -Parachute -UltraLight -RESERVED -UAV -Space -RESERVED -EmergencySurface -ServiceSurface -PointObstacle - - - - -NO_DATA -L15W23 -L25W28P5 -L25W34 -L35W33 -L35W38 -L45W39P5 -L45W45 -L55W45 -L55W52 -L65W59P5 -L65W67 -L75W72P5 -L75W80 -L85W80 -L85W90 - - - - -NoData -Left2m -Left4m -Left6m -Center -Right2m -Right4m -Right6m - - - - -NO_DATA -AppliedBySensor - - - - -Disabled -Rx-Only -Tx-Only -Rx and Tx Enabled +TriggerLow +TriggerHigh - - + + Disabled Enabled - + -Remain in AVOID_ADSB -Resume previous flight mode -RTL -Resume if AUTO else Loiter +First Relay +Second Relay +Third Relay +Fourth Relay +Servo - - - -seconds + +1000 2000 +1 +pwm - -seconds + +1000 2000 +1 +pwm - -meters + +0 32000 +1 +Meters - -meters + +0 5000 +1 +Milliseconds - -meters + + + +0 10000 +100 +cm - -meters + +-90 90 +1 +deg/s - - - -Disable -Chan5 -Chan6 -Chan7 -Chan8 -Chan9 -Chan10 -Chan11 -Chan12 -Chan13 -Chan14 -Chan15 -Chan16 - + + +-400 400 +1 +milligauss - -900 2100 + +-400 400 +1 +milligauss - -900 2100 + +-400 400 +1 +milligauss - + +-3.142 3.142 +0.01 +Radians + + -Disable -Chan1 -Chan3 -Chan3 -Chan4 -Chan5 -Chan6 -Chan7 -Chan8 -Chan9 -Chan10 -Chan11 -Chan12 -Chan13 -Chan14 -Chan15 -Chan16 +Disabled +Internal-Learning +EKF-Learning - - - + -Disable -Enable +Disabled +Enabled - -0 1 - - - - -0.08 0.30 -0.005 - - -0.01 0.5 -0.01 - - -0 1 -0.01 -Percent + + +Disabled +Enabled + - -0.0 0.02 -0.001 + + +Disabled +Use Throttle +Use Current + - -1 100 + +-1000 1000 1 -Hz - - -0.08 0.30 -0.005 - - -0.01 0.5 -0.01 - - -0 1 -0.01 -Percent - - -0.0 0.02 -0.001 +Offset per Amp or at Full Throttle - -1 100 + +-1000 1000 1 -Hz +Offset per Amp or at Full Throttle - -0.10 0.50 -0.005 - - -0.010 0.05 -0.01 - - -0 1 -0.01 -Percent - - -0.000 0.02 -0.001 - - -1 100 -1 -Hz - - -0.1 0.25 - - -0.5 0.9 - - - - -0.4 1.0 -0.1 -seconds - - -0.1 4.0 -0.1 - - -0 0.1 -0.01 - - -0 1.0 -0.05 - - -0 180 -1 -degrees/second - - -0 4500 -1 - - -0.1 4.0 -0.1 - - - - -0.4 1.0 -0.1 -seconds - - -0.1 3.0 -0.1 - - -0 0.1 -0.01 - - -0 0.5 -0.05 - - -0 100 -1 -degrees/second - - -0 100 -1 -degrees/second - - -0.7 1.5 -0.05 - - -0 4500 -1 - - -0.1 4.0 -0.1 - - - - -0 4 -0.25 - - -0 2 -0.25 - - -0 2 -0.25 - - -0.8 1.2 -0.05 - - -0 4500 -1 - - - - -0.4 1.0 -0.1 -seconds - - -0.1 10.0 -0.1 - - -0 1.0 -0.05 - - -0 0.1 -0.01 - - -0 4500 -1 - - -0 5 -0.1 -m/s - - -0.0 10.0 -0.1 - - -0.0 30.0 -0.1 -m/s - - -0.0 50.0 -0.1 -degree/(m/s) - - -0.0 4500.0 -0.1 -Centidegrees - - - - --400 400 -1 -milligauss - - --400 400 -1 -milligauss - - --400 400 -1 -milligauss - - --3.142 3.142 -0.01 -Radians - - - -Disabled -Internal-Learning -EKF-Learning - - - - -Disabled -Enabled - - - - -Disabled -Enabled - - - - -Disabled -Use Throttle -Use Current - - - --1000 1000 -1 -Offset per Amp or at Full Throttle - - --1000 1000 -1 -Offset per Amp or at Full Throttle - - --1000 1000 -1 -Offset per Amp or at Full Throttle + +-1000 1000 +1 +Offset per Amp or at Full Throttle @@ -3935,1654 +3441,1738 @@ 4 32 Very Strict -Default -Relaxed -Very Relaxed +Strict +Default +Relaxed 0.1 - - + + Disabled -ShowSlips -ShowOverruns +Enabled - + -50Hz -100Hz -200Hz -250Hz -300Hz -400Hz +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS -True - - - -1 8 -1 -True - - -1 8 -1 -True + +0.05 5.0 +0.05 +m/s - -1 8 -1 -True + +0.05 5.0 +0.05 +m/s - -1 8 -1 -True + +100 1000 +25 - - - + +0.1 10.0 +0.1 +m - -rad/s + +100 1000 +25 - -rad/s + +10 100 +5 +m - -rad/s + +0 250 +10 +milliseconds - -rad/s + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + - -rad/s + +0.1 10.0 +0.1 +m - -rad/s + +100 1000 +25 - -rad/s + +0 250 +10 +milliseconds - + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 rad/s - + +0.05 1.0 +0.05 rad/s - -0.8 1.2 + +100 1000 +25 - -0.8 1.2 + +0 250 +10 +milliseconds - -0.8 1.2 + +0.0001 0.1 +0.0001 +rad/s - --3.5 3.5 + +0.01 1.0 +0.01 m/s/s - --3.5 3.5 -m/s/s + +0.00001 0.001 +rad/s/s - --3.5 3.5 + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 m/s/s - -0.8 1.2 + +0.0 1.0 +0.1 - -0.8 1.2 + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed - -0.8 1.2 + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU - --3.5 3.5 -m/s/s + +50 200 +% - --3.5 3.5 -m/s/s + +0.5 50.0 +m - --3.5 3.5 -m/s/s + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU - -0.8 1.2 + +0.05 1.0 +0.05 +rad - -0.8 1.2 + +100 1000 +25 - -0.8 1.2 + +10 50 +5 +cs - --3.5 3.5 -m/s/s + +0.00001 0.01 +gauss/s - --3.5 3.5 -m/s/s + +0.00001 0.01 +gauss/s - --3.5 3.5 -m/s/s + +-1 70 +1 +% - -0 127 -Hz + +0 0.2 +0.01 - -0 127 -Hz + +0.1 10.0 +0.1 +m - - -Disabled -Enabled - + +100 1000 +25 - + +0 250 +10 +milliseconds + + +2.0 6.0 +0.5 +m/s + + + + Disabled -Enabled +Enabled - + -Disabled -Enabled +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS - -0.05 50 + +0.05 5.0 +0.05 +m/s - - -Never -Start-up only - + +0.05 5.0 +0.05 +m/s - - -Don't adjust the trims -Assume first orientation was level -Assume ACC_BODYFIX is perfectly aligned to the vehicle - + +100 1000 +25 - + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + -IMU 1 -IMU 2 -IMU 3 +Use Baro +Use Range Finder +Use GPS +Use Range Beacon - + +0.1 10.0 +0.1 m - -m + +100 1000 +25 - -m + +0 250 +10 +milliseconds +True - -m + +0.01 0.5 +0.01 +gauss - -m + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + - -m + +100 1000 +25 - -m + +0.5 5.0 +0.1 +m/s - -m + +100 1000 +25 - + +0.1 10.0 +0.1 m - -True + +100 1000 +25 - -True + +1.0 4.0 +0.1 +rad/s - -True + +0.05 1.0 +0.05 +rad/s - -True + +100 1000 +25 - -True + +0 250 +10 +milliseconds +True - -True + +0.0001 0.1 +0.0001 +rad/s - + +0.01 1.0 +0.01 +m/s/s - - - + +0.00001 0.001 +rad/s/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + 0.0 1.0 -.01 +0.1 - - -Disabled -Enabled - + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed - -0.1 0.4 -.01 + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU - -0.1 0.4 -.01 + +50 200 +% - -0 127 + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 1 -m/s +% - --0.1745 +0.1745 + +0 0.2 0.01 -Radians - --0.1745 +0.1745 -0.01 -Radians + +0.1 10.0 +0.1 +m - --0.1745 +0.1745 -0.01 -Radians + +100 1000 +25 - - -None -Yaw45 -Yaw90 -Yaw135 -Yaw180 -Yaw225 -Yaw270 -Yaw315 -Roll180 -Roll180Yaw45 -Roll180Yaw90 -Roll180Yaw135 -Pitch180 -Roll180Yaw225 -Roll180Yaw270 -Roll180Yaw315 -Roll90 -Roll90Yaw45 -Roll90Yaw90 -Roll90Yaw135 -Roll270 -Roll270Yaw45 -Roll270Yaw90 -Roll270Yaw136 -Pitch90 -Pitch270 -Pitch180Yaw90 -Pitch180Yaw270 -Roll90Pitch90 -Roll180Pitch90 -Roll270Pitch90 -Roll90Pitch180 -Roll270Pitch180 -Roll90Pitch270 -Roll180Pitch270 -Roll270Pitch270 -Roll90Pitch180Yaw90 -Roll90Yaw270 - - - -0.001 0.5 -.01 - - -0 10 -1 - - + +0 250 +10 +milliseconds +True + + +2.0 6.0 +0.5 +m/s + + +0.5 2.5 +0.1 +m/s/s + + + + Disabled -Enable EKF2 -Enable EKF3 +Enabled - - - + +0:Altitude,1:Circle,2:Polygon None -I2C-MS4525D0 -Analog -I2C-MS5525 +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All - + -Use -Don't Use +Report Only +RTL or Land - -0.1 - - -0.1 - - - - + +10 1000 +1 +Meters - + +30 10000 +Meters - - -Disable -Enable - + +1 10 +Meters - + +1 20 - + + + -Bus0 -Bus1 +Disabled +Enabled - - - -1 60 + +-200 +200 1 -seconds - - -0.6 1.0 -0.05 - - -0 0.1 -0.01 - - - - -0.1 20.0 -0.1 - - -0.1 10.0 -0.1 - - -3.0 10.0 -0.2 - - -0.1 1.0 -0.1 - - -0.0 0.5 -0.02 - - -1.0 10.0 -0.5 - - -1.0 5.0 -0.05 - - -0.5 2.0 -0.05 - - -5.0 30.0 -1.0 - - -0.0 2.0 -0.1 - - -0.1 1.0 -0.1 - - -0.0 20.0 -0.1 - --1 127 + +-200 +200 1 - --1 100 -0.1 - - --1.0 2.0 -0.1 - - -0 45 + +-18000 +18000 1 - --45 0 -1 + +m - -0.0 2.0 -0.1 + +m - -1.0 5.0 -0.2 + +m - -0.1 1.0 -0.1 + +0 255 - --5 40 + + + +True +True 1 +pascals - -0.0 20.0 -0.1 -m/s - - --2.0 2.0 -0.1 -m/s/m - - -0.1 1.0 -0.1 - - -0.0 0.5 -0.02 - - -0.0 0.5 -0.02 + +True +True +1 +degrees celsius - -0.1 1.0 + 0.1 +meters - + -Disable -Enable +FirstBaro +2ndBaro +3rdBaro - - - + -Retracted -Neutral -MavLink Targeting -RC Targeting -GPS Point +Disabled +Bus0 - --180.00 179.99 -1 -Degrees - - --180.00 179.99 -1 -Degrees - - --180.00 179.99 -1 -Degrees - - --180.00 179.99 -1 -Degrees - - --180.00 179.99 -1 -Degrees + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True - --180.00 179.99 -1 -Degrees + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True - + -Disabled -Enabled +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G - + Disabled Enabled - + -Disabled -Enabled +Any +FloatRTK +IntegerRTK +True - + Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +Enabled +NoChange - --18000 17999 -1 -Centi-Degrees - - --18000 17999 -1 -Centi-Degrees + +-100 90 +Degrees - + -Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +send to first GPS +send to 2nd GPS +send to all - --18000 17999 -1 -Centi-Degrees - - --18000 17999 -1 -Centi-Degrees + + +None +All +External only + - + Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +log every sample +log every 5 samples +True - --18000 17999 -1 -Centi-Degrees - - --18000 17999 -1 -Centi-Degrees + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + - -0 100 -1 + + +Do not save config +Save config +Save only when needed + - -0.0 0.2 -.005 -Seconds + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + - -0.0 0.2 -.005 -Seconds + + +Disables automatic configuration +Enable automatic configuration + - + -None -Servo -3DR Solo -Alexmos Serial -SToRM32 MAVLink -SToRM32 Serial +10Hz +8Hz +5Hz -True +milliseconds - + -Retracted -Neutral -MavLink Targeting -RC Targeting -GPS Point +10Hz +8Hz +5Hz +milliseconds - --180.00 179.99 -1 -Degrees + +m - --180.00 179.99 -1 -Degrees + +m - --180.00 179.99 -1 -Degrees + +m - --180.00 179.99 -1 -Degrees + +m - --180.00 179.99 -1 -Degrees + +m - --180.00 179.99 -1 -Degrees + +m - - -Disabled -Enabled - + +0 250 +milliseconds - - -Disabled -Enabled - + +0 250 +milliseconds - + + + Disabled -Enabled +Enabled - + -Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +None +Servo +EPM - --18000 17999 + +1000 2000 +PWM + + +1000 2000 +PWM + + +1000 2000 +PWM + + +0 255 +seconds + + +0 255 + + + + +-180 180 1 -Centi-Degrees +Degrees - --18000 17999 + +-180 180 1 -Centi-Degrees +Degrees - + +-180 180 +1 +Degrees + + -Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +Servo only +Servo with ExtGyro +DirectDrive VarPitch +DirectDrive FixedPitch - --18000 17999 + + +3-Servo CCPM +H1 Mechanical Mixing + + + +0 1000 1 -Centi-Degrees +PWM - --18000 17999 + +-90 90 1 -Centi-Degrees +Degrees - + +-10 10 +0.1 + + -Disabled -RC5 -RC6 -RC7 -RC8 -RC9 -RC10 -RC11 -RC12 +NoFlybar +Flybar - --18000 17999 + +0 1000 1 -Centi-Degrees +PWM - --18000 17999 + +0 1000 1 -Centi-Degrees +PWM - -0.0 0.2 -.005 -Seconds + +0 2000 - -0.0 0.2 -.005 -Seconds + +0 2000 - + -None -Servo -3DR Solo -Alexmos Serial -SToRM32 MAVLink -SToRM32 Serial +Reversed +Normal - - - - -None -File -MAVLink -BothFileAndMAVLink - + +1000 2000 +1 +PWM - + +1000 2000 +1 +PWM - - -Disabled -Enabled - + +1000 2000 +1 +PWM - + Disabled -Enabled +Passthrough +Max collective +Mid collective +Min collective - - -Disabled -Enabled - + +0 1000 +10 +PWM - - - + -Disabled -Analog Voltage Only -Analog Voltage and Current -SMBus -Bebop +Ch8 Input +SetPoint +Throttle Curve - - -Disabled -A0 -A1 -Pixhawk -A13 -PX4 - + +0 500 +1 +pwm - - -Disabled -A1 -A2 -Pixhawk -A12 -PX4 - + +0 60 +Seconds - + +0 60 +Seconds - -Amps/Volt + +0 1000 +10 - -Volts + +0 500 +10 - -50 -mAh + +0 1000 +10 - + +0 1000 +10 + + +0 18000 +100 +Centi-Degrees + + +0 10 1 -Watts - + +1 1000 +10 + + +0 500 +10 + + + + Disabled -Analog Voltage Only -Analog Voltage and Current -SMBus -Bebop +Enabled - + -Disabled -A0 -A1 -Pixhawk -A13 -PX4 +None +Chan1 +Chan2 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 - - -Disabled -A1 -A2 -Pixhawk -A12 -PX4 - + +0.1 5 +Seconds - + +1 10 +Seconds - -Amps/Volt + +100 100000 - -Volts + +1000 2000 - -50 -mAh + +1000 2000 - -1 -Amps + +1000 2000 - - - - -No PWMs -Two PWMs -Four PWMs -Six PWMs -Three PWMs and One Capture - -True + +1000 2000 - + -Disabled -Enabled -Auto +None +RPM1 +RPM2 -True - - -Disabled -Enabled -Auto - -True + +0 100 - - -Disabled -Enabled - -True + + + +0 500 +1 +Percent*10 - - -Disabled -50Hz -75Hz -100Hz -150Hz -200Hz -250Hz -300Hz - -True + +0 500 +1 +Percent*10 - --32767 32768 + +500 1000 +1 +Percent*10 - - -Disabled -Enabled -Dynamic ID/Update - + +500 1000 +1 +Percent*10 - -0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 + Disabled -Enabled - -True - - --1 80 -degreesC - - - -AUTO -PX4V1 -Pixhawk -Pixhawk2 -Pixracer -PixhawkMini -Pixhawk2Slim -VRBrain 5.1 -VRBrain 5.2 -VR Micro Brain 5.1 -VR Micro Brain 5.2 -VRBrain Core 1.0 -VRBrain 5.4 +Very Low +Low +Medium +High +Very High -True - - - - + + - + +rad/s - + +rad/s - + +rad/s - + +rad/s - + +rad/s - + +rad/s - -meters + +rad/s - -meters + +rad/s - -millibar + +rad/s - + +0.8 1.2 - + +0.8 1.2 - + +0.8 1.2 - + +-3.5 3.5 +m/s/s - + +-3.5 3.5 +m/s/s - + +-3.5 3.5 +m/s/s - -seconds + +0.8 1.2 - - - - -Disabled -Enabled - + +0.8 1.2 - --200 +200 -1 + +0.8 1.2 - --200 +200 -1 + +-3.5 3.5 +m/s/s - --18000 +18000 -1 + +-3.5 3.5 +m/s/s - -m + +-3.5 3.5 +m/s/s - -m + +0.8 1.2 - -m + +0.8 1.2 - -0 255 + +0.8 1.2 - - - -0 32766 -1 + +-3.5 3.5 +m/s/s - + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + -Resume Mission -Restart Mission +Disabled +Enabled - - - + + +Disabled +Enabled + - -0.1 -kilometers + + +Disabled +Enabled + - + +0.05 50 + + -DoNotIncludeHome -IncludeHome +Never +Start-up only - - - + -Disabled -Enabled +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle - + -GPS 3D Vel and 2D Pos -GPS 2D vel and 2D pos -GPS 2D pos -No GPS +IMU 1 +IMU 2 +IMU 3 - -0.05 5.0 -0.05 -m/s + +m - -0.05 5.0 -0.05 -m/s + +m - -100 1000 -25 + +m - -0.1 10.0 -0.1 + m - -100 1000 -25 + +m - -10 100 -5 + m - -0 250 -10 -msec + +m - - -Use Baro -Use Range Finder -Use GPS -Use Range Beacon - + +m - -0.1 10.0 -0.1 + m - -100 1000 -25 + +True - -0 250 -10 -msec + +True - -0.01 0.5 -0.01 -gauss + +True - - -When flying -When manoeuvring -Never -After first climb yaw reset -Always - + +True - -100 1000 -25 + +True - -0.5 5.0 -0.1 -m/s + +True - -100 1000 -25 + - -0.1 10.0 + + + +0 5 +0.5 +meters + + +0 90 0.1 -m +degrees - -100 1000 -25 + +centi-Degrees - -1.0 4.0 + 0.1 -rad/s +meters - -0.05 1.0 -0.05 -rad/s - - -100 1000 -25 - - -0 250 -10 -msec - - -0.0001 0.1 -0.0001 -rad/s - - -0.01 1.0 -0.01 -m/s/s - - -0.00001 0.001 -rad/s/s - - -0.000001 0.001 -1/s - - -0.00001 0.001 -m/s/s/s - - -0.01 1.0 + 0.1 -m/s/s +seconds - -0.0 1.0 + +0 30 0.1 +meters - -0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + +0 10 +0.1 +seconds - -1 127 + +0 30 +0.1 +m/s - -50 200 -% + +0 127 +1 +percent - -0.5 50.0 -m/s + +0 127 +1 +seconds - + Disabled -FirstIMU -FirstAndSecondIMU -AllIMUs +Servos to Neutral +Servos to Zero PWM - -0.05 1.0 -0.05 -rad - - -100 1000 -25 - - -10 50 -5 -cs + + +Disabled +Enabled + - -0.00001 0.01 -gauss/s + +0 100 +Percent - -0.00001 0.01 -gauss/s + + +Standard Glide Slope + - --1 70 + + + +1000 2000 1 -% +pwm - -0 0.2 -0.01 + +1000 2000 +1 +pwm - -0.1 10.0 -0.1 -m + + + + +None +File +MAVLink +BothFileAndMAVLink + - -100 1000 -25 + - -0 250 -10 -msec + + +Disabled +Enabled + - -2.0 6.0 -0.5 -m + + +Disabled +Enabled + - - - + Disabled -Enabled +Enabled - + + + +0 32766 +1 + + -GPS 3D Vel and 2D Pos -GPS 2D vel and 2D pos -GPS 2D pos -No GPS +Resume Mission +Restart Mission - -0.05 5.0 -0.05 -m/s + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + - -0.05 5.0 -0.05 -m/s + +-180.00 179.99 +1 +Degrees - -100 1000 -25 + +-180.00 179.99 +1 +Degrees - -0.1 10.0 -0.1 -m + +-180.00 179.99 +1 +Degrees - -100 1000 -25 + +-180.00 179.99 +1 +Degrees - -10 100 -5 -m + +-180.00 179.99 +1 +Degrees - -0 250 -10 -msec + +-180.00 179.99 +1 +Degrees - + -Use Baro -Use Range Finder -Use GPS -Use Range Beacon +Disabled +Enabled - -0.1 10.0 -0.1 -m + + +Disabled +Enabled + - -100 1000 -25 + + +Disabled +Enabled + - -0 250 -10 -msec - - -0.01 0.5 -0.01 -gauss - - + -When flying -When manoeuvring -Never -After first climb yaw reset -Always +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 - -100 1000 -25 + +-18000 17999 +1 +Centi-Degrees - -0.5 5.0 -0.1 -m/s + +-18000 17999 +1 +Centi-Degrees - -100 1000 -25 + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + - -0.1 10.0 -0.1 -m + +-18000 17999 +1 +Centi-Degrees - -100 1000 -25 + +-18000 17999 +1 +Centi-Degrees - -1.0 4.0 -0.1 -rad/s + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + - -0.05 1.0 -0.05 -rad/s + +-18000 17999 +1 +Centi-Degrees - -100 1000 -25 + +-18000 17999 +1 +Centi-Degrees - -0 250 -10 -msec + +0 100 +1 - -0.0001 0.1 -0.0001 -rad/s + +0.0 0.2 +.005 +Seconds - -0.01 1.0 -0.01 -m/s/s + +0.0 0.2 +.005 +Seconds - -0.00001 0.001 -rad/s/s + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True - -0.00001 0.001 -m/s/s/s + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + - -0.01 1.0 -0.1 -m/s/s + +-180.00 179.99 +1 +Degrees - -0.0 1.0 -0.1 + +-180.00 179.99 +1 +Degrees - -0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + +-180.00 179.99 +1 +Degrees - -1 127 + +-180.00 179.99 +1 +Degrees - -50 200 -% + +-180.00 179.99 +1 +Degrees - -0.5 50.0 -m/s + +-180.00 179.99 +1 +Degrees - + Disabled -FirstIMU -FirstAndSecondIMU -AllIMUs +Enabled - -0.05 1.0 -0.05 -rad - - -100 1000 -25 + + +Disabled +Enabled + - -10 50 -5 -cs + + +Disabled +Enabled + - -0.00001 0.01 -gauss/s + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + - -0.00001 0.01 -gauss/s + +-18000 17999 +1 +Centi-Degrees - --1 70 + +-18000 17999 1 -% +Centi-Degrees - -0 0.2 -0.01 + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + - -0.1 10.0 -0.1 -m + +-18000 17999 +1 +Centi-Degrees - -100 1000 -25 + +-18000 17999 +1 +Centi-Degrees - -0 250 -10 -msec + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + - -2.0 6.0 -0.5 -m + +-18000 17999 +1 +Centi-Degrees - -0.5 2.5 -0.1 -m/s/s + +-18000 17999 +1 +Centi-Degrees - - - + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + None -PX4-PWM +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial - -0.001 + + + +0 500 +pwm - -1 + +0.25 0.8 - -1 + +0.9:Low, 0.95:Default, 1.0:High - -0.1 + +6 35 +Volts - + +6 35 +Volts + + +0 200 +Amps + + -None -PX4-PWM +Normal +OneShot +OneShot125 +Brushed16kHz +True - -0.001 + +0 2000 - - - + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + Disabled -AnalogPin -RCChannelPwmValue +Learn +LearnAndSave - + -APM2 A0 -APM2 A1 -APM2 A13 -Pixracer -Pixhawk ADC4 -Pixhawk ADC3 - Pixhawk ADC6 -Pixhawk SBUS +PWM enabled while disarmed +PWM disabled while disarmed - -0 5.0 -0.01 -Volt + +5 80 +1 +Degrees - -0 5.0 -0.01 -Volt + +0 2 +0.1 +Seconds - - + + + +1 60 +1 +seconds - -0 2000 -Microseconds + +0.6 1.0 +0.05 - -0 2000 -Microseconds + +0 0.1 +0.01 @@ -5606,675 +5196,2950 @@ Enable - - - -0 5 -0.5 -meters - - -0 90 -0.1 -degrees - - -centi-Degrees - - -0.1 -meters - - -0.1 -seconds - - -0 30 -0.1 -meters - - -0 10 -0.1 -seconds - - -0 30 -0.1 -m/s - - -0 127 -1 -percent - - -0 127 -1 -seconds - - + -Disabled -Servos to Neutral -Servos to Zero PWM +Disable +ssd1306 +sh1106 - + + + Disabled -Enabled - - - -0 100 -Percent - - - -Standard Glide Slope +Enabled Always Land +Enabled Strict - - - + -Disabled -Enabled +None +CompanionComputer +IRLock +SITL_Gazebo +SITL - - -Disabled -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - - - - -Disabled -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +0 360 +1 +Centi-degrees - - -Disabled -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +-20 20 +1 +Centimeters - - -Disabled -Pixhawk AUXOUT1 -Pixhawk AUXOUT2 -Pixhawk AUXOUT3 -Pixhawk AUXOUT4 -Pixhawk AUXOUT5 -Pixhawk AUXOUT6 -PX4 FMU Relay1 -PX4 FMU Relay2 -PX4IO Relay1 -PX4IO Relay2 -PX4IO ACC1 -PX4IO ACC2 - + +-20 20 +1 +Centimeters - -0 3600 + + + +0.5 5 +0.1 +Hz - - + + -Disabled -Enabled +None +LightWareSF40C +MAVLink +TeraRangerTower - + -None -Chan1 -Chan2 -Chan3 -Chan4 -Chan5 -Chan6 -Chan7 -Chan8 -Chan9 -Chan10 -Chan11 -Chan12 -Chan13 -Chan14 -Chan15 -Chan16 +Default +Upside Down - -0.1 5 -Seconds + +-180 180 +degrees - -1 10 -Seconds + +0 360 +degrees - -100 100000 + +0 45 +degrees - -1000 2000 + +0 360 +degrees - -1000 2000 + +0 45 +degrees - -1000 2000 + +0 360 +degrees - -1000 2000 + +0 45 +degrees - + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + None -RPM1 -RPM2 +LightWareSF40C +MAVLink - -0 100 + + +Default +Upside Down + + + +-180 180 +degrees - - + + +0.4 1.0 +0.1 +seconds - + +0.1 3.0 +0.1 - + +0 0.1 +0.01 - + +0 0.5 +0.05 - - - -800 2200 + +0 100 1 -pwm +degrees/second - -800 2200 + +0 100 1 -pwm +degrees/second - -800 2200 + +0.7 1.5 +0.05 + + +0 4500 1 -pwm - - -Normal -Reversed - + +0.1 4.0 +0.1 - - -Disabled -RCPassThru -Flap -Flap_auto -Aileron -mount_pan -mount_tilt -mount_roll -mount_open -camera_trigger -release -mount2_pan -mount2_tilt -mount2_roll -mount2_open -DifferentialSpoiler1 -DifferentialSpoiler2 -AileronWithInput -Elevator -ElevatorWithInput -Rudder -Flaperon1 -Flaperon2 -GroundSteering -Parachute -EPM -LandingGear -EngineRunEnable -HeliRSC -HeliTailRSC -Motor1 -Motor2 -Motor3 -Motor4 -Motor5 -Motor6 -Motor7 -Motor8 -RCIN1 -RCIN2 -RCIN3 -RCIN4 -RCIN5 -RCIN6 -RCIN7 -RCIN8 -RCIN9 -RCIN10 -RCIN11 -RCIN12 -RCIN13 -RCIN14 -RCIN15 -RCIN16 -Ignition -Choke -Starter -Throttle -TrackerYaw -TrackerPitch - + + + +0.08 0.30 +0.005 - + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + + + + +0.1 +kilometers + + -Disable -Enable +DoNotIncludeHome +IncludeHome - - + + 800 2200 1 pwm - + 800 2200 1 pwm - + 800 2200 1 pwm - + Normal Reversed - + 0 200 pwm - - -1000 2000 + + +800 2200 1 pwm - -1000 2000 + +800 2200 1 pwm - - - -0 500 + +800 2200 1 -Percent*10 +pwm - -0 500 + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 1 -Percent*10 +pwm - -500 1000 + +800 2200 1 -Percent*10 +pwm - -500 1000 + +800 2200 1 -Percent*10 +pwm - + -Disabled -Very Low -Low -Medium -High -Very High +Normal +Reversed - - - -20 2000 -50 -cm/s + +0 200 +pwm - -100 1000 + + + +800 2200 1 -cm - - -0 1000 -50 -cm/s +pwm - -0 500 -10 -cm/s + +800 2200 +1 +pwm - -0 2000 -50 -cm/s + +800 2200 +1 +pwm - -50 500 -10 -cm/s/s + + +Normal +Reversed + - -50 500 -10 -cm/s/s + +0 200 +pwm - -500 5000 + + + +800 2200 1 -cm/s/s/s +pwm - -100 981 + +800 2200 1 -cm/s/s +pwm - -25 250 + +800 2200 1 -cm/s/s +pwm - + -Disable -Enable +Normal +Reversed - - - -0 10000 -100 -cm + +0 200 +pwm - --90 90 + + + +800 2200 1 -deg/s +pwm - - - -500 18000 -100 -Centi-Degrees/Sec + +800 2200 +1 +pwm - -0 72000 + +800 2200 +1 +pwm + + -Disabled -Slow -Medium -Fast +Normal +Reversed -1000 -Centi-Degrees/Sec/Sec - + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + -Disabled -Enabled +Normal +Reversed - -0 180000 + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + -Disabled -Slow -Medium -Fast +Normal +Reversed -1000 -Centi-Degrees/Sec/Sec - -0 180000 + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + -Disabled -Slow -Medium -Fast +Normal +Reversed -1000 -Centi-Degrees/Sec/Sec - + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + -Disabled -Enabled +Normal +Reversed - -3.000 12.000 + +0 200 +pwm - -3.000 12.000 + + + +800 2200 +1 +pwm - -3.000 6.000 + +800 2200 +1 +pwm - -0.5 10.0 + +800 2200 +1 +pwm - -0.08 0.30 -0.005 + + +Normal +Reversed + - -0.01 0.5 -0.01 + +0 200 +pwm - -0 1 -0.01 -Percent + + + +800 2200 +1 +pwm - -0.0 0.02 -0.001 + +800 2200 +1 +pwm - -1 100 + +800 2200 1 -Hz +pwm - -0.08 0.30 -0.005 + + +Normal +Reversed + - -0.01 0.5 -0.01 + +0 200 +pwm - -0 1 -0.01 -Percent + + + +800 2200 +1 +pwm - -0.0 0.02 -0.001 + +800 2200 +1 +pwm - -1 100 + +800 2200 1 -Hz +pwm - -0.10 0.50 -0.005 + + +Normal +Reversed + - -0.010 0.05 -0.01 + +0 200 +pwm - -0 1 -0.01 -Percent + + + +800 2200 +1 +pwm - -0.000 0.02 -0.001 + +800 2200 +1 +pwm - -1 100 + +800 2200 1 -Hz +pwm - -0.1 0.25 + + +Normal +Reversed + - -0.5 0.9 + +0 200 +pwm - - -Disabled 1 + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + +0.4 1.0 +0.1 +seconds + + +0.1 4.0 +0.1 + + +0 0.1 +0.01 + + +0 1.0 +0.05 + + +0 180 +1 +degrees/second + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +5 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +AnalogPin +RCChannelPwmValue + + + + +APM2 A0 +APM2 A1 +APM2 A13 +Pixracer +Pixhawk ADC4 +Pixhawk ADC3 + Pixhawk ADC6 +Pixhawk SBUS + + + +0 5.0 +0.01 +Volt + + +0 5.0 +0.01 +Volt + + + + + +0 2000 +Microseconds + + +0 2000 +Microseconds + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + + +Disable +Enable + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch - -0 1000 -Centi-Degrees - - -0.08 0.35 -0.005 - - -0.01 0.6 -0.01 - - -0 1 -0.01 - - -0.001 0.03 -0.001 - - -1 20 + + + +800 2200 1 -Hz - - -0.08 0.35 -0.005 - - -0.01 0.6 -0.01 - - -0 1 -0.01 +pwm - -0.001 0.03 -0.001 + +800 2200 +1 +pwm - -1 20 + +800 2200 1 -Hz +pwm - -0.180 0.60 -0.005 + + +Normal +Reversed + - -0.01 0.06 -0.01 + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + - -0 1 -0.01 + + + - -0.000 0.02 -0.001 + - -1 20 -1 -Hz + - - - -0.5 5 -0.1 -Hz + @@ -6301,462 +8166,331 @@ percentage - - - -Disabled -Enabled - - - -0:Altitude,1:Circle,2:Polygon - -None -Altitude -Circle -Altitude and Circle -Polygon -Altitude and Polygon -Circle and Polygon -All - - - - -Report Only -RTL or Land - - - -10 1000 -1 -Meters - - -30 10000 -Meters - - -1 10 -Meters - - -1 20 - - - - -0:StopAtFence,1:UseProximitySensor - -None -StopAtFence -UseProximitySensor -All - - - -0 4500 - - -3 30 -meters - - - - --180 180 -1 -Degrees - - --180 180 -1 -Degrees - - --180 180 -1 -Degrees - - - -Servo only -Servo with ExtGyro -DirectDrive VarPitch -DirectDrive FixedPitch - - - - -3-Servo CCPM -H1 Mechanical Mixing - - - -0 1000 -1 -PWM - - --90 90 -1 -Degrees + + - --10 10 -0.1 + +seconds - - -NoFlybar -Flybar - + +seconds - -0 1000 -1 -PWM + +seconds - -0 1000 -1 -PWM + + + +0.4 1.0 +0.1 +seconds - -0 2000 + +0.1 10.0 +0.1 - -0 2000 + +0 1.0 +0.05 - - -Reversed -Normal - + +0 0.1 +0.01 - -1000 2000 + +0 4500 1 -PWM - -1000 2000 -1 -PWM + +0 5 +0.1 +m/s - -1000 2000 -1 -PWM + +0.0 10.0 +0.1 - - -Disabled -Passthrough -Max collective -Mid collective -Min collective - + +0.0 30.0 +0.1 +m/s - -0 1000 -10 -PWM + +0.0 50.0 +0.1 +degree/(m/s) - - -Ch8 Input -SetPoint -Throttle Curve - + +0.0 4500.0 +0.1 +Centidegrees - -0 500 -1 -pwm + + + +0.1 20.0 +0.1 - -0 60 -Seconds + +0.1 10.0 +0.1 - -0 60 -Seconds + +3.0 10.0 +0.2 - -0 1000 -10 + +0.1 1.0 +0.1 - -0 500 -10 + +0.0 0.5 +0.02 - -0 1000 -10 + +1.0 10.0 +0.5 - -0 1000 -10 + +1.0 5.0 +0.05 - -0 18000 -100 -Centi-Degrees + +0.5 2.0 +0.05 - -0 10 -1 + +5.0 30.0 +1.0 - -1 1000 -10 + +0.0 2.0 +0.1 - -0 500 -10 + +0.1 1.0 +0.1 - - - -0 500 -pwm + +0.0 20.0 +0.1 - -0.25 0.8 + +-1 127 +1 - -0.9:Low, 0.95:Default, 1.0:High + +-1 100 +0.1 - -6 35 -Volts + +-1.0 2.0 +0.1 - -6 35 -Volts + +0 45 +1 - -0 200 -Amps + +-45 0 +1 - - -Normal -OneShot -OneShot125 -Brushed16kHz - -True + +0.0 2.0 +0.1 - -0 2000 + +1.0 5.0 +0.2 - -0 2000 + +0.1 1.0 +0.1 - -0.0:Low, 0.15:Default, 0.3:High + +-5 40 +1 - -0.0:Low, 0.1:Default, 0.2:High + +0.0 20.0 +0.1 +m/s - -0 10 -Seconds + +-2.0 2.0 +0.1 +m/s/m - -0.2 0.8 + +0.1 1.0 +0.1 - - -Disabled -Learn -LearnAndSave - + +0.0 0.5 +0.02 - - -PWM enabled while disarmed -PWM disabled while disarmed - + +0.0 0.5 +0.02 - -5 80 -1 -Degrees + +0.1 1.0 +0.1 - - - + -Disabled -Enabled Always Land -Enabled Strict +Disable +Enable - + + + -None -CompanionComputer -IRLock -SITL_Gazebo -SITL +Disable +Enable - -0 360 -1 -Centi-degrees - - --20 20 -1 -Centimeters - - --20 20 + 1 -Centimeters +meters - - + + -None -Pozyx +Disable +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 - --90 90 -0.000001 -degrees - - --180 180 -0.000001 -degrees - - -0 10000 -1 -meters + +900 2100 - --180 +180 -1 -degrees + +900 2100 - - - + -None -LightWareSF40C -MAVLink +Disable +Chan1 +Chan3 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 - + + + -Default -Upside Down +Disable +Enable - --180 180 - - -0 360 - - -0 45 - - -0 360 + +0 1 - -0 45 + + + +20 2000 +50 +cm/s - -0 360 + +100 1000 +1 +cm - -0 45 + +0 1000 +50 +cm/s - -0 360 + +0 500 +10 +cm/s - -0 45 + +0 2000 +50 +cm/s - -0 360 + +50 500 +10 +cm/s/s - -0 45 + +50 500 +10 +cm/s/s - -0 360 + +500 5000 +1 +cm/s/s/s - -0 45 + +100 981 +1 +cm/s/s - - -None -LightWareSF40C -MAVLink - + +25 250 +1 +cm/s/s - + -Default -Upside Down +Disable +Enable - --180 180 - - - - -Disabled -Enabled - - - - -None -Servo -EPM - - - -1000 2000 - - -1000 2000 - - -1000 2000 - - -0 255 -seconds - - -0 255 + + +0 4 +0.25 - - - + +0 2 +0.25 - + +0 2 +0.25 - + +0.8 1.2 +0.05 - + +0 4500 +1 diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml new file mode 100644 index 000000000..91f87f90f --- /dev/null +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml @@ -0,0 +1,8496 @@ + + + + + + + + +True + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +1 10 +1 + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + + +0 1 +0.01 + + +0 5 +0.01 + + +0 15 +0.1 +Degrees + + +0 1000 +1 +meters + + +0 100 +1 +meters + + + +Disabled +FBWMixing +DirectMixing + + + + +Disabled +Enabled + + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s/s + + +0 127 +1 +0.1 seconds + + +-100 100 +1 +Percent + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 10 +0.5 +seconds + + +0 100 +Percent + + + + +0 45 +1 +degrees + + +0:AUTO_ALWAYS,1:AUTO_LAND,2:AUTO_LOITER_TO_ALT,3:AUTO_LOITER_ALL,4:AUTO_WAYPOINTS,5:LOITER,6:RTL,7:CIRCLE,8:CRUISE,9:FBWB,10:GUIDED + + + +Default +L1Controller + + + + +Automatic + + + +-32767 32767 +1 +Meters + + +1 32767 +1 +Meters + + +0 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + + +None +GuidedMode +ReportOnly +GuidedModeThrPass +RTL_Mode + + + + + + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + + +NoAutoEnable +AutoEnable +AutoEnableDisableFloorOnly + + + + +FenceReturnPoint +NearestRallyPoint + + + + +Disabled +Enabled + + + +5 100 +1 +m/s + + +5 100 +1 +m/s + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 10000 +meters + + +1 10 +0.1 +m/s + + +-100 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 127 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +925 2200 +1 + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +CIRCLE/no change(if already in AUTO|GUIDED|LOITER) +CIRCLE +FBWA + + + +1 100 +0.5 +seconds + + + +Continue +ReturnToLaunch +Glide +Deploy Parachute + + + +1 300 +0.5 +seconds + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Heartbeat +HeartbeatAndREMRSSI +HeartbeatAndAUTO + + + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + +0 9000 +1 +centi-Degrees + + +0 9000 +1 +centi-Degrees + + +-9000 0 +1 +centi-Degrees + + +10 500 +1 +degrees/second + + +10 500 +1 +degrees/second + + + +Disabled +Enabled + + + +-100 100 +0.1 +Meters + + +10 360 +1 +degrees/second + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0.5 1.2 + + + +Disabled +Enabled + + + +-1000 1000 +percent + + +-1000 1000 +percent + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +PX4/Pixhawk-Default + + + + + + + +cm/s + + +m/s + + +cm/s + + +centi-Degrees + + +centimeters + + +centimeters + + + +Disabled +Enabled + + + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0 100 +Percent + + +0 100 +1 +m/s + + +0 100 +Percent + + +0 100 +1 +m/s + + + + + + + +Disabled +Channel1 +Channel2 +Channel3 +Channel4 +Channel5 +Channel6 +Channel7 +Channel8 + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 90 +0.1 +degrees + + + +Disable +Enable - go HOME then land +Enable - go directly to landing sequence + + + + +Disable +Enable + + + +10 127 +m/s/s + + +0:Disarm + + + + + +Disabled +Enabled + + + + + +True + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + + +Mission Planner and DroidPlanner + AP Planner 2 + + + + +Disabled +Enabled + + + +0 10 +.5 +Hz + + +0.0 1000.0 +10 +Centimeters + + +0.0 500.0 +10 + + +0:Feedback from mid stick,1:High throttle cancels landing,2:Disarm on land detection + +None +Feedback from mid stick +High throttle cancels landing +Disarm on land detection + + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0 8000 +1 +Centimeters + + +0.5 10.0 + +Disabled +Shallow +Steep + +.1 + + +0 2000 +50 +cm/s + + +0.01 2.0 +0.01 + + + +Disabled +Land +RTL + + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode + + + +100 900 + + + +Disabled +Enabled + + + + +Disabled +Mode1 +Mode2 +Mode1+2 +Mode3 +Mode1+3 +Mode2+3 +Mode1+2+3 +Mode4 +Mode1+4 +Mode2+4 +Mode1+2+4 +Mode3+4 +Mode1+3+4 +Mode2+3+4 +Mode1+2+3+4 +Mode5 +Mode1+5 +Mode2+5 +Mode1+2+5 +Mode3+5 +Mode1+3+5 +Mode2+3+5 +Mode1+2+3+5 +Mode4+5 +Mode1+4+5 +Mode2+4+5 +Mode1+2+4+5 +Mode3+4+5 +Mode1+3+4+5 +Mode2+3+4+5 +Mode1+2+3+4+5 +Mode6 +Mode1+6 +Mode2+6 +Mode1+2+6 +Mode3+6 +Mode1+3+6 +Mode2+3+6 +Mode1+2+3+6 +Mode4+6 +Mode1+4+6 +Mode2+4+6 +Mode1+2+4+6 +Mode3+4+6 +Mode1+3+4+6 +Mode2+3+4+6 +Mode1+2+3+4+6 +Mode5+6 +Mode1+5+6 +Mode2+5+6 +Mode1+2+5+6 +Mode3+5+6 +Mode1+3+5+6 +Mode2+3+5+6 +Mode1+2+3+5+6 +Mode4+5+6 +Mode1+4+5+6 +Mode2+4+5+6 +Mode1+2+4+5+6 +Mode3+4+5+6 +Mode1+3+4+5+6 +Mode2+3+4+5+6 +Mode1+2+3+4+5+6 + + + +-1 1000 +1 +Centimeters + + +0 3000 +10 +Centimeters + + + +Never change yaw +Face next waypoint +Face next waypoint except RTL +Face along GPS course + + + +0 60000 +1000 +ms + + +30 200 +10 +cm/s + + +0 500 +10 +cm/s + + +50 500 +10 +Centimeters/Second + + +50 500 +10 +cm/s/s + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode +Enabled always LAND + + + +925 1100 +1 +pwm + + +0 300 +1 +pwm + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW + +Default +Default+RCIN +Default+IMU +Default+Motors +NearlyAll-AC315 +NearlyAll +All+FastATT +All+MotBatt +All+FastIMU +All+FastIMU+PID +All+FullIMU +Disabled + + + + +Normal Start-up +Start-up in ESC Calibration mode if throttle high +Start-up in ESC Calibration mode regardless of throttle +Start-up and automatically calibrate ESCs +Disabled + + + + +None +Stab Roll/Pitch kP +Rate Roll/Pitch kP +Rate Roll/Pitch kI +Rate Roll/Pitch kD +Stab Yaw kP +Rate Yaw kP +Rate Yaw kD +Altitude Hold kP +Throttle Rate kP +Throttle Accel kP +Throttle Accel kI +Throttle Accel kD +Loiter Speed +Loiter Pos kP +Velocity XY kP +Velocity XY kI +WP Speed +Acro RollPitch kP +Acro Yaw kP +Heli Ext Gyro +OF Loiter kP +OF Loiter kI +OF Loiter kD +Declination +Circle Rate +RangeFinder Gain +Rate Pitch kP +Rate Pitch kI +Rate Pitch kD +Rate Roll kP +Rate Roll kI +Rate Roll kD +Rate Pitch FF +Rate Roll FF +Rate Yaw FF + + + +0 32767 + + +0 32767 + + + +Plus +X +V +H +V-Tail +A-Tail +Y6B + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + +0 127 +Seconds + + +1000 8000 +Centi-degrees + + +0 100 + +Very Soft +Soft +Medium +Crisp +Very Crisp + +10 + + +4 12 +deg/sec + + +2000 4500 +Centi-degrees + + + +No repositioning +Repositioning + + + + +Land +AltHold +Land even in Stabilize + + + +0.6:Strict, 0.8:Default, 1.0:Relaxed + + + +Disabled +Enabled + + + +50 490 +1 +Hz + + +1 10 + + +1 10 + + +0 3 +0.1 + + +0 3 +0.1 + + + +Disabled +Leveling +Leveling and Limited + + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0.1 6.0 +0.1 + + +0.02 1.00 +0.01 + + +0 4500 +10 +cm/s/s + + +1.000 8.000 + + +0.500 1.500 +0.05 + + +0.000 3.000 + + +0 1000 +Percent*10 + + +0.000 0.400 + + +1.000 100.000 +Hz + + +1.000 3.000 + + +0.500 2.000 + + +0:Roll,1:Pitch,2:Yaw + +All +Roll Only +Pitch Only +Yaw Only +Roll and Pitch +Roll and Yaw +Pitch and Yaw + + + +0.05 0.10 + + +0.001 0.006 + + + +Stopped +Running + + + + +Do Not Use in RTL and Land +Use in RTL and Land + + + +0 5 + + + +Auto +Guided +RTL +Land +Brake +Throw + + + + +Upward Throw +Drop + + + + +Disabled +Enabled + + + +0:ADSBMavlinkProcessing + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0 1 + + + +Undefined +Quad +Hexa +Octa +OctaQuad +Y6 +Heli +Tri +SingleCopter +CoaxCopter + + + + + + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 20 +0.1 +seconds + + +0 20 +0.1 +seconds + + +0 100 +1 +degrees/second + + +0 20 +1 +seconds + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10 +0.1 +seconds + + + +Position +OnOff +ContinuousRotation + + + + +Position +OnOff +ContinuousRotation + + + +0 50 +0.1 +degrees/second + + +0 50 +0.1 +degrees/second + + +0 2 +0.01 +seconds + + +0 2 +0.01 +seconds + + +-10 10 +0.1 +degrees + + +-10 10 +0.1 +degrees + + +0 360 +0.1 +degrees + + +0 100 +1 +meters + + + +Barometer +GPS +GPS vehicle only + + + +1 10 +1 +Hz + + +-90 0 +1 +Degrees + + +0 90 +1 +Degrees + + +0:ATTITUDE,1:GPS,2:RCIN,3:IMU,4:RCOUT,5:COMPASS + +Default +Disabled + + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +1 255 + + + + + + +True + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +APM2-Default +PX4/Pixhawk-Default + + + + + + + + +MANUAL +LEARNING +STEERING +HOLD +AUTO +RTL +GUIDED + + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 30 +1 +seconds + + +0:Steering,1:Throttle + +None +Steering +Throttle + + + + +Disabled +Enabled + + + + +Disabled +APM TriggerPin0 +APM TriggerPin1 +APM TriggerPin2 +APM TriggerPin3 +APM TriggerPin4 +APM TriggerPin5 +APM TriggerPin6 +APM TriggerPin7 +APM TriggerPin8 +Pixhawk TriggerPin50 +Pixhawk TriggerPin51 +Pixhawk TriggerPin52 +Pixhawk TriggerPin53 +Pixhawk TriggerPin54 +Pixhawk TriggerPin55 + + + +0 20 +0.1 +m/s/s + + +0 100 +0.1 +m/s + + +0 100 +1 +percent + + +0 100 +0.1 +meters + + +0 100 +1 +percent + + +0 100 +1 +m/s + + +0 360 +1 +degrees + + + +Nothing +LearnWaypoint + + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +SkidSteeringOutput + + + + +Disabled +SkidSteeringInput + + + + +Nothing +RTL +HOLD + + + +seconds + + + +Disabled +Enabled + + + +925 1100 +1 + + + +Disabled +Enabled + + + + +Disabled +HOLD +HoldAndDisarm + + + +0 1000 +1 +centimeters + + +-45 45 +1 +centimeters + + +0 100 +0.1 +seconds + + +1 100 +1 + + + + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + +0 1000 +0.1 +meters + + +0.2 10 +0.1 +gravities + + + + + +Disabled +Enabled + + + +1 100 + + +1 100000 + + +-1 16777215 + + + +NoInfo +Light +Small +Large +HighVortexlarge +Heavy +HighlyManuv +Rotocraft +RESERVED +Glider +LightAir +Parachute +UltraLight +RESERVED +UAV +Space +RESERVED +EmergencySurface +ServiceSurface +PointObstacle + + + + +NO_DATA +L15W23 +L25W28P5 +L25W34 +L35W33 +L35W38 +L45W39P5 +L45W45 +L55W45 +L55W52 +L65W59P5 +L65W67 +L75W72P5 +L75W80 +L85W80 +L85W90 + + + + +NoData +Left2m +Left4m +Left6m +Center +Right2m +Right4m +Right6m + + + + +NO_DATA +AppliedBySensor + + + + +Disabled +Rx-Only +Tx-Only +Rx and Tx Enabled + + + + + + + + + + + + + + + + + + + + + +meters + + +meters + + +millibar + + + + + + + + + + + + + + +seconds + + + + +0.0 1.0 +.01 + + + +Disabled +Enabled + + + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 + + + +0.001 0.5 +.01 + + +0 10 +1 + + + +Disabled +Enable EKF2 +Enable EKF3 + + + + + + +Disabled +THR_MIN PWM when disarmed +0 PWM when disarmed + + + +0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration + +None +All +Barometer +Compass +GPS Lock +INS(INertial Sensors - accels & gyros) +Parameters(unused) +RC Failsafe +Board voltage +Battery Level +Airspeed +LoggingAvailable +Hardware safety switch +GPS configuration + + + +0.25 3.0 +m/s/s + + +0.1 +Volts + + +0.1 +Volts + + + + + +None +I2C-MS4525D0 +Analog +I2C-MS5525 + + + + +Use +Don't Use + + + +0.1 + + +0.1 + + + + + + + + + +Disable +Enable + + + + + + +Bus0 +Bus1 + + + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +3.000 12.000 + + +3.000 12.000 + + +3.000 6.000 + + +0.5 10.0 + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + +Disabled 1 + + + +0 1000 +Centi-Degrees + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.180 0.60 +0.005 + + +0.01 0.06 +0.01 + + +0 1 +0.01 + + +0.000 0.02 +0.001 + + +1 20 +1 +Hz + + + + + +Disabled +Enabled + + + + +Remain in AVOID_ADSB +Resume previous flight mode +RTL +Resume if AUTO else Loiter + + + + + +seconds + + +seconds + + +meters + + +meters + + +meters + + +meters + + + + +0:StopAtFence,1:UseProximitySensor + +None +StopAtFence +UseProximitySensor +All + + + +0 4500 + + +3 30 +meters + + + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Watts + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Amps + + + + + +None +Pozyx + + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10000 +1 +meters + + +-180 +180 +1 +degrees + + + + + +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled + +True + + + +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz + +True + + +-32767 32768 + + + +Disabled +Enabled +Dynamic ID/Update + + + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 + +Disabled +Enabled + +True + + +-1 80 +degreesC + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + +Disabled +Enabled + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +0 3600 + + + + + +Servo +Relay + + + +0 50 +deciseconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 +meters + + + +Low +High + + + +0 10000 +milliseconds + + +0 180 +Degrees + + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + + + + +TriggerLow +TriggerHigh + + + + + + +Disabled +Enabled + + + + +First Relay +Second Relay +Third Relay +Fourth Relay +Servo + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + +0 32000 +1 +Meters + + +0 5000 +1 +Milliseconds + + + + +0 10000 +100 +cm + + +-90 90 +1 +deg/s + + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-3.142 3.142 +0.01 +Radians + + + +Disabled +Internal-Learning +EKF-Learning + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Use Throttle +Use Current + + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +FirstCompass +SecondCompass +ThirdCompass + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + + + + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 32 + +Very Strict +Strict +Default +Relaxed + +0.1 + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +milliseconds + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +2.0 6.0 +0.5 +m/s + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +2.0 6.0 +0.5 +m/s + + +0.5 2.5 +0.1 +m/s/s + + + + + +Disabled +Enabled + + + +0:Altitude,1:Circle,2:Polygon + +None +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All + + + + +Report Only +RTL or Land + + + +10 1000 +1 +Meters + + +30 10000 +Meters + + +1 10 +Meters + + +1 20 + + + + + +Disabled +Enabled + + + +-200 +200 +1 + + +-200 +200 +1 + + +-18000 +18000 +1 + + +m + + +m + + +m + + +0 255 + + + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +0.1 +meters + + + +FirstBaro +2ndBaro +3rdBaro + + + + +Disabled +Bus0 + + + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G + + + + +Disabled +Enabled + + + + +Any +FloatRTK +IntegerRTK + +True + + + +Disabled +Enabled +NoChange + + + +-100 90 +Degrees + + + +send to first GPS +send to 2nd GPS +send to all + + + + +None +All +External only + + + + +Disabled +log every sample +log every 5 samples + +True + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Do not save config +Save config +Save only when needed + + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Disables automatic configuration +Enable automatic configuration + + + + +10Hz +8Hz +5Hz + +milliseconds + + + +10Hz +8Hz +5Hz + +milliseconds + + +m + + +m + + +m + + +m + + +m + + +m + + +0 250 +milliseconds + + +0 250 +milliseconds + + + + + +Disabled +Enabled + + + + +None +Servo +EPM + + + +1000 2000 +PWM + + +1000 2000 +PWM + + +1000 2000 +PWM + + +0 255 +seconds + + +0 255 + + + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + + +Servo only +Servo with ExtGyro +DirectDrive VarPitch +DirectDrive FixedPitch + + + + +3-Servo CCPM +H1 Mechanical Mixing + + + +0 1000 +1 +PWM + + +-90 90 +1 +Degrees + + +-10 10 +0.1 + + + +NoFlybar +Flybar + + + +0 1000 +1 +PWM + + +0 1000 +1 +PWM + + +0 2000 + + +0 2000 + + + +Reversed +Normal + + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + + +Disabled +Passthrough +Max collective +Mid collective +Min collective + + + +0 1000 +10 +PWM + + + +Ch8 Input +SetPoint +Throttle Curve + + + +0 500 +1 +pwm + + +0 60 +Seconds + + +0 60 +Seconds + + +0 1000 +10 + + +0 500 +10 + + +0 1000 +10 + + +0 1000 +10 + + +0 18000 +100 +Centi-Degrees + + +0 10 +1 + + +1 1000 +10 + + +0 500 +10 + + + + + +Disabled +Enabled + + + + +None +Chan1 +Chan2 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +0.1 5 +Seconds + + +1 10 +Seconds + + +100 100000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + + +None +RPM1 +RPM2 + + + +0 100 + + + + +0 500 +1 +Percent*10 + + +0 500 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + + +Disabled +Very Low +Low +Medium +High +Very High + + + + + + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0.05 50 + + + +Never +Start-up only + + + + +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle + + + + +IMU 1 +IMU 2 +IMU 3 + + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +True + + +True + + +True + + +True + + +True + + +True + + + + + + +0 5 +0.5 +meters + + +0 90 +0.1 +degrees + + +centi-Degrees + + +0.1 +meters + + +0.1 +seconds + + +0 30 +0.1 +meters + + +0 10 +0.1 +seconds + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 127 +1 +seconds + + + +Disabled +Servos to Neutral +Servos to Zero PWM + + + + +Disabled +Enabled + + + +0 100 +Percent + + + +Standard Glide Slope + + + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + + + + +None +File +MAVLink +BothFileAndMAVLink + + + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + + +0 32766 +1 + + + +Resume Mission +Restart Mission + + + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0 100 +1 + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + + + + + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 +Brushed16kHz + +True + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + +PWM enabled while disarmed +PWM disabled while disarmed + + + +5 80 +1 +Degrees + + +0 2 +0.1 +Seconds + + + + +1 60 +1 +seconds + + +0.6 1.0 +0.05 + + +0 0.1 +0.01 + + + + + +Off +Low +Medium +High + + + + +Disable +Enable + + + + +Disable +Enable + + + + +Disable +ssd1306 +sh1106 + + + + + + +Disabled +Enabled Always Land +Enabled Strict + + + + +None +CompanionComputer +IRLock +SITL_Gazebo +SITL + + + +0 360 +1 +Centi-degrees + + +-20 20 +1 +Centimeters + + +-20 20 +1 +Centimeters + + + + +0.5 5 +0.1 +Hz + + + + + +None +LightWareSF40C +MAVLink +TeraRangerTower + + + + +Default +Upside Down + + + +-180 180 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + + +None +LightWareSF40C +MAVLink + + + + +Default +Upside Down + + + +-180 180 +degrees + + + + +0.4 1.0 +0.1 +seconds + + +0.1 3.0 +0.1 + + +0 0.1 +0.01 + + +0 0.5 +0.05 + + +0 100 +1 +degrees/second + + +0 100 +1 +degrees/second + + +0.7 1.5 +0.05 + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + + + + +0.1 +kilometers + + + +DoNotIncludeHome +IncludeHome + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + +0.4 1.0 +0.1 +seconds + + +0.1 4.0 +0.1 + + +0 0.1 +0.01 + + +0 1.0 +0.05 + + +0 180 +1 +degrees/second + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +5 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +AnalogPin +RCChannelPwmValue + + + + +APM2 A0 +APM2 A1 +APM2 A13 +Pixracer +Pixhawk ADC4 +Pixhawk ADC3 + Pixhawk ADC6 +Pixhawk SBUS + + + +0 5.0 +0.01 +Volt + + +0 5.0 +0.01 +Volt + + + + + +0 2000 +Microseconds + + +0 2000 +Microseconds + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + + +Disable +Enable + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + + + + + + + + + + + + +Disabled +Enabled + + + +0 100 +percentage + + +1000 2000 +ms + + +0 1000 +cm/s + + +0 100 +percentage + + + + + + +seconds + + +seconds + + +seconds + + + + +0.4 1.0 +0.1 +seconds + + +0.1 10.0 +0.1 + + +0 1.0 +0.05 + + +0 0.1 +0.01 + + +0 4500 +1 + + +0 5 +0.1 +m/s + + +0.0 10.0 +0.1 + + +0.0 30.0 +0.1 +m/s + + +0.0 50.0 +0.1 +degree/(m/s) + + +0.0 4500.0 +0.1 +Centidegrees + + + + +0.1 20.0 +0.1 + + +0.1 10.0 +0.1 + + +3.0 10.0 +0.2 + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +1.0 10.0 +0.5 + + +1.0 5.0 +0.05 + + +0.5 2.0 +0.05 + + +5.0 30.0 +1.0 + + +0.0 2.0 +0.1 + + +0.1 1.0 +0.1 + + +0.0 20.0 +0.1 + + +-1 127 +1 + + +-1 100 +0.1 + + +-1.0 2.0 +0.1 + + +0 45 +1 + + +-45 0 +1 + + +0.0 2.0 +0.1 + + +1.0 5.0 +0.2 + + +0.1 1.0 +0.1 + + +-5 40 +1 + + +0.0 20.0 +0.1 +m/s + + +-2.0 2.0 +0.1 +m/s/m + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +0.0 0.5 +0.02 + + +0.1 1.0 +0.1 + + + +Disable +Enable + + + + + + +Disable +Enable + + + +1 +meters + + + + + +Disable +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +900 2100 + + +900 2100 + + + +Disable +Chan1 +Chan3 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + + + + +Disable +Enable + + + +0 1 + + + + +20 2000 +50 +cm/s + + +100 1000 +1 +cm + + +0 1000 +50 +cm/s + + +0 500 +10 +cm/s + + +0 2000 +50 +cm/s + + +50 500 +10 +cm/s/s + + +50 500 +10 +cm/s/s + + +500 5000 +1 +cm/s/s/s + + +100 981 +1 +cm/s/s + + +25 250 +1 +cm/s/s + + + +Disable +Enable + + + + + +0 4 +0.25 + + +0 2 +0.25 + + +0 2 +0.25 + + +0.8 1.2 +0.05 + + +0 4500 +1 + + + diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml new file mode 100644 index 000000000..91f87f90f --- /dev/null +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml @@ -0,0 +1,8496 @@ + + + + + + + + +True + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +1 10 +1 + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + + +0 1 +0.01 + + +0 5 +0.01 + + +0 15 +0.1 +Degrees + + +0 1000 +1 +meters + + +0 100 +1 +meters + + + +Disabled +FBWMixing +DirectMixing + + + + +Disabled +Enabled + + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s/s + + +0 127 +1 +0.1 seconds + + +-100 100 +1 +Percent + + +0 30 +0.1 +m/s + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 10 +0.5 +seconds + + +0 100 +Percent + + + + +0 45 +1 +degrees + + +0:AUTO_ALWAYS,1:AUTO_LAND,2:AUTO_LOITER_TO_ALT,3:AUTO_LOITER_ALL,4:AUTO_WAYPOINTS,5:LOITER,6:RTL,7:CIRCLE,8:CRUISE,9:FBWB,10:GUIDED + + + +Default +L1Controller + + + + +Automatic + + + +-32767 32767 +1 +Meters + + +1 32767 +1 +Meters + + +0 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + +-32767 32767 +1 +Meters + + + +None +GuidedMode +ReportOnly +GuidedModeThrPass +RTL_Mode + + + + + + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + +0 32767 +1 +meters + + + +NoAutoEnable +AutoEnable +AutoEnableDisableFloorOnly + + + + +FenceReturnPoint +NearestRallyPoint + + + + +Disabled +Enabled + + + +5 100 +1 +m/s + + +5 100 +1 +m/s + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 10000 +meters + + +1 10 +0.1 +m/s + + +-100 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 127 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +925 2200 +1 + + +0 100 +1 +Percent + + + +Disabled +Enabled + + + + +CIRCLE/no change(if already in AUTO|GUIDED|LOITER) +CIRCLE +FBWA + + + +1 100 +0.5 +seconds + + + +Continue +ReturnToLaunch +Glide +Deploy Parachute + + + +1 300 +0.5 +seconds + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Heartbeat +HeartbeatAndREMRSSI +HeartbeatAndAUTO + + + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + + +Manual +CIRCLE +STABILIZE +TRAINING +ACRO +FBWA +FBWB +CRUISE +AUTOTUNE +Auto +RTL +Loiter +AVOID_ADSB +Guided +QSTABILIZE +QHOVER +QLOITER +QLAND +QRTL + + + +0 9000 +1 +centi-Degrees + + +0 9000 +1 +centi-Degrees + + +-9000 0 +1 +centi-Degrees + + +10 500 +1 +degrees/second + + +10 500 +1 +degrees/second + + + +Disabled +Enabled + + + +-100 100 +0.1 +Meters + + +10 360 +1 +degrees/second + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0.5 1.2 + + + +Disabled +Enabled + + + +-1000 1000 +percent + + +-1000 1000 +percent + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +PX4/Pixhawk-Default + + + + + + + +cm/s + + +m/s + + +cm/s + + +centi-Degrees + + +centimeters + + +centimeters + + + +Disabled +Enabled + + + + + + +Disabled +UpUp +UpDown +DownUp +DownDown +UpUpSwap +UpDownSwap +DownUpSwap +DownDownSwap + + + +0 100 +Percent + + +0 100 +1 +m/s + + +0 100 +Percent + + +0 100 +1 +m/s + + + + + + + +Disabled +Channel1 +Channel2 +Channel3 +Channel4 +Channel5 +Channel6 +Channel7 +Channel8 + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0 90 +0.1 +degrees + + + +Disable +Enable - go HOME then land +Enable - go directly to landing sequence + + + + +Disable +Enable + + + +10 127 +m/s/s + + +0:Disarm + + + + + +Disabled +Enabled + + + + + +True + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + + +Mission Planner and DroidPlanner + AP Planner 2 + + + + +Disabled +Enabled + + + +0 10 +.5 +Hz + + +0.0 1000.0 +10 +Centimeters + + +0.0 500.0 +10 + + +0:Feedback from mid stick,1:High throttle cancels landing,2:Disarm on land detection + +None +Feedback from mid stick +High throttle cancels landing +Disarm on land detection + + + +0 30 +1 +seconds + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0 8000 +1 +Centimeters + + +0.5 10.0 + +Disabled +Shallow +Steep + +.1 + + +0 2000 +50 +cm/s + + +0.01 2.0 +0.01 + + + +Disabled +Land +RTL + + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode + + + +100 900 + + + +Disabled +Enabled + + + + +Disabled +Mode1 +Mode2 +Mode1+2 +Mode3 +Mode1+3 +Mode2+3 +Mode1+2+3 +Mode4 +Mode1+4 +Mode2+4 +Mode1+2+4 +Mode3+4 +Mode1+3+4 +Mode2+3+4 +Mode1+2+3+4 +Mode5 +Mode1+5 +Mode2+5 +Mode1+2+5 +Mode3+5 +Mode1+3+5 +Mode2+3+5 +Mode1+2+3+5 +Mode4+5 +Mode1+4+5 +Mode2+4+5 +Mode1+2+4+5 +Mode3+4+5 +Mode1+3+4+5 +Mode2+3+4+5 +Mode1+2+3+4+5 +Mode6 +Mode1+6 +Mode2+6 +Mode1+2+6 +Mode3+6 +Mode1+3+6 +Mode2+3+6 +Mode1+2+3+6 +Mode4+6 +Mode1+4+6 +Mode2+4+6 +Mode1+2+4+6 +Mode3+4+6 +Mode1+3+4+6 +Mode2+3+4+6 +Mode1+2+3+4+6 +Mode5+6 +Mode1+5+6 +Mode2+5+6 +Mode1+2+5+6 +Mode3+5+6 +Mode1+3+5+6 +Mode2+3+5+6 +Mode1+2+3+5+6 +Mode4+5+6 +Mode1+4+5+6 +Mode2+4+5+6 +Mode1+2+4+5+6 +Mode3+4+5+6 +Mode1+3+4+5+6 +Mode2+3+4+5+6 +Mode1+2+3+4+5+6 + + + +-1 1000 +1 +Centimeters + + +0 3000 +10 +Centimeters + + + +Never change yaw +Face next waypoint +Face next waypoint except RTL +Face along GPS course + + + +0 60000 +1000 +ms + + +30 200 +10 +cm/s + + +0 500 +10 +cm/s + + +50 500 +10 +Centimeters/Second + + +50 500 +10 +cm/s/s + + + +Disabled +Enabled always RTL +Enabled Continue with Mission in Auto Mode +Enabled always LAND + + + +925 1100 +1 +pwm + + +0 300 +1 +pwm + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + +Stabilize +Acro +AltHold +Auto +Guided +Loiter +RTL +Circle +Land +Drift +Sport +Flip +AutoTune +PosHold +Brake +Throw +Avoid_ADSB +Guided_NoGPS + + + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW + +Default +Default+RCIN +Default+IMU +Default+Motors +NearlyAll-AC315 +NearlyAll +All+FastATT +All+MotBatt +All+FastIMU +All+FastIMU+PID +All+FullIMU +Disabled + + + + +Normal Start-up +Start-up in ESC Calibration mode if throttle high +Start-up in ESC Calibration mode regardless of throttle +Start-up and automatically calibrate ESCs +Disabled + + + + +None +Stab Roll/Pitch kP +Rate Roll/Pitch kP +Rate Roll/Pitch kI +Rate Roll/Pitch kD +Stab Yaw kP +Rate Yaw kP +Rate Yaw kD +Altitude Hold kP +Throttle Rate kP +Throttle Accel kP +Throttle Accel kI +Throttle Accel kD +Loiter Speed +Loiter Pos kP +Velocity XY kP +Velocity XY kI +WP Speed +Acro RollPitch kP +Acro Yaw kP +Heli Ext Gyro +OF Loiter kP +OF Loiter kI +OF Loiter kD +Declination +Circle Rate +RangeFinder Gain +Rate Pitch kP +Rate Pitch kI +Rate Pitch kD +Rate Roll kP +Rate Roll kI +Rate Roll kD +Rate Pitch FF +Rate Roll FF +Rate Yaw FF + + + +0 32767 + + +0 32767 + + + +Plus +X +V +H +V-Tail +A-Tail +Y6B + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +Super Simple Mode +Acro Trainer +Sprayer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost Copter Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw +Avoidance +PrecLoiter +Object Avoidance +ArmDisarm + + + +0 127 +Seconds + + +1000 8000 +Centi-degrees + + +0 100 + +Very Soft +Soft +Medium +Crisp +Very Crisp + +10 + + +4 12 +deg/sec + + +2000 4500 +Centi-degrees + + + +No repositioning +Repositioning + + + + +Land +AltHold +Land even in Stabilize + + + +0.6:Strict, 0.8:Default, 1.0:Relaxed + + + +Disabled +Enabled + + + +50 490 +1 +Hz + + +1 10 + + +1 10 + + +0 3 +0.1 + + +0 3 +0.1 + + + +Disabled +Leveling +Leveling and Limited + + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0.1 6.0 +0.1 + + +0.02 1.00 +0.01 + + +0 4500 +10 +cm/s/s + + +1.000 8.000 + + +0.500 1.500 +0.05 + + +0.000 3.000 + + +0 1000 +Percent*10 + + +0.000 0.400 + + +1.000 100.000 +Hz + + +1.000 3.000 + + +0.500 2.000 + + +0:Roll,1:Pitch,2:Yaw + +All +Roll Only +Pitch Only +Yaw Only +Roll and Pitch +Roll and Yaw +Pitch and Yaw + + + +0.05 0.10 + + +0.001 0.006 + + + +Stopped +Running + + + + +Do Not Use in RTL and Land +Use in RTL and Land + + + +0 5 + + + +Auto +Guided +RTL +Land +Brake +Throw + + + + +Upward Throw +Drop + + + + +Disabled +Enabled + + + +0:ADSBMavlinkProcessing + + +-0.5 1.0 + +Disabled +Very Low +Low +Medium +High +Very High + + + +0 1 + + + +Undefined +Quad +Hexa +Octa +OctaQuad +Y6 +Heli +Tri +SingleCopter +CoaxCopter + + + + + + + +True + +ArduPlane +AntennaTracker +Copter +Rover + + + +1 255 + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 20 +0.1 +seconds + + +0 20 +0.1 +seconds + + +0 100 +1 +degrees/second + + +0 20 +1 +seconds + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10 +0.1 +seconds + + + +Position +OnOff +ContinuousRotation + + + + +Position +OnOff +ContinuousRotation + + + +0 50 +0.1 +degrees/second + + +0 50 +0.1 +degrees/second + + +0 2 +0.01 +seconds + + +0 2 +0.01 +seconds + + +-10 10 +0.1 +degrees + + +-10 10 +0.1 +degrees + + +0 360 +0.1 +degrees + + +0 100 +1 +meters + + + +Barometer +GPS +GPS vehicle only + + + +1 10 +1 +Hz + + +-90 0 +1 +Degrees + + +0 90 +1 +Degrees + + +0:ATTITUDE,1:GPS,2:RCIN,3:IMU,4:RCOUT,5:COMPASS + +Default +Disabled + + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +0.0 3.0 +0.01 + + +0.0 3.0 +0.01 + + +0 4000 +10 +Percent*10 + + +0.001 0.1 +0.001 + + +1 255 + + + + + + +True + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:MODE,7:IMU,8:CMD,9:CURRENT,10:COMPASS,11:TECS,12:CAMERA,13:RC,14:SONAR,15:ARM/DISARM,19:IMU_RAW + +Disabled +APM2-Default +PX4/Pixhawk-Default + + + + + + + + +MANUAL +LEARNING +STEERING +HOLD +AUTO +RTL +GUIDED + + + +1 255 + + +1 255 + + + +Disabled +Enabled + + + +0 30 +1 +seconds + + +0:Steering,1:Throttle + +None +Steering +Throttle + + + + +Disabled +Enabled + + + + +Disabled +APM TriggerPin0 +APM TriggerPin1 +APM TriggerPin2 +APM TriggerPin3 +APM TriggerPin4 +APM TriggerPin5 +APM TriggerPin6 +APM TriggerPin7 +APM TriggerPin8 +Pixhawk TriggerPin50 +Pixhawk TriggerPin51 +Pixhawk TriggerPin52 +Pixhawk TriggerPin53 +Pixhawk TriggerPin54 +Pixhawk TriggerPin55 + + + +0 20 +0.1 +m/s/s + + +0 100 +0.1 +m/s + + +0 100 +1 +percent + + +0 100 +0.1 +meters + + +0 100 +1 +percent + + +0 100 +1 +m/s + + +0 360 +1 +degrees + + + +Nothing +LearnWaypoint + + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + +0 100 +1 +Percent + + + +Disabled +SkidSteeringOutput + + + + +Disabled +SkidSteeringInput + + + + +Nothing +RTL +HOLD + + + +seconds + + + +Disabled +Enabled + + + +925 1100 +1 + + + +Disabled +Enabled + + + + +Disabled +HOLD +HoldAndDisarm + + + +0 1000 +1 +centimeters + + +-45 45 +1 +centimeters + + +0 100 +0.1 +seconds + + +1 100 +1 + + + + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + + +Manual +LEARNING +STEERING +HOLD +Auto +RTL +Guided + + + +0 1000 +0.1 +meters + + +0.2 10 +0.1 +gravities + + + + + +Disabled +Enabled + + + +1 100 + + +1 100000 + + +-1 16777215 + + + +NoInfo +Light +Small +Large +HighVortexlarge +Heavy +HighlyManuv +Rotocraft +RESERVED +Glider +LightAir +Parachute +UltraLight +RESERVED +UAV +Space +RESERVED +EmergencySurface +ServiceSurface +PointObstacle + + + + +NO_DATA +L15W23 +L25W28P5 +L25W34 +L35W33 +L35W38 +L45W39P5 +L45W45 +L55W45 +L55W52 +L65W59P5 +L65W67 +L75W72P5 +L75W80 +L85W80 +L85W90 + + + + +NoData +Left2m +Left4m +Left6m +Center +Right2m +Right4m +Right6m + + + + +NO_DATA +AppliedBySensor + + + + +Disabled +Rx-Only +Tx-Only +Rx and Tx Enabled + + + + + + + + + + + + + + + + + + + + + +meters + + +meters + + +millibar + + + + + + + + + + + + + + +seconds + + + + +0.0 1.0 +.01 + + + +Disabled +Enabled + + + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 + + + +0.001 0.5 +.01 + + +0 10 +1 + + + +Disabled +Enable EKF2 +Enable EKF3 + + + + + + +Disabled +THR_MIN PWM when disarmed +0 PWM when disarmed + + + +0:All,1:Barometer,2:Compass,3:GPS lock,4:INS,5:Parameters,6:RC,7:Board voltage,8:Battery Level,9:Airspeed,10:Logging Available,11:Hardware safety switch,12:GPS Configuration + +None +All +Barometer +Compass +GPS Lock +INS(INertial Sensors - accels & gyros) +Parameters(unused) +RC Failsafe +Board voltage +Battery Level +Airspeed +LoggingAvailable +Hardware safety switch +GPS configuration + + + +0.25 3.0 +m/s/s + + +0.1 +Volts + + +0.1 +Volts + + + + + +None +I2C-MS4525D0 +Analog +I2C-MS5525 + + + + +Use +Don't Use + + + +0.1 + + +0.1 + + + + + + + + + +Disable +Enable + + + + + + +Bus0 +Bus1 + + + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +3.000 12.000 + + +3.000 12.000 + + +3.000 6.000 + + +0.5 10.0 + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + +Disabled 1 + + + +0 1000 +Centi-Degrees + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.08 0.35 +0.005 + + +0.01 0.6 +0.01 + + +0 1 +0.01 + + +0.001 0.03 +0.001 + + +1 20 +1 +Hz + + +0.180 0.60 +0.005 + + +0.01 0.06 +0.01 + + +0 1 +0.01 + + +0.000 0.02 +0.001 + + +1 20 +1 +Hz + + + + + +Disabled +Enabled + + + + +Remain in AVOID_ADSB +Resume previous flight mode +RTL +Resume if AUTO else Loiter + + + + + +seconds + + +seconds + + +meters + + +meters + + +meters + + +meters + + + + +0:StopAtFence,1:UseProximitySensor + +None +StopAtFence +UseProximitySensor +All + + + +0 4500 + + +3 30 +meters + + + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Watts + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Amps + + + + + +None +Pozyx + + + +-90 90 +0.000001 +degrees + + +-180 180 +0.000001 +degrees + + +0 10000 +1 +meters + + +-180 +180 +1 +degrees + + + + + +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled + +True + + + +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz + +True + + +-32767 32768 + + + +Disabled +Enabled +Dynamic ID/Update + + + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 + +Disabled +Enabled + +True + + +-1 80 +degreesC + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + +Disabled +Enabled + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +0 3600 + + + + + +Servo +Relay + + + +0 50 +deciseconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 +meters + + + +Low +High + + + +0 10000 +milliseconds + + +0 180 +Degrees + + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + + + + +TriggerLow +TriggerHigh + + + + + + +Disabled +Enabled + + + + +First Relay +Second Relay +Third Relay +Fourth Relay +Servo + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + +0 32000 +1 +Meters + + +0 5000 +1 +Milliseconds + + + + +0 10000 +100 +cm + + +-90 90 +1 +deg/s + + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-3.142 3.142 +0.01 +Radians + + + +Disabled +Internal-Learning +EKF-Learning + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Use Throttle +Use Current + + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +FirstCompass +SecondCompass +ThirdCompass + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + + + + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 32 + +Very Strict +Strict +Default +Relaxed + +0.1 + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +milliseconds + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +2.0 6.0 +0.5 +m/s + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +2.0 6.0 +0.5 +m/s + + +0.5 2.5 +0.1 +m/s/s + + + + + +Disabled +Enabled + + + +0:Altitude,1:Circle,2:Polygon + +None +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All + + + + +Report Only +RTL or Land + + + +10 1000 +1 +Meters + + +30 10000 +Meters + + +1 10 +Meters + + +1 20 + + + + + +Disabled +Enabled + + + +-200 +200 +1 + + +-200 +200 +1 + + +-18000 +18000 +1 + + +m + + +m + + +m + + +0 255 + + + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +0.1 +meters + + + +FirstBaro +2ndBaro +3rdBaro + + + + +Disabled +Bus0 + + + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G + + + + +Disabled +Enabled + + + + +Any +FloatRTK +IntegerRTK + +True + + + +Disabled +Enabled +NoChange + + + +-100 90 +Degrees + + + +send to first GPS +send to 2nd GPS +send to all + + + + +None +All +External only + + + + +Disabled +log every sample +log every 5 samples + +True + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Do not save config +Save config +Save only when needed + + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Disables automatic configuration +Enable automatic configuration + + + + +10Hz +8Hz +5Hz + +milliseconds + + + +10Hz +8Hz +5Hz + +milliseconds + + +m + + +m + + +m + + +m + + +m + + +m + + +0 250 +milliseconds + + +0 250 +milliseconds + + + + + +Disabled +Enabled + + + + +None +Servo +EPM + + + +1000 2000 +PWM + + +1000 2000 +PWM + + +1000 2000 +PWM + + +0 255 +seconds + + +0 255 + + + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + +-180 180 +1 +Degrees + + + +Servo only +Servo with ExtGyro +DirectDrive VarPitch +DirectDrive FixedPitch + + + + +3-Servo CCPM +H1 Mechanical Mixing + + + +0 1000 +1 +PWM + + +-90 90 +1 +Degrees + + +-10 10 +0.1 + + + +NoFlybar +Flybar + + + +0 1000 +1 +PWM + + +0 1000 +1 +PWM + + +0 2000 + + +0 2000 + + + +Reversed +Normal + + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + +1000 2000 +1 +PWM + + + +Disabled +Passthrough +Max collective +Mid collective +Min collective + + + +0 1000 +10 +PWM + + + +Ch8 Input +SetPoint +Throttle Curve + + + +0 500 +1 +pwm + + +0 60 +Seconds + + +0 60 +Seconds + + +0 1000 +10 + + +0 500 +10 + + +0 1000 +10 + + +0 1000 +10 + + +0 18000 +100 +Centi-Degrees + + +0 10 +1 + + +1 1000 +10 + + +0 500 +10 + + + + + +Disabled +Enabled + + + + +None +Chan1 +Chan2 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +0.1 5 +Seconds + + +1 10 +Seconds + + +100 100000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +1000 2000 + + + +None +RPM1 +RPM2 + + + +0 100 + + + + +0 500 +1 +Percent*10 + + +0 500 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + +500 1000 +1 +Percent*10 + + + +Disabled +Very Low +Low +Medium +High +Very High + + + + + + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0.05 50 + + + +Never +Start-up only + + + + +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle + + + + +IMU 1 +IMU 2 +IMU 3 + + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +True + + +True + + +True + + +True + + +True + + +True + + + + + + +0 5 +0.5 +meters + + +0 90 +0.1 +degrees + + +centi-Degrees + + +0.1 +meters + + +0.1 +seconds + + +0 30 +0.1 +meters + + +0 10 +0.1 +seconds + + +0 30 +0.1 +m/s + + +0 127 +1 +percent + + +0 127 +1 +seconds + + + +Disabled +Servos to Neutral +Servos to Zero PWM + + + + +Disabled +Enabled + + + +0 100 +Percent + + + +Standard Glide Slope + + + + + +1000 2000 +1 +pwm + + +1000 2000 +1 +pwm + + + + + +None +File +MAVLink +BothFileAndMAVLink + + + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + + +0 32766 +1 + + + +Resume Mission +Restart Mission + + + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0 100 +1 + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + + + + + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 +Brushed16kHz + +True + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + +PWM enabled while disarmed +PWM disabled while disarmed + + + +5 80 +1 +Degrees + + +0 2 +0.1 +Seconds + + + + +1 60 +1 +seconds + + +0.6 1.0 +0.05 + + +0 0.1 +0.01 + + + + + +Off +Low +Medium +High + + + + +Disable +Enable + + + + +Disable +Enable + + + + +Disable +ssd1306 +sh1106 + + + + + + +Disabled +Enabled Always Land +Enabled Strict + + + + +None +CompanionComputer +IRLock +SITL_Gazebo +SITL + + + +0 360 +1 +Centi-degrees + + +-20 20 +1 +Centimeters + + +-20 20 +1 +Centimeters + + + + +0.5 5 +0.1 +Hz + + + + + +None +LightWareSF40C +MAVLink +TeraRangerTower + + + + +Default +Upside Down + + + +-180 180 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + + +None +LightWareSF40C +MAVLink + + + + +Default +Upside Down + + + +-180 180 +degrees + + + + +0.4 1.0 +0.1 +seconds + + +0.1 3.0 +0.1 + + +0 0.1 +0.01 + + +0 0.5 +0.05 + + +0 100 +1 +degrees/second + + +0 100 +1 +degrees/second + + +0.7 1.5 +0.05 + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + + + + +0.1 +kilometers + + + +DoNotIncludeHome +IncludeHome + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + +0.4 1.0 +0.1 +seconds + + +0.1 4.0 +0.1 + + +0 0.1 +0.01 + + +0 1.0 +0.05 + + +0 180 +1 +degrees/second + + +0 4500 +1 + + +0.1 4.0 +0.1 + + + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +5 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +AnalogPin +RCChannelPwmValue + + + + +APM2 A0 +APM2 A1 +APM2 A13 +Pixracer +Pixhawk ADC4 +Pixhawk ADC3 + Pixhawk ADC6 +Pixhawk SBUS + + + +0 5.0 +0.01 +Volt + + +0 5.0 +0.01 +Volt + + + + + +0 2000 +Microseconds + + +0 2000 +Microseconds + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + + +Disable +Enable + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch + + + + + + + + + + + + + + + + +Disabled +Enabled + + + +0 100 +percentage + + +1000 2000 +ms + + +0 1000 +cm/s + + +0 100 +percentage + + + + + + +seconds + + +seconds + + +seconds + + + + +0.4 1.0 +0.1 +seconds + + +0.1 10.0 +0.1 + + +0 1.0 +0.05 + + +0 0.1 +0.01 + + +0 4500 +1 + + +0 5 +0.1 +m/s + + +0.0 10.0 +0.1 + + +0.0 30.0 +0.1 +m/s + + +0.0 50.0 +0.1 +degree/(m/s) + + +0.0 4500.0 +0.1 +Centidegrees + + + + +0.1 20.0 +0.1 + + +0.1 10.0 +0.1 + + +3.0 10.0 +0.2 + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +1.0 10.0 +0.5 + + +1.0 5.0 +0.05 + + +0.5 2.0 +0.05 + + +5.0 30.0 +1.0 + + +0.0 2.0 +0.1 + + +0.1 1.0 +0.1 + + +0.0 20.0 +0.1 + + +-1 127 +1 + + +-1 100 +0.1 + + +-1.0 2.0 +0.1 + + +0 45 +1 + + +-45 0 +1 + + +0.0 2.0 +0.1 + + +1.0 5.0 +0.2 + + +0.1 1.0 +0.1 + + +-5 40 +1 + + +0.0 20.0 +0.1 +m/s + + +-2.0 2.0 +0.1 +m/s/m + + +0.1 1.0 +0.1 + + +0.0 0.5 +0.02 + + +0.0 0.5 +0.02 + + +0.1 1.0 +0.1 + + + +Disable +Enable + + + + + + +Disable +Enable + + + +1 +meters + + + + + +Disable +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + +900 2100 + + +900 2100 + + + +Disable +Chan1 +Chan3 +Chan3 +Chan4 +Chan5 +Chan6 +Chan7 +Chan8 +Chan9 +Chan10 +Chan11 +Chan12 +Chan13 +Chan14 +Chan15 +Chan16 + + + + + + +Disable +Enable + + + +0 1 + + + + +20 2000 +50 +cm/s + + +100 1000 +1 +cm + + +0 1000 +50 +cm/s + + +0 500 +10 +cm/s + + +0 2000 +50 +cm/s + + +50 500 +10 +cm/s/s + + +50 500 +10 +cm/s/s + + +500 5000 +1 +cm/s/s/s + + +100 981 +1 +cm/s/s + + +25 250 +1 +cm/s/s + + + +Disable +Enable + + + + + +0 4 +0.25 + + +0 2 +0.25 + + +0 2 +0.25 + + +0.8 1.2 +0.05 + + +0 4500 +1 + + + diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc index 7e2b6a212..941a2a14a 100644 --- a/src/FirmwarePlugin/APM/APMResources.qrc +++ b/src/FirmwarePlugin/APM/APMResources.qrc @@ -43,10 +43,12 @@ APMParameterFactMetaData.Plane.3.3.xml APMParameterFactMetaData.Plane.3.5.xml APMParameterFactMetaData.Plane.3.7.xml + APMParameterFactMetaData.Plane.3.8.xml APMParameterFactMetaData.Copter.3.3.xml APMParameterFactMetaData.Copter.3.4.xml APMParameterFactMetaData.Copter.3.5.xml APMParameterFactMetaData.Rover.3.0.xml + APMParameterFactMetaData.Rover.3.2.xml APMParameterFactMetaData.Sub.3.4.xml CopterGeoFenceEditor.qml PlaneGeoFenceEditor.qml diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index 33fb7fb02..3c3ef9e5e 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -67,36 +67,51 @@ ArduCopterFirmwarePlugin::ArduCopterFirmwarePlugin(void) setSupportedModes(supportedFlightModes); if (!_remapParamNameIntialized) { - FirmwarePlugin::remapParamNameMap_t& remap = _remapParamName[3][4]; - - remap["ATC_ANG_RLL_P"] = QStringLiteral("STB_RLL_P"); - remap["ATC_ANG_PIT_P"] = QStringLiteral("STB_PIT_P"); - remap["ATC_ANG_YAW_P"] = QStringLiteral("STB_YAW_P"); - - remap["ATC_RAT_RLL_P"] = QStringLiteral("RATE_RLL_P"); - remap["ATC_RAT_RLL_I"] = QStringLiteral("RATE_RLL_I"); - remap["ATC_RAT_RLL_IMAX"] = QStringLiteral("RATE_RLL_IMAX"); - remap["ATC_RAT_RLL_D"] = QStringLiteral("RATE_RLL_D"); - remap["ATC_RAT_RLL_FILT"] = QStringLiteral("RATE_RLL_FILT_HZ"); - - remap["ATC_RAT_PIT_P"] = QStringLiteral("RATE_PIT_P"); - remap["ATC_RAT_PIT_I"] = QStringLiteral("RATE_PIT_I"); - remap["ATC_RAT_PIT_IMAX"] = QStringLiteral("RATE_PIT_IMAX"); - remap["ATC_RAT_PIT_D"] = QStringLiteral("RATE_PIT_D"); - remap["ATC_RAT_PIT_FILT"] = QStringLiteral("RATE_PIT_FILT_HZ"); - - remap["ATC_RAT_YAW_P"] = QStringLiteral("RATE_YAW_P"); - remap["ATC_RAT_YAW_I"] = QStringLiteral("RATE_YAW_I"); - remap["ATC_RAT_YAW_IMAX"] = QStringLiteral("RATE_YAW_IMAX"); - remap["ATC_RAT_YAW_D"] = QStringLiteral("RATE_YAW_D"); - remap["ATC_RAT_YAW_FILT"] = QStringLiteral("RATE_YAW_FILT_HZ"); + FirmwarePlugin::remapParamNameMap_t& remapV3_4 = _remapParamName[3][4]; + + remapV3_4["ATC_ANG_RLL_P"] = QStringLiteral("STB_RLL_P"); + remapV3_4["ATC_ANG_PIT_P"] = QStringLiteral("STB_PIT_P"); + remapV3_4["ATC_ANG_YAW_P"] = QStringLiteral("STB_YAW_P"); + + remapV3_4["ATC_RAT_RLL_P"] = QStringLiteral("RATE_RLL_P"); + remapV3_4["ATC_RAT_RLL_I"] = QStringLiteral("RATE_RLL_I"); + remapV3_4["ATC_RAT_RLL_IMAX"] = QStringLiteral("RATE_RLL_IMAX"); + remapV3_4["ATC_RAT_RLL_D"] = QStringLiteral("RATE_RLL_D"); + remapV3_4["ATC_RAT_RLL_FILT"] = QStringLiteral("RATE_RLL_FILT_HZ"); + + remapV3_4["ATC_RAT_PIT_P"] = QStringLiteral("RATE_PIT_P"); + remapV3_4["ATC_RAT_PIT_I"] = QStringLiteral("RATE_PIT_I"); + remapV3_4["ATC_RAT_PIT_IMAX"] = QStringLiteral("RATE_PIT_IMAX"); + remapV3_4["ATC_RAT_PIT_D"] = QStringLiteral("RATE_PIT_D"); + remapV3_4["ATC_RAT_PIT_FILT"] = QStringLiteral("RATE_PIT_FILT_HZ"); + + remapV3_4["ATC_RAT_YAW_P"] = QStringLiteral("RATE_YAW_P"); + remapV3_4["ATC_RAT_YAW_I"] = QStringLiteral("RATE_YAW_I"); + remapV3_4["ATC_RAT_YAW_IMAX"] = QStringLiteral("RATE_YAW_IMAX"); + remapV3_4["ATC_RAT_YAW_D"] = QStringLiteral("RATE_YAW_D"); + remapV3_4["ATC_RAT_YAW_FILT"] = QStringLiteral("RATE_YAW_FILT_HZ"); + + FirmwarePlugin::remapParamNameMap_t& remapV3_5 = _remapParamName[3][5]; + + remapV3_5["SERVO5_FUNCTION"] = QStringLiteral("RC5_FUNCTION"); + remapV3_5["SERVO6_FUNCTION"] = QStringLiteral("RC6_FUNCTION"); + remapV3_5["SERVO7_FUNCTION"] = QStringLiteral("RC7_FUNCTION"); + remapV3_5["SERVO8_FUNCTION"] = QStringLiteral("RC8_FUNCTION"); + remapV3_5["SERVO9_FUNCTION"] = QStringLiteral("RC9_FUNCTION"); + remapV3_5["SERVO10_FUNCTION"] = QStringLiteral("RC10_FUNCTION"); + remapV3_5["SERVO11_FUNCTION"] = QStringLiteral("RC11_FUNCTION"); + remapV3_5["SERVO12_FUNCTION"] = QStringLiteral("RC12_FUNCTION"); + remapV3_5["SERVO13_FUNCTION"] = QStringLiteral("RC13_FUNCTION"); + remapV3_5["SERVO14_FUNCTION"] = QStringLiteral("RC14_FUNCTION"); + + _remapParamNameIntialized = true; } } int ArduCopterFirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const { - // Remapping supports up to 3.4 - return majorVersionNumber == 3 ? 4: Vehicle::versionNotSetValue; + // Remapping supports up to 3.5 + return majorVersionNumber == 3 ? 5 : Vehicle::versionNotSetValue; } bool ArduCopterFirmwarePlugin::isCapable(const Vehicle* vehicle, FirmwareCapabilities capabilities) diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc index da2380d4f..88a380a1b 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc @@ -7,12 +7,11 @@ * ****************************************************************************/ - -/// @file -/// @author Pritam Ghanghas - #include "ArduPlaneFirmwarePlugin.h" +bool ArduPlaneFirmwarePlugin::_remapParamNameIntialized = false; +FirmwarePlugin::remapParamNameMajorVersionMap_t ArduPlaneFirmwarePlugin::_remapParamName; + APMPlaneMode::APMPlaneMode(uint32_t mode, bool settable) : APMCustomMode(mode, settable) { @@ -63,6 +62,29 @@ ArduPlaneFirmwarePlugin::ArduPlaneFirmwarePlugin(void) supportedFlightModes << APMPlaneMode(APMPlaneMode::QLAND ,true); supportedFlightModes << APMPlaneMode(APMPlaneMode::QRTL ,true); setSupportedModes(supportedFlightModes); + + if (!_remapParamNameIntialized) { + FirmwarePlugin::remapParamNameMap_t& remapV3_8 = _remapParamName[3][8]; + + remapV3_8["SERVO5_FUNCTION"] = QStringLiteral("RC5_FUNCTION"); + remapV3_8["SERVO6_FUNCTION"] = QStringLiteral("RC6_FUNCTION"); + remapV3_8["SERVO7_FUNCTION"] = QStringLiteral("RC7_FUNCTION"); + remapV3_8["SERVO8_FUNCTION"] = QStringLiteral("RC8_FUNCTION"); + remapV3_8["SERVO9_FUNCTION"] = QStringLiteral("RC9_FUNCTION"); + remapV3_8["SERVO10_FUNCTION"] = QStringLiteral("RC10_FUNCTION"); + remapV3_8["SERVO11_FUNCTION"] = QStringLiteral("RC11_FUNCTION"); + remapV3_8["SERVO12_FUNCTION"] = QStringLiteral("RC12_FUNCTION"); + remapV3_8["SERVO13_FUNCTION"] = QStringLiteral("RC13_FUNCTION"); + remapV3_8["SERVO14_FUNCTION"] = QStringLiteral("RC14_FUNCTION"); + + _remapParamNameIntialized = true; + } +} + +int ArduPlaneFirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const +{ + // Remapping supports up to 3.8 + return majorVersionNumber == 3 ? 8 : Vehicle::versionNotSetValue; } QString ArduPlaneFirmwarePlugin::takeControlFlightMode(void) diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h index 8067354b9..3ec63a61a 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h @@ -58,6 +58,12 @@ public: // Overrides from FirmwarePlugin QString offlineEditingParamFile(Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Plane.OfflineEditing.params"); } QString takeControlFlightMode(void) final; + const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } + int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const final; + +private: + static bool _remapParamNameIntialized; + static FirmwarePlugin::remapParamNameMajorVersionMap_t _remapParamName; }; #endif diff --git a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc index 5c4a57599..bbb7ed961 100644 --- a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc @@ -7,12 +7,11 @@ * ****************************************************************************/ - -/// @file -/// @author Pritam Ghanghas - #include "ArduRoverFirmwarePlugin.h" +bool ArduRoverFirmwarePlugin::_remapParamNameIntialized = false; +FirmwarePlugin::remapParamNameMajorVersionMap_t ArduRoverFirmwarePlugin::_remapParamName; + APMRoverMode::APMRoverMode(uint32_t mode, bool settable) : APMCustomMode(mode, settable) { @@ -41,4 +40,28 @@ ArduRoverFirmwarePlugin::ArduRoverFirmwarePlugin(void) supportedFlightModes << APMRoverMode(APMRoverMode::GUIDED ,true); supportedFlightModes << APMRoverMode(APMRoverMode::INITIALIZING ,false); setSupportedModes(supportedFlightModes); + + if (!_remapParamNameIntialized) { + FirmwarePlugin::remapParamNameMap_t& remapV3_2 = _remapParamName[3][2]; + + remapV3_2["SERVO5_FUNCTION"] = QStringLiteral("RC5_FUNCTION"); + remapV3_2["SERVO6_FUNCTION"] = QStringLiteral("RC6_FUNCTION"); + remapV3_2["SERVO7_FUNCTION"] = QStringLiteral("RC7_FUNCTION"); + remapV3_2["SERVO8_FUNCTION"] = QStringLiteral("RC8_FUNCTION"); + remapV3_2["SERVO9_FUNCTION"] = QStringLiteral("RC9_FUNCTION"); + remapV3_2["SERVO10_FUNCTION"] = QStringLiteral("RC10_FUNCTION"); + remapV3_2["SERVO11_FUNCTION"] = QStringLiteral("RC11_FUNCTION"); + remapV3_2["SERVO12_FUNCTION"] = QStringLiteral("RC12_FUNCTION"); + remapV3_2["SERVO13_FUNCTION"] = QStringLiteral("RC13_FUNCTION"); + remapV3_2["SERVO14_FUNCTION"] = QStringLiteral("RC14_FUNCTION"); + + _remapParamNameIntialized = true; + } } + +int ArduRoverFirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const +{ + // Remapping supports up to 3.2 + return majorVersionNumber == 3 ? 2 : Vehicle::versionNotSetValue; +} + diff --git a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h index 78a43877e..88e5e5272 100644 --- a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.h @@ -49,6 +49,14 @@ class ArduRoverFirmwarePlugin : public APMFirmwarePlugin public: ArduRoverFirmwarePlugin(void); + + // Overrides from FirmwarePlugin + const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } + int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const final; + +private: + static bool _remapParamNameIntialized; + static FirmwarePlugin::remapParamNameMajorVersionMap_t _remapParamName; }; #endif -- GitLab From 1dff8ed34232cc7e49ddc145e14e93985c2b9973 Mon Sep 17 00:00:00 2001 From: James Goppert Date: Sat, 28 Jan 2017 07:26:55 -0500 Subject: [PATCH 286/398] Fix analyze view for mavlink 2.0. --- src/ui/MAVLinkDecoder.cc | 60 ++++++++++++++++++---------------------- src/ui/MAVLinkDecoder.h | 30 ++++++++++++++------ 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/ui/MAVLinkDecoder.cc b/src/ui/MAVLinkDecoder.cc index ff22edf6b..04603ef0f 100644 --- a/src/ui/MAVLinkDecoder.cc +++ b/src/ui/MAVLinkDecoder.cc @@ -10,16 +10,6 @@ MAVLinkDecoder::MAVLinkDecoder(MAVLinkProtocol* protocol) : // http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/ moveToThread(this); - memset(receivedMessages, 0, sizeof(mavlink_message_t)*256); - for (unsigned int i = 0; i<255;++i) - { - componentID[i] = -1; - componentMulti[i] = false; - onboardTimeOffset[i] = 0; - onboardToGCSUnixTimeOffsetAndDelay[i] = 0; - firstOnboardTime[i] = 0; - } - // Fill filter // Allow system status // messageFilter.insert(MAVLINK_MSG_ID_HEARTBEAT, false); @@ -68,15 +58,11 @@ void MAVLinkDecoder::run() void MAVLinkDecoder::receiveMessage(LinkInterface* link,mavlink_message_t message) { - if (message.msgid >= cMessageIds) { - // No support for messag ids above 255 - return; - } - Q_UNUSED(link); - memcpy(receivedMessages+message.msgid, &message, sizeof(mavlink_message_t)); - uint8_t msgid = message.msgid; + msgDict[message.msgid] = message; + + uint32_t msgid = message.msgid; const mavlink_message_info_t* msgInfo = mavlink_get_message_info(&message); // Store an arrival time for this message. This value ends up being calculated later. @@ -87,15 +73,15 @@ void MAVLinkDecoder::receiveMessage(LinkInterface* link,mavlink_message_t messag { mavlink_system_time_t timebase; mavlink_msg_system_time_decode(&message, &timebase); - onboardTimeOffset[message.sysid] = (timebase.time_unix_usec+500)/1000 - timebase.time_boot_ms; - onboardToGCSUnixTimeOffsetAndDelay[message.sysid] = static_cast(QGC::groundTimeMilliseconds() - (timebase.time_unix_usec+500)/1000); + sysDict[msgid].onboardTimeOffset = (timebase.time_unix_usec+500)/1000 - timebase.time_boot_ms; + sysDict[msgid].onboardToGCSUnixTimeOffsetAndDelay = static_cast(QGC::groundTimeMilliseconds() - (timebase.time_unix_usec+500)/1000); } else { // See if first value is a time value and if it is, use that as the arrival time for this data. uint8_t fieldid = 0; - uint8_t* m = (uint8_t*)&((mavlink_message_t*)(receivedMessages+msgid))->payload64[0]; + uint8_t* m = (uint8_t*)(msgDict[msgid].payload64); if (QString(msgInfo->fields[fieldid].name) == QString("time_boot_ms") && msgInfo->fields[fieldid].type == MAVLINK_TYPE_UINT32_T) { @@ -126,7 +112,7 @@ quint64 MAVLinkDecoder::getUnixTimeFromMs(int systemID, quint64 time) quint64 ret = 0; if (time == 0) { - ret = QGC::groundTimeMilliseconds() - onboardToGCSUnixTimeOffsetAndDelay[systemID]; + ret = QGC::groundTimeMilliseconds() - sysDict[systemID].onboardToGCSUnixTimeOffsetAndDelay; } // Check if time is smaller than 40 years, // assuming no system without Unix timestamp @@ -150,15 +136,15 @@ quint64 MAVLinkDecoder::getUnixTimeFromMs(int systemID, quint64 time) else if (time < 1261440000000) #endif { - if (onboardTimeOffset[systemID] == 0 || time < (firstOnboardTime[systemID]-100)) + if (sysDict[systemID].onboardTimeOffset == 0 || time < (sysDict[systemID].firstOnboardTime -100)) { - firstOnboardTime[systemID] = time; - onboardTimeOffset[systemID] = QGC::groundTimeMilliseconds() - time; + sysDict[systemID].firstOnboardTime = time; + sysDict[systemID].onboardTimeOffset = QGC::groundTimeMilliseconds() - time; } - if (time > firstOnboardTime[systemID]) firstOnboardTime[systemID] = time; + if (time > sysDict[systemID].firstOnboardTime ) sysDict[systemID].firstOnboardTime = time; - ret = time + onboardTimeOffset[systemID]; + ret = time + sysDict[systemID].onboardTimeOffset; } else { @@ -204,28 +190,36 @@ void MAVLinkDecoder::emitFieldValue(mavlink_message_t* msg, int fieldid, quint64 bool multiComponentSourceDetected = false; const mavlink_message_info_t* msgInfo = mavlink_get_message_info(msg); + uint32_t msgid = msg->msgid; + + // create new system data if it wasn't dectected yet + if (!(sysDict.keys().contains(msgid))) { + sysDict[msgid] = SystemData(); + } + // Store component ID - if (componentID[msg->msgid] == -1) + if (sysDict[msgid].componentID == -1) { - componentID[msg->msgid] = msg->compid; + sysDict[msgid].componentID = msg->compid; } else { // Got this message already - if (componentID[msg->msgid] != msg->compid) + if (sysDict[msgid].componentID != msg->compid) { - componentMulti[msg->msgid] = true; + sysDict[msgid].componentMulti = true; } } - if (componentMulti[msg->msgid] == true) multiComponentSourceDetected = true; + if (sysDict[msgid].componentMulti == true) { + multiComponentSourceDetected = true; + } // Add field tree widget item - uint8_t msgid = msg->msgid; if (messageFilter.contains(msgid)) return; QString fieldName(msgInfo->fields[fieldid].name); QString fieldType; - uint8_t* m = (uint8_t*)&((mavlink_message_t*)(receivedMessages+msgid))->payload64[0]; + uint8_t* m = (uint8_t*)(msgDict[msgid].payload64); QString name("%1.%2"); QString unit(""); diff --git a/src/ui/MAVLinkDecoder.h b/src/ui/MAVLinkDecoder.h index ffe761f37..9f715bace 100644 --- a/src/ui/MAVLinkDecoder.h +++ b/src/ui/MAVLinkDecoder.h @@ -2,8 +2,28 @@ #define MAVLINKDECODER_H #include +#include + #include "MAVLinkProtocol.h" +struct SystemData { + /** + * @brief Holds mavlink system data + */ + SystemData() : + componentID(-1), + componentMulti(false), + onboardTimeOffset(0), + onboardToGCSUnixTimeOffsetAndDelay(0), + firstOnboardTime(0) { + } + int componentID; ///< Multi component detection + bool componentMulti; ///< Multi components detected + quint64 onboardTimeOffset; ///< Offset of onboard time from Unix epoch (of the receiving GCS) + qint64 onboardToGCSUnixTimeOffsetAndDelay; ///< Offset of onboard time and GCS Unix time + quint64 firstOnboardTime; ///< First seen onboard time +}; + class MAVLinkDecoder : public QThread { Q_OBJECT @@ -26,16 +46,10 @@ protected: /** @brief Shift a timestamp in Unix time if necessary */ quint64 getUnixTimeFromMs(int systemID, quint64 time); - static const size_t cMessageIds = 256; - - mavlink_message_t receivedMessages[cMessageIds]; ///< Available / known messages QMap messageFilter; ///< Message/field names not to emit QMap textMessageFilter; ///< Message/field names not to emit in text mode - int componentID[cMessageIds]; ///< Multi component detection - bool componentMulti[cMessageIds]; ///< Multi components detected - quint64 onboardTimeOffset[cMessageIds]; ///< Offset of onboard time from Unix epoch (of the receiving GCS) - qint64 onboardToGCSUnixTimeOffsetAndDelay[cMessageIds]; ///< Offset of onboard time and GCS Unix time - quint64 firstOnboardTime[cMessageIds]; ///< First seen onboard time + QHash msgDict; ///< dictionary of all mavlink messages + QHash sysDict; ///< dictionary of all systmes QThread* creationThread; ///< QThread on which the object is created }; -- GitLab From 1f9ea81b29dff1bd03c6fee01cb9914b4bc5bd15 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 14:09:39 -0800 Subject: [PATCH 287/398] Ignore heartbeats from non-default components --- src/Vehicle/Vehicle.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 886330ca9..6c2fc34d4 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -828,6 +828,10 @@ void Vehicle::_handleHomePosition(mavlink_message_t& message) void Vehicle::_handleHeartbeat(mavlink_message_t& message) { + if (message.compid != _defaultComponentId) { + return; + } + _connectionActive(); mavlink_heartbeat_t heartbeat; -- GitLab From c3b6c2021014eb0b6dab10f11b14dc8c6d35e2e3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 14:09:53 -0800 Subject: [PATCH 288/398] Fix unit test to new mock link component id --- src/FactSystem/FactSystemTest.qml | 2 +- src/FactSystem/FactSystemTestBase.cc | 4 ++-- src/comm/MockLink.cc | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/FactSystem/FactSystemTest.qml b/src/FactSystem/FactSystemTest.qml index a9155de82..8ac4d9ada 100644 --- a/src/FactSystem/FactSystemTest.qml +++ b/src/FactSystem/FactSystemTest.qml @@ -32,7 +32,7 @@ FactPanel { TextInput { text: fact2.value - property Fact fact2: controller.getParameterFact(1, "RC_MAP_THROTTLE") + property Fact fact2: controller.getParameterFact(200, "RC_MAP_THROTTLE") onAccepted: fact2.value = text } diff --git a/src/FactSystem/FactSystemTestBase.cc b/src/FactSystem/FactSystemTestBase.cc index 58fbe6ea8..35f920229 100644 --- a/src/FactSystem/FactSystemTestBase.cc +++ b/src/FactSystem/FactSystemTestBase.cc @@ -58,8 +58,8 @@ void FactSystemTestBase::_parameter_default_component_id_test(void) void FactSystemTestBase::_parameter_specific_component_id_test(void) { - QVERIFY(_vehicle->parameterManager()->parameterExists(1, "RC_MAP_THROTTLE")); - Fact* fact = _vehicle->parameterManager()->getParameter(1, "RC_MAP_THROTTLE"); + QVERIFY(_vehicle->parameterManager()->parameterExists(200, "RC_MAP_THROTTLE")); + Fact* fact = _vehicle->parameterManager()->getParameter(200, "RC_MAP_THROTTLE"); QVERIFY(fact != NULL); QVariant factValue = fact->rawValue(); QCOMPARE(factValue.isValid(), true); diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index 24def6a8a..b3cd24ff6 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -215,7 +215,6 @@ void MockLink::_loadParams(void) QStringList paramData = line.split("\t"); Q_ASSERT(paramData.count() == 5); - int componentId = paramData.at(1).toInt(); QString paramName = paramData.at(2); QString valStr = paramData.at(3); uint paramType = paramData.at(4).toUInt(); @@ -251,7 +250,7 @@ void MockLink::_loadParams(void) qCDebug(MockLinkVerboseLog) << "Loading param" << paramName << paramValue; - _mapParamName2Value[componentId][paramName] = paramValue; + _mapParamName2Value[_vehicleComponentId][paramName] = paramValue; _mapParamName2MavParamType[paramName] = static_cast(paramType); } } -- GitLab From 979c5031da9a82beb3043084953f8ca24521202d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 16:03:13 -0800 Subject: [PATCH 289/398] Correctly wait for default component params to come through --- src/FactSystem/ParameterManager.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 3ad3112ec..0f1c5d83e 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -219,7 +219,13 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _waitingParamTimeoutTimer.start(); qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer: totalWaitingParamCount:" << totalWaitingParamCount; } else { - qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; + if (!_mapParameterName2Variant.contains(_vehicle->defaultComponentId())) { + // Still waiting for parameters from default component + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Restarting _waitingParamTimeoutTimer (still waiting for default component params)"; + _waitingParamTimeoutTimer.start(); + } else { + qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; + } } // Update progress bar for waiting reads @@ -949,6 +955,11 @@ void ParameterManager::_checkInitialLoadComplete(void) } } + if (!_mapParameterName2Variant.contains(_vehicle->defaultComponentId())) { + // No default component params yet, not done yet + return; + } + // We aren't waiting for any more initial parameter updates, initial parameter loading is complete _initialLoadComplete = true; -- GitLab From 3480f76e140e3a0cbe441d167d1c3385c94e0a74 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 16:03:36 -0800 Subject: [PATCH 290/398] Add param loading to new airframe page --- .../APM/APMAirframeComponent.qml | 114 ++++++++++++++++++ .../APM/APMAutoPilotPlugin.cc | 1 - 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponent.qml b/src/AutoPilotPlugins/APM/APMAirframeComponent.qml index 7c1cde362..bcf4cef90 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponent.qml +++ b/src/AutoPilotPlugins/APM/APMAirframeComponent.qml @@ -29,6 +29,73 @@ SetupPage { property Fact _oldFrameParam: controller.getParameterFact(-1, "FRAME", false) property Fact _newFrameParam: controller.getParameterFact(-1, "FRAME_CLASS", false) property Fact _frameTypeParam: controller.getParameterFact(-1, "FRAME_TYPE", false) + property var _flatParamList: ListModel { + ListElement { + name: "3DR Aero M" + file: "3DR_AERO_M.param" + } + ListElement { + name: "3DR Aero RTF" + file: "3DR_Aero_RTF.param" + } + ListElement { + name: "3DR Rover" + file: "3DR_Rover.param" + } + ListElement { + name: "3DR Tarot" + file: "3DR_Tarot.bgsc" + } + ListElement { + name: "Parrot Bebop" + file: "Parrot_Bebop.param" + } + ListElement { + name: "Storm32" + file: "SToRM32-MAVLink.param" + } + ListElement { + name: "3DR X8-M RTF" + file: "3DR_X8-M_RTF.param" + } + ListElement { + name: "3DR Y6A" + file: "3DR_Y6A_RTF.param" + } + ListElement { + name: "3DR X8+ RTF" + file: "3DR_X8+_RTF.param" + } + ListElement { + name: "3DR QUAD X4 RTF" + file: "3DR_QUAD_X4_RTF.param" + } + ListElement { + name: "3DR X8" + file: "3DR_X8_RTF.param" + } + ListElement { + name: "Iris with GoPro" + file: "Iris with Front Mount Go Pro.param" + } + ListElement { + name: "Iris with Tarot" + file: "Iris with Tarot Gimbal.param" + } + ListElement { + name: "3DR Iris+" + file: "3DR_Iris+.param" + } + ListElement { + name: "Iris" + file: "Iris.param" + } + ListElement { + name: "3DR Y6B" + file: "3DR_Y6B_RTF.param" + } + } + APMAirframeComponentController { id: controller @@ -91,6 +158,48 @@ SetupPage { } } + Component { + id: selectParamFileDialogComponent + + QGCViewDialog { + QGCLabel { + id: applyParamsText + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: _margins + wrapMode: Text.WordWrap + text: qsTr("Select your drone to load the default parameters for it. ") + } + + Flow { + anchors.margins: _margins + anchors.top: applyParamsText.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing : _margins + layoutDirection: Qt.Vertical; + + Repeater { + id: airframePicker + model: _flatParamList + + delegate: QGCButton { + width: parent.width / 2.1 + height: (ScreenTools.defaultFontPixelHeight * 14) / 5 + text: name + + onClicked : { + controller.loadParameters(file) + hideDialog() + } + } + } + } + } + } + Component { id: oldFramePageComponent @@ -163,6 +272,11 @@ SetupPage { indexModel: false width: ScreenTools.defaultFontPixelWidth * 15 } + + QGCButton { + text: qsTr("Load common parameters") + onClicked: showDialog(selectParamFileDialogComponent, qsTr("Load common parameters"), qgcView.showDialogDefaultWidth, StandardButton.Close) + } } } } // SetupPage diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 23e5daa84..200b04d86 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -16,7 +16,6 @@ #include "VehicleComponent.h" #include "APMAirframeComponent.h" #include "APMAirframeComponentAirframes.h" -#include "APMAirframeComponentController.h" #include "APMAirframeLoader.h" #include "APMFlightModesComponent.h" #include "APMRadioComponent.h" -- GitLab From e71a5fcbaa7e2879915de5002fcb17a9b5d81a02 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 15 Feb 2017 18:36:14 -0800 Subject: [PATCH 291/398] Support for default mission item altitude setting --- src/MissionManager/RallyPointController.cc | 3 +- src/MissionManager/SimpleMissionItem.cc | 5 +- src/MissionManager/SimpleMissionItem.h | 2 - src/MissionManager/SimpleMissionItemTest.cc | 5 +- src/QmlControls/QGroundControlQmlGlobal.cc | 10 ++++ src/QmlControls/QGroundControlQmlGlobal.h | 3 ++ src/QmlControls/QGroundControlQmlGlobal.json | 10 ++++ src/ui/preferences/GeneralSettings.qml | 57 ++++++++++++-------- 8 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index a1a7126a0..5a6a090aa 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -20,6 +20,7 @@ #include "ParameterManager.h" #include "JsonHelper.h" #include "SimpleMissionItem.h" +#include "QGroundControlQmlGlobal.h" #ifndef __mobile__ #include "QGCFileDialog.h" @@ -266,7 +267,7 @@ void RallyPointController::addPoint(QGeoCoordinate point) if (_points.count()) { defaultAlt = qobject_cast(_points[_points.count() - 1])->coordinate().altitude(); } else { - defaultAlt = SimpleMissionItem::defaultAltitude; + defaultAlt = QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble(); } point.setAltitude(defaultAlt); RallyPoint* newPoint = new RallyPoint(point, this); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index ccfdde11d..355a59c0a 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -17,8 +17,7 @@ #include "JsonHelper.h" #include "MissionCommandTree.h" #include "MissionCommandUIInfo.h" - -const double SimpleMissionItem::defaultAltitude = 50.0; +#include "QGroundControlQmlGlobal.h" FactMetaData* SimpleMissionItem::_altitudeMetaData = NULL; FactMetaData* SimpleMissionItem::_commandMetaData = NULL; @@ -528,7 +527,7 @@ void SimpleMissionItem::_syncFrameToAltitudeRelativeToHome(void) void SimpleMissionItem::setDefaultsForCommand(void) { // We set these global defaults first, then if there are param defaults they will get reset - _missionItem.setParam7(defaultAltitude); + _missionItem.setParam7(QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble()); MAV_CMD command = (MAV_CMD)this->command(); const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index 7efca0a64..e3d508ae9 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -75,8 +75,6 @@ public: bool relativeAltitude(void) { return _missionItem.frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; } - static const double defaultAltitude; - MissionItem& missionItem(void) { return _missionItem; } // Overrides from VisualMissionItem diff --git a/src/MissionManager/SimpleMissionItemTest.cc b/src/MissionManager/SimpleMissionItemTest.cc index 097fbec3d..a51728815 100644 --- a/src/MissionManager/SimpleMissionItemTest.cc +++ b/src/MissionManager/SimpleMissionItemTest.cc @@ -11,6 +11,7 @@ #include "SimpleMissionItemTest.h" #include "SimpleMissionItem.h" #include "QGCApplication.h" +#include "QGroundControlQmlGlobal.h" const SimpleMissionItemTest::ItemInfo_t SimpleMissionItemTest::_rgItemInfo[] = { { MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT }, @@ -139,7 +140,7 @@ void SimpleMissionItemTest::_testDefaultValues(void) item.missionItem().setCommand(MAV_CMD_NAV_WAYPOINT); item.missionItem().setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT); - QCOMPARE(item.missionItem().param7(), SimpleMissionItem::defaultAltitude); + QCOMPARE(item.missionItem().param7(), QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble()); } void SimpleMissionItemTest::_testSignals(void) @@ -224,7 +225,7 @@ void SimpleMissionItemTest::_testSignals(void) // dirtyChanged // Check that changing to the same coordinate does not signal - simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, SimpleMissionItem::defaultAltitude)); + simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble())); QVERIFY(multiSpy->checkNoSignals()); // Check that actually changing coordinate signals correctly diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 19f0c8ced..fab97270e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -30,6 +30,7 @@ SettingsFact* QGroundControlQmlGlobal::_offlineEditingVehicleTypeFact = SettingsFact* QGroundControlQmlGlobal::_offlineEditingCruiseSpeedFact = NULL; SettingsFact* QGroundControlQmlGlobal::_offlineEditingHoverSpeedFact = NULL; SettingsFact* QGroundControlQmlGlobal::_batteryPercentRemainingAnnounceFact = NULL; +SettingsFact* QGroundControlQmlGlobal::_defaultMissionItemAltitudeFact = NULL; const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; @@ -339,6 +340,15 @@ Fact* QGroundControlQmlGlobal::batteryPercentRemainingAnnounce(void) return _batteryPercentRemainingAnnounceFact; } +Fact* QGroundControlQmlGlobal::defaultMissionItemAltitude(void) +{ + if (!_defaultMissionItemAltitudeFact) { + _defaultMissionItemAltitudeFact = _createSettingsFact(QStringLiteral("DefaultMissionItemAltitude")); + } + + return _defaultMissionItemAltitudeFact; +} + int QGroundControlQmlGlobal::supportedFirmwareCount() { return _firmwarePluginManager->knownFirmwareTypes().count(); diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 47a46e647..7a912e574 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -99,6 +99,7 @@ public: Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) + Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) Q_PROPERTY(int supportedFirmwareCount READ supportedFirmwareCount CONSTANT) Q_PROPERTY(QGeoCoordinate lastKnownHomePosition READ lastKnownHomePosition CONSTANT) @@ -196,6 +197,7 @@ public: static Fact* areaUnits (void); static Fact* speedUnits (void); static Fact* batteryPercentRemainingAnnounce(void); + static Fact* defaultMissionItemAltitude (void); int supportedFirmwareCount (); @@ -267,6 +269,7 @@ private: static SettingsFact* _speedUnitsFact; static FactMetaData* _speedUnitsMetaData; static SettingsFact* _batteryPercentRemainingAnnounceFact; + static SettingsFact* _defaultMissionItemAltitudeFact; static const char* _virtualTabletJoystickKey; static const char* _baseFontPointSizeKey; diff --git a/src/QmlControls/QGroundControlQmlGlobal.json b/src/QmlControls/QGroundControlQmlGlobal.json index f48c1b2a1..e58a6e2a6 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.json +++ b/src/QmlControls/QGroundControlQmlGlobal.json @@ -44,5 +44,15 @@ "units": "%", "min": 0, "max": 100 +}, +{ + "name": "DefaultMissionItemAltitude", + "shortDescription": "Default value for altitude", + "longDescription": "This value specifies the default altitude for new items added to a mission.", + "type": "double", + "defaultValue": 50.0, + "min": 0.0, + "units": "meters", + "decimalPlaces": 2 } ] diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 59618eafe..a02c919eb 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -206,6 +206,28 @@ QGCView { } } //----------------------------------------------------------------- + //-- Palette Styles + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + anchors.baseline: paletteCombo.baseline + text: qsTr("UI Style:") + width: _labelWidth + } + QGCComboBox { + id: paletteCombo + width: _editFieldWidth + model: [ qsTr("Indoor"), qsTr("Outdoor") ] + currentIndex: QGroundControl.isDarkStyle ? 0 : 1 + onActivated: { + if (index != -1) { + currentIndex = index + QGroundControl.isDarkStyle = index === 0 ? true : false + } + } + } + } + //----------------------------------------------------------------- //-- Audio preferences QGCCheckBox { text: qsTr("Mute all audio output") @@ -295,6 +317,19 @@ QGCView { visible: QGroundControl.corePlugin.options.enableVirtualJoystick } //----------------------------------------------------------------- + //-- Default mission item altitude + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + anchors.baseline: defaultItemAltitudeField.baseline + text: qsTr("Default mission item altitude:") + } + FactTextField { + id: defaultItemAltitudeField + fact: QGroundControl.defaultMissionItemAltitude + } + } + //----------------------------------------------------------------- //-- AutoLoad Row { spacing: ScreenTools.defaultFontPixelWidth @@ -358,28 +393,6 @@ QGCView { } } } - //----------------------------------------------------------------- - //-- Palette Styles - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - anchors.baseline: paletteCombo.baseline - text: qsTr("UI Style:") - width: _labelWidth - } - QGCComboBox { - id: paletteCombo - width: _editFieldWidth - model: [ qsTr("Indoor"), qsTr("Outdoor") ] - currentIndex: QGroundControl.isDarkStyle ? 0 : 1 - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.isDarkStyle = index === 0 ? true : false - } - } - } - } } } //----------------------------------------------------------------- -- GitLab From f16514fbd1a987f9b8986b63265658403068744f Mon Sep 17 00:00:00 2001 From: Danny Schrader Date: Thu, 16 Feb 2017 17:57:19 -0500 Subject: [PATCH 292/398] removed FirmwarePlugin message. Changed abortLanding() to abortLanding(double). --- src/FirmwarePlugin/FirmwarePlugin.cc | 7 ------- src/FirmwarePlugin/FirmwarePlugin.h | 3 --- src/FlightDisplay/FlightDisplayViewWidgets.qml | 4 ++-- src/Vehicle/Vehicle.cc | 6 ++---- src/Vehicle/Vehicle.h | 2 +- 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index f42ed6efd..fb6d02816 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -16,7 +16,6 @@ static FirmwarePluginFactoryRegister* _instance = NULL; const char* guided_mode_not_supported_by_vehicle = "Guided mode not supported by Vehicle."; -const char* landing_aborted = "Landing aborted."; const char* FirmwarePlugin::px4FollowMeFlightMode = "Follow Me"; @@ -232,12 +231,6 @@ void FirmwarePlugin::pauseVehicle(Vehicle* vehicle) qgcApp()->showMessage(guided_mode_not_supported_by_vehicle); } -void FirmwarePlugin::abortLanding(Vehicle* vehicle) -{ - Q_UNUSED(vehicle); - qgcApp()->showMessage(landing_aborted); -} - void FirmwarePlugin::guidedModeRTL(Vehicle* vehicle) { // Not supported by generic vehicle diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index f5c27eab0..0c0eb0f77 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -105,9 +105,6 @@ public: /// If not, vehicle will be left in Loiter. virtual void pauseVehicle(Vehicle* vehicle); - /// Command vehicle to abort landing - virtual void abortLanding(Vehicle* vehicle); - /// Command vehicle to return to launch virtual void guidedModeRTL(Vehicle* vehicle); diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index f60854c4a..b2667bb36 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -240,7 +240,7 @@ Item { //_activeVehicle.guidedModeOrbit(QGroundControl.flightMapPosition, 50.0) break; case confirmAbort: - _activeVehicle.abortLanding() + _activeVehicle.abortLanding(50) // hardcoded value for climbOutAltitude that is currently ignored break; default: console.warn(qsTr("Internal error: unknown confirmActionCode"), confirmActionCode) @@ -366,7 +366,7 @@ Item { QGCButton { pointSize: _guidedModeBar._fontPointSize text: qsTr("Abort") - visible: _activeVehicle && _activeVehicle.flying + visible: _activeVehicle && _activeVehicle.flying && _activeVehicle.fixedWing onClicked: _guidedModeBar.confirmAction(_guidedModeBar.confirmAbort) } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index f9399cbc3..fa5b2f125 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1838,14 +1838,12 @@ void Vehicle::pauseVehicle(void) _firmwarePlugin->pauseVehicle(this); } -void Vehicle::abortLanding(void) +void Vehicle::abortLanding(double climbOutAltitude) { sendMavCommand(defaultComponentId(), MAV_CMD_DO_GO_AROUND, true, // show error if fails - 50); // this is a dummy value that is currently ignored - - _firmwarePlugin->abortLanding(this); + climbOutAltitude); } bool Vehicle::guidedMode(void) const diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 9d168eb00..d880b8769 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -356,7 +356,7 @@ public: Q_INVOKABLE void emergencyStop(void); /// Command vehicle to abort landing - Q_INVOKABLE void abortLanding(void); + Q_INVOKABLE void abortLanding(double climbOutAltitude); /// Alter the current mission item on the vehicle Q_INVOKABLE void setCurrentMissionSequence(int seq); -- GitLab From 17e4a3305cbed9f9fb6a6991b63748d8ae7573c6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 16 Feb 2017 17:01:13 -0800 Subject: [PATCH 293/398] Use new ToolStrip control in Plan View --- qgroundcontrol.qrc | 4 + src/FlightMap/Widgets/CenterMapDropPanel.qml | 103 +++++++ src/FlightMap/Widgets/MapFitFunctions.qml | 144 ++++++++++ src/FlightMap/qmldir | 6 +- src/MissionEditor/MissionEditor.qml | 267 +++++++++--------- src/QmlControls/DropPanel.qml | 162 +++++++++++ .../QGroundControl.Controls.qmldir | 2 + src/QmlControls/ToolStrip.qml | 183 ++++++++++++ 8 files changed, 737 insertions(+), 134 deletions(-) create mode 100644 src/FlightMap/Widgets/CenterMapDropPanel.qml create mode 100644 src/FlightMap/Widgets/MapFitFunctions.qml create mode 100644 src/QmlControls/DropPanel.qml create mode 100644 src/QmlControls/ToolStrip.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 99607527a..9fe698a3f 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -94,6 +94,8 @@ src/QmlControls/SubMenuButton.qml src/QmlControls/VehicleRotationCal.qml src/QmlControls/VehicleSummaryRow.qml + src/QmlControls/ToolStrip.qml + src/QmlControls/DropPanel.qml src/ViewWidgets/ViewWidget.qml src/FactSystem/FactControls/FactBitmask.qml src/FactSystem/FactControls/FactCheckBox.qml @@ -111,6 +113,8 @@ src/FlightDisplay/MultiVehicleList.qml src/FlightDisplay/qmldir src/FlightMap/Widgets/CenterMapDropButton.qml + src/FlightMap/Widgets/CenterMapDropPanel.qml + src/FlightMap/Widgets/MapFitFunctions.qml src/FlightMap/FlightMap.qml src/FlightMap/Widgets/InstrumentSwipeView.qml src/FlightMap/MapScale.qml diff --git a/src/FlightMap/Widgets/CenterMapDropPanel.qml b/src/FlightMap/Widgets/CenterMapDropPanel.qml new file mode 100644 index 000000000..fe3a43b6d --- /dev/null +++ b/src/FlightMap/Widgets/CenterMapDropPanel.qml @@ -0,0 +1,103 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Layouts 1.2 +import QtPositioning 5.3 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 + +ColumnLayout { + id: root + spacing: ScreenTools.defaultFontPixelWidth * 0.5 + + property var map + property var fitFunctions + property bool showMission: true + property bool showAllItems: true + property bool showFollowVehicle: false + property bool followVehicle: false + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + QGCLabel { text: qsTr("Center map on:") } + + QGCButton { + text: qsTr("Mission") + Layout.fillWidth: true + visible: showMission + enabled: !followVehicleCheckBox.checked + + onClicked: { + dropPanel.hide() + fitFunctions.fitMapViewportToMissionItems() + } + } + + QGCButton { + text: qsTr("All items") + Layout.fillWidth: true + visible: showAllItems + enabled: !followVehicleCheckBox.checked + + onClicked: { + dropPanel.hide() + fitFunctions.fitMapViewportToAllItems() + } + } + + QGCButton { + text: qsTr("Home") + Layout.fillWidth: true + enabled: !followVehicleCheckBox.checked + + onClicked: { + dropPanel.hide() + map.center = fitFunctions.fitHomePosition() + } + } + + QGCButton { + text: qsTr("Current Location") + Layout.fillWidth: true + enabled: mainWindow.gcsPosition.isValid && !followVehicleCheckBox.checked + + onClicked: { + dropPanel.hide() + map.center = mainWindow.gcsPosition + } + } + + QGCButton { + text: qsTr("Vehicle") + Layout.fillWidth: true + enabled: _activeVehicle && _activeVehicle.latitude != 0 && _activeVehicle.longitude != 0 && !followVehicleCheckBox.checked + + onClicked: { + dropPanel.hide() + map.center = activeVehicle.coordinate + } + } + + QGCCheckBox { + id: followVehicleCheckBox + text: qsTr("Follow Vehicle") + checked: followVehicle + visible: showFollowVehicle + + onClicked: { + dropPanel.hide() + _root.followVehicle = checked + } + } +} // Column diff --git a/src/FlightMap/Widgets/MapFitFunctions.qml b/src/FlightMap/Widgets/MapFitFunctions.qml new file mode 100644 index 000000000..dc76a54fe --- /dev/null +++ b/src/FlightMap/Widgets/MapFitFunctions.qml @@ -0,0 +1,144 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtPositioning 5.3 + +import QGroundControl 1.0 + +/// Set of functions for fitting the map viewpoer to a specific constraint +Item { + property var map + property rect mapFitViewport + property bool usePlannedHomePosition ///< true: planned home position used for calculations, false: vehicle home position use for calculations + property var mapGeoFenceController + property var mapMissionController + property var mapRallyPointController + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + function fitHomePosition() { + var homePosition = QtPositioning.coordinate() + var activeVehicle = QGroundControl.multiVehicleManager.activeVehicle + if (usePlannedHomePosition) { + homePosition = mapMissionController.visualItems.get(0).coordinate + } else if (activeVehicle) { + homePosition = activeVehicle.homePosition + } + return homePosition + } + + /// Normalize latitude to range: 0 to 180, S to N + function normalizeLat(lat) { + return lat + 90.0 + } + + /// Normalize longitude to range: 0 to 360, W to E + function normalizeLon(lon) { + return lon + 180.0 + } + + /// Fits the visible region of the map to inclues all of the specified coordinates. If no coordinates + /// are specified the map will center to fitHomePosition() + function fitMapViewportToAllCoordinates(coordList) { + if (coordList.length == 0) { + map.center = fitHomePosition() + return + } + + // Create the normalized lat/lon corners for the coordinate bounding rect from the list of coordinates + var north = normalizeLat(coordList[0].latitude) + var south = north + var east = normalizeLon(coordList[0].longitude) + var west = east + for (var i=1; i 2) { + for (var i=0; i + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Item { + id: _root + z: QGroundControl.zOrderWidgets + visible: false + + signal clicked() + property real radius: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.75 : ScreenTools.defaultFontPixelHeight * 1.25 + property real viewportMargins: 0 + property real topMargin: parent.height - ScreenTools.availableHeight + property var toolStrip + + + width: radius * 2 + height: radius * 2 + + // Should be an enum but that get's into the whole problem of creating a singleton which isn't worth the effort + readonly property int dropLeft: 1 + readonly property int dropRight: 2 + readonly property int dropUp: 3 + readonly property int dropDown: 4 + + readonly property real _arrowBaseWidth: radius // Width of long side of arrow + readonly property real _arrowPointHeight: radius * 0.666 // Height is long side to point + readonly property real _dropCornerRadius: ScreenTools.defaultFontPixelWidth * 0.5 + readonly property real _dropCornerRadiusX2: _dropCornerRadius * 2 + readonly property real _dropMargin: _dropCornerRadius + readonly property real _dropMarginX2: _dropMargin * 2 + + property var _dropEdgeTopPoint + property real _dropEdgeHeight + property alias _dropDownComponent: dropDownLoader.sourceComponent + property real _viewportMaxLeft: -x + viewportMargins + property real _viewportMaxRight: parent.width - (viewportMargins * 2) - x + property real _viewportMaxTop: -y + viewportMargins + topMargin + property real _viewportMaxBottom: parent.height - (viewportMargins * 2) - y + + function show(panelEdgeTopPoint, panelEdgeHeight, panelComponent) { + _dropEdgeTopPoint = panelEdgeTopPoint + _dropEdgeHeight = panelEdgeHeight + _dropDownComponent = panelComponent + _calcPositions() + visible = true + } + + function hide() { + if (visible) { + visible = false + _dropDownComponent = undefined + toolStrip.uncheckAll() + } + } + + function _calcPositions() { + var dropComponentWidth = dropDownLoader.item.width + var dropComponentHeight = dropDownLoader.item.height + var dropRectWidth = dropComponentWidth + _dropMarginX2 + var dropRectHeight = dropComponentHeight + _dropMarginX2 + + dropItemHolderRect.width = dropRectWidth + dropItemHolderRect.height = dropRectHeight + + dropDownItem.width = dropComponentWidth + _dropMarginX2 + dropDownItem.height = dropComponentHeight + _dropMarginX2 + + dropDownItem.width += _arrowPointHeight + + dropDownItem.y = _dropEdgeTopPoint.y -(dropDownItem.height / 2) + radius + + dropItemHolderRect.y = 0 + + dropDownItem.x = _dropEdgeTopPoint.x + _dropMargin + dropItemHolderRect.x = _arrowPointHeight + + // Validate that dropdown is within viewport + dropDownItem.y = Math.max(dropDownItem.y, _viewportMaxTop) + dropDownItem.y = Math.min(dropDownItem.y + dropDownItem.height, _viewportMaxBottom) - dropDownItem.height + + // Arrow points + arrowCanvas.arrowPoint.y = (_dropEdgeTopPoint.y + radius) - dropDownItem.y + arrowCanvas.arrowPoint.x = 0 + arrowCanvas.arrowBase1.x = _arrowPointHeight + arrowCanvas.arrowBase1.y = arrowCanvas.arrowPoint.y - (_arrowBaseWidth / 2) + arrowCanvas.arrowBase2.x = arrowCanvas.arrowBase1.x + arrowCanvas.arrowBase2.y = arrowCanvas.arrowBase1.y + _arrowBaseWidth + arrowCanvas.requestPaint() + } // function - _calcPositions + + QGCPalette { id: qgcPal } + + /* + MouseArea { + x: _viewportMaxLeft + y: _viewportMaxTop + width: _viewportMaxRight -_viewportMaxLeft + height: _viewportMaxBottom - _viewportMaxTop + visible: checked + onClicked: { + checked = false + _root.clicked() + } + }*/ + + Item { + id: dropDownItem + + Canvas { + id: arrowCanvas + anchors.fill: parent + + property var arrowPoint: Qt.point(0, 0) + property var arrowBase1: Qt.point(0, 0) + property var arrowBase2: Qt.point(0, 0) + + onPaint: { + var context = getContext("2d") + context.reset() + context.beginPath() + + context.moveTo(dropItemHolderRect.x, dropItemHolderRect.y) + context.lineTo(dropItemHolderRect.x + dropItemHolderRect.width, dropItemHolderRect.y) + context.lineTo(dropItemHolderRect.x + dropItemHolderRect.width, dropItemHolderRect.y + dropItemHolderRect.height) + context.lineTo(dropItemHolderRect.x, dropItemHolderRect.y + dropItemHolderRect.height) + context.lineTo(arrowBase2.x, arrowBase2.y) + context.lineTo(arrowPoint.x, arrowPoint.y) + context.lineTo(arrowBase1.x, arrowBase1.y) + context.lineTo(dropItemHolderRect.x, dropItemHolderRect.y) + context.closePath() + context.fillStyle = qgcPal.windowShade + context.fill() + } + } // Canvas - arrowCanvas + + Item { + id: dropItemHolderRect + + Loader { + id: dropDownLoader + x: _dropMargin + y: _dropMargin + + property var dropPanel: _root + } + } + } // Item - dropDownItem +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index c33e30fea..eed1fdc47 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -4,6 +4,7 @@ AnalyzePage 1.0 AnalyzePage.qml AppMessages 1.0 AppMessages.qml ClickableColor 1.0 ClickableColor.qml DropButton 1.0 DropButton.qml +DropPanel 1.0 DropPanel.qml ExclusiveGroupItem 1.0 ExclusiveGroupItem.qml FactSliderPanel 1.0 FactSliderPanel.qml FlightModeDropdown 1.0 FlightModeDropdown.qml @@ -49,6 +50,7 @@ SetupPage 1.0 SetupPage.qml SignalStrength 1.0 SignalStrength.qml SliderSwitch 1.0 SliderSwitch.qml SubMenuButton 1.0 SubMenuButton.qml +ToolStrip 1.0 ToolStrip.qml VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml ViewWidget 1.0 ViewWidget.qml diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml new file mode 100644 index 000000000..9c2e7c3ec --- /dev/null +++ b/src/QmlControls/ToolStrip.qml @@ -0,0 +1,183 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 1.2 + +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Rectangle { + id: _root + color: qgcPal.window + width: ScreenTools.defaultFontPixelWidth * 6 + height: buttonStripColumn.height + (buttonStripColumn.anchors.margins * 2) + radius: _radius + + property string title: "Title" + property alias model: repeater.model + property var showAlternateIcon + property var rotateImage + property var buttonEnabled + + signal clicked(int index, bool checked) + + readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _buttonSpacing: ScreenTools.defaultFontPixelWidth + + ExclusiveGroup { + id: dropButtonsExclusiveGroup + } + + function uncheckAll() { + dropButtonsExclusiveGroup.current = null + // Signal all toggles as off + for (var i=0; i Date: Fri, 17 Feb 2017 10:23:00 -0500 Subject: [PATCH 294/398] Fix regression bug with Bluetooth. --- src/comm/BluetoothLink.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/comm/BluetoothLink.cc b/src/comm/BluetoothLink.cc index c5a019acd..2ffe1e725 100644 --- a/src/comm/BluetoothLink.cc +++ b/src/comm/BluetoothLink.cc @@ -31,6 +31,7 @@ BluetoothLink::BluetoothLink(SharedLinkConfigurationPointer& config) : LinkInterface(config) + , _config(qobject_cast(config.data())) , _connectState(false) , _targetSocket(NULL) #ifdef __ios__ -- GitLab From dd4de9063613cc535cbd84a016ef0210f02eb988 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 17 Feb 2017 18:19:49 -0500 Subject: [PATCH 295/398] Move instrument widgets off to a loader. --- qgroundcontrol.qrc | 4 +- src/FlightDisplay/FlightDisplayView.qml | 33 ++---- src/FlightDisplay/FlightDisplayViewVideo.qml | 4 +- .../FlightDisplayViewWidgets.qml | 108 +++++++++--------- src/FlightDisplay/MultiVehicleList.qml | 5 +- src/FlightMap/Widgets/QGCAttitudeWidget.qml | 12 +- src/FlightMap/Widgets/QGCCompassWidget.qml | 6 +- src/FlightMap/Widgets/QGCInstrumentWidget.qml | 32 ++---- .../Widgets/QGCInstrumentWidgetAlternate.qml | 44 +++---- src/FlightMap/qmldir | 2 - 10 files changed, 102 insertions(+), 148 deletions(-) diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 9fe698a3f..36e85ae3c 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -125,8 +125,8 @@ src/FlightMap/Widgets/QGCAttitudeHUD.qml src/FlightMap/Widgets/QGCAttitudeWidget.qml src/FlightMap/Widgets/QGCCompassWidget.qml - src/FlightMap/Widgets/QGCInstrumentWidget.qml - src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml + src/FlightMap/Widgets/QGCInstrumentWidget.qml + src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml src/MissionEditor/QGCMapPolygonControls.qml src/FlightMap/Widgets/QGCPitchIndicator.qml src/FlightMap/QGCVideoBackground.qml diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index ec47cffb2..473136265 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -33,25 +33,14 @@ QGCView { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + property bool activeVehicleJoystickEnabled: _activeVehicle ? _activeVehicle.joystickEnabled : false + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, true) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false - - property real _roll: _activeVehicle ? _activeVehicle.roll.value : _defaultRoll - property real _pitch: _activeVehicle ? _activeVehicle.pitch.value : _defaultPitch - property real _heading: _activeVehicle ? _activeVehicle.heading.value : _defaultHeading - - property Fact _emptyFact: Fact { } - property Fact _groundSpeedFact: _activeVehicle ? _activeVehicle.groundSpeed : _emptyFact - property Fact _airSpeedFact: _activeVehicle ? _activeVehicle.airSpeed : _emptyFact - - property bool activeVehicleJoystickEnabled: _activeVehicle ? _activeVehicle.joystickEnabled : false - - property real _savedZoomLevel: 0 - property real _margins: ScreenTools.defaultFontPixelWidth / 2 - - - property real pipSize: mainWindow.width * 0.2 + property real _savedZoomLevel: 0 + property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real _pipSize: mainWindow.width * 0.2 readonly property bool isBackgroundDark: _mainIsMap ? (_flightMap ? _flightMap.isSatelliteMap : true) : true readonly property real _defaultRoll: 0 @@ -136,8 +125,8 @@ QGCView { anchors.left: _panel.left anchors.bottom: _panel.bottom visible: _mainIsMap || _isPipVisible - width: _mainIsMap ? _panel.width : pipSize - height: _mainIsMap ? _panel.height : pipSize * (9/16) + width: _mainIsMap ? _panel.width : _pipSize + height: _mainIsMap ? _panel.height : _pipSize * (9/16) states: [ State { name: "pipMode" @@ -166,8 +155,8 @@ QGCView { Item { id: _flightVideo z: _mainIsMap ? _panel.z + 2 : _panel.z + 1 - width: !_mainIsMap ? _panel.width : pipSize - height: !_mainIsMap ? _panel.height : pipSize * (9/16) + width: !_mainIsMap ? _panel.width : _pipSize + height: !_mainIsMap ? _panel.height : _pipSize * (9/16) anchors.left: _panel.left anchors.bottom: _panel.bottom visible: QGroundControl.videoManager.hasVideo && (!_mainIsMap || _isPipVisible) @@ -204,8 +193,8 @@ QGCView { QGCPipable { id: _flightVideoPipControl z: _flightVideo.z + 3 - width: pipSize - height: pipSize * (9/16) + width: _pipSize + height: _pipSize * (9/16) anchors.left: _panel.left anchors.bottom: _panel.bottom anchors.margins: ScreenTools.defaultFontPixelHeight diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 2b8252094..86d08aff7 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -45,8 +45,8 @@ Item { QGCAttitudeHUD { id: attitudeHUD visible: !_mainIsMap - rollAngle: _roll - pitchAngle: _pitch + rollAngle: _activeVehicle ? _activeVehicle.roll.value : 0 + pitchAngle: _activeVehicle ? _activeVehicle.pitch.value : 0 width: ScreenTools.defaultFontPixelHeight * (30) height: ScreenTools.defaultFontPixelHeight * (30) active: QGroundControl.multiVehicleManager.activeVehicleAvailable diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 9743c72f3..4654da7ba 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -25,22 +25,21 @@ import QGroundControl.FlightMap 1.0 Item { id: _root - property alias guidedModeBar: _guidedModeBar - property bool gotoEnabled: _activeVehicle && _activeVehicle.guidedMode && _activeVehicle.flying + property alias guidedModeBar: _guidedModeBar + property bool gotoEnabled: _activeVehicle && _activeVehicle.guidedMode && _activeVehicle.flying property var qgcView property bool isBackgroundDark - property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _isSatellite: _mainIsMap ? (_flightMap ? _flightMap.isSatelliteMap : true) : true - property bool _lightWidgetBorders: _isSatellite - property bool _useAlternateInstruments: QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _isSatellite: _mainIsMap ? (_flightMap ? _flightMap.isSatelliteMap : true) : true + property bool _lightWidgetBorders: _isSatellite - readonly property real _margins: ScreenTools.defaultFontPixelHeight / 2 + readonly property real _margins: ScreenTools.defaultFontPixelHeight * 0.5 QGCMapPalette { id: mapPal; lightColors: isBackgroundDark } - QGCPalette { id: qgcPal } + QGCPalette { id: qgcPal } - function getGadgetWidth() { + function getPreferredInstrumentWidth() { if(ScreenTools.isMobile) { return ScreenTools.isTinyScreen ? mainWindow.width * 0.2 : mainWindow.width * 0.15 } @@ -48,6 +47,28 @@ Item { return Math.min(w, 200) } + function _setInstrumentWidget() { + var useAlternateInstruments = QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen + if(useAlternateInstruments) { + instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" + instrumentsLoader.state = "topMode" + } else { + instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidget.qml" + instrumentsLoader.state = "centerMode" + } + } + + Connections { + target: QGroundControl + onVirtualTabletJoystickChanged: { + _setInstrumentWidget() + } + } + + Component.onCompleted: { + _setInstrumentWidget() + } + //-- Map warnings Column { anchors.horizontalCenter: parent.horizontalCenter @@ -74,54 +95,33 @@ Item { } //-- Instrument Panel - QGCInstrumentWidget { - id: instrumentGadget - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.right: altitudeSlider.visible ? altitudeSlider.left : parent.right - anchors.verticalCenter: parent.verticalCenter - visible: !_useAlternateInstruments - size: getGadgetWidth() - heading: _heading - rollAngle: _roll - pitchAngle: _pitch - groundSpeedFact: _groundSpeedFact - airSpeedFact: _airSpeedFact - lightBorders: _lightWidgetBorders - z: QGroundControl.zOrderWidgets - qgcView: _root.qgcView - maxHeight: parent.height - (anchors.margins * 2) - } - - QGCInstrumentWidgetAlternate { - id: instrumentGadgetAlternate + Loader { + id: instrumentsLoader anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.top: parent.top anchors.right: altitudeSlider.visible ? altitudeSlider.left : parent.right - visible: _useAlternateInstruments - width: ScreenTools.isTinyScreen ? getGadgetWidth() * 1.5 : getGadgetWidth() - heading: _heading - rollAngle: _roll - pitchAngle: _pitch - groundSpeedFact: _groundSpeedFact - airSpeedFact: _airSpeedFact - lightBorders: _lightWidgetBorders - qgcView: _root.qgcView - maxHeight: parent.height - (anchors.margins * 2) z: QGroundControl.zOrderWidgets + property var qgcView: _root.qgcView + property real maxHeight:parent.height - (anchors.margins * 2) + states: [ + State { + name: "topMode" + AnchorChanges { + target: instrumentsLoader + anchors.verticalCenter: undefined + anchors.top: _root ? _root.top : undefined + } + }, + State { + name: "centerMode" + AnchorChanges { + target: instrumentsLoader + anchors.top: undefined + anchors.verticalCenter: _root ? _root.verticalCenter : undefined + } + } + ] } - /* - ValuesWidget { - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: instrumentGadgetAlternate.bottom - anchors.horizontalCenter: instrumentGadgetAlternate.horizontalCenter - width: getGadgetWidth() - qgcView: _root.qgcView - textColor: _isSatellite ? "white" : "black" - visible: _useAlternateInstruments - maxHeight: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.y - y : parent.height - anchors.margins - y - }*/ - //-- Guided mode buttons Rectangle { id: _guidedModeBar @@ -131,7 +131,7 @@ Item { width: guidedModeColumn.width + (_margins * 2) height: guidedModeColumn.height + (_margins * 2) radius: ScreenTools.defaultFontPixelHeight * 0.25 - color: _lightWidgetBorders ? Qt.rgba(qgcPal.mapWidgetBorderLight.r, qgcPal.mapWidgetBorderLight.g, qgcPal.mapWidgetBorderLight.b, 0.8) : Qt.rgba(qgcPal.mapWidgetBorderDark.r, qgcPal.mapWidgetBorderDark.g, qgcPal.mapWidgetBorderDark.b, 0.75) + color: _isSatellite ? Qt.rgba(qgcPal.mapWidgetBorderLight.r, qgcPal.mapWidgetBorderLight.g, qgcPal.mapWidgetBorderLight.b, 0.8) : Qt.rgba(qgcPal.mapWidgetBorderDark.r, qgcPal.mapWidgetBorderDark.g, qgcPal.mapWidgetBorderDark.b, 0.75) visible: _activeVehicle z: QGroundControl.zOrderWidgets state: "Shown" @@ -303,7 +303,7 @@ Item { QGCLabel { anchors.horizontalCenter: parent.horizontalCenter - color: _lightWidgetBorders ? qgcPal.mapWidgetBorderDark : qgcPal.mapWidgetBorderLight + color: _isSatellite ? qgcPal.mapWidgetBorderDark : qgcPal.mapWidgetBorderLight text: "Click in map to move vehicle" visible: gotoEnabled } diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index 9278056d6..a266bf0b6 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -52,15 +52,12 @@ QGCListView { QGCCompassWidget { size: _widgetHeight - heading: _vehicle.heading.rawValue vehicle: _vehicle } QGCAttitudeWidget { size: _widgetHeight - active: true - rollAngle: _vehicle.roll.rawValue - pitchAngle: _vehicle.pitch.rawValue + vehicle: _vehicle } } diff --git a/src/FlightMap/Widgets/QGCAttitudeWidget.qml b/src/FlightMap/Widgets/QGCAttitudeWidget.qml index 9f252926c..b3358db3d 100644 --- a/src/FlightMap/Widgets/QGCAttitudeWidget.qml +++ b/src/FlightMap/Widgets/QGCAttitudeWidget.qml @@ -17,22 +17,18 @@ import QtQuick 2.4 import QtGraphicalEffects 1.0 +import QGroundControl 1.0 import QGroundControl.Controls 1.0 Item { id: root - property bool active: false ///< true: actively connected to data provider, false: show inactive control - property real rollAngle : _defaultRollAngle - property real pitchAngle: _defaultPitchAngle property bool showPitch: true + property var vehicle: null property real size - readonly property real _defaultRollAngle: 0 - readonly property real _defaultPitchAngle: 0 - - property real _rollAngle: active ? rollAngle : _defaultRollAngle - property real _pitchAngle: active ? pitchAngle : _defaultPitchAngle + property real _rollAngle: vehicle ? vehicle.roll.rawValue : 0 + property real _pitchAngle: vehicle ? vehicle.pitch.rawValue : 0 width: size height: size diff --git a/src/FlightMap/Widgets/QGCCompassWidget.qml b/src/FlightMap/Widgets/QGCCompassWidget.qml index b941fbf91..a8e107180 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -25,12 +25,12 @@ Item { id: root property real size: _defaultSize - property real heading: 0 property var vehicle: null property real _defaultSize: ScreenTools.defaultFontPixelHeight * (10) property real _sizeRatio: ScreenTools.isTinyScreen ? (size / _defaultSize) * 0.5 : size / _defaultSize property int _fontSize: ScreenTools.defaultFontPointSize * _sizeRatio + property real _heading: vehicle ? vehicle.heading.rawValue : 0 width: size height: size @@ -58,7 +58,7 @@ Item { transform: Rotation { origin.x: pointer.width / 2 origin.y: pointer.height / 2 - angle: heading + angle: _heading } } @@ -79,7 +79,7 @@ Item { color: Qt.rgba(0,0,0,0.65) QGCLabel { - text: vehicle ? heading.toFixed(0) : qsTr("OFF") + text: vehicle ? _heading.toFixed(0) : qsTr("OFF") font.family: vehicle ? ScreenTools.demiboldFontFamily : ScreenTools.normalFontFamily font.pointSize: _fontSize < 8 ? 8 : _fontSize; color: "white" diff --git a/src/FlightMap/Widgets/QGCInstrumentWidget.qml b/src/FlightMap/Widgets/QGCInstrumentWidget.qml index 879f740c4..a5b81c55a 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidget.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidget.qml @@ -26,27 +26,15 @@ import QGroundControl.Palette 1.0 Item { id: instrumentPanel height: instrumentColumn.y + instrumentColumn.height + _topBottomMargin - width: size - - property alias heading: compass.heading - property alias rollAngle: attitudeWidget.rollAngle - property alias pitchAngle: attitudeWidget.pitchAngle - property real size: _defaultSize - property bool lightBorders: true - property bool active: false - property var qgcView - property real maxHeight - - property Fact _emptyFact: Fact { } - property Fact groundSpeedFact: _emptyFact - property Fact airSpeedFact: _emptyFact - - property real _defaultSize: ScreenTools.defaultFontPixelHeight * (9) + width: getPreferredInstrumentWidth() + property var _qgcView: qgcView + property real _maxHeight: maxHeight + property real _defaultSize: ScreenTools.defaultFontPixelHeight * (9) property color _backgroundColor: qgcPal.window property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33 - property real _topBottomMargin: (size * 0.05) / 2 - property real _availableValueHeight: maxHeight - (attitudeWidget.height + _spacer1.height + _spacer2.height + (_spacing * 4)) - (_showCompass ? compass.height : 0) + property real _topBottomMargin: (width * 0.05) / 2 + property real _availableValueHeight: _maxHeight - (attitudeWidget.height + _spacer1.height + _spacer2.height + (_spacing * 4)) - (_showCompass ? compass.height : 0) property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle readonly property bool _showCompass: true // !ScreenTools.isShortScreen @@ -57,10 +45,10 @@ Item { anchors.left: parent.left anchors.right: parent.right height: (_showCompass ? instrumentColumn.height : attitudeWidget.height) + (_topBottomMargin * 2) - radius: size / 2 + radius: width / 2 color: _backgroundColor border.width: 1 - border.color: lightBorders ? qgcPal.mapWidgetBorderLight : qgcPal.mapWidgetBorderDark + border.color: _isSatellite ? qgcPal.mapWidgetBorderLight : qgcPal.mapWidgetBorderDark } MouseArea { @@ -83,7 +71,7 @@ Item { QGCAttitudeWidget { id: attitudeWidget size: parent.width * 0.95 - active: instrumentPanel.active + vehicle: _activeVehicle anchors.horizontalCenter: parent.horizontalCenter } @@ -131,7 +119,7 @@ Item { anchors.margins: 1 anchors.left: parent.left anchors.right: parent.right - qgcView: instrumentPanel.qgcView + qgcView: instrumentPanel._qgcView textColor: qgcPal.text backgroundColor: _backgroundColor maxHeight: _availableValueHeight diff --git a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml index 2ce355663..03d5e34f3 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml @@ -20,38 +20,24 @@ import QGroundControl.Palette 1.0 /// Instrument panel shown when virtual thumbsticks are visible Rectangle { id: root + width: ScreenTools.isTinyScreen ? getPreferredInstrumentWidth() * 1.5 : getPreferredInstrumentWidth() height: _outerRadius * 2 radius: _outerRadius color: qgcPal.window border.width: 1 - border.color: lightBorders ? qgcPal.mapWidgetBorderLight : qgcPal.mapWidgetBorderDark - - property alias heading: compass.heading - property alias rollAngle: attitude.rollAngle - property alias pitchAngle: attitude.pitchAngle - property real size: _defaultSize - property bool active: false - property bool lightBorders: true - property var qgcView - property real maxHeight - - property Fact _emptyFact: Fact { } - property Fact groundSpeedFact: _emptyFact - property Fact airSpeedFact: _emptyFact - property Fact altitudeFact: _emptyFact - - property real _innerRadius: (width - (_topBottomMargin * 3)) / 4 - property real _outerRadius: _innerRadius + _topBottomMargin - - property real _defaultSize: ScreenTools.defaultFontPixelHeight * (9) - - property real _sizeRatio: ScreenTools.isTinyScreen ? (size / _defaultSize) * 0.5 : size / _defaultSize - property real _bigFontSize: ScreenTools.defaultFontPointSize * 2.5 * _sizeRatio - property real _normalFontSize:ScreenTools.defaultFontPointSize * 1.5 * _sizeRatio - property real _labelFontSize: ScreenTools.defaultFontPointSize * 0.75 * _sizeRatio - property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33 - property real _topBottomMargin: (size * 0.05) / 2 + border.color: _isSatellite ? qgcPal.mapWidgetBorderLight : qgcPal.mapWidgetBorderDark + + property real _innerRadius: (width - (_topBottomMargin * 3)) / 4 + property real _outerRadius: _innerRadius + _topBottomMargin + property real _defaultSize: ScreenTools.defaultFontPixelHeight * (9) + property real _sizeRatio: ScreenTools.isTinyScreen ? (width / _defaultSize) * 0.5 : width / _defaultSize + property real _bigFontSize: ScreenTools.defaultFontPointSize * 2.5 * _sizeRatio + property real _normalFontSize: ScreenTools.defaultFontPointSize * 1.5 * _sizeRatio + property real _labelFontSize: ScreenTools.defaultFontPointSize * 0.75 * _sizeRatio + property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33 + property real _topBottomMargin: (width * 0.05) / 2 property real _availableValueHeight: maxHeight - (root.height + _valuesItem.anchors.topMargin) + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle QGCPalette { id: qgcPal } @@ -60,7 +46,7 @@ Rectangle { anchors.leftMargin: _topBottomMargin anchors.left: parent.left size: _innerRadius * 2 - active: root.active + vehicle: _activeVehicle anchors.verticalCenter: parent.verticalCenter } @@ -69,7 +55,7 @@ Rectangle { anchors.leftMargin: _spacing anchors.left: attitude.right size: _innerRadius * 2 - vehicle: QGroundControl.multiVehicleManager.activeVehicle + vehicle: _activeVehicle anchors.verticalCenter: parent.verticalCenter } diff --git a/src/FlightMap/qmldir b/src/FlightMap/qmldir index c6d02e2cf..84cdd15c3 100644 --- a/src/FlightMap/qmldir +++ b/src/FlightMap/qmldir @@ -14,8 +14,6 @@ QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml QGCAttitudeHUD 1.0 QGCAttitudeHUD.qml QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml QGCCompassWidget 1.0 QGCCompassWidget.qml -QGCInstrumentWidget 1.0 QGCInstrumentWidget.qml -QGCInstrumentWidgetAlternate 1.0 QGCInstrumentWidgetAlternate.qml QGCPitchIndicator 1.0 QGCPitchIndicator.qml QGCSlider 1.0 QGCSlider.qml ValuesWidget 1.0 ValuesWidget.qml -- GitLab From 205138cf98a679dbcc1c9308372dc83bb9ed4fa1 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 17 Feb 2017 15:55:21 -0800 Subject: [PATCH 296/398] Fixed Wing landing pattern starting point Also added ui support for multiple complex item types --- qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 2 + src/MissionEditor/FWLandingPatternEditor.qml | 41 ++++ src/MissionEditor/MissionEditor.qml | 51 +++-- src/MissionEditor/MissionItemEditor.qml | 6 +- .../FWLandingPattern.FactMetaData.json | 2 + .../FixedWingLandingComplexItem.cc | 182 ++++++++++++++++++ .../FixedWingLandingComplexItem.h | 76 ++++++++ src/MissionManager/MissionController.cc | 20 +- src/MissionManager/MissionController.h | 11 +- src/MissionManager/SimpleMissionItem.cc | 2 + src/MissionManager/SurveyMissionItem.cc | 2 + src/MissionManager/VisualMissionItem.h | 2 + 13 files changed, 372 insertions(+), 27 deletions(-) create mode 100644 src/MissionEditor/FWLandingPatternEditor.qml create mode 100644 src/MissionManager/FWLandingPattern.FactMetaData.json create mode 100644 src/MissionManager/FixedWingLandingComplexItem.cc create mode 100644 src/MissionManager/FixedWingLandingComplexItem.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 3ef7283da..02a09e8e1 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -429,6 +429,7 @@ HEADERS += \ src/LogCompressor.h \ src/MG.h \ src/MissionManager/ComplexMissionItem.h \ + src/MissionManager/FixedWingLandingComplexItem.h \ src/MissionManager/GeoFenceController.h \ src/MissionManager/GeoFenceManager.h \ src/MissionManager/MissionCommandList.h \ @@ -592,6 +593,7 @@ SOURCES += \ src/JsonHelper.cc \ src/LogCompressor.cc \ src/MissionManager/ComplexMissionItem.cc \ + src/MissionManager/FixedWingLandingComplexItem.cc \ src/MissionManager/GeoFenceController.cc \ src/MissionManager/GeoFenceManager.cc \ src/MissionManager/MissionCommandList.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 9fe698a3f..aae4d36b5 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -149,6 +149,7 @@ src/VehicleSetup/SetupView.qml src/MissionEditor/SimpleItemEditor.qml src/MissionEditor/SurveyItemEditor.qml + src/MissionEditor/FWLandingPatternEditor.qml src/ui/preferences/TcpSettings.qml src/test.qml src/ui/preferences/UdpSettings.qml @@ -169,6 +170,7 @@ src/Vehicle/VibrationFact.json src/QmlControls/QGroundControlQmlGlobal.json src/MissionManager/RallyPoint.FactMetaData.json + src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json src/comm/USBBoardInfo.json diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml new file mode 100644 index 000000000..e30049ade --- /dev/null +++ b/src/MissionEditor/FWLandingPatternEditor.qml @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 + +// Editor for Fixed Wing Landing Pattern complex mission item +Rectangle { + id: _root + height: visible ? (editorColumn.height + (_margin * 2)) : 0 + width: availableWidth + color: qgcPal.windowShadeDark + radius: _radius + + // The following properties must be available up the hierarchy chain + //property real availableWidth ///< Width for control + //property var missionItem ///< Mission Item for editor + + property real _margin: ScreenTools.defaultFontPixelWidth * 0.25 + + Column { + id: editorColumn + + QGCLabel { text: "WIP" } + } +} diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 1389766e2..a71ce19d6 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -89,15 +89,6 @@ QGCView { } } - function addSurveyItem() { - var coordinate = editorMap.center - coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces) - coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces) - coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces) - var sequenceNumber = missionController.insertComplexMissionItem(coordinate, missionController.visualItems.count) - setCurrentItem(sequenceNumber) - } - MapFitFunctions { id: mapFitFunctions map: editorMap @@ -877,8 +868,9 @@ QGCView { toggle: true }, { - name: "Pattern", - iconSource: "/qmlimages/MapDrawShape.svg" + name: "Pattern", + iconSource: "/qmlimages/MapDrawShape.svg", + dropPanelComponent: patternDropPanel }, { name: "Sync", @@ -899,13 +891,8 @@ QGCView { ] onClicked: { - switch (index) { - case 0: + if (index == 0) { _addWaypointOnClick = checked - break - case 1: - addSurveyItem() - break } } } @@ -1084,6 +1071,7 @@ QGCView { checked: QGroundControl.flightMapSettings.mapType === text text: modelData exclusiveGroup: _mapTypeButtonsExclusiveGroup + onClicked: { QGroundControl.flightMapSettings.mapType = text dropPanel.hide() @@ -1093,4 +1081,33 @@ QGCView { } } } + + Component { + id: patternDropPanel + + ColumnLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.5 + + QGCLabel { text: qsTr("Create complex pattern:") } + + Repeater { + model: missionController.complexMissionItemNames + + QGCButton { + text: modelData + Layout.fillWidth: true + + onClicked: { + var coordinate = editorMap.center + coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces) + coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces) + coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces) + var sequenceNumber = missionController.insertComplexMissionItem(modelData, coordinate, missionController.visualItems.count) + setCurrentItem(sequenceNumber) + dropPanel.hide() + } + } + } + } // Column + } } // QGCVIew diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index e9f4797ff..25b68ffe3 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -141,9 +141,7 @@ Rectangle { anchors.fill: commandPicker visible: missionItem.sequenceNumber == 0 || !missionItem.isCurrentItem || !missionItem.isSimpleItem verticalAlignment: Text.AlignVCenter - text: missionItem.sequenceNumber == 0 ? - qsTr("Mission Settings") : - (missionItem.isSimpleItem ? missionItem.commandName : qsTr("Survey")) + text: missionItem.sequenceNumber == 0 ? qsTr("Mission Settings") : missionItem.commandName color: _outerTextColor } @@ -154,7 +152,7 @@ Rectangle { anchors.left: parent.left anchors.top: commandPicker.bottom height: item ? item.height : 0 - source: missionItem.sequenceNumber == 0 ? "qrc:/qml/MissionSettingsEditor.qml" : (missionItem.isSimpleItem ? "qrc:/qml/SimpleItemEditor.qml" : "qrc:/qml/SurveyItemEditor.qml") + source: missionItem.sequenceNumber == 0 ? "qrc:/qml/MissionSettingsEditor.qml" : missionItem.editorQml onLoaded: { item.visible = Qt.binding(function() { return _currentItem; }) diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc new file mode 100644 index 000000000..55fd5dfb7 --- /dev/null +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -0,0 +1,182 @@ +/**************************************************************************** + * + * (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 "FixedWingLandingComplexItem.h" +#include "JsonHelper.h" +#include "MissionController.h" +#include "QGCGeo.h" +#include "QGroundControlQmlGlobal.h" + +#include + +QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "FixedWingLandingComplexItemLog") + +const char* FixedWingLandingComplexItem::jsonComplexItemTypeValue = "fwLandingPattern"; + +QMap FixedWingLandingComplexItem::_metaDataMap; + +FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent) + : ComplexMissionItem(vehicle, parent) + , _sequenceNumber(0) + , _dirty(false) +{ + _editorQml = "qrc:/qml/FWLandingPatternEditor.qml"; + + if (_metaDataMap.isEmpty()) { + _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/FWLandingPattern.FactMetaData.json"), NULL /* metaDataParent */); + } +} + +int FixedWingLandingComplexItem::lastSequenceNumber(void) const +{ + return _sequenceNumber; +} + +void FixedWingLandingComplexItem::setCoordinate(const QGeoCoordinate& coordinate) +{ + if (_coordinate != coordinate) { + _coordinate = coordinate; + emit coordinateChanged(_coordinate); + } +} + +void FixedWingLandingComplexItem::setDirty(bool dirty) +{ + if (_dirty != dirty) { + _dirty = dirty; + emit dirtyChanged(_dirty); + } +} + +void FixedWingLandingComplexItem::save(QJsonObject& saveObject) const +{ + saveObject[JsonHelper::jsonVersionKey] = 1; + saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; + saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; + + // FIXME: Need real implementation +} + +void FixedWingLandingComplexItem::setSequenceNumber(int sequenceNumber) +{ + if (_sequenceNumber != sequenceNumber) { + _sequenceNumber = sequenceNumber; + emit sequenceNumberChanged(sequenceNumber); + emit lastSequenceNumberChanged(lastSequenceNumber()); + } +} + +bool FixedWingLandingComplexItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) +{ + // FIXME: Need real implementation + Q_UNUSED(complexObject); + Q_UNUSED(sequenceNumber); + + errorString = "NYI"; + return false; +} + +double FixedWingLandingComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const +{ + // FIXME: Need real implementation + Q_UNUSED(other); + + double greatestDistance = 0.0; + +#if 0 + for (int i=0; i<_gridPoints.count(); i++) { + QGeoCoordinate currentCoord = _gridPoints[i].value(); + double distance = currentCoord.distanceTo(other); + if (distance > greatestDistance) { + greatestDistance = distance; + } + } +#endif + + return greatestDistance; +} + +void FixedWingLandingComplexItem::_setExitCoordinate(const QGeoCoordinate& coordinate) +{ + if (_exitCoordinate != coordinate) { + _exitCoordinate = coordinate; + emit exitCoordinateChanged(coordinate); + } +} + +bool FixedWingLandingComplexItem::specifiesCoordinate(void) const +{ + return true; +} + +QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const +{ + // FIXME: Need real implementation + QmlObjectListModel* pMissionItems = new QmlObjectListModel; + +#if 0 + int seqNum = _sequenceNumber; + for (int i=0; i<_gridPoints.count(); i++) { + QGeoCoordinate coord = _gridPoints[i].value(); + double altitude = _gridAltitudeFact.rawValue().toDouble(); + + MissionItem* item = new MissionItem(seqNum++, // sequence number + MAV_CMD_NAV_WAYPOINT, // MAV_CMD + _gridAltitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, // MAV_FRAME + 0.0, 0.0, 0.0, 0.0, // param 1-4 + coord.latitude(), + coord.longitude(), + altitude, + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); + + if (_cameraTrigger && i == 0) { + // Turn on camera + MissionItem* item = new MissionItem(seqNum++, // sequence number + MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD + MAV_FRAME_MISSION, // MAV_FRAME + _cameraTriggerDistanceFact.rawValue().toDouble(), // trigger distance + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7 + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); + } + } + + if (_cameraTrigger) { + // Turn off camera + MissionItem* item = new MissionItem(seqNum++, // sequence number + MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD + MAV_FRAME_MISSION, // MAV_FRAME + 0.0, // trigger distance + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7 + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); + } +#endif + + return pMissionItems; +} + +double FixedWingLandingComplexItem::complexDistance(void) const +{ + // FIXME: Need real implementation + return 0; +} + +void FixedWingLandingComplexItem::setCruiseSpeed(double cruiseSpeed) +{ + // FIXME: Need real implementation + Q_UNUSED(cruiseSpeed); +} diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h new file mode 100644 index 000000000..0c9f76b43 --- /dev/null +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -0,0 +1,76 @@ +/**************************************************************************** + * + * (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 FixedWingLandingComplexItem_H +#define FixedWingLandingComplexItem_H + +#include "ComplexMissionItem.h" +#include "MissionItem.h" +#include "Fact.h" +#include "QGCLoggingCategory.h" + +Q_DECLARE_LOGGING_CATEGORY(FixedWingLandingComplexItemLog) + +class FixedWingLandingComplexItem : public ComplexMissionItem +{ + Q_OBJECT + +public: + FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent = NULL); + + // Overrides from ComplexMissionItem + + double complexDistance (void) const final; + int lastSequenceNumber (void) const final; + QmlObjectListModel* getMissionItems (void) const final; + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; + double greatestDistanceTo (const QGeoCoordinate &other) const final; + void setCruiseSpeed (double cruiseSpeed) final; + + // Overrides from VisualMissionItem + + bool dirty (void) const final { return _dirty; } + bool isSimpleItem (void) const final { return false; } + bool isStandaloneCoordinate (void) const final { return false; } + bool specifiesCoordinate (void) const final; + QString commandDescription (void) const final { return "Landing Pattern"; } + QString commandName (void) const final { return "Landing Pattern"; } + QString abbreviation (void) const final { return "L"; } + QGeoCoordinate coordinate (void) const final { return _coordinate; } + QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } + int sequenceNumber (void) const final { return _sequenceNumber; } + double flightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } + + bool coordinateHasRelativeAltitude (void) const final { return true; } + bool exitCoordinateHasRelativeAltitude (void) const final { return true; } + bool exitCoordinateSameAsEntry (void) const final { return true; } + + void setDirty (bool dirty) final; + void setCoordinate (const QGeoCoordinate& coordinate) final; + void setSequenceNumber (int sequenceNumber) final; + void save (QJsonObject& saveObject) const final; + + static const char* jsonComplexItemTypeValue; + +signals: + +private slots: + +private: + void _setExitCoordinate(const QGeoCoordinate& coordinate); + + int _sequenceNumber; + bool _dirty; + QGeoCoordinate _coordinate; + QGeoCoordinate _exitCoordinate; + + static QMap _metaDataMap; +}; + +#endif diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 71d2b6fff..70011b715 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -16,6 +16,7 @@ #include "QGCApplication.h" #include "SimpleMissionItem.h" #include "SurveyMissionItem.h" +#include "FixedWingLandingComplexItem.h" #include "JsonHelper.h" #include "ParameterManager.h" #include "QGroundControlQmlGlobal.h" @@ -59,7 +60,9 @@ MissionController::MissionController(QObject *parent) , _missionCruiseTime(0.0) , _missionMaxTelemetry(0.0) { - + _surveyMissionItemName = tr("Survey"); + _fwLandingMissionItemName = tr("Fixed Wing Landing"); + _complexMissionItemNames << _surveyMissionItemName << _fwLandingMissionItemName; } MissionController::~MissionController() @@ -218,12 +221,21 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) return newItem->sequenceNumber(); } -int MissionController::insertComplexMissionItem(QGeoCoordinate coordinate, int i) +int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i) { + ComplexMissionItem* newItem; + int sequenceNumber = _nextSequenceNumber(); - SurveyMissionItem* newItem = new SurveyMissionItem(_activeVehicle, _visualItems); + if (itemName == _surveyMissionItemName) { + newItem = new SurveyMissionItem(_activeVehicle, _visualItems); + } else if (itemName == _fwLandingMissionItemName) { + newItem = new FixedWingLandingComplexItem(_activeVehicle, _visualItems); + } else { + qWarning() << "Internal error: Unknown complex item:" << itemName; + return sequenceNumber; + } newItem->setSequenceNumber(sequenceNumber); - newItem->setCoordinate(coordinate); + newItem->setCoordinate(mapCenterCoordinate); _initVisualItem(newItem); _visualItems->insert(i, newItem); diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 5a341ad19..f1b45fb34 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -39,6 +39,8 @@ public: Q_PROPERTY(QmlObjectListModel* complexVisualItems READ complexVisualItems NOTIFY complexVisualItemsChanged) Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) + Q_PROPERTY(QStringList complexMissionItemNames MEMBER _complexMissionItemNames CONSTANT) + Q_PROPERTY(double missionDistance READ missionDistance NOTIFY missionDistanceChanged) Q_PROPERTY(double missionTime READ missionTime NOTIFY missionTimeChanged) Q_PROPERTY(double missionHoverDistance READ missionHoverDistance NOTIFY missionHoverDistanceChanged) @@ -55,9 +57,11 @@ public: Q_INVOKABLE int insertSimpleMissionItem(QGeoCoordinate coordinate, int i); /// Add a new complex mission item to the list + /// @param itemName: Name of complex item to create (from complexMissionItemNames) + /// @param mapCenterCoordinate: coordinate for current center of map /// @param i: index to insert at /// @return Sequence number for new item - Q_INVOKABLE int insertComplexMissionItem(QGeoCoordinate coordinate, int i); + Q_INVOKABLE int insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i); /// Loads the mission items from the specified file /// @param[in] vehicle Vehicle we are loading items for @@ -180,7 +184,10 @@ private: double _missionHoverTime; double _missionCruiseDistance; double _missionCruiseTime; - double _missionMaxTelemetry; + double _missionMaxTelemetry; + QString _surveyMissionItemName; + QString _fwLandingMissionItemName; + QStringList _complexMissionItemNames; static const char* _settingsGroup; static const char* _jsonFileTypeValue; diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 355a59c0a..5464ddfd3 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -64,6 +64,8 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent) , _syncingAltitudeRelativeToHomeAndFrame (false) , _syncingHeadingDegreesAndParam4 (false) { + _editorQml = "qrc:/qml/SimpleItemEditor.qml"; + _altitudeRelativeToHomeFact.setRawValue(true); _setupMetaData(); diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index 03a8f4356..954fc3cf2 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -86,6 +86,8 @@ SurveyMissionItem::SurveyMissionItem(Vehicle* vehicle, QObject* parent) , _cameraResolutionHeightFact (0, _cameraResolutionHeightFactName, FactMetaData::valueTypeUint32) , _cameraFocalLengthFact (0, _cameraFocalLengthFactName, FactMetaData::valueTypeDouble) { + _editorQml = "qrc:/qml/SurveyItemEditor.qml"; + if (_metaDataMap.isEmpty()) { _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/Survey.FactMetaData.json"), NULL /* metaDataParent */); } diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index ab2612b78..a4ad9419d 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -78,6 +78,7 @@ public: Q_PROPERTY(bool specifiesCoordinate READ specifiesCoordinate NOTIFY specifiesCoordinateChanged) ///< Item is associated with a coordinate position Q_PROPERTY(bool isStandaloneCoordinate READ isStandaloneCoordinate NOTIFY isStandaloneCoordinateChanged) ///< Waypoint line does not go through item Q_PROPERTY(bool isSimpleItem READ isSimpleItem NOTIFY isSimpleItemChanged) ///< Simple or Complex MissionItem + Q_PROPERTY(QString editorQml MEMBER _editorQml CONSTANT) ///< Qml code for editing this item /// List of child mission items. Child mission item are subsequent mision items which do not specify a coordinate. They /// are shown next to the exitCoordinate indidcator in the ui. @@ -161,6 +162,7 @@ protected: double _altPercent; ///< Percent of total altitude change in mission double _azimuth; ///< Azimuth to previous waypoint double _distance; ///< Distance to previous waypoint + QString _editorQml; ///< Qml resource for editing item /// This is used to reference any subsequent mission items which do not specify a coordinate. QmlObjectListModel _childItems; -- GitLab From e390054d922eec35cb7620a7fa2c91a907530f50 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 17 Feb 2017 23:04:19 -0500 Subject: [PATCH 297/398] Allow replacing instrument widget with custom widget --- .../FlightDisplayViewWidgets.qml | 51 ++++++++++++++----- src/api/QGCOptions.cc | 19 +++++++ src/api/QGCOptions.h | 32 ++++++++++++ src/ui/AppSettings.qml | 4 +- 4 files changed, 92 insertions(+), 14 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 4654da7ba..f6475d47d 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -15,12 +15,12 @@ import QtQuick.Dialogs 1.2 import QtLocation 5.3 import QtPositioning 5.2 -import QGroundControl 1.0 -import QGroundControl.ScreenTools 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.Palette 1.0 -import QGroundControl.Vehicle 1.0 -import QGroundControl.FlightMap 1.0 +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.FlightMap 1.0 Item { id: _root @@ -48,13 +48,29 @@ Item { } function _setInstrumentWidget() { - var useAlternateInstruments = QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen - if(useAlternateInstruments) { - instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" - instrumentsLoader.state = "topMode" + if(QGroundControl.corePlugin.options.instrumentWidget.source.toString().length) { + instrumentsLoader.source = QGroundControl.corePlugin.options.instrumentWidget.source + switch(QGroundControl.corePlugin.options.instrumentWidget.widgetPosition) { + case CustomInstrumentWidget.POS_TOP_RIGHT: + instrumentsLoader.state = "topMode" + break; + case CustomInstrumentWidget.POS_BOTTOM_RIGHT: + instrumentsLoader.state = "bottomMode" + break; + case CustomInstrumentWidget.POS_CENTER_RIGHT: + default: + instrumentsLoader.state = "centerMode" + break; + } } else { - instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidget.qml" - instrumentsLoader.state = "centerMode" + var useAlternateInstruments = QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen + if(useAlternateInstruments) { + instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" + instrumentsLoader.state = "topMode" + } else { + instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidget.qml" + instrumentsLoader.state = "centerMode" + } } } @@ -108,6 +124,7 @@ Item { AnchorChanges { target: instrumentsLoader anchors.verticalCenter: undefined + anchors.bottom: undefined anchors.top: _root ? _root.top : undefined } }, @@ -116,8 +133,18 @@ Item { AnchorChanges { target: instrumentsLoader anchors.top: undefined + anchors.bottom: undefined anchors.verticalCenter: _root ? _root.verticalCenter : undefined } + }, + State { + name: "bottomMode" + AnchorChanges { + target: instrumentsLoader + anchors.top: undefined + anchors.verticalCenter: undefined + anchors.bottom: _root ? _root.bottom : undefined + } } ] } diff --git a/src/api/QGCOptions.cc b/src/api/QGCOptions.cc index 3b598fe3d..0955cec12 100644 --- a/src/api/QGCOptions.cc +++ b/src/api/QGCOptions.cc @@ -8,6 +8,7 @@ ****************************************************************************/ #include "QGCOptions.h" +#include /// @file /// @brief Core Plugin Interface for QGroundControl - Application Options @@ -15,5 +16,23 @@ QGCOptions::QGCOptions(QObject* parent) : QObject(parent) + , _defaultInstrumentWidget(NULL) { + qmlRegisterUncreatableType("QGroundControl", 1, 0, "CustomInstrumentWidget", "Reference only"); } + +CustomInstrumentWidget* +QGCOptions::instrumentWidget() +{ + if(!_defaultInstrumentWidget) { + _defaultInstrumentWidget = new CustomInstrumentWidget(this); + } + return _defaultInstrumentWidget; +} + +CustomInstrumentWidget::CustomInstrumentWidget(QObject* parent) + : QObject(parent) +{ +} + + diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index 1845d3abb..dbe141d4f 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -11,11 +11,13 @@ #include #include +#include /// @file /// @brief Core Plugin Interface for QGroundControl - Application Options /// @author Gus Grubba +class CustomInstrumentWidget; class QGCOptions : public QObject { Q_OBJECT @@ -32,6 +34,8 @@ public: Q_PROPERTY(double toolbarHeightMultiplier READ toolbarHeightMultiplier CONSTANT) Q_PROPERTY(double defaultFontPointSize READ defaultFontPointSize CONSTANT) Q_PROPERTY(bool enablePlanViewSelector READ enablePlanViewSelector CONSTANT) + Q_PROPERTY(CustomInstrumentWidget* instrumentWidget READ instrumentWidget CONSTANT) + //! Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? /*! @@ -83,4 +87,32 @@ public: @return True or false */ virtual bool enablePlanViewSelector () { return true; } + //! Provides an alternate instrument widget for the Fly View + /*! + @return An alternate widget (see QGCInstrumentWidget.qml, the default widget) + */ + virtual CustomInstrumentWidget* instrumentWidget(); +private: + CustomInstrumentWidget* _defaultInstrumentWidget; +}; + +//----------------------------------------------------------------------------- +class CustomInstrumentWidget : public QObject +{ + Q_OBJECT +public: + //-- Widget Position + enum Pos { + POS_TOP_RIGHT = 0, + POS_CENTER_RIGHT = 1, + POS_BOTTOM_RIGHT = 2, + }; + Q_ENUMS(Pos) + CustomInstrumentWidget(QObject* parent = NULL); + Q_PROPERTY(QUrl source READ source CONSTANT) + Q_PROPERTY(Pos widgetPosition READ widgetPosition NOTIFY widgetPositionChanged) + virtual QUrl source () { return QUrl(); } + virtual Pos widgetPosition () { return POS_CENTER_RIGHT; } +signals: + void widgetPositionChanged (); }; diff --git a/src/ui/AppSettings.qml b/src/ui/AppSettings.qml index 337aac725..5599e0252 100644 --- a/src/ui/AppSettings.qml +++ b/src/ui/AppSettings.qml @@ -35,8 +35,8 @@ Rectangle { QGCPalette { id: qgcPal } Component.onCompleted: { - //-- Default to General Settings - __rightPanel.source = "GeneralSettings.qml" + //-- Default Settings + __rightPanel.source = QGroundControl.corePlugin.settings[QGroundControl.corePlugin.defaltSettings].url } QGCFlickable { -- GitLab From f5705bedc8d7e04b049e918b6f63af2d9b66ed4d Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 17 Feb 2017 23:05:03 -0500 Subject: [PATCH 298/398] Removing dead weight. This entire controller should be removed and the functionality moved to Vehicle. --- src/ui/toolbar/MainToolBarController.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h index c8f42b284..bb167cdcb 100644 --- a/src/ui/toolbar/MainToolBarController.h +++ b/src/ui/toolbar/MainToolBarController.h @@ -22,13 +22,6 @@ #include "Vehicle.h" #include "UASMessageView.h" -#define TOOL_BAR_SETTINGS_GROUP "TOOLBAR_SETTINGS_GROUP" -#define TOOL_BAR_SHOW_BATTERY "ShowBattery" -#define TOOL_BAR_SHOW_GPS "ShowGPS" -#define TOOL_BAR_SHOW_MAV "ShowMav" -#define TOOL_BAR_SHOW_MESSAGES "ShowMessages" -#define TOOL_BAR_SHOW_RSSI "ShowRSSI" - class MainToolBarController : public QObject { Q_OBJECT @@ -45,8 +38,6 @@ public: Q_PROPERTY(unsigned int telemetryLNoise READ telemetryLNoise NOTIFY telemetryLNoiseChanged) Q_PROPERTY(unsigned int telemetryRNoise READ telemetryRNoise NOTIFY telemetryRNoiseChanged) - void viewStateChanged (const QString& key, bool value); - int telemetryRRSSI () { return _telemetryRRSSI; } int telemetryLRSSI () { return _telemetryLRSSI; } unsigned int telemetryRXErrors () { return _telemetryRXErrors; } @@ -58,7 +49,6 @@ public: signals: void telemetryRRSSIChanged (int value); void telemetryLRSSIChanged (int value); - void heightChanged (double height); void telemetryRXErrorsChanged (unsigned int value); void telemetryFixedChanged (unsigned int value); void telemetryTXBufferChanged (unsigned int value); @@ -81,8 +71,6 @@ private: uint32_t _telemetryLNoise; uint32_t _telemetryRNoise; - QStringList _toolbarMessageQueue; - QMutex _toolbarMessageQueueMutex; }; #endif // MainToolBarController_H -- GitLab From 6dc57634747a8ea0ae8ed68045e9be7592a717de Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 17 Feb 2017 23:05:43 -0500 Subject: [PATCH 299/398] Fix for custom plugin to access code within QGC --- qgroundcontrol.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 3ef7283da..6c2c893ac 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -21,6 +21,7 @@ include(QGCCommon.pri) TARGET = QGroundControl TEMPLATE = app +QGCROOT = $$PWD DebugBuild { DESTDIR = $${OUT_PWD}/debug -- GitLab From 075b5ec55fc9199495a734385f02ca59f476b722 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 17 Feb 2017 23:06:23 -0500 Subject: [PATCH 300/398] Settings interface should define the default settings --- src/api/QGCCorePlugin.cc | 5 +++++ src/api/QGCCorePlugin.h | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index a530547dd..89eac8c4c 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -126,6 +126,11 @@ QVariantList &QGCCorePlugin::settings() return _p->settingsList; } +int QGCCorePlugin::defaltSettings() +{ + return 0; +} + QGCOptions* QGCCorePlugin::options() { if(!_p->defaultOptions) { diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 9d1a1afa0..492da621d 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -32,20 +32,27 @@ public: QGCCorePlugin(QGCApplication* app); ~QGCCorePlugin(); - Q_PROPERTY(QVariantList settings READ settings CONSTANT) - Q_PROPERTY(QGCOptions* options READ options CONSTANT) + Q_PROPERTY(QVariantList settings READ settings CONSTANT) + Q_PROPERTY(int defaltSettings READ defaltSettings CONSTANT) + Q_PROPERTY(QGCOptions* options READ options CONSTANT) //! The list of settings under the Settings Menu /*! @return A list of QGCSettings */ - virtual QVariantList& settings (); + virtual QVariantList& settings (); + + //! The default settings panel to show + /*! + @return The settings index + */ + virtual int defaltSettings (); //! Global options /*! @return An instance of QGCOptions */ - virtual QGCOptions* options (); + virtual QGCOptions* options (); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); -- GitLab From e32333218740023cba4382f7cda89e066e969ce7 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 18 Feb 2017 08:53:39 -0500 Subject: [PATCH 301/398] Removing toolbar controller --- qgroundcontrol.pro | 2 - src/QGCApplication.cc | 2 - src/Vehicle/Vehicle.cc | 43 ++++++++++- src/Vehicle/Vehicle.h | 51 ++++++++++--- src/ui/toolbar/MainToolBar.qml | 2 - src/ui/toolbar/MainToolBarController.cc | 92 ------------------------ src/ui/toolbar/MainToolBarController.h | 76 -------------------- src/ui/toolbar/MainToolBarIndicators.qml | 16 ++--- 8 files changed, 89 insertions(+), 195 deletions(-) delete mode 100644 src/ui/toolbar/MainToolBarController.cc delete mode 100644 src/ui/toolbar/MainToolBarController.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 6c2c893ac..90d18be9e 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -487,7 +487,6 @@ HEADERS += \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ - src/ui/toolbar/MainToolBarController.h \ DebugBuild { HEADERS += \ @@ -647,7 +646,6 @@ SOURCES += \ src/main.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ - src/ui/toolbar/MainToolBarController.cc \ DebugBuild { SOURCES += \ diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index d1d0f4deb..e3d48f20d 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -64,7 +64,6 @@ #include "QGroundControlQmlGlobal.h" #include "FlightMapSettings.h" #include "CoordinateVector.h" -#include "MainToolBarController.h" #include "MissionController.h" #include "GeoFenceController.h" #include "RallyPointController.h" @@ -378,7 +377,6 @@ void QGCApplication::_initCommon(void) qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ParameterEditorController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ScreenToolsController"); - qmlRegisterType ("QGroundControl.Controllers", 1, 0, "MainToolBarController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "MissionController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "GeoFenceController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "RallyPointController"); diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 6c2fc34d4..349e3054c 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -101,6 +101,13 @@ Vehicle::Vehicle(LinkInterface* link, , _globalPositionIntMessageAvailable(false) , _cruiseSpeed(QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->rawValue().toDouble()) , _hoverSpeed(QGroundControlQmlGlobal::offlineEditingHoverSpeed()->rawValue().toDouble()) + , _telemetryRRSSI(0) + , _telemetryLRSSI(0) + , _telemetryRXErrors(0) + , _telemetryFixed(0) + , _telemetryTXBuffer(0) + , _telemetryLNoise(0) + , _telemetryRNoise(0) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -148,6 +155,7 @@ Vehicle::Vehicle(LinkInterface* link, _mavlink = qgcApp()->toolbox()->mavlinkProtocol(); connect(_mavlink, &MAVLinkProtocol::messageReceived, this, &Vehicle::_mavlinkMessageReceived); + connect(_mavlink, &MAVLinkProtocol::radioStatusChanged, this, &Vehicle::_telemetryChanged); connect(this, &Vehicle::_sendMessageOnLinkOnThread, this, &Vehicle::_sendMessageOnLink, Qt::QueuedConnection); connect(this, &Vehicle::flightModeChanged, this, &Vehicle::_handleFlightModeChanged); @@ -408,6 +416,37 @@ void Vehicle::resetCounters() _heardFrom = false; } +void Vehicle::_telemetryChanged(LinkInterface*, unsigned rxerrors, unsigned fixed, int rssi, int remrssi, unsigned txbuf, unsigned noise, unsigned remnoise) +{ + if(_telemetryLRSSI != rssi) { + _telemetryLRSSI = rssi; + emit telemetryLRSSIChanged(_telemetryLRSSI); + } + if(_telemetryRRSSI != remrssi) { + _telemetryRRSSI = remrssi; + emit telemetryRRSSIChanged(_telemetryRRSSI); + } + if(_telemetryRXErrors != rxerrors) { + _telemetryRXErrors = rxerrors; + emit telemetryRXErrorsChanged(_telemetryRXErrors); + } + if(_telemetryFixed != fixed) { + _telemetryFixed = fixed; + emit telemetryFixedChanged(_telemetryFixed); + } + if(_telemetryTXBuffer != txbuf) { + _telemetryTXBuffer = txbuf; + emit telemetryTXBufferChanged(_telemetryTXBuffer); + } + if(_telemetryLNoise != noise) { + _telemetryLNoise = noise; + emit telemetryLNoiseChanged(_telemetryLNoise); + } + if(_telemetryRNoise != remnoise) { + _telemetryRNoise = remnoise; + emit telemetryRNoiseChanged(_telemetryRNoise); + } +} void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message) { @@ -1306,8 +1345,8 @@ QStringList Vehicle::joystickModes(void) void Vehicle::_activeJoystickChanged(void) { - _loadSettings(); - _startJoystick(true); + _loadSettings(); + _startJoystick(true); } bool Vehicle::joystickEnabled(void) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 95e0da21f..29d8b1350 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -268,6 +268,13 @@ public: Q_PROPERTY(QString vehicleImageOpaque READ vehicleImageOpaque CONSTANT) Q_PROPERTY(QString vehicleImageOutline READ vehicleImageOutline CONSTANT) Q_PROPERTY(QString vehicleImageCompass READ vehicleImageCompass CONSTANT) + Q_PROPERTY(int telemetryRRSSI READ telemetryRRSSI NOTIFY telemetryRRSSIChanged) + Q_PROPERTY(int telemetryLRSSI READ telemetryLRSSI NOTIFY telemetryLRSSIChanged) + Q_PROPERTY(unsigned int telemetryRXErrors READ telemetryRXErrors NOTIFY telemetryRXErrorsChanged) + Q_PROPERTY(unsigned int telemetryFixed READ telemetryFixed NOTIFY telemetryFixedChanged) + Q_PROPERTY(unsigned int telemetryTXBuffer READ telemetryTXBuffer NOTIFY telemetryTXBufferChanged) + Q_PROPERTY(unsigned int telemetryLNoise READ telemetryLNoise NOTIFY telemetryLNoiseChanged) + Q_PROPERTY(unsigned int telemetryRNoise READ telemetryRNoise NOTIFY telemetryRNoiseChanged) /// true: Vehicle is flying, false: Vehicle is on ground Q_PROPERTY(bool flying READ flying WRITE setFlying NOTIFY flyingChanged) @@ -525,7 +532,14 @@ public: double cruiseSpeed () const { return _cruiseSpeed; } double hoverSpeed () const { return _hoverSpeed; } QString firmwareTypeString () const; - QString vehicleTypeString () const; + QString vehicleTypeString () const; + int telemetryRRSSI () { return _telemetryRRSSI; } + int telemetryLRSSI () { return _telemetryLRSSI; } + unsigned int telemetryRXErrors () { return _telemetryRXErrors; } + unsigned int telemetryFixed () { return _telemetryFixed; } + unsigned int telemetryTXBuffer () { return _telemetryTXBuffer; } + unsigned int telemetryLNoise () { return _telemetryLNoise; } + unsigned int telemetryRNoise () { return _telemetryRNoise; } Fact* roll (void) { return &_rollFact; } Fact* heading (void) { return &_headingFact; } @@ -632,16 +646,23 @@ signals: /// Used internally to move sendMessage call to main thread void _sendMessageOnLinkOnThread(LinkInterface* link, mavlink_message_t message); - void messageTypeChanged (); - void newMessageCountChanged (); - void messageCountChanged (); - void formatedMessagesChanged(); - void formatedMessageChanged (); - void latestErrorChanged (); - void longitudeChanged (); - void currentConfigChanged (); - void flowImageIndexChanged (); - void rcRSSIChanged (int rcRSSI); + void messageTypeChanged (); + void newMessageCountChanged (); + void messageCountChanged (); + void formatedMessagesChanged (); + void formatedMessageChanged (); + void latestErrorChanged (); + void longitudeChanged (); + void currentConfigChanged (); + void flowImageIndexChanged (); + void rcRSSIChanged (int rcRSSI); + void telemetryRRSSIChanged (int value); + void telemetryLRSSIChanged (int value); + void telemetryRXErrorsChanged (unsigned int value); + void telemetryFixedChanged (unsigned int value); + void telemetryTXBufferChanged (unsigned int value); + void telemetryLNoiseChanged (unsigned int value); + void telemetryRNoiseChanged (unsigned int value); void firmwareMajorVersionChanged(int major); void firmwareMinorVersionChanged(int minor); @@ -674,6 +695,7 @@ signals: private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); + void _telemetryChanged(LinkInterface* link, unsigned rxerrors, unsigned fixed, int rssi, int remrssi, unsigned txbuf, unsigned noise, unsigned remnoise); void _linkInactiveOrDeleted(LinkInterface* link); void _sendMessageOnLink(LinkInterface* link, mavlink_message_t message); void _sendMessageMultipleNext(void); @@ -789,6 +811,13 @@ private: bool _globalPositionIntMessageAvailable; double _cruiseSpeed; double _hoverSpeed; + int _telemetryRRSSI; + int _telemetryLRSSI; + uint32_t _telemetryRXErrors; + uint32_t _telemetryFixed; + uint32_t _telemetryTXBuffer; + uint32_t _telemetryLNoise; + uint32_t _telemetryRNoise; typedef struct { int component; diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index dee03d8f5..8f656ceea 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -42,8 +42,6 @@ Rectangle { signal showFlyView signal showAnalyzeView - MainToolBarController { id: _controller } - function checkSettingsButton() { settingsButton.checked = true } diff --git a/src/ui/toolbar/MainToolBarController.cc b/src/ui/toolbar/MainToolBarController.cc deleted file mode 100644 index 5001be8e3..000000000 --- a/src/ui/toolbar/MainToolBarController.cc +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/** - * @file - * @brief QGC Main Tool Bar - * @author Gus Grubba - */ - -#include -#include - -#include "MainToolBarController.h" -#include "ScreenToolsController.h" -#include "UASMessageView.h" -#include "UASMessageHandler.h" -#include "QGCApplication.h" -#include "MultiVehicleManager.h" -#include "UAS.h" -#include "ParameterManager.h" - -MainToolBarController::MainToolBarController(QObject* parent) - : QObject(parent) - , _vehicle(NULL) - , _mav(NULL) - , _telemetryRRSSI(0) - , _telemetryLRSSI(0) -{ - _activeVehicleChanged(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()); - connect(qgcApp()->toolbox()->mavlinkProtocol(), &MAVLinkProtocol::radioStatusChanged, this, &MainToolBarController::_telemetryChanged); - connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &MainToolBarController::_activeVehicleChanged); -} - -MainToolBarController::~MainToolBarController() -{ - -} - -void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle) -{ - // Disconnect the previous one (if any) - if (_vehicle) { - _mav = NULL; - _vehicle = NULL; - } - - // Connect new system - if (vehicle) - { - _vehicle = vehicle; - _mav = vehicle->uas(); - } -} - -void MainToolBarController::_telemetryChanged(LinkInterface*, unsigned rxerrors, unsigned fixed, int rssi, int remrssi, unsigned txbuf, unsigned noise, unsigned remnoise) -{ - if(_telemetryLRSSI != rssi) { - _telemetryLRSSI = rssi; - emit telemetryLRSSIChanged(_telemetryLRSSI); - } - if(_telemetryRRSSI != remrssi) { - _telemetryRRSSI = remrssi; - emit telemetryRRSSIChanged(_telemetryRRSSI); - } - if(_telemetryRXErrors != rxerrors) { - _telemetryRXErrors = rxerrors; - emit telemetryRXErrorsChanged(_telemetryRXErrors); - } - if(_telemetryFixed != fixed) { - _telemetryFixed = fixed; - emit telemetryFixedChanged(_telemetryFixed); - } - if(_telemetryTXBuffer != txbuf) { - _telemetryTXBuffer = txbuf; - emit telemetryTXBufferChanged(_telemetryTXBuffer); - } - if(_telemetryLNoise != noise) { - _telemetryLNoise = noise; - emit telemetryLNoiseChanged(_telemetryLNoise); - } - if(_telemetryRNoise != remnoise) { - _telemetryRNoise = remnoise; - emit telemetryRNoiseChanged(_telemetryRNoise); - } -} diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h deleted file mode 100644 index bb167cdcb..000000000 --- a/src/ui/toolbar/MainToolBarController.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/** - * @file - * @brief QGC Main Tool Bar - * @author Gus Grubba - */ - -#ifndef MainToolBarController_H -#define MainToolBarController_H - -#include - -#include "Vehicle.h" -#include "UASMessageView.h" - -class MainToolBarController : public QObject -{ - Q_OBJECT - -public: - MainToolBarController(QObject* parent = NULL); - ~MainToolBarController(); - - Q_PROPERTY(int telemetryRRSSI READ telemetryRRSSI NOTIFY telemetryRRSSIChanged) - Q_PROPERTY(int telemetryLRSSI READ telemetryLRSSI NOTIFY telemetryLRSSIChanged) - Q_PROPERTY(unsigned int telemetryRXErrors READ telemetryRXErrors NOTIFY telemetryRXErrorsChanged) - Q_PROPERTY(unsigned int telemetryFixed READ telemetryFixed NOTIFY telemetryFixedChanged) - Q_PROPERTY(unsigned int telemetryTXBuffer READ telemetryTXBuffer NOTIFY telemetryTXBufferChanged) - Q_PROPERTY(unsigned int telemetryLNoise READ telemetryLNoise NOTIFY telemetryLNoiseChanged) - Q_PROPERTY(unsigned int telemetryRNoise READ telemetryRNoise NOTIFY telemetryRNoiseChanged) - - int telemetryRRSSI () { return _telemetryRRSSI; } - int telemetryLRSSI () { return _telemetryLRSSI; } - unsigned int telemetryRXErrors () { return _telemetryRXErrors; } - unsigned int telemetryFixed () { return _telemetryFixed; } - unsigned int telemetryTXBuffer () { return _telemetryTXBuffer; } - unsigned int telemetryLNoise () { return _telemetryLNoise; } - unsigned int telemetryRNoise () { return _telemetryRNoise; } - -signals: - void telemetryRRSSIChanged (int value); - void telemetryLRSSIChanged (int value); - void telemetryRXErrorsChanged (unsigned int value); - void telemetryFixedChanged (unsigned int value); - void telemetryTXBufferChanged (unsigned int value); - void telemetryLNoiseChanged (unsigned int value); - void telemetryRNoiseChanged (unsigned int value); - -private slots: - void _activeVehicleChanged (Vehicle* vehicle); - void _telemetryChanged (LinkInterface* link, unsigned rxerrors, unsigned fixed, int rssi, int remrssi, unsigned txbuf, unsigned noise, unsigned remnoise); - -private: - Vehicle* _vehicle; - UASInterface* _mav; - double _remoteRSSIstore; - int _telemetryRRSSI; - int _telemetryLRSSI; - uint32_t _telemetryRXErrors; - uint32_t _telemetryFixed; - uint32_t _telemetryTXBuffer; - uint32_t _telemetryLNoise; - uint32_t _telemetryRNoise; - -}; - -#endif // MainToolBarController_H diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index 73c339325..9a0d30b57 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -273,19 +273,19 @@ Item { anchors.horizontalCenter: parent.horizontalCenter QGCLabel { text: qsTr("Local RSSI:") } - QGCLabel { text: _controller.telemetryLRSSI + " dBm" } + QGCLabel { text: _activeVehicle.telemetryLRSSI + " dBm" } QGCLabel { text: qsTr("Remote RSSI:") } - QGCLabel { text: _controller.telemetryRRSSI + " dBm" } + QGCLabel { text: _activeVehicle.telemetryRRSSI + " dBm" } QGCLabel { text: qsTr("RX Errors:") } - QGCLabel { text: _controller.telemetryRXErrors } + QGCLabel { text: _activeVehicle.telemetryRXErrors } QGCLabel { text: qsTr("Errors Fixed:") } - QGCLabel { text: _controller.telemetryFixed } + QGCLabel { text: _activeVehicle.telemetryFixed } QGCLabel { text: qsTr("TX Buffer:") } - QGCLabel { text: _controller.telemetryTXBuffer } + QGCLabel { text: _activeVehicle.telemetryTXBuffer } QGCLabel { text: qsTr("Local Noise:") } - QGCLabel { text: _controller.telemetryLNoise } + QGCLabel { text: _activeVehicle.telemetryLNoise } QGCLabel { text: qsTr("Remote Noise:") } - QGCLabel { text: _controller.telemetryRNoise } + QGCLabel { text: _activeVehicle.telemetryRNoise } } } @@ -440,7 +440,7 @@ Item { source: "/qmlimages/TelemRSSI.svg" fillMode: Image.PreserveAspectFit color: qgcPal.buttonText - visible: _controller.telemetryLRSSI < 0 + visible: _activeVehicle ? (_activeVehicle.telemetryLRSSI < 0) : false MouseArea { anchors.fill: parent -- GitLab From 616e11370b926cf7dcd24b88116e5609a436a751 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 11:59:50 -0800 Subject: [PATCH 302/398] Add/use new SettingsManager implementation * Holds all the settings for the app * Allows core plugin to override default values --- qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 2 +- src/FactSystem/FactMetaData.cc | 41 ++--- src/FactSystem/SettingsFact.cc | 11 +- src/FactSystem/SettingsFact.h | 2 +- src/FlightMap/MapScale.qml | 9 +- src/MissionEditor/MissionSettingsEditor.qml | 23 +-- src/MissionManager/MissionCommandTree.cc | 8 +- src/MissionManager/MissionCommandTree.h | 4 +- src/MissionManager/MissionController.cc | 8 +- src/MissionManager/RallyPointController.cc | 3 +- src/MissionManager/SimpleMissionItem.cc | 3 +- src/MissionManager/SimpleMissionItemTest.cc | 5 +- src/QGCToolbox.cc | 8 + src/QGCToolbox.h | 3 + src/QmlControls/QGroundControlQmlGlobal.cc | 151 +--------------- src/QmlControls/QGroundControlQmlGlobal.h | 95 ++-------- src/SettingsManager.cc | 170 ++++++++++++++++++ src/SettingsManager.h | 112 ++++++++++++ ...rolQmlGlobal.json => SettingsManager.json} | 0 src/Vehicle/MultiVehicleManager.cc | 6 +- src/Vehicle/Vehicle.cc | 23 ++- src/Vehicle/Vehicle.h | 2 + src/api/QGCCorePlugin.cc | 7 + src/api/QGCCorePlugin.h | 6 + src/ui/preferences/GeneralSettings.qml | 11 +- 26 files changed, 424 insertions(+), 291 deletions(-) create mode 100644 src/SettingsManager.cc create mode 100644 src/SettingsManager.h rename src/{QmlControls/QGroundControlQmlGlobal.json => SettingsManager.json} (100%) diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 955bc2fef..71fe844ff 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -474,6 +474,7 @@ HEADERS += \ src/QmlControls/RCChannelMonitorController.h \ src/QmlControls/ScreenToolsController.h \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ + src/SettingsManager.h \ src/Vehicle/MAVLinkLogManager.h \ src/VehicleSetup/JoystickConfigController.h \ src/audio/QGCAudioWorker.h \ @@ -635,6 +636,7 @@ SOURCES += \ src/QmlControls/RCChannelMonitorController.cc \ src/QmlControls/ScreenToolsController.cc \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ + src/SettingsManager.cc \ src/Vehicle/MAVLinkLogManager.cc \ src/VehicleSetup/JoystickConfigController.cc \ src/audio/QGCAudioWorker.cpp \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 48fdc1462..8e9e8fc77 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -168,7 +168,7 @@ src/Vehicle/GPSFact.json src/Vehicle/WindFact.json src/Vehicle/VibrationFact.json - src/QmlControls/QGroundControlQmlGlobal.json + src/SettingsManager.json src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index 51fe0d09f..6d9f47f58 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -14,8 +14,9 @@ /// @author Don Gagne #include "FactMetaData.h" -#include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" #include "JsonHelper.h" +#include "QGCApplication.h" #include #include @@ -45,21 +46,21 @@ const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] // Translations driven by app settings const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = { - { "m", "m", false, QGroundControlQmlGlobal::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "meters", "meters", false, QGroundControlQmlGlobal::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m/s", "m/s", true, QGroundControlQmlGlobal::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m^2", "m^2", false, QGroundControlQmlGlobal::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m", "ft", false, QGroundControlQmlGlobal::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "meters", "ft", false, QGroundControlQmlGlobal::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m^2", "km^2", false, QGroundControlQmlGlobal::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, - { "m^2", "ha", false, QGroundControlQmlGlobal::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, - { "m^2", "ft^2", false, QGroundControlQmlGlobal::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, - { "m^2", "ac", false, QGroundControlQmlGlobal::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, - { "m^2", "mi^2", false, QGroundControlQmlGlobal::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, - { "m/s", "ft/s", true, QGroundControlQmlGlobal::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m/s", "mph", true, QGroundControlQmlGlobal::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, - { "m/s", "km/h", true, QGroundControlQmlGlobal::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, - { "m/s", "kn", true, QGroundControlQmlGlobal::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, + { "m", "m", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "meters", "meters", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m/s", "m/s", true, SettingsManager::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m^2", "m^2", false, SettingsManager::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "meters", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m^2", "km^2", false, SettingsManager::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, + { "m^2", "ha", false, SettingsManager::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, + { "m^2", "ft^2", false, SettingsManager::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, + { "m^2", "ac", false, SettingsManager::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, + { "m^2", "mi^2", false, SettingsManager::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, + { "m/s", "ft/s", true, SettingsManager::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m/s", "mph", true, SettingsManager::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, + { "m/s", "km/h", true, SettingsManager::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, + { "m/s", "kn", true, SettingsManager::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, }; const char* FactMetaData::_decimalPlacesJsonKey = "decimalPlaces"; @@ -612,8 +613,8 @@ void FactMetaData::_setAppSettingsTranslators(void) const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == _rawUnits.toLower() && - ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::speedUnits()->rawValue().toUInt()) || - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt()))) { + ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->speedUnits()->rawValue().toUInt()) || + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt()))) { _cookedUnits = pAppSettingsTranslation->cookedUnits; setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator); return; @@ -628,7 +629,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt())) { + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt())) { return pAppSettingsTranslation; } } @@ -642,7 +643,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::areaUnits()->rawValue().toUInt()) + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->areaUnits()->rawValue().toUInt()) ) { return pAppSettingsTranslation; } diff --git a/src/FactSystem/SettingsFact.cc b/src/FactSystem/SettingsFact.cc index a3fb1821b..71b2ddca1 100644 --- a/src/FactSystem/SettingsFact.cc +++ b/src/FactSystem/SettingsFact.cc @@ -9,6 +9,8 @@ #include "SettingsFact.h" +#include "QGCCorePlugin.h" +#include "QGCApplication.h" #include @@ -18,8 +20,8 @@ SettingsFact::SettingsFact(QObject* parent) } -SettingsFact::SettingsFact(QString settingGroup, QString settingName, FactMetaData::ValueType_t type, const QVariant& defaultValue, QObject* parent) - : Fact(0, settingName, type, parent) +SettingsFact::SettingsFact(QString settingGroup, FactMetaData* metaData, QObject* parent) + : Fact(0, metaData->name(), metaData->type(), parent) , _settingGroup(settingGroup) { QSettings settings; @@ -28,7 +30,10 @@ SettingsFact::SettingsFact(QString settingGroup, QString settingName, FactMetaDa settings.beginGroup(_settingGroup); } - _rawValue = settings.value(_name, defaultValue); + // Allow core plugin a chance to override the default value + metaData->setRawDefaultValue(qgcApp()->toolbox()->corePlugin()->overrideSettingsDefault(metaData->name(), metaData->rawDefaultValue())); + setMetaData(metaData); + _rawValue = settings.value(_name, metaData->rawDefaultValue()); connect(this, &Fact::rawValueChanged, this, &SettingsFact::_rawValueChanged); } diff --git a/src/FactSystem/SettingsFact.h b/src/FactSystem/SettingsFact.h index 70582454a..0984d4764 100644 --- a/src/FactSystem/SettingsFact.h +++ b/src/FactSystem/SettingsFact.h @@ -23,7 +23,7 @@ class SettingsFact : public Fact public: SettingsFact(QObject* parent = NULL); - SettingsFact(QString settingGroup, QString settingName, FactMetaData::ValueType_t type, const QVariant& defaultValue, QObject* parent = NULL); + SettingsFact(QString settingGroup, FactMetaData* metaData, QObject* parent = NULL); SettingsFact(const SettingsFact& other, QObject* parent = NULL); const SettingsFact& operator=(const SettingsFact& other); diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index 26a5dfb6f..a861e41d5 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -10,9 +10,10 @@ import QtQuick 2.4 import QtQuick.Controls 1.3 -import QGroundControl 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.SettingsManager 1.0 /// Map scale control Item { @@ -114,7 +115,7 @@ Item { var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, scale.y)) var scaleLineMeters = Math.round(leftCoord.distanceTo(rightCoord)) - if (QGroundControl.distanceUnits.value == QGroundControl.DistanceUnitsFeet) { + if (QGroundControl.settingsManager.distanceUnits.value == QGroundControl.settingsManager.DistanceUnitsFeet) { calculateFeetRatio(scaleLineMeters, scaleLinePixelLength) } else { calculateMetersRatio(scaleLineMeters, scaleLinePixelLength) diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index a7805187f..3e711443a 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -2,12 +2,13 @@ import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Layouts 1.2 -import QGroundControl 1.0 -import QGroundControl.ScreenTools 1.0 -import QGroundControl.Vehicle 1.0 -import QGroundControl.Controls 1.0 -import QGroundControl.FactControls 1.0 -import QGroundControl.Palette 1.0 +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.SettingsManager 1.0 // Editor for Mission Settings Rectangle { @@ -121,7 +122,7 @@ Rectangle { Layout.fillWidth: true } FactComboBox { - fact: QGroundControl.offlineEditingFirmwareType + fact: QGroundControl.settingsManager.offlineEditingFirmwareType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -145,7 +146,7 @@ Rectangle { } FactComboBox { id: offlineVehicleCombo - fact: QGroundControl.offlineEditingVehicleType + fact: QGroundControl.settingsManager.offlineEditingVehicleType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -169,7 +170,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.offlineEditingCruiseSpeed + fact: QGroundControl.settingsManager.offlineEditingCruiseSpeed visible: _showCruiseSpeed Layout.preferredWidth: _fieldWidth } @@ -181,7 +182,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.offlineEditingHoverSpeed visible: _showHoverSpeed Layout.preferredWidth: _fieldWidth } @@ -195,7 +196,7 @@ Rectangle { QGCLabel { text: qsTr("Hover speed:"); Layout.fillWidth: true } FactTextField { Layout.preferredWidth: _fieldWidth - fact: QGroundControl.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.offlineEditingHoverSpeed } } diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index 1a261c18f..31f69f52f 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -17,11 +17,13 @@ #include "QGroundControlQmlGlobal.h" #include "MissionCommandUIInfo.h" #include "MissionCommandList.h" +#include "SettingsManager.h" #include MissionCommandTree::MissionCommandTree(QGCApplication* app, bool unitTest) : QGCTool(app) + , _settingsManager(NULL) , _unitTest(unitTest) { } @@ -30,6 +32,8 @@ void MissionCommandTree::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); + _settingsManager = toolbox->settingsManager(); + #ifdef UNITTEST_BUILD if (_unitTest) { // Load unit testing tree @@ -249,7 +253,7 @@ void MissionCommandTree::_baseVehicleInfo(Vehicle* vehicle, MAV_AUTOPILOT& baseF baseVehicleType = _baseVehicleType(vehicle->vehicleType()); } else { // No Vehicle means offline editing - baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)QGroundControlQmlGlobal::offlineEditingFirmwareType()->rawValue().toInt()); - baseVehicleType = _baseVehicleType((MAV_TYPE)QGroundControlQmlGlobal::offlineEditingVehicleType()->rawValue().toInt()); + baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)_settingsManager->offlineEditingFirmwareType()->rawValue().toInt()); + baseVehicleType = _baseVehicleType((MAV_TYPE)_settingsManager->offlineEditingVehicleType()->rawValue().toInt()); } } diff --git a/src/MissionManager/MissionCommandTree.h b/src/MissionManager/MissionCommandTree.h index 624bada90..2ac323793 100644 --- a/src/MissionManager/MissionCommandTree.h +++ b/src/MissionManager/MissionCommandTree.h @@ -19,6 +19,7 @@ class MissionCommandUIInfo; class MissionCommandList; +class SettingsManager; #ifdef UNITTEST_BUILD class MissionCommandTreeTest; #endif @@ -87,7 +88,8 @@ private: /// Collapsed hierarchy for specific vehicle type QMap> _availableCategories; - bool _unitTest; ///< true: running in unit test mode + SettingsManager* _settingsManager; + bool _unitTest; ///< true: running in unit test mode #ifdef UNITTEST_BUILD friend class MissionCommandTreeTest; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 70011b715..53a209823 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -20,6 +20,7 @@ #include "JsonHelper.h" #include "ParameterManager.h" #include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" #ifndef __mobile__ #include "MainWindow.h" @@ -431,17 +432,18 @@ bool MissionController::_loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObje // Mission Settings QGeoCoordinate homeCoordinate; + SettingsManager* settingsManager = qgcApp()->toolbox()->settingsManager(); if (!JsonHelper::loadGeoCoordinate(json[_jsonPlannedHomePositionKey], true /* altitudeRequired */, homeCoordinate, errorString)) { return false; } if (json.contains(_jsonVehicleTypeKey) && vehicle->isOfflineEditingVehicle()) { - QGroundControlQmlGlobal::offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); + settingsManager->offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); } if (json.contains(_jsonCruiseSpeedKey)) { - QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); + settingsManager->offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); } if (json.contains(_jsonHoverSpeedKey)) { - QGroundControlQmlGlobal::offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); + settingsManager->offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); } SimpleMissionItem* homeItem = new SimpleMissionItem(vehicle, visualItems); diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 5a6a090aa..66e0b7667 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -21,6 +21,7 @@ #include "JsonHelper.h" #include "SimpleMissionItem.h" #include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" #ifndef __mobile__ #include "QGCFileDialog.h" @@ -267,7 +268,7 @@ void RallyPointController::addPoint(QGeoCoordinate point) if (_points.count()) { defaultAlt = qobject_cast(_points[_points.count() - 1])->coordinate().altitude(); } else { - defaultAlt = QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble(); + defaultAlt = qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble(); } point.setAltitude(defaultAlt); RallyPoint* newPoint = new RallyPoint(point, this); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 5464ddfd3..3d6c0de22 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -18,6 +18,7 @@ #include "MissionCommandTree.h" #include "MissionCommandUIInfo.h" #include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" FactMetaData* SimpleMissionItem::_altitudeMetaData = NULL; FactMetaData* SimpleMissionItem::_commandMetaData = NULL; @@ -529,7 +530,7 @@ void SimpleMissionItem::_syncFrameToAltitudeRelativeToHome(void) void SimpleMissionItem::setDefaultsForCommand(void) { // We set these global defaults first, then if there are param defaults they will get reset - _missionItem.setParam7(QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble()); + _missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); MAV_CMD command = (MAV_CMD)this->command(); const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); diff --git a/src/MissionManager/SimpleMissionItemTest.cc b/src/MissionManager/SimpleMissionItemTest.cc index a51728815..cf453d34e 100644 --- a/src/MissionManager/SimpleMissionItemTest.cc +++ b/src/MissionManager/SimpleMissionItemTest.cc @@ -12,6 +12,7 @@ #include "SimpleMissionItem.h" #include "QGCApplication.h" #include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" const SimpleMissionItemTest::ItemInfo_t SimpleMissionItemTest::_rgItemInfo[] = { { MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT }, @@ -140,7 +141,7 @@ void SimpleMissionItemTest::_testDefaultValues(void) item.missionItem().setCommand(MAV_CMD_NAV_WAYPOINT); item.missionItem().setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT); - QCOMPARE(item.missionItem().param7(), QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble()); + QCOMPARE(item.missionItem().param7(), qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); } void SimpleMissionItemTest::_testSignals(void) @@ -225,7 +226,7 @@ void SimpleMissionItemTest::_testSignals(void) // dirtyChanged // Check that changing to the same coordinate does not signal - simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, QGroundControlQmlGlobal::defaultMissionItemAltitude()->rawValue().toDouble())); + simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble())); QVERIFY(multiSpy->checkNoSignals()); // Check that actually changing coordinate signals correctly diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 5ca81d981..f67e57bae 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -29,6 +29,7 @@ #include "MAVLinkLogManager.h" #include "QGCCorePlugin.h" #include "QGCOptions.h" +#include "SettingsManager.h" #if defined(QGC_CUSTOM_BUILD) #include CUSTOMHEADER @@ -55,7 +56,11 @@ QGCToolbox::QGCToolbox(QGCApplication* app) , _videoManager(NULL) , _mavlinkLogManager(NULL) , _corePlugin(NULL) + , _settingsManager(NULL) { + // SettingsManager must be first so settings are available to any subsequent tools + _settingsManager = new SettingsManager(app); + //-- Scan and load plugins _scanAndLoadPlugins(app); _audioOutput = new GAudioOutput(app); @@ -81,6 +86,9 @@ QGCToolbox::QGCToolbox(QGCApplication* app) void QGCToolbox::setChildToolboxes(void) { + // SettingsManager must be first so settings are available to any subsequent tools + _settingsManager->setToolbox(this); + _corePlugin->setToolbox(this); _audioOutput->setToolbox(this); _factSystem->setToolbox(this); diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 717bae81a..762996fd5 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -32,6 +32,7 @@ class QGCPositionManager; class VideoManager; class MAVLinkLogManager; class QGCCorePlugin; +class SettingsManager; /// This is used to manage all of our top level services/tools class QGCToolbox { @@ -56,6 +57,7 @@ public: VideoManager* videoManager(void) { return _videoManager; } MAVLinkLogManager* mavlinkLogManager(void) { return _mavlinkLogManager; } QGCCorePlugin* corePlugin(void) { return _corePlugin; } + SettingsManager* settingsManager(void) { return _settingsManager; } #ifndef __mobile__ GPSManager* gpsManager(void) { return _gpsManager; } @@ -86,6 +88,7 @@ private: VideoManager* _videoManager; MAVLinkLogManager* _mavlinkLogManager; QGCCorePlugin* _corePlugin; + SettingsManager* _settingsManager; friend class QGCApplication; }; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index fab97270e..df23461f7 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -19,19 +19,6 @@ static const char* kQmlGlobalKeyName = "QGCQml"; -SettingsFact* QGroundControlQmlGlobal::_distanceUnitsFact = NULL; -FactMetaData* QGroundControlQmlGlobal::_distanceUnitsMetaData = NULL; -SettingsFact* QGroundControlQmlGlobal::_areaUnitsFact = NULL; -FactMetaData* QGroundControlQmlGlobal::_areaUnitsMetaData = NULL; -SettingsFact* QGroundControlQmlGlobal::_speedUnitsFact = NULL; -FactMetaData* QGroundControlQmlGlobal::_speedUnitsMetaData = NULL; -SettingsFact* QGroundControlQmlGlobal::_offlineEditingFirmwareTypeFact = NULL; -SettingsFact* QGroundControlQmlGlobal::_offlineEditingVehicleTypeFact = NULL; -SettingsFact* QGroundControlQmlGlobal::_offlineEditingCruiseSpeedFact = NULL; -SettingsFact* QGroundControlQmlGlobal::_offlineEditingHoverSpeedFact = NULL; -SettingsFact* QGroundControlQmlGlobal::_batteryPercentRemainingAnnounceFact = NULL; -SettingsFact* QGroundControlQmlGlobal::_defaultMissionItemAltitudeFact = NULL; - const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; const char* QGroundControlQmlGlobal::_missionAutoLoadDirKey = "MissionAutoLoadDir"; @@ -48,6 +35,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _mavlinkLogManager(NULL) , _corePlugin(NULL) , _firmwarePluginManager(NULL) + , _settingsManager(NULL) , _virtualTabletJoystick(false) , _baseFontPointSize(0.0) { @@ -67,6 +55,7 @@ QGroundControlQmlGlobal::~QGroundControlQmlGlobal() void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) { QGCTool::setToolbox(toolbox); + _flightMapSettings = toolbox->flightMapSettings(); _linkManager = toolbox->linkManager(); _multiVehicleManager = toolbox->multiVehicleManager(); @@ -77,6 +66,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _mavlinkLogManager = toolbox->mavlinkLogManager(); _corePlugin = toolbox->corePlugin(); _firmwarePluginManager = toolbox->firmwarePluginManager(); + _settingsManager = toolbox->settingsManager(); } void QGroundControlQmlGlobal::saveGlobalSetting (const QString& key, const QString& value) @@ -224,131 +214,6 @@ void QGroundControlQmlGlobal::setBaseFontPointSize(qreal size) } } -SettingsFact* QGroundControlQmlGlobal::_createSettingsFact(const QString& name) -{ - SettingsFact* fact; - FactMetaData* metaData = nameToMetaDataMap()[name]; - - fact = new SettingsFact(QString(), name, metaData->type(), metaData->rawDefaultValue()); - fact->setMetaData(metaData); - - return fact; -} - -Fact* QGroundControlQmlGlobal::offlineEditingFirmwareType(void) -{ - if (!_offlineEditingFirmwareTypeFact) { - _offlineEditingFirmwareTypeFact = _createSettingsFact(QStringLiteral("OfflineEditingFirmwareType")); - } - - return _offlineEditingFirmwareTypeFact; -} - -Fact* QGroundControlQmlGlobal::offlineEditingVehicleType(void) -{ - if (!_offlineEditingVehicleTypeFact) { - _offlineEditingVehicleTypeFact = _createSettingsFact(QStringLiteral("OfflineEditingVehicleType")); - } - - return _offlineEditingVehicleTypeFact; -} - -Fact* QGroundControlQmlGlobal::offlineEditingCruiseSpeed(void) -{ - if (!_offlineEditingCruiseSpeedFact) { - _offlineEditingCruiseSpeedFact = _createSettingsFact(QStringLiteral("OfflineEditingCruiseSpeed")); - } - return _offlineEditingCruiseSpeedFact; -} - -Fact* QGroundControlQmlGlobal::offlineEditingHoverSpeed(void) -{ - if (!_offlineEditingHoverSpeedFact) { - _offlineEditingHoverSpeedFact = _createSettingsFact(QStringLiteral("OfflineEditingHoverSpeed")); - } - return _offlineEditingHoverSpeedFact; -} - -Fact* QGroundControlQmlGlobal::distanceUnits(void) -{ - if (!_distanceUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - _distanceUnitsFact = new SettingsFact(QString(), "DistanceUnits", FactMetaData::valueTypeUint32, DistanceUnitsMeters); - _distanceUnitsMetaData = new FactMetaData(FactMetaData::valueTypeUint32); - - enumStrings << "Feet" << "Meters"; - enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); - - _distanceUnitsMetaData->setEnumInfo(enumStrings, enumValues); - _distanceUnitsFact->setMetaData(_distanceUnitsMetaData); - } - - return _distanceUnitsFact; - -} - -Fact* QGroundControlQmlGlobal::areaUnits(void) -{ - if (!_areaUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - _areaUnitsFact = new SettingsFact(QString(), "AreaUnits", FactMetaData::valueTypeUint32, AreaUnitsSquareMeters); - _areaUnitsMetaData = new FactMetaData(FactMetaData::valueTypeUint32); - - enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; - enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); - - _areaUnitsMetaData->setEnumInfo(enumStrings, enumValues); - _areaUnitsFact->setMetaData(_areaUnitsMetaData); - } - - return _areaUnitsFact; - -} - -Fact* QGroundControlQmlGlobal::speedUnits(void) -{ - if (!_speedUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - _speedUnitsFact = new SettingsFact(QString(), "SpeedUnits", FactMetaData::valueTypeUint32, SpeedUnitsMetersPerSecond); - _speedUnitsMetaData = new FactMetaData(FactMetaData::valueTypeUint32); - - enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; - enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); - - _speedUnitsMetaData->setEnumInfo(enumStrings, enumValues); - _speedUnitsFact->setMetaData(_speedUnitsMetaData); - } - - return _speedUnitsFact; -} - -Fact* QGroundControlQmlGlobal::batteryPercentRemainingAnnounce(void) -{ - if (!_batteryPercentRemainingAnnounceFact) { - _batteryPercentRemainingAnnounceFact = _createSettingsFact(QStringLiteral("batteryPercentRemainingAnnounce")); - } - - return _batteryPercentRemainingAnnounceFact; -} - -Fact* QGroundControlQmlGlobal::defaultMissionItemAltitude(void) -{ - if (!_defaultMissionItemAltitudeFact) { - _defaultMissionItemAltitudeFact = _createSettingsFact(QStringLiteral("DefaultMissionItemAltitude")); - } - - return _defaultMissionItemAltitudeFact; -} - int QGroundControlQmlGlobal::supportedFirmwareCount() { return _firmwarePluginManager->knownFirmwareTypes().count(); @@ -363,16 +228,6 @@ bool QGroundControlQmlGlobal::linesIntersect(QPointF line1A, QPointF line1B, QPo intersectPoint != line1A && intersectPoint != line1B; } -QMap& QGroundControlQmlGlobal::nameToMetaDataMap(void) { - static QMap map; - - if (map.isEmpty()) { - map = FactMetaData::createMapFromJsonFile(":/json/QGroundControlQmlGlobal.json", NULL); - } - - return map; -} - QString QGroundControlQmlGlobal::missionAutoLoadDir(void) { QSettings settings; diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 7a912e574..bbd048997 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -37,32 +37,6 @@ public: QGroundControlQmlGlobal(QGCApplication* app); ~QGroundControlQmlGlobal(); - enum DistanceUnits { - DistanceUnitsFeet = 0, - DistanceUnitsMeters - }; - - enum AreaUnits { - AreaUnitsSquareFeet = 0, - AreaUnitsSquareMeters, - AreaUnitsSquareKilometers, - AreaUnitsHectares, - AreaUnitsAcres, - AreaUnitsSquareMiles, - }; - - enum SpeedUnits { - SpeedUnitsFeetPerSecond = 0, - SpeedUnitsMetersPerSecond, - SpeedUnitsMilesPerHour, - SpeedUnitsKilometersPerHour, - SpeedUnitsKnots, - }; - - Q_ENUMS(DistanceUnits) - Q_ENUMS(AreaUnits) - Q_ENUMS(SpeedUnits) - Q_PROPERTY(FlightMapSettings* flightMapSettings READ flightMapSettings CONSTANT) Q_PROPERTY(LinkManager* linkManager READ linkManager CONSTANT) Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager CONSTANT) @@ -72,6 +46,9 @@ public: Q_PROPERTY(VideoManager* videoManager READ videoManager CONSTANT) Q_PROPERTY(MAVLinkLogManager* mavlinkLogManager READ mavlinkLogManager CONSTANT) Q_PROPERTY(QGCCorePlugin* corePlugin READ corePlugin CONSTANT) + Q_PROPERTY(SettingsManager* settingsManager READ settingsManager CONSTANT) + + Q_PROPERTY(int supportedFirmwareCount READ supportedFirmwareCount 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 @@ -91,17 +68,6 @@ public: Q_PROPERTY(bool isVersionCheckEnabled READ isVersionCheckEnabled WRITE setIsVersionCheckEnabled NOTIFY isVersionCheckEnabledChanged) Q_PROPERTY(int mavlinkSystemID READ mavlinkSystemID WRITE setMavlinkSystemID NOTIFY mavlinkSystemIDChanged) - Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) - Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) - Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) - Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) - Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) - Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) - Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) - Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) - Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) - Q_PROPERTY(int supportedFirmwareCount READ supportedFirmwareCount CONSTANT) - Q_PROPERTY(QGeoCoordinate lastKnownHomePosition READ lastKnownHomePosition CONSTANT) Q_PROPERTY(QGeoCoordinate flightMapPosition MEMBER _flightMapPosition NOTIFY flightMapPositionChanged) Q_PROPERTY(double flightMapZoom MEMBER _flightMapZoom NOTIFY flightMapZoomChanged) @@ -163,19 +129,20 @@ public: // Property accesors - FlightMapSettings* flightMapSettings () { return _flightMapSettings; } - LinkManager* linkManager () { return _linkManager; } - MultiVehicleManager* multiVehicleManager () { return _multiVehicleManager; } - QGCMapEngineManager* mapEngineManager () { return _mapEngineManager; } - QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; } - MissionCommandTree* missionCommandTree () { return _missionCommandTree; } - VideoManager* videoManager () { return _videoManager; } - MAVLinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } - QGCCorePlugin* corePlugin () { return _corePlugin; } - - qreal zOrderTopMost () { return 1000; } - qreal zOrderWidgets () { return 100; } - qreal zOrderMapItems () { return 50; } + FlightMapSettings* flightMapSettings () { return _flightMapSettings; } + LinkManager* linkManager () { return _linkManager; } + MultiVehicleManager* multiVehicleManager () { return _multiVehicleManager; } + QGCMapEngineManager* mapEngineManager () { return _mapEngineManager; } + QGCPositionManager* qgcPositionManger () { return _qgcPositionManager; } + MissionCommandTree* missionCommandTree () { return _missionCommandTree; } + VideoManager* videoManager () { return _videoManager; } + MAVLinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } + QGCCorePlugin* corePlugin () { return _corePlugin; } + SettingsManager* settingsManager () { return _settingsManager; } + + qreal zOrderTopMost () { return 1000; } + qreal zOrderWidgets () { return 100; } + qreal zOrderMapItems () { return 50; } bool isDarkStyle () { return _app->styleIsDark(); } bool isAudioMuted () { return _toolbox->audioOutput()->isMuted(); } @@ -189,16 +156,6 @@ public: QGeoCoordinate lastKnownHomePosition() { return qgcApp()->lastKnownHomePosition(); } - static Fact* offlineEditingFirmwareType (void); - static Fact* offlineEditingVehicleType (void); - static Fact* offlineEditingCruiseSpeed (void); - static Fact* offlineEditingHoverSpeed (void); - static Fact* distanceUnits (void); - static Fact* areaUnits (void); - static Fact* speedUnits (void); - static Fact* batteryPercentRemainingAnnounce(void); - static Fact* defaultMissionItemAltitude (void); - int supportedFirmwareCount (); void setIsDarkStyle (bool dark); @@ -238,9 +195,6 @@ signals: void missionAutoLoadDirChanged (QString missionAutoLoadDir); private: - static SettingsFact* _createSettingsFact(const QString& name); - static QMap& nameToMetaDataMap(void); - FlightMapSettings* _flightMapSettings; LinkManager* _linkManager; MultiVehicleManager* _multiVehicleManager; @@ -251,26 +205,13 @@ private: MAVLinkLogManager* _mavlinkLogManager; QGCCorePlugin* _corePlugin; FirmwarePluginManager* _firmwarePluginManager; + SettingsManager* _settingsManager; bool _virtualTabletJoystick; qreal _baseFontPointSize; QGeoCoordinate _flightMapPosition; double _flightMapZoom; - // These are static so they are available to C++ code as well as Qml - static SettingsFact* _offlineEditingFirmwareTypeFact; - static SettingsFact* _offlineEditingVehicleTypeFact; - static SettingsFact* _offlineEditingCruiseSpeedFact; - static SettingsFact* _offlineEditingHoverSpeedFact; - static SettingsFact* _distanceUnitsFact; - static FactMetaData* _distanceUnitsMetaData; - static SettingsFact* _areaUnitsFact; - static FactMetaData* _areaUnitsMetaData; - static SettingsFact* _speedUnitsFact; - static FactMetaData* _speedUnitsMetaData; - static SettingsFact* _batteryPercentRemainingAnnounceFact; - static SettingsFact* _defaultMissionItemAltitudeFact; - static const char* _virtualTabletJoystickKey; static const char* _baseFontPointSizeKey; static const char* _missionAutoLoadDirKey; diff --git a/src/SettingsManager.cc b/src/SettingsManager.cc new file mode 100644 index 000000000..0408440f9 --- /dev/null +++ b/src/SettingsManager.cc @@ -0,0 +1,170 @@ +/**************************************************************************** + * + * (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 "SettingsManager.h" +#include "QGCApplication.h" + +QGC_LOGGING_CATEGORY(SettingsManagerLog, "SettingsManagerLog") + +const char* SettingsManager::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; +const char* SettingsManager::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; +const char* SettingsManager::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; +const char* SettingsManager::offlineEditingHoverSpeedSettingsName = "OfflineEditingHoverSpeed"; +const char* SettingsManager::distanceUnitsSettingsName = "DistanceUnits"; +const char* SettingsManager::areaUnitsSettingsName = "AreaUnits"; +const char* SettingsManager::speedUnitsSettingsName = "SpeedUnits"; +const char* SettingsManager::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; +const char* SettingsManager::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; + +SettingsManager::SettingsManager(QGCApplication* app) + : QGCTool(app) + , _offlineEditingFirmwareTypeFact(NULL) + , _offlineEditingVehicleTypeFact(NULL) + , _offlineEditingCruiseSpeedFact(NULL) + , _offlineEditingHoverSpeedFact(NULL) + , _distanceUnitsFact(NULL) + , _areaUnitsFact(NULL) + , _speedUnitsFact(NULL) + , _batteryPercentRemainingAnnounceFact(NULL) + , _defaultMissionItemAltitudeFact(NULL) +{ + +} + +void SettingsManager::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only"); + + _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(":/json/SettingsManager.json", this); +} + +SettingsFact* SettingsManager::_createSettingsFact(const QString& name) +{ + return new SettingsFact(QString() /* no settings group */, _nameToMetaDataMap[name], this); +} + +Fact* SettingsManager::offlineEditingFirmwareType(void) +{ + if (!_offlineEditingFirmwareTypeFact) { + _offlineEditingFirmwareTypeFact = _createSettingsFact(offlineEditingFirmwareTypeSettingsName); + } + + return _offlineEditingFirmwareTypeFact; +} + +Fact* SettingsManager::offlineEditingVehicleType(void) +{ + if (!_offlineEditingVehicleTypeFact) { + _offlineEditingVehicleTypeFact = _createSettingsFact(offlineEditingVehicleTypeSettingsName); + } + + return _offlineEditingVehicleTypeFact; +} + +Fact* SettingsManager::offlineEditingCruiseSpeed(void) +{ + if (!_offlineEditingCruiseSpeedFact) { + _offlineEditingCruiseSpeedFact = _createSettingsFact(offlineEditingCruiseSpeedSettingsName); + } + return _offlineEditingCruiseSpeedFact; +} + +Fact* SettingsManager::offlineEditingHoverSpeed(void) +{ + if (!_offlineEditingHoverSpeedFact) { + _offlineEditingHoverSpeedFact = _createSettingsFact(offlineEditingHoverSpeedSettingsName); + } + return _offlineEditingHoverSpeedFact; +} + +Fact* SettingsManager::batteryPercentRemainingAnnounce(void) +{ + if (!_batteryPercentRemainingAnnounceFact) { + _batteryPercentRemainingAnnounceFact = _createSettingsFact(batteryPercentRemainingAnnounceSettingsName); + } + + return _batteryPercentRemainingAnnounceFact; +} + +Fact* SettingsManager::defaultMissionItemAltitude(void) +{ + if (!_defaultMissionItemAltitudeFact) { + _defaultMissionItemAltitudeFact = _createSettingsFact(defaultMissionItemAltitudeSettingsName); + } + + return _defaultMissionItemAltitudeFact; +} + +Fact* SettingsManager::distanceUnits(void) +{ + if (!_distanceUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet" << "Meters"; + enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(distanceUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(DistanceUnitsMeters); + + _distanceUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + + } + + return _distanceUnitsFact; + +} + +Fact* SettingsManager::areaUnits(void) +{ + if (!_areaUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; + enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(areaUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(AreaUnitsSquareMeters); + + _areaUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _areaUnitsFact; + +} + +Fact* SettingsManager::speedUnits(void) +{ + if (!_speedUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; + enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(speedUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); + + _speedUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _speedUnitsFact; +} diff --git a/src/SettingsManager.h b/src/SettingsManager.h new file mode 100644 index 000000000..bae2fd3b9 --- /dev/null +++ b/src/SettingsManager.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * + * (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 SettingsManager_H +#define SettingsManager_H + +#include "QGCLoggingCategory.h" +#include "Joystick.h" +#include "MultiVehicleManager.h" +#include "QGCToolbox.h" + +#include + +Q_DECLARE_LOGGING_CATEGORY(SettingsManagerLog) + +/// Provides access to all app settings +class SettingsManager : public QGCTool +{ + Q_OBJECT + +public: + SettingsManager(QGCApplication* app); + + enum DistanceUnits { + DistanceUnitsFeet = 0, + DistanceUnitsMeters + }; + + enum AreaUnits { + AreaUnitsSquareFeet = 0, + AreaUnitsSquareMeters, + AreaUnitsSquareKilometers, + AreaUnitsHectares, + AreaUnitsAcres, + AreaUnitsSquareMiles, + }; + + enum SpeedUnits { + SpeedUnitsFeetPerSecond = 0, + SpeedUnitsMetersPerSecond, + SpeedUnitsMilesPerHour, + SpeedUnitsKilometersPerHour, + SpeedUnitsKnots, + }; + + Q_ENUMS(DistanceUnits) + Q_ENUMS(AreaUnits) + Q_ENUMS(SpeedUnits) + + Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) + Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) + Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) + Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) + Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) + Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) + Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) + Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) + Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) + + Fact* offlineEditingFirmwareType (void); + Fact* offlineEditingVehicleType (void); + Fact* offlineEditingCruiseSpeed (void); + Fact* offlineEditingHoverSpeed (void); + Fact* distanceUnits (void); + Fact* areaUnits (void); + Fact* speedUnits (void); + Fact* batteryPercentRemainingAnnounce(void); + Fact* defaultMissionItemAltitude (void); + + // Override from QGCTool + virtual void setToolbox(QGCToolbox *toolbox); + + static const char* offlineEditingFirmwareTypeSettingsName; + static const char* offlineEditingVehicleTypeSettingsName; + static const char* offlineEditingCruiseSpeedSettingsName; + static const char* offlineEditingHoverSpeedSettingsName; + static const char* distanceUnitsSettingsName; + static const char* areaUnitsSettingsName; + static const char* speedUnitsSettingsName; + static const char* batteryPercentRemainingAnnounceSettingsName; + static const char* defaultMissionItemAltitudeSettingsName; + +public slots: + +signals: + +private slots: + +private: + SettingsFact* _createSettingsFact(const QString& name); + + QMap _nameToMetaDataMap; + + SettingsFact* _offlineEditingFirmwareTypeFact; + SettingsFact* _offlineEditingVehicleTypeFact; + SettingsFact* _offlineEditingCruiseSpeedFact; + SettingsFact* _offlineEditingHoverSpeedFact; + SettingsFact* _distanceUnitsFact; + SettingsFact* _areaUnitsFact; + SettingsFact* _speedUnitsFact; + SettingsFact* _batteryPercentRemainingAnnounceFact; + SettingsFact* _defaultMissionItemAltitudeFact; +}; + +#endif diff --git a/src/QmlControls/QGroundControlQmlGlobal.json b/src/SettingsManager.json similarity index 100% rename from src/QmlControls/QGroundControlQmlGlobal.json rename to src/SettingsManager.json diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index fdab658a1..45d3656c0 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -15,6 +15,7 @@ #include "FollowMe.h" #include "QGroundControlQmlGlobal.h" #include "ParameterManager.h" +#include "SettingsManager.h" #if defined (__ios__) || defined(__android__) #include "MobileScreenMgr.h" @@ -62,8 +63,9 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo); - _offlineEditingVehicle = new Vehicle(static_cast(QGroundControlQmlGlobal::offlineEditingFirmwareType()->rawValue().toInt()), - static_cast(QGroundControlQmlGlobal::offlineEditingVehicleType()->rawValue().toInt()), + SettingsManager* settingsManager = toolbox->settingsManager(); + _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->offlineEditingFirmwareType()->rawValue().toInt()), + static_cast(settingsManager->offlineEditingVehicleType()->rawValue().toInt()), _firmwarePluginManager, this); } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 349e3054c..0ad9378c5 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -27,6 +27,7 @@ #include "FollowMe.h" #include "MissionCommandTree.h" #include "QGroundControlQmlGlobal.h" +#include "SettingsManager.h" QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog") @@ -75,6 +76,7 @@ Vehicle::Vehicle(LinkInterface* link, , _autopilotPlugin(NULL) , _mavlink(NULL) , _soloFirmware(false) + , _settingsManager(qgcApp()->toolbox()->settingsManager()) , _joystickMode(JoystickModeRC) , _joystickEnabled(false) , _uas(NULL) @@ -99,8 +101,8 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(QGroundControlQmlGlobal::offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) , _telemetryRRSSI(0) , _telemetryLRSSI(0) , _telemetryRXErrors(0) @@ -231,6 +233,9 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _firmwarePlugin(NULL) , _firmwarePluginInstanceData(NULL) , _autopilotPlugin(NULL) + , _mavlink(NULL) + , _soloFirmware(false) + , _settingsManager(qgcApp()->toolbox()->settingsManager()) , _joystickMode(JoystickModeRC) , _joystickEnabled(false) , _uas(NULL) @@ -255,8 +260,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(QGroundControlQmlGlobal::offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(QGroundControlQmlGlobal::offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -318,10 +323,10 @@ void Vehicle::_commonInit(void) connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); // Offline editing vehicle tracks settings changes for offline editing settings - connect(QGroundControlQmlGlobal::offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); - connect(QGroundControlQmlGlobal::offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); - connect(QGroundControlQmlGlobal::offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); - connect(QGroundControlQmlGlobal::offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); + connect(_settingsManager->offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); + connect(_settingsManager->offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); + connect(_settingsManager->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); + connect(_settingsManager->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); // Build FactGroup object model @@ -786,7 +791,7 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) } _batteryFactGroup.percentRemaining()->setRawValue(sysStatus.battery_remaining); - if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < QGroundControlQmlGlobal::batteryPercentRemainingAnnounce()->rawValue().toInt()) { + if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _settingsManager->batteryPercentRemainingAnnounce()->rawValue().toInt()) { if (!_lowBatteryAnnounceTimer.isValid() || _lowBatteryAnnounceTimer.elapsed() > _lowBatteryAnnounceRepeatMSecs) { _lowBatteryAnnounceTimer.restart(); _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 29d8b1350..8a8fea4fc 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -37,6 +37,7 @@ class RallyPointManager; class ParameterManager; class JoystickManager; class UASMessage; +class SettingsManager; Q_DECLARE_LOGGING_CATEGORY(VehicleLog) @@ -775,6 +776,7 @@ private: AutoPilotPlugin* _autopilotPlugin; MAVLinkProtocol* _mavlink; bool _soloFirmware; + SettingsManager* _settingsManager; QList _links; diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 89eac8c4c..eab76cc0f 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -139,3 +139,10 @@ QGCOptions* QGCCorePlugin::options() return _p->defaultOptions; } +QVariant QGCCorePlugin::overrideSettingsDefault(QString name, QVariant defaultValue) +{ + Q_UNUSED(name); + + // No overrides for base plugin + return defaultValue; +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 492da621d..b668a9d7c 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -54,6 +54,12 @@ public: */ virtual QGCOptions* options (); + /// Allows the core plugin to override the default value for the specified setting + /// @param name - Setting name + /// @param defaultValue - Standard default value for setting + /// @return New default value for setting, if no override just return passed in defaultValue + virtual QVariant overrideSettingsDefault(QString name, QVariant defaultValue); + // Override from QGCTool void setToolbox (QGCToolbox *toolbox); private: diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index a02c919eb..f0f231b24 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -23,6 +23,7 @@ import QGroundControl.ScreenTools 1.0 import QGroundControl.MultiVehicleManager 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controllers 1.0 +import QGroundControl.SettingsManager 1.0 QGCView { id: qgcView @@ -31,7 +32,7 @@ QGCView { anchors.fill: parent anchors.margins: ScreenTools.defaultFontPixelWidth - property Fact _percentRemainingAnnounce: QGroundControl.batteryPercentRemainingAnnounce + property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.batteryPercentRemainingAnnounce property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -93,7 +94,7 @@ QGCView { FactComboBox { id: distanceUnitsCombo width: _editFieldWidth - fact: QGroundControl.distanceUnits + fact: QGroundControl.settingsManager.distanceUnits indexModel: false } } @@ -107,7 +108,7 @@ QGCView { FactComboBox { id: areaUnitsCombo width: _editFieldWidth - fact: QGroundControl.areaUnits + fact: QGroundControl.settingsManager.areaUnits indexModel: false } } @@ -121,7 +122,7 @@ QGCView { FactComboBox { id: speedUnitsCombo width: _editFieldWidth - fact: QGroundControl.speedUnits + fact: QGroundControl.settingsManager.speedUnits indexModel: false } } @@ -326,7 +327,7 @@ QGCView { } FactTextField { id: defaultItemAltitudeField - fact: QGroundControl.defaultMissionItemAltitude + fact: QGroundControl.settingsManager.defaultMissionItemAltitude } } //----------------------------------------------------------------- -- GitLab From 6dce17eeda3f67a32a824d76ddb442b5bec03728 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 14:24:12 -0800 Subject: [PATCH 303/398] Fix DropPanel positioning --- src/QmlControls/DropPanel.qml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/QmlControls/DropPanel.qml b/src/QmlControls/DropPanel.qml index 2dc61a3a0..a0f2ab117 100644 --- a/src/QmlControls/DropPanel.qml +++ b/src/QmlControls/DropPanel.qml @@ -23,7 +23,6 @@ Item { signal clicked() property real radius: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.75 : ScreenTools.defaultFontPixelHeight * 1.25 property real viewportMargins: 0 - property real topMargin: parent.height - ScreenTools.availableHeight property var toolStrip @@ -48,7 +47,7 @@ Item { property alias _dropDownComponent: dropDownLoader.sourceComponent property real _viewportMaxLeft: -x + viewportMargins property real _viewportMaxRight: parent.width - (viewportMargins * 2) - x - property real _viewportMaxTop: -y + viewportMargins + topMargin + property real _viewportMaxTop: -y + viewportMargins property real _viewportMaxBottom: parent.height - (viewportMargins * 2) - y function show(panelEdgeTopPoint, panelEdgeHeight, panelComponent) { @@ -104,19 +103,6 @@ Item { QGCPalette { id: qgcPal } - /* - MouseArea { - x: _viewportMaxLeft - y: _viewportMaxTop - width: _viewportMaxRight -_viewportMaxLeft - height: _viewportMaxBottom - _viewportMaxTop - visible: checked - onClicked: { - checked = false - _root.clicked() - } - }*/ - Item { id: dropDownItem -- GitLab From b26358482653aafd45f6a52c63ccc1d16bad4150 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 14:24:37 -0800 Subject: [PATCH 304/398] Small screen support --- src/QmlControls/ToolStrip.qml | 41 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml index 9c2e7c3ec..337b1b40f 100644 --- a/src/QmlControls/ToolStrip.qml +++ b/src/QmlControls/ToolStrip.qml @@ -25,16 +25,17 @@ Rectangle { property var showAlternateIcon property var rotateImage property var buttonEnabled + property var buttonVisible signal clicked(int index, bool checked) - readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2 - readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 - readonly property real _buttonSpacing: ScreenTools.defaultFontPixelWidth + readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _buttonSpacing: ScreenTools.defaultFontPixelWidth + readonly property bool _showOptionalElements: !ScreenTools.isShortScreen - ExclusiveGroup { - id: dropButtonsExclusiveGroup - } + QGCPalette { id: qgcPal } + ExclusiveGroup { id: dropButtonsExclusiveGroup } function uncheckAll() { dropButtonsExclusiveGroup.current = null @@ -65,31 +66,34 @@ Rectangle { QGCLabel { anchors.horizontalCenter: parent.horizontalCenter text: title + visible: _showOptionalElements } - Item { width: 1; height: _buttonSpacing } + Item { width: 1; height: _buttonSpacing; visible: _showOptionalElements } Rectangle { anchors.left: parent.left anchors.right: parent.right height: 1 color: qgcPal.text + visible: _showOptionalElements } Repeater { id: repeater delegate: Column { - id: buttonColumn - width: buttonStripColumn.width + id: buttonColumn + width: buttonStripColumn.width + visible: _root.buttonVisible ? _root.buttonVisible[index] : true property bool checked: false property ExclusiveGroup exclusiveGroup: dropButtonsExclusiveGroup - property var _iconSource: modelData.iconSource - property var _alternateIconSource: modelData.alternateIconSource - property var _source: _root.showAlternateIcon[index] ? _alternateIconSource : _iconSource - property bool rotateImage: _root.rotateImage[index] + property var _iconSource: modelData.iconSource + property var _alternateIconSource: modelData.alternateIconSource + property var _source: (_root.showAlternateIcon && _root.showAlternateIcon[index]) ? _alternateIconSource : _iconSource + property bool rotateImage: _root.rotateImage ? _root.rotateImage[index] : false onExclusiveGroupChanged: { if (exclusiveGroup) { @@ -106,7 +110,11 @@ Rectangle { } } - Item { width: 1; height: _buttonSpacing } + Item { + width: 1 + height: _buttonSpacing + visible: index == 0 ? _showOptionalElements : true + } Rectangle { anchors.left: parent.left @@ -139,8 +147,8 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - height: parent.height + buttonLabel.height + buttonColumn.spacing - visible: _root.buttonEnabled[index] + height: parent.height + (_showOptionalElements? buttonLabel.height + buttonColumn.spacing : 0) + visible: _root.buttonEnabled ? _root.buttonEnabled[index] : true onClicked: { if (modelData.dropPanelComponent === undefined) { @@ -171,6 +179,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter font.pointSize: ScreenTools.smallFontPointSize text: modelData.name + visible: _showOptionalElements } } } -- GitLab From e36b3dd24714d14babb283d3fb492d650e2f5d33 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 14:24:52 -0800 Subject: [PATCH 305/398] Add zoom buttons --- src/MissionEditor/MissionEditor.qml | 75 ++++++++++++----------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index a71ce19d6..19c53733f 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -100,7 +100,7 @@ QGCView { property real toolbarHeight: qgcView.height - ScreenTools.availableHeight property real rightPanelWidth: _rightPanelWidth - property real leftToolWidth: mapFitFunctions.x + mapFitFunctions.width + property real leftToolWidth: toolStrip.x + toolStrip.width } MissionController { @@ -810,46 +810,8 @@ QGCView { } } - /* - - FIXME: Need to put these back into ToolStrip - - //-- Zoom Map In - RoundButton { - id: mapZoomPlus - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: mapTypeButton.bottom - anchors.left: mapTypeButton.left - visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen - buttonImage: "/qmlimages/ZoomPlus.svg" - lightBorders: _lightWidgetBorders - - onClicked: { - if(editorMap) - editorMap.zoomLevel += 0.5 - checked = false - } - } - - //-- Zoom Map Out - RoundButton { - id: mapZoomMinus - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: mapZoomPlus.bottom - anchors.left: mapZoomPlus.left - visible: !ScreenTools.isTinyScreen && !ScreenTools.isShortScreen - buttonImage: "/qmlimages/ZoomMinus.svg" - lightBorders: _lightWidgetBorders - onClicked: { - if(editorMap) - editorMap.zoomLevel -= 0.5 - checked = false - } - } - - */ - ToolStrip { + id: toolStrip anchors.leftMargin: ScreenTools.defaultFontPixelWidth anchors.left: parent.left anchors.topMargin: _toolButtonTopMargin @@ -857,9 +819,12 @@ QGCView { color: qgcPal.window title: qsTr("Plan") z: QGroundControl.zOrderWidgets - showAlternateIcon: [ false, false, _syncDropDownController.dirty, false, false ] - rotateImage: [ false, false, _syncDropDownController.syncInProgress, false, false ] - buttonEnabled: [ true, true, !_syncDropDownController.syncInProgress, true, true ] + showAlternateIcon: [ false, false, _syncDropDownController.dirty, false, false, false, false ] + rotateImage: [ false, false, _syncDropDownController.syncInProgress, false, false, false, false ] + buttonEnabled: [ true, true, !_syncDropDownController.syncInProgress, true, true, true, true ] + buttonVisible: [ true, true, true, true, true, _showZoom, _showZoom ] + + property bool _showZoom: !ScreenTools.isShortScreen model: [ { @@ -887,12 +852,34 @@ QGCView { name: "Map", iconSource: "/qmlimages/MapType.svg", dropPanelComponent: mapTypeDropPanel + }, + { + name: "In", + iconSource: "/qmlimages/ZoomPlus.svg" + }, + { + name: "Out", + iconSource: "/qmlimages/ZoomMinus.svg" } ] onClicked: { - if (index == 0) { + switch (index == 0) { + case 0: _addWaypointOnClick = checked + break + case 5: + editorMap.zoomLevel += 0.5 + break + case 6: + editorMap.zoomLevel -= 0.5 + break + case 5: + editorMap.zoomLevel += 0.5 + break + case 6: + editorMap.zoomLevel -= 0.5 + break } } } -- GitLab From 6bf85ada2b45fe459a45fbb23661a0fcccda3a47 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 14:25:01 -0800 Subject: [PATCH 306/398] Use ToolStrip on fly view --- src/FlightDisplay/FlightDisplayViewMap.qml | 201 ++++++++------------- 1 file changed, 78 insertions(+), 123 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index c7a2258c4..27bdeddd4 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -71,151 +71,106 @@ FlightMap { Component.onCompleted: start(false /* editMode */) } - QGCMapLabel { - id: flyLabel - map: flightMap - text: qsTr("Fly") - visible: !ScreenTools.isShortScreen - anchors.topMargin: _toolButtonTopMargin - anchors.horizontalCenter: centerMapDropButton.horizontalCenter - anchors.top: parent.top - } - - //-- Vertical Tool Buttons - ExclusiveGroup { - id: dropButtonsExclusiveGroup + id: _mapTypeButtonsExclusiveGroup } - ExclusiveGroup { - id: mapTypeButtonsExclusiveGroup - } + ToolStrip { + id: toolStrip + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.left: parent.left + anchors.topMargin: _toolButtonTopMargin + anchors.top: parent.top + color: qgcPal.window + title: qsTr("Fly") + z: QGroundControl.zOrderWidgets + buttonVisible: [ true, true, _showZoom, _showZoom ] + + property bool _showZoom: !ScreenTools.isShortScreen + + model: [ + { + name: "Center", + iconSource: "/qmlimages/MapCenter.svg", + dropPanelComponent: centerMapDropPanel + }, + { + name: "Map", + iconSource: "/qmlimages/MapType.svg", + dropPanelComponent: mapTypeDropPanel + }, + { + name: "In", + iconSource: "/qmlimages/ZoomPlus.svg" + }, + { + name: "Out", + iconSource: "/qmlimages/ZoomMinus.svg" + } + ] - //-- Dismiss Drop Down (if any) - MouseArea { - anchors.fill: parent - enabled: dropButtonsExclusiveGroup.current != null onClicked: { - if (dropButtonsExclusiveGroup.current) { - dropButtonsExclusiveGroup.current.checked = false + switch (index) { + case 2: + _flightMap.zoomLevel += 0.5 + break + case 3: + _flightMap.zoomLevel -= 0.5 + break } - dropButtonsExclusiveGroup.current = null } } - // IMPORTANT NOTE: Drop Buttons must be parented directly to the map. If they are placed in a Column for example the drop control positioning - // will not work correctly. - - //-- Map Center Control - CenterMapDropButton { - id: centerMapDropButton - anchors.topMargin: flyLabel.visible ? ScreenTools.defaultFontPixelHeight / 2 : _toolButtonTopMargin - anchors.leftMargin: ScreenTools.defaultFontPixelHeight - anchors.left: parent.left - anchors.top: flyLabel.visible ? flyLabel.bottom : parent.top - z: QGroundControl.zOrderWidgets - exclusiveGroup: dropButtonsExclusiveGroup - map: _flightMap - mapFitViewport: Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin) - usePlannedHomePosition: false - geoFenceController: geoFenceController - missionController: missionController - rallyPointController: rallyPointController - showFollowVehicle: true - followVehicle: _followVehicle - onFollowVehicleChanged: _followVehicle = followVehicle - - property real leftToolWidth: centerMapDropButton.x + centerMapDropButton.width - } + // Toolstrip drop panel compomnents - //-- Map Type Control - DropButton { - id: mapTypeButton - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: centerMapDropButton.bottom - anchors.left: centerMapDropButton.left - dropDirection: dropRight - buttonImage: "/qmlimages/MapType.svg" - viewportMargins: ScreenTools.defaultFontPixelWidth / 2 - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap + MapFitFunctions { + id: mapFitFunctions + map: _flightMap + mapFitViewport: Qt.rect(leftToolWidth, _toolButtonTopMargin, flightMap.width - leftToolWidth - rightPanelWidth, flightMap.height - _toolButtonTopMargin) + usePlannedHomePosition: false + mapMissionController: missionController + mapGeoFenceController: geoFenceController + mapRallyPointController: rallyPointController - dropDownComponent: Component { - Column { - spacing: ScreenTools.defaultFontPixelWidth + property real leftToolWidth: toolStrip.x + toolStrip.width + } - Row { - spacing: ScreenTools.defaultFontPixelWidth + Component { + id: centerMapDropPanel - Repeater { - model: QGroundControl.flightMapSettings.mapTypes + CenterMapDropPanel { + map: _flightMap + fitFunctions: mapFitFunctions + } + } - QGCButton { - checkable: true - checked: QGroundControl.flightMapSettings.mapType === text - text: modelData - width: clearButton.width - exclusiveGroup: mapTypeButtonsExclusiveGroup + Component { + id: mapTypeDropPanel - onClicked: { - QGroundControl.flightMapSettings.mapType = text - checked = true - dropButtonsExclusiveGroup.current = null - } - } - } - } + Column { + spacing: ScreenTools.defaultFontPixelHeight / 2 - QGCButton { - id: clearButton - text: qsTr("Clear Flight Trails") - enabled: QGroundControl.multiVehicleManager.activeVehicle - onClicked: { - QGroundControl.multiVehicleManager.activeVehicle.clearTrajectoryPoints() - dropButtonsExclusiveGroup.current = null + QGCLabel { text: qsTr("Map type:") } + Row { + spacing: ScreenTools.defaultFontPixelWidth + Repeater { + model: QGroundControl.flightMapSettings.mapTypes + + QGCButton { + checkable: true + checked: QGroundControl.flightMapSettings.mapType === text + text: modelData + exclusiveGroup: _mapTypeButtonsExclusiveGroup + onClicked: { + QGroundControl.flightMapSettings.mapType = text + dropPanel.hide() + } } } } } } - //-- Zoom Map In - RoundButton { - id: mapZoomPlus - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: mapTypeButton.bottom - anchors.left: mapTypeButton.left - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomPlus.svg" - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap - onClicked: { - if(_flightMap) - _flightMap.zoomLevel += 0.5 - checked = false - } - } - - //-- Zoom Map Out - RoundButton { - id: mapZoomMinus - anchors.topMargin: ScreenTools.defaultFontPixelHeight - anchors.top: mapZoomPlus.bottom - anchors.left: mapZoomPlus.left - visible: !ScreenTools.isTinyScreen && _mainIsMap - buttonImage: "/qmlimages/ZoomMinus.svg" - exclusiveGroup: dropButtonsExclusiveGroup - z: QGroundControl.zOrderWidgets - lightBorders: isSatelliteMap - onClicked: { - if(_flightMap) - _flightMap.zoomLevel -= 0.5 - checked = false - } - } - // Add trajectory points to the map MapItemView { model: _mainIsMap ? _activeVehicle ? _activeVehicle.trajectoryPoints : 0 : 0 -- GitLab From 205061e8f45e85b7c0181c02321a38e2aa01c500 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 18:19:32 -0800 Subject: [PATCH 307/398] Complex item visual dynamically added to map --- qgroundcontrol.qrc | 2 + src/MissionEditor/ComplexMissionItem.qml | 43 ++++++++++++++ src/MissionEditor/MissionEditor.qml | 21 ++----- src/MissionEditor/SurveyComplexItem.qml | 59 +++++++++++++++++++ src/MissionManager/ComplexMissionItem.h | 4 ++ .../FixedWingLandingComplexItem.h | 1 + src/MissionManager/SurveyMissionItem.h | 1 + .../QGroundControl.Controls.qmldir | 2 + 8 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 src/MissionEditor/ComplexMissionItem.qml create mode 100644 src/MissionEditor/SurveyComplexItem.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 8e9e8fc77..b25a9fa69 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -41,6 +41,7 @@ src/VehicleSetup/PX4FlowSensor.qml src/AnalyzeView/AnalyzePage.qml src/QmlControls/AppMessages.qml + src/MissionEditor/ComplexMissionItem.qml src/QmlControls/ClickableColor.qml src/QmlControls/DropButton.qml src/QmlControls/ExclusiveGroupItem.qml @@ -92,6 +93,7 @@ src/ui/toolbar/SignalStrength.qml src/QmlControls/SliderSwitch.qml src/QmlControls/SubMenuButton.qml + src/MissionEditor/SurveyComplexItem.qml src/QmlControls/VehicleRotationCal.qml src/QmlControls/VehicleSummaryRow.qml src/QmlControls/ToolStrip.qml diff --git a/src/MissionEditor/ComplexMissionItem.qml b/src/MissionEditor/ComplexMissionItem.qml new file mode 100644 index 000000000..5ee3527d3 --- /dev/null +++ b/src/MissionEditor/ComplexMissionItem.qml @@ -0,0 +1,43 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtLocation 5.3 +import QtPositioning 5.2 + +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 + + +/// Mission item edit control +Item { + id: _root + + property var map ///< Map control to place item in + + property var _complexItem + + Component.onCompleted: { + if (object.mapVisualQML) { + var component = Qt.createComponent(object.mapVisualQML) + if (component.status === Component.Error) { + console.log("Error loading Qml: ", object.mapVisualQML, component.errorString()) + } + _complexItem = component.createObject(map, { "map": _root.map }) + } + } + + Component.onDestruction: { + if (_complexItem) { + _complexItem.destroy() + } + } +} diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index a71ce19d6..2bcb265e4 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -445,25 +445,12 @@ QGCView { } } - // Add the complex mission item polygon to the map - MapItemView { - model: missionController.complexVisualItems - - delegate: MapPolygon { - color: 'green' - path: object.polygonPath - opacity: 0.5 - } - } - - // Add the complex mission item grid to the map - MapItemView { + // Add the complex mission item to the map + Repeater { model: missionController.complexVisualItems - delegate: MapPolyline { - line.color: "white" - line.width: 2 - path: object.gridPoints + delegate: ComplexMissionItem { + map: editorMap } } diff --git a/src/MissionEditor/SurveyComplexItem.qml b/src/MissionEditor/SurveyComplexItem.qml new file mode 100644 index 000000000..d3a2382cb --- /dev/null +++ b/src/MissionEditor/SurveyComplexItem.qml @@ -0,0 +1,59 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtLocation 5.3 +import QtPositioning 5.2 + +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 + +/// Survey Complex Mission Item visuals +Item { + property var map ///< Map control to place item in + + property var _polygon + property var _grid + + Component.onCompleted: { + _polygon = polygonComponent.createObject(map) + _grid = gridComponent.createObject(map) + map.addMapItem(_polygon) + map.addMapItem(_grid) + } + + Component.onDestruction: { + _polygon.destroy() + _grid.destroy() + } + + // Survey area polygon + Component { + id: polygonComponent + + MapPolygon { + color: "green" + opacity: 0.5 + path: object.polygonPath + } + } + + // Survey grid lines + Component { + id: gridComponent + + MapPolyline { + line.color: "white" + line.width: 2 + path: object.gridPoints + } + } +} diff --git a/src/MissionManager/ComplexMissionItem.h b/src/MissionManager/ComplexMissionItem.h index ad512e6ee..5aa8b072d 100644 --- a/src/MissionManager/ComplexMissionItem.h +++ b/src/MissionManager/ComplexMissionItem.h @@ -21,6 +21,7 @@ public: const ComplexMissionItem& operator=(const ComplexMissionItem& other); + Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT) Q_PROPERTY(int lastSequenceNumber READ lastSequenceNumber NOTIFY lastSequenceNumberChanged) Q_PROPERTY(double complexDistance READ complexDistance NOTIFY complexDistanceChanged) @@ -52,6 +53,9 @@ public: /// This mission item attribute specifies the type of the complex item. static const char* jsonComplexItemTypeKey; + /// @return The QML resource file which contains the control which visualizes the item on the map. + virtual QString mapVisualQML(void) const = 0; + signals: void lastSequenceNumberChanged (int lastSequenceNumber); void complexDistanceChanged (double complexDistance); diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 0c9f76b43..26a5c4766 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -32,6 +32,7 @@ public: bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; double greatestDistanceTo (const QGeoCoordinate &other) const final; void setCruiseSpeed (double cruiseSpeed) final; + QString mapVisualQML (void) const final { return QString(); } // Overrides from VisualMissionItem diff --git a/src/MissionManager/SurveyMissionItem.h b/src/MissionManager/SurveyMissionItem.h index 7f46a28ca..6ad612d57 100644 --- a/src/MissionManager/SurveyMissionItem.h +++ b/src/MissionManager/SurveyMissionItem.h @@ -83,6 +83,7 @@ public: bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; double greatestDistanceTo (const QGeoCoordinate &other) const final; void setCruiseSpeed (double cruiseSpeed) final; + QString mapVisualQML (void) const final { return QStringLiteral("SurveyComplexItem.qml"); } // Overrides from VisualMissionItem diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index eed1fdc47..2040fedc4 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -3,6 +3,7 @@ Module QGroundControl.Controls AnalyzePage 1.0 AnalyzePage.qml AppMessages 1.0 AppMessages.qml ClickableColor 1.0 ClickableColor.qml +ComplexMissionItem 1.0 ComplexMissionItem.qml DropButton 1.0 DropButton.qml DropPanel 1.0 DropPanel.qml ExclusiveGroupItem 1.0 ExclusiveGroupItem.qml @@ -50,6 +51,7 @@ SetupPage 1.0 SetupPage.qml SignalStrength 1.0 SignalStrength.qml SliderSwitch 1.0 SliderSwitch.qml SubMenuButton 1.0 SubMenuButton.qml +SurveyComplexItem 1.0 SurveyComplexItem.qml ToolStrip 1.0 ToolStrip.qml VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml -- GitLab From 526e5c531c5c2b3c9387a064122a279c4c3016ff Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 20:17:23 -0800 Subject: [PATCH 308/398] Fact string support --- src/FactSystem/FactControls/FactTextField.qml | 8 +++-- src/FactSystem/FactMetaData.cc | 34 +++++++++++------- src/FactSystem/FactMetaData.h | 5 ++- src/FactSystem/FactSystem.cc | 1 + .../APM/APMParameterMetaData.cc | 36 ++++++++++--------- .../PX4/PX4ParameterMetaData.cc | 36 ++++++++++--------- 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index 494a73f40..dc6206170 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -16,12 +16,14 @@ QGCTextField { showUnits: true showHelp: true - property Fact fact: null + property Fact fact: null + property string _validateString + property bool _factIsString: fact ? fact.type === FactMetaData.valueTypeString : false // At this point all Facts are numeric - inputMethodHints: ScreenTools.isiOS ? - Qt.ImhNone : // iOS numeric keyboard has not done button, we can't use it + inputMethodHints: (_factIsString || ScreenTools.isiOS) ? + Qt.ImhNone : // iOS numeric keyboard has no done button, we can't use it Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard onEditingFinished: { diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index 6d9f47f58..ade759c9a 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -7,12 +7,6 @@ * ****************************************************************************/ - -/// @file -/// @brief Object which exposes a FactMetaData -/// -/// @author Don Gagne - #include "FactMetaData.h" #include "SettingsManager.h" #include "JsonHelper.h" @@ -157,7 +151,7 @@ QVariant FactMetaData::rawDefaultValue(void) const void FactMetaData::setRawDefaultValue(const QVariant& rawDefaultValue) { - if (_rawMin <= rawDefaultValue && rawDefaultValue <= _rawMax) { + if (_type == valueTypeString || (_rawMin <= rawDefaultValue && rawDefaultValue <= _rawMax)) { _rawDefaultValue = rawDefaultValue; _defaultValueAvailable = true; } else { @@ -208,6 +202,8 @@ QVariant FactMetaData::_minForType(void) const return QVariant(-std::numeric_limits::max()); case valueTypeDouble: return QVariant(-std::numeric_limits::max()); + case valueTypeString: + return QVariant(); } // Make windows compiler happy, even switch is full cased @@ -233,6 +229,8 @@ QVariant FactMetaData::_maxForType(void) const return QVariant(std::numeric_limits::max()); case valueTypeDouble: return QVariant(std::numeric_limits::max()); + case valueTypeString: + return QVariant(); } // Make windows compiler happy, even switch is full cased @@ -285,6 +283,10 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO } } break; + case FactMetaData::valueTypeString: + convertOk = true; + typedValue = QVariant(rawValue.toString()); + break; } if (!convertOk) { @@ -340,6 +342,10 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co } } break; + case FactMetaData::valueTypeString: + convertOk = true; + typedValue = QVariant(cookedValue.toString()); + break; } if (!convertOk) { @@ -556,7 +562,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << QStringLiteral("Uint32") << QStringLiteral("Int32") << QStringLiteral("Float") - << QStringLiteral("Double"); + << QStringLiteral("Double") + << QStringLiteral("String"); knownTypes << valueTypeUint8 << valueTypeInt8 @@ -565,7 +572,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << valueTypeUint32 << valueTypeInt32 << valueTypeFloat - << valueTypeDouble; + << valueTypeDouble + << valueTypeString; for (int i=0; i types; - keys << _nameJsonKey << _decimalPlacesJsonKey << _typeJsonKey << _shortDescriptionJsonKey << _longDescriptionJsonKey << _unitsJsonKey << _defaultValueJsonKey << _minJsonKey << _maxJsonKey; - types << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Double << QJsonValue::Double; + keys << _nameJsonKey << _decimalPlacesJsonKey << _typeJsonKey << _shortDescriptionJsonKey << _longDescriptionJsonKey << _unitsJsonKey << _minJsonKey << _maxJsonKey; + types << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Double; if (!JsonHelper::validateKeyTypes(json, keys, types, errorString)) { qWarning() << errorString; return new FactMetaData(valueTypeUint32, metaDataParent); @@ -811,7 +819,7 @@ FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QObjec metaData->setRawUnits(json[_unitsJsonKey].toString()); } if (json.contains(_defaultValueJsonKey)) { - metaData->setRawDefaultValue(json[_defaultValueJsonKey].toDouble()); + metaData->setRawDefaultValue(json[_defaultValueJsonKey]); } if (json.contains(_minJsonKey)) { metaData->setRawMin(json[_minJsonKey].toDouble()); diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index 07d4b4d23..abe3b7a12 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -37,9 +37,12 @@ public: valueTypeUint32, valueTypeInt32, valueTypeFloat, - valueTypeDouble + valueTypeDouble, + valueTypeString } ValueType_t; + Q_ENUM(ValueType_t) + typedef QVariant (*Translator)(const QVariant& from); FactMetaData(QObject* parent = NULL); diff --git a/src/FactSystem/FactSystem.cc b/src/FactSystem/FactSystem.cc index e88b8ce55..0b9cb3de3 100644 --- a/src/FactSystem/FactSystem.cc +++ b/src/FactSystem/FactSystem.cc @@ -30,6 +30,7 @@ void FactSystem::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); qmlRegisterType (_factSystemQmlUri, 1, 0, "Fact"); + qmlRegisterType (_factSystemQmlUri, 1, 0, "FactMetaData"); qmlRegisterType(_factSystemQmlUri, 1, 0, "FactPanelController"); qmlRegisterUncreatableType(_factSystemQmlUri, 1, 0, "FactGroup", "ReferenceOnly"); diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index 929fe9e5e..fa05f49b9 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -39,22 +39,26 @@ QVariant APMParameterMetaData::_stringToTypedVariant(const QString& string, int convertTo = QVariant::Int; // keep compiler warning happy switch (type) { - case FactMetaData::valueTypeUint8: - case FactMetaData::valueTypeUint16: - case FactMetaData::valueTypeUint32: - convertTo = QVariant::UInt; - break; - case FactMetaData::valueTypeInt8: - case FactMetaData::valueTypeInt16: - case FactMetaData::valueTypeInt32: - convertTo = QVariant::Int; - break; - case FactMetaData::valueTypeFloat: - convertTo = QMetaType::Float; - break; - case FactMetaData::valueTypeDouble: - convertTo = QVariant::Double; - break; + case FactMetaData::valueTypeUint8: + case FactMetaData::valueTypeUint16: + case FactMetaData::valueTypeUint32: + convertTo = QVariant::UInt; + break; + case FactMetaData::valueTypeInt8: + case FactMetaData::valueTypeInt16: + case FactMetaData::valueTypeInt32: + convertTo = QVariant::Int; + break; + case FactMetaData::valueTypeFloat: + convertTo = QMetaType::Float; + break; + case FactMetaData::valueTypeDouble: + convertTo = QVariant::Double; + break; + case FactMetaData::valueTypeString: + qWarning() << "Internal Error: No support for string parameters"; + convertTo = QVariant::String; + break; } *convertOk = var.convert(convertTo); diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc index 7fcfaeb7b..3adbce57e 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc @@ -39,22 +39,26 @@ QVariant PX4ParameterMetaData::_stringToTypedVariant(const QString& string, Fact int convertTo = QVariant::Int; // keep compiler warning happy switch (type) { - case FactMetaData::valueTypeUint8: - case FactMetaData::valueTypeUint16: - case FactMetaData::valueTypeUint32: - convertTo = QVariant::UInt; - break; - case FactMetaData::valueTypeInt8: - case FactMetaData::valueTypeInt16: - case FactMetaData::valueTypeInt32: - convertTo = QVariant::Int; - break; - case FactMetaData::valueTypeFloat: - convertTo = QMetaType::Float; - break; - case FactMetaData::valueTypeDouble: - convertTo = QVariant::Double; - break; + case FactMetaData::valueTypeUint8: + case FactMetaData::valueTypeUint16: + case FactMetaData::valueTypeUint32: + convertTo = QVariant::UInt; + break; + case FactMetaData::valueTypeInt8: + case FactMetaData::valueTypeInt16: + case FactMetaData::valueTypeInt32: + convertTo = QVariant::Int; + break; + case FactMetaData::valueTypeFloat: + convertTo = QMetaType::Float; + break; + case FactMetaData::valueTypeDouble: + convertTo = QVariant::Double; + break; + case FactMetaData::valueTypeString: + qWarning() << "Internal Error: No support for string parameters"; + convertTo = QVariant::String; + break; } *convertOk = var.convert(convertTo); -- GitLab From 80ee708ce6f1ca5d487689609f464ecdc563afba Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 18 Feb 2017 20:17:45 -0800 Subject: [PATCH 309/398] Convert mission auto load dir setting to SettingsFact --- src/QmlControls/QGroundControlQmlGlobal.cc | 13 ------ src/QmlControls/QGroundControlQmlGlobal.h | 8 +--- src/SettingsManager.cc | 11 +++++ src/SettingsManager.h | 48 +++++++++++----------- src/SettingsManager.json | 7 ++++ src/Vehicle/Vehicle.cc | 2 +- src/ui/preferences/GeneralSettings.qml | 34 ++++++++++----- 7 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index df23461f7..c43db9012 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -21,7 +21,6 @@ static const char* kQmlGlobalKeyName = "QGCQml"; const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; -const char* QGroundControlQmlGlobal::_missionAutoLoadDirKey = "MissionAutoLoadDir"; QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) : QGCTool(app) @@ -227,15 +226,3 @@ bool QGroundControlQmlGlobal::linesIntersect(QPointF line1A, QPointF line1B, QPo return QLineF(line1A, line1B).intersect(QLineF(line2A, line2B), &intersectPoint) == QLineF::BoundedIntersection && intersectPoint != line1A && intersectPoint != line1B; } - -QString QGroundControlQmlGlobal::missionAutoLoadDir(void) -{ - QSettings settings; - return settings.value(_missionAutoLoadDirKey).toString(); -} - -void QGroundControlQmlGlobal::setMissionAutoLoadDir(const QString& missionAutoLoadDir) -{ - QSettings settings; - settings.setValue(_missionAutoLoadDirKey, missionAutoLoadDir); -} diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index bbd048997..7eaa1c5eb 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -61,7 +61,6 @@ public: Q_PROPERTY(bool isSaveLogPromptNotArmed READ isSaveLogPromptNotArmed WRITE setIsSaveLogPromptNotArmed NOTIFY isSaveLogPromptNotArmedChanged) Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) - Q_PROPERTY(QString missionAutoLoadDir READ missionAutoLoadDir WRITE setMissionAutoLoadDir NOTIFY missionAutoLoadDirChanged STORED false) //------------------------------------------------------------------------- // MavLink Protocol @@ -127,6 +126,8 @@ public: Q_INVOKABLE bool linesIntersect(QPointF xLine1, QPointF yLine1, QPointF xLine2, QPointF yLine2); + Q_INVOKABLE QString urlToLocalFile(QUrl url) { return url.toLocalFile(); } + // Property accesors FlightMapSettings* flightMapSettings () { return _flightMapSettings; } @@ -174,9 +175,6 @@ public: QString qgcVersion(void) const { return qgcApp()->applicationVersion(); } - static QString missionAutoLoadDir(void); - static void setMissionAutoLoadDir(const QString& missionAutoLoadDir); - // Overrides from QGCTool virtual void setToolbox(QGCToolbox* toolbox); @@ -192,7 +190,6 @@ signals: void mavlinkSystemIDChanged (int id); void flightMapPositionChanged (QGeoCoordinate flightMapPosition); void flightMapZoomChanged (double flightMapZoom); - void missionAutoLoadDirChanged (QString missionAutoLoadDir); private: FlightMapSettings* _flightMapSettings; @@ -214,7 +211,6 @@ private: static const char* _virtualTabletJoystickKey; static const char* _baseFontPointSizeKey; - static const char* _missionAutoLoadDirKey; }; #endif diff --git a/src/SettingsManager.cc b/src/SettingsManager.cc index 0408440f9..42b842253 100644 --- a/src/SettingsManager.cc +++ b/src/SettingsManager.cc @@ -21,6 +21,7 @@ const char* SettingsManager::areaUnitsSettingsName = "Are const char* SettingsManager::speedUnitsSettingsName = "SpeedUnits"; const char* SettingsManager::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; const char* SettingsManager::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; +const char* SettingsManager::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; SettingsManager::SettingsManager(QGCApplication* app) : QGCTool(app) @@ -33,6 +34,7 @@ SettingsManager::SettingsManager(QGCApplication* app) , _speedUnitsFact(NULL) , _batteryPercentRemainingAnnounceFact(NULL) , _defaultMissionItemAltitudeFact(NULL) + , _missionAutoLoadDirFact(NULL) { } @@ -103,6 +105,15 @@ Fact* SettingsManager::defaultMissionItemAltitude(void) return _defaultMissionItemAltitudeFact; } +Fact* SettingsManager::missionAutoLoadDir(void) +{ + if (!_missionAutoLoadDirFact) { + _missionAutoLoadDirFact = _createSettingsFact(missionAutoLoadDirSettingsName); + } + + return _missionAutoLoadDirFact; +} + Fact* SettingsManager::distanceUnits(void) { if (!_distanceUnitsFact) { diff --git a/src/SettingsManager.h b/src/SettingsManager.h index bae2fd3b9..14c6c4ec5 100644 --- a/src/SettingsManager.h +++ b/src/SettingsManager.h @@ -54,25 +54,27 @@ public: Q_ENUMS(AreaUnits) Q_ENUMS(SpeedUnits) - Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) - Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) - Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) - Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) - Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) - Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) - Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) - Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) - Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) - - Fact* offlineEditingFirmwareType (void); - Fact* offlineEditingVehicleType (void); - Fact* offlineEditingCruiseSpeed (void); - Fact* offlineEditingHoverSpeed (void); - Fact* distanceUnits (void); - Fact* areaUnits (void); - Fact* speedUnits (void); - Fact* batteryPercentRemainingAnnounce(void); - Fact* defaultMissionItemAltitude (void); + Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) + Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) + Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) + Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) + Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) + Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) + Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) + Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) + Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) + Q_PROPERTY(Fact* missionAutoLoadDir READ missionAutoLoadDir CONSTANT) + + Fact* offlineEditingFirmwareType (void); + Fact* offlineEditingVehicleType (void); + Fact* offlineEditingCruiseSpeed (void); + Fact* offlineEditingHoverSpeed (void); + Fact* distanceUnits (void); + Fact* areaUnits (void); + Fact* speedUnits (void); + Fact* batteryPercentRemainingAnnounce (void); + Fact* defaultMissionItemAltitude (void); + Fact* missionAutoLoadDir (void); // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); @@ -86,13 +88,8 @@ public: static const char* speedUnitsSettingsName; static const char* batteryPercentRemainingAnnounceSettingsName; static const char* defaultMissionItemAltitudeSettingsName; + static const char* missionAutoLoadDirSettingsName; -public slots: - -signals: - -private slots: - private: SettingsFact* _createSettingsFact(const QString& name); @@ -107,6 +104,7 @@ private: SettingsFact* _speedUnitsFact; SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; + SettingsFact* _missionAutoLoadDirFact; }; #endif diff --git a/src/SettingsManager.json b/src/SettingsManager.json index e58a6e2a6..aca30132d 100644 --- a/src/SettingsManager.json +++ b/src/SettingsManager.json @@ -54,5 +54,12 @@ "min": 0.0, "units": "meters", "decimalPlaces": 2 +}, +{ + "name": "MissionAutoLoadDir", + "shortDescription": "Mission auto load directory", + "longDescription": "The directory from which missions will be automaticallu loaded onto a vehicle when it connects.", + "type": "string", + "defaultValue": "" } ] diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 0ad9378c5..5eab8248b 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1579,7 +1579,7 @@ void Vehicle::_parametersReady(bool parametersReady) { if (parametersReady && !_missionManagerInitialRequestSent) { _missionManagerInitialRequestSent = true; - QString missionAutoLoadDirPath = QGroundControlQmlGlobal::missionAutoLoadDir(); + QString missionAutoLoadDirPath = _settingsManager->missionAutoLoadDir()->rawValue().toString(); if (missionAutoLoadDirPath.isEmpty()) { _missionManager->requestMissionItems(); } else { diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index f0f231b24..3783b1ba0 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -33,6 +33,7 @@ QGCView { anchors.margins: ScreenTools.defaultFontPixelWidth property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.batteryPercentRemainingAnnounce + property Fact _autoLoadDir: QGroundControl.settingsManager.missionAutoLoadDir property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -331,30 +332,41 @@ QGCView { } } //----------------------------------------------------------------- - //-- AutoLoad + //-- Mission AutoLoad Row { spacing: ScreenTools.defaultFontPixelWidth QGCCheckBox { id: autoLoadCheckbox anchors.verticalCenter: parent.verticalCenter text: qsTr("AutoLoad mission directory:") - checked: QGroundControl.missionAutoLoadDir != "" + checked: _autoLoadDir.valueString onClicked: { - autoLoadDir.enabled = checked - if (!checked) { - QGroundControl.missionAutoLoadDir = "" - autoLoadDir.text = "" + if (checked) { + _autoLoadDir.rawValue = QGroundControl.urlToLocalFile(autoloadDirPicker.shortcuts.home) + } else { + _autoLoadDir.rawValue = "" } } } - QGCTextField { - id: autoLoadDir + FactTextField { + id: autoLoadDirField width: _editFieldWidth enabled: autoLoadCheckbox.checked anchors.verticalCenter: parent.verticalCenter - text: QGroundControl.missionAutoLoadDir - onEditingFinished: QGroundControl.missionAutoLoadDir = text + fact: _autoLoadDir + } + QGCButton { + text: qsTr("Browse") + onClicked: autoloadDirPicker.visible = true + + FileDialog { + id: autoloadDirPicker + title: qsTr("Choose the location of mission file.") + folder: shortcuts.home + selectFolder: true + onAccepted: _autoLoadDir.rawValue = QGroundControl.urlToLocalFile(autoloadDirPicker.fileUrl) + } } } //----------------------------------------------------------------- @@ -561,7 +573,7 @@ QGCView { readOnly: true text: QGroundControl.videoManager.videoSavePath } - Button { + QGCButton { text: "Browse" onClicked: fileDialog.visible = true } -- GitLab From f854e93a78b008a4da06564e34ea86c571f170ac Mon Sep 17 00:00:00 2001 From: Pritam Ghanghas Date: Mon, 20 Feb 2017 02:47:27 +0530 Subject: [PATCH 310/398] Fixed a small binding loop in RallyPointEditorHeader.qml --- src/MissionEditor/RallyPointEditorHeader.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionEditor/RallyPointEditorHeader.qml b/src/MissionEditor/RallyPointEditorHeader.qml index 8a3828a6f..ada44fc27 100644 --- a/src/MissionEditor/RallyPointEditorHeader.qml +++ b/src/MissionEditor/RallyPointEditorHeader.qml @@ -37,7 +37,7 @@ QGCFlickable { anchors.left: parent.left anchors.right: parent.right anchors.top: editorLabel.bottom - height: helpLabel.y + helpLabel.height + (_margin * 2) + height: infoLabel.height + helpLabel.height + (_margin * 2) color: qgcPal.windowShadeDark radius: _radius -- GitLab From 9b384a958b4faf2e14fcf53e38e1b12807573c2f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 19 Feb 2017 19:53:15 -0800 Subject: [PATCH 311/398] Add bool support to FactSystem Convert bool settings to SettingsFact --- src/FactSystem/Fact.h | 5 ++- src/FactSystem/FactControls/FactCheckBox.qml | 6 +++- src/FactSystem/FactControls/FactTextField.qml | 3 +- src/FactSystem/FactMetaData.cc | 30 ++++++++++++++--- src/FactSystem/FactMetaData.h | 5 ++- src/FactSystem/SettingsFact.cc | 6 +++- .../APM/APMParameterMetaData.cc | 4 +++ .../PX4/PX4ParameterMetaData.cc | 4 +++ src/GAudioOutput.cc | 25 ++------------ src/GAudioOutput.h | 11 ------- src/QGCApplication.cc | 28 ---------------- src/QGCApplication.h | 11 ------- src/QmlControls/QGroundControlQmlGlobal.cc | 18 ---------- src/QmlControls/QGroundControlQmlGlobal.h | 12 ------- src/SettingsManager.cc | 33 +++++++++++++++++++ src/SettingsManager.h | 12 +++++++ src/SettingsManager.json | 23 ++++++++++++- src/comm/MAVLinkProtocol.cc | 4 ++- src/ui/MainWindow.cc | 5 --- src/ui/MainWindow.ui | 9 ----- src/ui/preferences/GeneralSettings.qml | 22 ++++--------- 21 files changed, 130 insertions(+), 146 deletions(-) diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 4d5078695..c3db1f46b 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -55,7 +55,6 @@ public: Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(bool rebootRequired READ rebootRequired CONSTANT) Q_PROPERTY(QString shortDescription READ shortDescription CONSTANT) - Q_PROPERTY(FactMetaData::ValueType_t type READ type CONSTANT) Q_PROPERTY(QString units READ cookedUnits CONSTANT) Q_PROPERTY(QVariant value READ cookedValue WRITE setCookedValue NOTIFY valueChanged) Q_PROPERTY(QVariant rawValue READ rawValue WRITE setRawValue NOTIFY rawValueChanged) @@ -63,6 +62,8 @@ public: Q_PROPERTY(QString valueString READ cookedValueString NOTIFY valueChanged) Q_PROPERTY(QString enumOrValueString READ enumOrValueString NOTIFY valueChanged) Q_PROPERTY(double increment READ increment CONSTANT) + Q_PROPERTY(bool typeIsString READ typeIsString CONSTANT) + Q_PROPERTY(bool typeIsBool READ typeIsBool CONSTANT) /// Convert and validate value /// @param convertOnly true: validate type conversion only, false: validate against meta data as well @@ -103,6 +104,8 @@ public: bool rebootRequired (void) const; QString enumOrValueString (void); // This is not const, since an unknown value can modify the enum lists double increment (void) const; + bool typeIsString (void) const { return type() == FactMetaData::valueTypeString; } + bool typeIsBool (void) const { return type() == FactMetaData::valueTypeBool; } /// Returns the values as a string with full 18 digit precision if float/double. QString rawValueStringFullPrecision(void) const; diff --git a/src/FactSystem/FactControls/FactCheckBox.qml b/src/FactSystem/FactControls/FactCheckBox.qml index 06d0eec0f..f64d20041 100644 --- a/src/FactSystem/FactControls/FactCheckBox.qml +++ b/src/FactSystem/FactControls/FactCheckBox.qml @@ -11,7 +11,11 @@ QGCCheckBox { property variant checkedValue: 1 property variant uncheckedValue: 0 - checkedState: fact ? (fact.value === checkedValue ? Qt.Checked : Qt.Unchecked) : Qt.Unchecked + checkedState: fact ? + (fact.typeIsBool ? + (fact.value === true ? Qt.Checked : Qt.Unchecked) : + (fact.value === checkedValue ? Qt.Checked : Qt.Unchecked)) : + Qt.Unchecked text: qsTr("Label") diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index dc6206170..3dd02aa68 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -19,10 +19,9 @@ QGCTextField { property Fact fact: null property string _validateString - property bool _factIsString: fact ? fact.type === FactMetaData.valueTypeString : false // At this point all Facts are numeric - inputMethodHints: (_factIsString || ScreenTools.isiOS) ? + inputMethodHints: (fact.typeIsString || ScreenTools.isiOS) ? Qt.ImhNone : // iOS numeric keyboard has no done button, we can't use it Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index ade759c9a..aa0575d19 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -204,6 +204,8 @@ QVariant FactMetaData::_minForType(void) const return QVariant(-std::numeric_limits::max()); case valueTypeString: return QVariant(); + case valueTypeBool: + return QVariant(0); } // Make windows compiler happy, even switch is full cased @@ -231,6 +233,8 @@ QVariant FactMetaData::_maxForType(void) const return QVariant(std::numeric_limits::max()); case valueTypeString: return QVariant(); + case valueTypeBool: + return QVariant(1); } // Make windows compiler happy, even switch is full cased @@ -287,6 +291,10 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO convertOk = true; typedValue = QVariant(rawValue.toString()); break; + case FactMetaData::valueTypeBool: + convertOk = true; + typedValue = QVariant(rawValue.toBool()); + break; } if (!convertOk) { @@ -346,6 +354,10 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co convertOk = true; typedValue = QVariant(cookedValue.toString()); break; + case FactMetaData::valueTypeBool: + convertOk = true; + typedValue = QVariant(cookedValue.toBool()); + break; } if (!convertOk) { @@ -563,7 +575,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << QStringLiteral("Int32") << QStringLiteral("Float") << QStringLiteral("Double") - << QStringLiteral("String"); + << QStringLiteral("String") + << QStringLiteral("Bool"); knownTypes << valueTypeUint8 << valueTypeInt8 @@ -573,7 +586,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << valueTypeInt32 << valueTypeFloat << valueTypeDouble - << valueTypeString; + << valueTypeString + << valueTypeBool; for (int i=0; isetRawUnits(json[_unitsJsonKey].toString()); } if (json.contains(_defaultValueJsonKey)) { - metaData->setRawDefaultValue(json[_defaultValueJsonKey]); + metaData->setRawDefaultValue(json[_defaultValueJsonKey].toVariant()); } if (json.contains(_minJsonKey)) { - metaData->setRawMin(json[_minJsonKey].toDouble()); + QVariant typedValue; + QString errorString; + metaData->convertAndValidateRaw(json[_minJsonKey].toVariant(), true /* convertOnly */, typedValue, errorString); + metaData->setRawMin(typedValue); } if (json.contains(_maxJsonKey)) { - metaData->setRawMax(json[_maxJsonKey].toDouble()); + QVariant typedValue; + QString errorString; + metaData->convertAndValidateRaw(json[_maxJsonKey].toVariant(), true /* convertOnly */, typedValue, errorString); + metaData->setRawMax(typedValue); } return metaData; diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index abe3b7a12..3bc51f295 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -38,11 +38,10 @@ public: valueTypeInt32, valueTypeFloat, valueTypeDouble, - valueTypeString + valueTypeString, + valueTypeBool } ValueType_t; - Q_ENUM(ValueType_t) - typedef QVariant (*Translator)(const QVariant& from); FactMetaData(QObject* parent = NULL); diff --git a/src/FactSystem/SettingsFact.cc b/src/FactSystem/SettingsFact.cc index 71b2ddca1..d4915cd0d 100644 --- a/src/FactSystem/SettingsFact.cc +++ b/src/FactSystem/SettingsFact.cc @@ -33,7 +33,11 @@ SettingsFact::SettingsFact(QString settingGroup, FactMetaData* metaData, QObject // Allow core plugin a chance to override the default value metaData->setRawDefaultValue(qgcApp()->toolbox()->corePlugin()->overrideSettingsDefault(metaData->name(), metaData->rawDefaultValue())); setMetaData(metaData); - _rawValue = settings.value(_name, metaData->rawDefaultValue()); + + QVariant typedValue; + QString errorString; + metaData->convertAndValidateRaw(settings.value(_name, metaData->rawDefaultValue()), true /* conertOnly */, typedValue, errorString); + _rawValue = typedValue; connect(this, &Fact::rawValueChanged, this, &SettingsFact::_rawValueChanged); } diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index fa05f49b9..ac9a11fda 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -59,6 +59,10 @@ QVariant APMParameterMetaData::_stringToTypedVariant(const QString& string, qWarning() << "Internal Error: No support for string parameters"; convertTo = QVariant::String; break; + case FactMetaData::valueTypeBool: + qWarning() << "Internal Error: No support for string parameters"; + convertTo = QVariant::Bool; + break; } *convertOk = var.convert(convertTo); diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc index 3adbce57e..a3c9e1e20 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc @@ -59,6 +59,10 @@ QVariant PX4ParameterMetaData::_stringToTypedVariant(const QString& string, Fact qWarning() << "Internal Error: No support for string parameters"; convertTo = QVariant::String; break; + case FactMetaData::valueTypeBool: + qWarning() << "Internal Error: No support for string parameters"; + convertTo = QVariant::Bool; + break; } *convertOk = var.convert(convertTo); diff --git a/src/GAudioOutput.cc b/src/GAudioOutput.cc index 584b5ac5c..c20f3a0b2 100644 --- a/src/GAudioOutput.cc +++ b/src/GAudioOutput.cc @@ -18,31 +18,25 @@ */ #include -#include #include #include "GAudioOutput.h" #include "QGCApplication.h" #include "QGC.h" +#include "SettingsManager.h" #if defined __android__ #include #include #endif -const char* GAudioOutput::_mutedKey = "AudioMuted"; - GAudioOutput::GAudioOutput(QGCApplication* app) : QGCTool(app) - , muted(false) #ifndef __android__ , thread(new QThread()) , worker(new QGCAudioWorker()) #endif { - QSettings settings; - muted = settings.value(_mutedKey, false).toBool(); - muted |= app->runningUnitTests(); #ifndef __android__ worker->moveToThread(thread); connect(this, &GAudioOutput::textToSpeak, worker, &QGCAudioWorker::say); @@ -60,23 +54,10 @@ GAudioOutput::~GAudioOutput() } -void GAudioOutput::mute(bool mute) -{ - QSettings settings; - muted = mute; - settings.setValue(_mutedKey, mute); -#ifndef __android__ - emit mutedChanged(mute); -#endif -} - -bool GAudioOutput::isMuted() -{ - return muted; -} - bool GAudioOutput::say(const QString& inText) { + bool muted = qgcApp()->toolbox()->settingsManager()->audioMuted()->rawValue().toBool(); + muted |= qgcApp()->runningUnitTests(); if (!muted && !qgcApp()->runningUnitTests()) { #if defined __android__ #if defined QGC_SPEECH_ENABLED diff --git a/src/GAudioOutput.h b/src/GAudioOutput.h index 4827e4bb3..9a1e40f9d 100644 --- a/src/GAudioOutput.h +++ b/src/GAudioOutput.h @@ -62,30 +62,19 @@ public: AUDIO_SEVERITY_DEBUG = 7 }; - /** @brief Get the mute state */ - bool isMuted(); - public slots: /** @brief Say this text */ bool say(const QString& text); - /** @brief Mute/unmute sound */ - void mute(bool mute); signals: - void mutedChanged(bool); bool textToSpeak(QString text); void beepOnce(); protected: - bool muted; - #if !defined __android__ QThread* thread; QGCAudioWorker* worker; #endif - -private: - static const char* _mutedKey; }; #endif // AUDIOOUTPUT_H diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index e3d48f20d..a92a4ed89 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -115,8 +115,6 @@ const char* QGCApplication::telemetryFileExtension = "tlog"; const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot"; const char* QGCApplication::_settingsVersionKey = "SettingsVersion"; -const char* QGCApplication::_promptFlightDataSave = "PromptFLightDataSave"; -const char* QGCApplication::_promptFlightDataSaveNotArmed = "PromptFLightDataSaveNotArmed"; const char* QGCApplication::_styleKey = "StyleIsDark"; const char* QGCApplication::_lastKnownHomePositionLatKey = "LastKnownHomePositionLat"; const char* QGCApplication::_lastKnownHomePositionLonKey = "LastKnownHomePositionLon"; @@ -466,32 +464,6 @@ void QGCApplication::clearDeleteAllSettingsNextBoot(void) settings.remove(_deleteAllSettingsKey); } -bool QGCApplication::promptFlightDataSave(void) -{ - QSettings settings; - - return settings.value(_promptFlightDataSave, true).toBool(); -} - -bool QGCApplication::promptFlightDataSaveNotArmed(void) -{ - QSettings settings; - - return settings.value(_promptFlightDataSaveNotArmed, false).toBool(); -} - -void QGCApplication::setPromptFlightDataSave(bool promptForSave) -{ - QSettings settings; - settings.setValue(_promptFlightDataSave, promptForSave); -} - -void QGCApplication::setPromptFlightDataSaveNotArmed(bool promptForSave) -{ - QSettings settings; - settings.setValue(_promptFlightDataSaveNotArmed, promptForSave); -} - /// @brief Returns the QGCApplication object singleton. QGCApplication* qgcApp(void) { diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 07bc901c3..c080b2490 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -75,15 +75,6 @@ public: /// @brief Clears the persistent flag to delete all settings the next time QGroundControl is started. void clearDeleteAllSettingsNextBoot(void); - /// @return true: Prompt to save log file when vehicle goes away - bool promptFlightDataSave(void); - - /// @return true: Prompt to save log file even if vehicle was not armed - bool promptFlightDataSaveNotArmed(void); - - void setPromptFlightDataSave(bool promptForSave); - void setPromptFlightDataSaveNotArmed(bool promptForSave); - /// @brief Returns truee if unit test are being run bool runningUnitTests(void) { return _runningUnitTests; } @@ -195,8 +186,6 @@ private: static const char* _settingsVersionKey; ///< Settings key which hold settings version static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted - static const char* _promptFlightDataSave; ///< Settings key for promptFlightDataSave - static const char* _promptFlightDataSaveNotArmed; ///< Settings key for promptFlightDataSaveNotArmed static const char* _styleKey; ///< Settings key for UI style static const char* _lastKnownHomePositionLatKey; static const char* _lastKnownHomePositionLonKey; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index c43db9012..66e65e167 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -163,24 +163,6 @@ void QGroundControlQmlGlobal::setIsDarkStyle(bool dark) emit isDarkStyleChanged(dark); } -void QGroundControlQmlGlobal::setIsAudioMuted(bool muted) -{ - qgcApp()->toolbox()->audioOutput()->mute(muted); - emit isAudioMutedChanged(muted); -} - -void QGroundControlQmlGlobal::setIsSaveLogPrompt(bool prompt) -{ - qgcApp()->setPromptFlightDataSave(prompt); - emit isSaveLogPromptChanged(prompt); -} - -void QGroundControlQmlGlobal::setIsSaveLogPromptNotArmed(bool prompt) -{ - qgcApp()->setPromptFlightDataSaveNotArmed(prompt); - emit isSaveLogPromptNotArmedChanged(prompt); -} - void QGroundControlQmlGlobal::setIsVersionCheckEnabled(bool enable) { qgcApp()->toolbox()->mavlinkProtocol()->enableVersionCheck(enable); diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 7eaa1c5eb..dc28ca655 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -56,9 +56,6 @@ public: // Various QGC settings exposed to Qml Q_PROPERTY(bool isDarkStyle READ isDarkStyle WRITE setIsDarkStyle NOTIFY isDarkStyleChanged) // TODO: Should be in ScreenTools? - Q_PROPERTY(bool isAudioMuted READ isAudioMuted WRITE setIsAudioMuted NOTIFY isAudioMutedChanged) - Q_PROPERTY(bool isSaveLogPrompt READ isSaveLogPrompt WRITE setIsSaveLogPrompt NOTIFY isSaveLogPromptChanged) - Q_PROPERTY(bool isSaveLogPromptNotArmed READ isSaveLogPromptNotArmed WRITE setIsSaveLogPromptNotArmed NOTIFY isSaveLogPromptNotArmedChanged) Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) @@ -146,9 +143,6 @@ public: qreal zOrderMapItems () { return 50; } bool isDarkStyle () { return _app->styleIsDark(); } - bool isAudioMuted () { return _toolbox->audioOutput()->isMuted(); } - bool isSaveLogPrompt () { return _app->promptFlightDataSave(); } - bool isSaveLogPromptNotArmed () { return _app->promptFlightDataSaveNotArmed(); } bool virtualTabletJoystick () { return _virtualTabletJoystick; } qreal baseFontPointSize () { return _baseFontPointSize; } @@ -160,9 +154,6 @@ public: int supportedFirmwareCount (); void setIsDarkStyle (bool dark); - void setIsAudioMuted (bool muted); - void setIsSaveLogPrompt (bool prompt); - void setIsSaveLogPromptNotArmed (bool prompt); void setVirtualTabletJoystick (bool enabled); void setBaseFontPointSize (qreal size); @@ -180,9 +171,6 @@ public: signals: void isDarkStyleChanged (bool dark); - void isAudioMutedChanged (bool muted); - void isSaveLogPromptChanged (bool prompt); - void isSaveLogPromptNotArmedChanged (bool prompt); void virtualTabletJoystickChanged (bool enabled); void baseFontPointSizeChanged (qreal size); void isMultiplexingEnabledChanged (bool enabled); diff --git a/src/SettingsManager.cc b/src/SettingsManager.cc index 42b842253..115c610c0 100644 --- a/src/SettingsManager.cc +++ b/src/SettingsManager.cc @@ -22,6 +22,9 @@ const char* SettingsManager::speedUnitsSettingsName = "Spe const char* SettingsManager::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; const char* SettingsManager::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; const char* SettingsManager::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; +const char* SettingsManager::promptFlightTelemetrySaveName = "PromptFLightDataSave"; +const char* SettingsManager::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; +const char* SettingsManager::audioMutedName = "AudioMuted"; SettingsManager::SettingsManager(QGCApplication* app) : QGCTool(app) @@ -35,6 +38,9 @@ SettingsManager::SettingsManager(QGCApplication* app) , _batteryPercentRemainingAnnounceFact(NULL) , _defaultMissionItemAltitudeFact(NULL) , _missionAutoLoadDirFact(NULL) + , _promptFlightTelemetrySave(NULL) + , _promptFlightTelemetrySaveNotArmed(NULL) + , _audioMuted(NULL) { } @@ -114,6 +120,33 @@ Fact* SettingsManager::missionAutoLoadDir(void) return _missionAutoLoadDirFact; } +Fact* SettingsManager::promptFlightTelemetrySave(void) +{ + if (!_promptFlightTelemetrySave) { + _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); + } + + return _promptFlightTelemetrySave; +} + +Fact* SettingsManager::promptFlightTelemetrySaveNotArmed(void) +{ + if (!_promptFlightTelemetrySaveNotArmed) { + _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); + } + + return _promptFlightTelemetrySaveNotArmed; +} + +Fact* SettingsManager::audioMuted(void) +{ + if (!_audioMuted) { + _audioMuted = _createSettingsFact(audioMutedName); + } + + return _audioMuted; +} + Fact* SettingsManager::distanceUnits(void) { if (!_distanceUnitsFact) { diff --git a/src/SettingsManager.h b/src/SettingsManager.h index 14c6c4ec5..1df5d57a0 100644 --- a/src/SettingsManager.h +++ b/src/SettingsManager.h @@ -64,6 +64,9 @@ public: Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) Q_PROPERTY(Fact* missionAutoLoadDir READ missionAutoLoadDir CONSTANT) + Q_PROPERTY(Fact* promptFlightTelemetrySave READ promptFlightTelemetrySave CONSTANT) + Q_PROPERTY(Fact* promptFlightTelemetrySaveNotArmed READ promptFlightTelemetrySaveNotArmed CONSTANT) + Q_PROPERTY(Fact* audioMuted READ audioMuted CONSTANT) Fact* offlineEditingFirmwareType (void); Fact* offlineEditingVehicleType (void); @@ -75,6 +78,9 @@ public: Fact* batteryPercentRemainingAnnounce (void); Fact* defaultMissionItemAltitude (void); Fact* missionAutoLoadDir (void); + Fact* promptFlightTelemetrySave (void); + Fact* promptFlightTelemetrySaveNotArmed (void); + Fact* audioMuted (void); // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); @@ -89,6 +95,9 @@ public: static const char* batteryPercentRemainingAnnounceSettingsName; static const char* defaultMissionItemAltitudeSettingsName; static const char* missionAutoLoadDirSettingsName; + static const char* promptFlightTelemetrySaveName; + static const char* promptFlightTelemetrySaveNotArmedName; + static const char* audioMutedName; private: SettingsFact* _createSettingsFact(const QString& name); @@ -105,6 +114,9 @@ private: SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; SettingsFact* _missionAutoLoadDirFact; + SettingsFact* _promptFlightTelemetrySave; + SettingsFact* _promptFlightTelemetrySaveNotArmed; + SettingsFact* _audioMuted; }; #endif diff --git a/src/SettingsManager.json b/src/SettingsManager.json index aca30132d..439727ccc 100644 --- a/src/SettingsManager.json +++ b/src/SettingsManager.json @@ -58,8 +58,29 @@ { "name": "MissionAutoLoadDir", "shortDescription": "Mission auto load directory", - "longDescription": "The directory from which missions will be automaticallu loaded onto a vehicle when it connects.", + "longDescription": "The directory from which missions will be automatically loaded onto a vehicle when it connects. The mission file must be named AutoLoad#.mission where the # is replaced with the vehicle id.", "type": "string", "defaultValue": "" +}, +{ + "name": "PromptFLightDataSave", + "shortDescription": "Prompt to save Flight Data Log after each flight", + "longDescription": "If this option is enabled you will be prompted to save Flight Data Log after each flight completes.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "PromptFLightDataSaveNotArmed", + "shortDescription": "Prompt to save Flight Data Log even if vehicle was not armed", + "longDescription": "If this option is enabled you will be prompted to save Flight Data Log even if vehicle was never armed.", + "type": "bool", + "defaultValue": false +}, +{ + "name": "AudioMuted", + "shortDescription": "Mute audio output", + "longDescription": "If this option is enabled all audio output will be muted.", + "type": "bool", + "defaultValue": false } ] diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index d32455a9f..3ca92d845 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -35,6 +35,7 @@ #include "QGCApplication.h" #include "QGCLoggingCategory.h" #include "MultiVehicleManager.h" +#include "SettingsManager.h" Q_DECLARE_METATYPE(mavlink_message_t) @@ -422,7 +423,8 @@ void MAVLinkProtocol::_stopLogging(void) { if (_closeLogFile()) { // If the signals are not connected it means we are running a unit test. In that case just delete log files - if ((_vehicleWasArmed || _app->promptFlightDataSaveNotArmed()) && _app->promptFlightDataSave()) { + SettingsManager* settingsManager = _app->toolbox()->settingsManager(); + if ((_vehicleWasArmed || settingsManager->promptFlightTelemetrySaveNotArmed()->rawValue().toBool()) && settingsManager->promptFlightTelemetrySave()->rawValue().toBool()) { emit saveTempFlightDataLog(_tempLogFile.fileName()); } else { QFile::remove(_tempLogFile.fileName()); diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 5b1fb93b4..c24a052ed 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -463,11 +463,6 @@ void MainWindow::configureWindowName() **/ void MainWindow::connectCommonActions() { - // Audio output - _ui.actionMuteAudioOutput->setChecked(qgcApp()->toolbox()->audioOutput()->isMuted()); - connect(qgcApp()->toolbox()->audioOutput(), &GAudioOutput::mutedChanged, _ui.actionMuteAudioOutput, &QAction::setChecked); - connect(_ui.actionMuteAudioOutput, &QAction::triggered, qgcApp()->toolbox()->audioOutput(), &GAudioOutput::mute); - // Connect internal actions connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::vehicleAdded, this, &MainWindow::_vehicleAdded); } diff --git a/src/ui/MainWindow.ui b/src/ui/MainWindow.ui index 4d6bb7f53..1e2fcd86f 100644 --- a/src/ui/MainWindow.ui +++ b/src/ui/MainWindow.ui @@ -59,7 +59,6 @@ File - @@ -86,14 +85,6 @@ Manage Communication Links - - - true - - - Mute Audio Output - - true diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 3783b1ba0..75fe1906c 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -231,34 +231,26 @@ QGCView { } //----------------------------------------------------------------- //-- Audio preferences - QGCCheckBox { - text: qsTr("Mute all audio output") - checked: QGroundControl.isAudioMuted - onClicked: { - QGroundControl.isAudioMuted = checked - } + FactCheckBox { + text: qsTr("Mute all audio output") + fact: QGroundControl.settingsManager.audioMuted } //----------------------------------------------------------------- //-- Prompt Save Log QGCCheckBox { id: promptSaveLog text: qsTr("Prompt to save Flight Data Log after each flight") - checked: QGroundControl.isSaveLogPrompt + checked: QGroundControl.settingsManager.promptFlightTelemetrySave + //fact: QGroundControl.settingsManager.promptFlightTelemetrySave visible: !ScreenTools.isMobile - onClicked: { - QGroundControl.isSaveLogPrompt = checked - } } //----------------------------------------------------------------- //-- Prompt Save even if not armed - QGCCheckBox { + FactCheckBox { text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - checked: QGroundControl.isSaveLogPromptNotArmed + fact: QGroundControl.settingsManager.promptFlightTelemetrySaveNotArmed visible: !ScreenTools.isMobile enabled: promptSaveLog.checked - onClicked: { - QGroundControl.isSaveLogPromptNotArmed = checked - } } //----------------------------------------------------------------- //-- Clear settings -- GitLab From 549936ecef02359addf7cf62b10d2366876744a2 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 20 Feb 2017 09:23:22 -0500 Subject: [PATCH 312/398] Update mavlink v2 --- libs/mavlink/include/mavlink/v2.0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/mavlink/include/mavlink/v2.0 b/libs/mavlink/include/mavlink/v2.0 index 094e765e6..f29883acd 160000 --- a/libs/mavlink/include/mavlink/v2.0 +++ b/libs/mavlink/include/mavlink/v2.0 @@ -1 +1 @@ -Subproject commit 094e765e6353390ac2973023287b7022e696a57e +Subproject commit f29883acd90be17987d0cba79a1c569a5427d01d -- GitLab From d50af9fc6a97959529d06fb14b4d4fc189229667 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 10:27:55 -0800 Subject: [PATCH 313/398] Beta firmware drops using new format --- src/VehicleSetup/FirmwareUpgradeController.cc | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index 66d6e2697..838da4133 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -195,12 +195,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, StableFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4-heli/ArduCopter-v4.px4"}, { AutoPilotStackAPM, StableFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/stable/PX4/ArduPlane-v4.px4"}, { AutoPilotStackAPM, StableFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/stable/PX4/APMrover2-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-quad/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa-quad/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-hexa/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-tri/ArduCopter-v4.px4"}, - { AutoPilotStackAPM, BetaFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-y6/ArduCopter-v4.px4"}, + { AutoPilotStackAPM, BetaFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4/ArduCopter-v4.px4"}, { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v4.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v4.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v4.px4"}, @@ -224,12 +219,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, StableFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, StableFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/stable/PX4/ArduPlane-v2.px4"}, { AutoPilotStackAPM, StableFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/stable/PX4/APMrover2-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-hexa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-tri/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-y6/ArduCopter-v2.px4"}, + { AutoPilotStackAPM, BetaFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4/ArduCopter-v2.px4"}, { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v2.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v2.px4"}, @@ -253,15 +243,10 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, StableFirmware, HeliFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/stable/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, StableFirmware, PlaneFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Plane/stable/PX4/ArduPlane-v2.px4"}, { AutoPilotStackAPM, StableFirmware, RoverFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Rover/stable/PX4/APMrover2-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, QuadFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, X8Firmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-octa-quad/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, HexaFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-hexa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, OctoFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-octa/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, YFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-tri/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, Y6Firmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-y6/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/beta/PX4-heli/ArduCopter-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Plane/beta/PX4/ArduPlane-v2.px4"}, - { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Rover/beta/PX4/APMrover2-v2.px4"}, + { AutoPilotStackAPM, BetaFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4/ArduCopter-v2.px4"}, + { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v2.px4"}, + { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v2.px4"}, + { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, QuadFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-quad/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, X8Firmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-octa-quad/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, HexaFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Copter/latest/PX4-hexa/ArduCopter-v2.px4"}, @@ -287,12 +272,7 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, StableFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4-heli/ArduCopter-v1.px4"}, { AutoPilotStackAPM, StableFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/stable/PX4/ArduPlane-v1.px4"}, { AutoPilotStackAPM, StableFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/stable/PX4/APMrover2-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, QuadFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-quad/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, X8Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa-quad/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, HexaFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-hexa/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, OctoFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-octa/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, YFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-tri/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, Y6Firmware, "http://firmware.ardupilot.org/Copter/beta/PX4-y6/ArduCopter-v1.px4"}, + { AutoPilotStackAPM, BetaFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4/ArduCopter-v1.px4"}, { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v1.px4"}, { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v1.px4"}, { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v1.px4"}, -- GitLab From 01d977cb7419e8b2b241319a54c75961499c6486 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Mon, 20 Feb 2017 14:02:26 -0500 Subject: [PATCH 314/398] Allow plugins to change/replace toolbar indicators. --- qgroundcontrol.qrc | 8 + src/api/QGCCorePlugin.cc | 17 +- src/api/QGCCorePlugin.h | 39 +- src/ui/AppSettings.qml | 2 +- src/ui/toolbar/BatteryIndicator.qml | 129 ++++++ src/ui/toolbar/GPS.qml | 34 -- src/ui/toolbar/GPSIndicator.qml | 122 +++++ src/ui/toolbar/MainToolBarIndicators.qml | 531 +--------------------- src/ui/toolbar/MessageIndicator.qml | 69 +++ src/ui/toolbar/ModeIndicator.qml | 63 +++ src/ui/toolbar/RCRSSIIndicator.qml | 105 +++++ src/ui/toolbar/TelemetryRSSIIndicator.qml | 87 ++++ 12 files changed, 632 insertions(+), 574 deletions(-) create mode 100644 src/ui/toolbar/BatteryIndicator.qml delete mode 100644 src/ui/toolbar/GPS.qml create mode 100644 src/ui/toolbar/GPSIndicator.qml create mode 100644 src/ui/toolbar/MessageIndicator.qml create mode 100644 src/ui/toolbar/ModeIndicator.qml create mode 100644 src/ui/toolbar/RCRSSIIndicator.qml create mode 100644 src/ui/toolbar/TelemetryRSSIIndicator.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index b25a9fa69..296df8deb 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -2,6 +2,14 @@ src/FactSystem/FactSystemTest.qml + + src/ui/toolbar/BatteryIndicator.qml + src/ui/toolbar/GPSIndicator.qml + src/ui/toolbar/MessageIndicator.qml + src/ui/toolbar/ModeIndicator.qml + src/ui/toolbar/RCRSSIIndicator.qml + src/ui/toolbar/TelemetryRSSIIndicator.qml + src/AutoPilotPlugins/PX4/AirframeComponent.qml src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index eab76cc0f..b59c274a3 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -65,6 +65,7 @@ public: QGCSettings* pDebug; #endif QVariantList settingsList; + QVariantList toolBarIndicatorList; QGCOptions* defaultOptions; }; @@ -126,7 +127,7 @@ QVariantList &QGCCorePlugin::settings() return _p->settingsList; } -int QGCCorePlugin::defaltSettings() +int QGCCorePlugin::defaultSettings() { return 0; } @@ -142,7 +143,19 @@ QGCOptions* QGCCorePlugin::options() QVariant QGCCorePlugin::overrideSettingsDefault(QString name, QVariant defaultValue) { Q_UNUSED(name); - // No overrides for base plugin return defaultValue; } + +QVariantList& QGCCorePlugin::toolBarIndicators() +{ + if(_p->toolBarIndicatorList.size() == 0) { + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/MessageIndicator.qml"))); + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/GPSIndicator.qml"))); + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/TelemetryRSSIIndicator.qml"))); + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/RCRSSIIndicator.qml"))); + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/BatteryIndicator.qml"))); + _p->toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/ModeIndicator.qml"))); + } + return _p->toolBarIndicatorList; +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index b668a9d7c..582821a3a 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -32,36 +32,35 @@ public: QGCCorePlugin(QGCApplication* app); ~QGCCorePlugin(); - Q_PROPERTY(QVariantList settings READ settings CONSTANT) - Q_PROPERTY(int defaltSettings READ defaltSettings CONSTANT) - Q_PROPERTY(QGCOptions* options READ options CONSTANT) + Q_PROPERTY(QVariantList settings READ settings CONSTANT) + Q_PROPERTY(int defaultSettings READ defaultSettings CONSTANT) + Q_PROPERTY(QGCOptions* options READ options CONSTANT) + Q_PROPERTY(QVariantList toolBarIndicators READ toolBarIndicators CONSTANT) - //! The list of settings under the Settings Menu - /*! - @return A list of QGCSettings - */ - virtual QVariantList& settings (); + /// The list of settings under the Settings Menu + /// @return A list of QGCSettings + virtual QVariantList& settings (); - //! The default settings panel to show - /*! - @return The settings index - */ - virtual int defaltSettings (); + /// Allows the core plugin to override the toolbar indicators + /// @return A list of QUrl with the indicators (see MainToolBarIndicators.qml) + virtual QVariantList& toolBarIndicators (); - //! Global options - /*! - @return An instance of QGCOptions - */ - virtual QGCOptions* options (); + /// The default settings panel to show + /// @return The settings index + virtual int defaultSettings (); + + /// Global options + /// @return An instance of QGCOptions + virtual QGCOptions* options (); /// Allows the core plugin to override the default value for the specified setting /// @param name - Setting name /// @param defaultValue - Standard default value for setting /// @return New default value for setting, if no override just return passed in defaultValue - virtual QVariant overrideSettingsDefault(QString name, QVariant defaultValue); + virtual QVariant overrideSettingsDefault (QString name, QVariant defaultValue); // Override from QGCTool - void setToolbox (QGCToolbox *toolbox); + void setToolbox (QGCToolbox *toolbox); private: QGCCorePlugin_p* _p; }; diff --git a/src/ui/AppSettings.qml b/src/ui/AppSettings.qml index 5599e0252..4b161474d 100644 --- a/src/ui/AppSettings.qml +++ b/src/ui/AppSettings.qml @@ -36,7 +36,7 @@ Rectangle { Component.onCompleted: { //-- Default Settings - __rightPanel.source = QGroundControl.corePlugin.settings[QGroundControl.corePlugin.defaltSettings].url + __rightPanel.source = QGroundControl.corePlugin.settings[QGroundControl.corePlugin.defaultSettings].url } QGCFlickable { diff --git a/src/ui/toolbar/BatteryIndicator.qml b/src/ui/toolbar/BatteryIndicator.qml new file mode 100644 index 000000000..344c7714a --- /dev/null +++ b/src/ui/toolbar/BatteryIndicator.qml @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Battery Indicator +Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: batteryIndicatorRow.width + + function getBatteryColor() { + if(activeVehicle) { + if(activeVehicle.battery.percentRemaining.value > 75) { + return qgcPal.text + } + if(activeVehicle.battery.percentRemaining.value > 50) { + return colorOrange + } + if(activeVehicle.battery.percentRemaining.value > 0.1) { + return colorRed + } + } + return colorGrey + } + + function getBatteryPercentageText() { + if(activeVehicle) { + if(activeVehicle.battery.percentRemaining.value > 98.9) { + return "100%" + } + if(activeVehicle.battery.percentRemaining.value > 0.1) { + return activeVehicle.battery.percentRemaining.valueString + activeVehicle.battery.percentRemaining.units + } + if(activeVehicle.battery.voltage.value >= 0) { + return activeVehicle.battery.voltage.valueString + activeVehicle.battery.voltage.units + } + } + return "N/A" + } + + Component { + id: batteryInfo + + Rectangle { + width: battCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: battCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: battCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(battGrid.width, battLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: battLabel + text: qsTr("Battery Status") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: battGrid + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("Voltage:") } + QGCLabel { text: (activeVehicle && activeVehicle.battery.voltage.value != -1) ? (activeVehicle.battery.voltage.valueString + " " + activeVehicle.battery.voltage.units) : "N/A" } + QGCLabel { text: qsTr("Accumulated Consumption:") } + QGCLabel { text: (activeVehicle && activeVehicle.battery.mahConsumed.value != -1) ? (activeVehicle.battery.mahConsumed.valueString + " " + activeVehicle.battery.mahConsumed.units) : "N/A" } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + Row { + id: batteryIndicatorRow + anchors.top: parent.top + anchors.bottom: parent.bottom + opacity: (activeVehicle && activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + sourceSize.width: width + source: "/qmlimages/Battery.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.text + } + QGCLabel { + text: getBatteryPercentageText() + font.pointSize: ScreenTools.mediumFontPointSize + color: getBatteryColor() + anchors.verticalCenter: parent.verticalCenter + } + } + MouseArea { + anchors.fill: parent + onClicked: mainWindow.showPopUp(batteryInfo, mapToItem(toolBar, x, y).x + (width / 2)) + } +} diff --git a/src/ui/toolbar/GPS.qml b/src/ui/toolbar/GPS.qml deleted file mode 100644 index af0001d84..000000000 --- a/src/ui/toolbar/GPS.qml +++ /dev/null @@ -1,34 +0,0 @@ -/**************************************************************************** - * - * (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. - * - ****************************************************************************/ - - -/** - * @file - * @brief QGC Main Tool GPS - * @author Gus Grubba - */ - -import QtQuick 2.4 -import QGroundControl.Controls 1.0 - -Item { - id: gpsRoot - property real size: 50 - property real percent: 0 - width: size - height: size - Image { - source: "/qmlimages/Gps.svg" - fillMode: Image.PreserveAspectFit - mipmap: true - smooth: true - anchors.fill: parent - opacity: (percent + 25) * 0.8 - } -} diff --git a/src/ui/toolbar/GPSIndicator.qml b/src/ui/toolbar/GPSIndicator.qml new file mode 100644 index 000000000..d73479069 --- /dev/null +++ b/src/ui/toolbar/GPSIndicator.qml @@ -0,0 +1,122 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- GPS Indicator +Item { + id: satelitte + width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 + anchors.top: parent.top + anchors.bottom: parent.bottom + + Component { + id: gpsInfo + + Rectangle { + width: gpsCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: gpsCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: gpsCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(gpsGrid.width, gpsLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: gpsLabel + text: (activeVehicle && activeVehicle.gps.count.value >= 0) ? qsTr("GPS Status") : qsTr("GPS Data Unavailable") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: gpsGrid + visible: (activeVehicle && activeVehicle.gps.count.value >= 0) + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + columns: 2 + + QGCLabel { text: qsTr("GPS Count:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.count.valueString : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("GPS Lock:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.lock.enumStringValue : qsTr("N/A", "No data to display") } + QGCLabel { text: qsTr("HDOP:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.hdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("VDOP:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.vdop.valueString : qsTr("--.--", "No data to display") } + QGCLabel { text: qsTr("Course Over Ground:") } + QGCLabel { text: activeVehicle ? activeVehicle.gps.courseOverGround.valueString : qsTr("--.--", "No data to display") } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + QGCColoredImage { + id: gpsIcon + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + source: "/qmlimages/Gps.svg" + fillMode: Image.PreserveAspectFit + sourceSize.height: height + opacity: (activeVehicle && activeVehicle.gps.count.value >= 0) ? 1 : 0.5 + color: qgcPal.buttonText + } + + Column { + id: gpsValuesColumn + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 + anchors.left: gpsIcon.right + + QGCLabel { + anchors.horizontalCenter: hdopValue.horizontalCenter + visible: activeVehicle && !isNaN(activeVehicle.gps.hdop.value) + color: qgcPal.buttonText + text: activeVehicle ? activeVehicle.gps.count.valueString : "" + } + + QGCLabel { + id: hdopValue + visible: activeVehicle && !isNaN(activeVehicle.gps.hdop.value) + color: qgcPal.buttonText + text: activeVehicle ? activeVehicle.gps.hdop.value.toFixed(1) : "" + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + var centerX = mapToItem(toolBar, x, y).x + (width / 2) + mainWindow.showPopUp(gpsInfo, centerX) + } + } +} diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index 9a0d30b57..16caea17a 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -19,551 +19,48 @@ import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 Item { - property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _communicationLost: _activeVehicle ? _activeVehicle.connectionLost : false + property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool communicationLost: activeVehicle ? activeVehicle.connectionLost : false QGCPalette { id: qgcPal } - function getBatteryColor() { - if(_activeVehicle) { - if(_activeVehicle.battery.percentRemaining.value > 75) { - return qgcPal.text - } - if(_activeVehicle.battery.percentRemaining.value > 50) { - return colorOrange - } - if(_activeVehicle.battery.percentRemaining.value > 0.1) { - return colorRed - } - } - return colorGrey - } - - function getRSSIColor(value) { - if(value >= 0) - return colorGrey; - if(value > -60) - return colorGreen; - if(value > -90) - return colorOrange; - return colorRed; - } - - function getMessageColor() { - if (_activeVehicle) { - if (_activeVehicle.messageTypeNone) - return colorGrey - if (_activeVehicle.messageTypeNormal) - return colorBlue; - if (_activeVehicle.messageTypeWarning) - return colorOrange; - if (_activeVehicle.messageTypeError) - return colorRed; - // Cannot be so make make it obnoxious to show error - console.log("Invalid vehicle message type") - return "purple"; - } - //-- It can only get here when closing (vehicle gone while window active) - return "white"; - } - - function getBatteryVoltageText() { - if (_activeVehicle.battery.voltage.value >= 0) { - return _activeVehicle.battery.voltage.valueString + _activeVehicle.battery.voltage.units - } - return 'N/A'; - } - - function getBatteryPercentageText() { - if(_activeVehicle) { - if(_activeVehicle.battery.percentRemaining.value > 98.9) { - return "100%" - } - if(_activeVehicle.battery.percentRemaining.value > 0.1) { - return _activeVehicle.battery.percentRemaining.valueString + _activeVehicle.battery.percentRemaining.units - } - if(_activeVehicle.battery.voltage.value >= 0) { - return _activeVehicle.battery.voltage.valueString + _activeVehicle.battery.voltage.units - } - } - return "N/A" - } - - //--------------------------------------------- - // GPS Info - Component { - id: gpsInfo - - Rectangle { - width: gpsCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: gpsCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - border.color: qgcPal.text - - Column { - id: gpsCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(gpsGrid.width, gpsLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: gpsLabel - text: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? qsTr("GPS Status") : qsTr("GPS Data Unavailable") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: gpsGrid - visible: (_activeVehicle && _activeVehicle.gps.count.value >= 0) - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - columns: 2 - - QGCLabel { text: qsTr("GPS Count:") } - QGCLabel { text: _activeVehicle ? _activeVehicle.gps.count.valueString : qsTr("N/A", "No data to display") } - QGCLabel { text: qsTr("GPS Lock:") } - QGCLabel { text: _activeVehicle ? _activeVehicle.gps.lock.enumStringValue : qsTr("N/A", "No data to display") } - QGCLabel { text: qsTr("HDOP:") } - QGCLabel { text: _activeVehicle ? _activeVehicle.gps.hdop.valueString : qsTr("--.--", "No data to display") } - QGCLabel { text: qsTr("VDOP:") } - QGCLabel { text: _activeVehicle ? _activeVehicle.gps.vdop.valueString : qsTr("--.--", "No data to display") } - QGCLabel { text: qsTr("Course Over Ground:") } - QGCLabel { text: _activeVehicle ? _activeVehicle.gps.courseOverGround.valueString : qsTr("--.--", "No data to display") } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // Battery Info - Component { - id: batteryInfo - - Rectangle { - width: battCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: battCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - border.color: qgcPal.text - - Column { - id: battCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(battGrid.width, battLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: battLabel - text: qsTr("Battery Status") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: battGrid - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("Voltage:") } - QGCLabel { text: (_activeVehicle && _activeVehicle.battery.voltage.value != -1) ? (_activeVehicle.battery.voltage.valueString + " " + _activeVehicle.battery.voltage.units) : "N/A" } - QGCLabel { text: qsTr("Accumulated Consumption:") } - QGCLabel { text: (_activeVehicle && _activeVehicle.battery.mahConsumed.value != -1) ? (_activeVehicle.battery.mahConsumed.valueString + " " + _activeVehicle.battery.mahConsumed.units) : "N/A" } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // RC RSSI Info - Component { - id: rcRSSIInfo - - Rectangle { - width: rcrssiCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: rcrssiCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - border.color: qgcPal.text - - Column { - id: rcrssiCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(rcrssiGrid.width, rssiLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: rssiLabel - text: _activeVehicle ? (_activeVehicle.rcRSSI != 255 ? qsTr("RC RSSI Status") : qsTr("RC RSSI Data Unavailable")) : qsTr("N/A", "No data available") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: rcrssiGrid - visible: _activeVehicle && _activeVehicle.rcRSSI != 255 - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("RSSI:") } - QGCLabel { text: _activeVehicle ? (_activeVehicle.rcRSSI + "%") : 0 } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - - //--------------------------------------------- - // Telemetry RSSI Info - Component { - id: telemRSSIInfo - - Rectangle { - width: telemCol.width + ScreenTools.defaultFontPixelWidth * 3 - height: telemCol.height + ScreenTools.defaultFontPixelHeight * 2 - radius: ScreenTools.defaultFontPixelHeight * 0.5 - color: qgcPal.window - border.color: qgcPal.text - - Column { - id: telemCol - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - width: Math.max(telemGrid.width, telemLabel.width) - anchors.margins: ScreenTools.defaultFontPixelHeight - anchors.centerIn: parent - - QGCLabel { - id: telemLabel - text: qsTr("Telemetry RSSI Status") - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter - } - - GridLayout { - id: telemGrid - anchors.margins: ScreenTools.defaultFontPixelHeight - columnSpacing: ScreenTools.defaultFontPixelWidth - columns: 2 - anchors.horizontalCenter: parent.horizontalCenter - - QGCLabel { text: qsTr("Local RSSI:") } - QGCLabel { text: _activeVehicle.telemetryLRSSI + " dBm" } - QGCLabel { text: qsTr("Remote RSSI:") } - QGCLabel { text: _activeVehicle.telemetryRRSSI + " dBm" } - QGCLabel { text: qsTr("RX Errors:") } - QGCLabel { text: _activeVehicle.telemetryRXErrors } - QGCLabel { text: qsTr("Errors Fixed:") } - QGCLabel { text: _activeVehicle.telemetryFixed } - QGCLabel { text: qsTr("TX Buffer:") } - QGCLabel { text: _activeVehicle.telemetryTXBuffer } - QGCLabel { text: qsTr("Local Noise:") } - QGCLabel { text: _activeVehicle.telemetryLNoise } - QGCLabel { text: qsTr("Remote Noise:") } - QGCLabel { text: _activeVehicle.telemetryRNoise } - } - } - - Component.onCompleted: { - var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) - x = pos.x - y = pos.y + ScreenTools.defaultFontPixelHeight - } - } - } - Row { id: indicatorRow anchors.top: parent.top anchors.bottom: parent.bottom spacing: ScreenTools.defaultFontPixelWidth * 1.5 - visible: !_communicationLost - - //------------------------------------------------------------------------- - //-- Message Indicator - Item { - id: messages - width: height - anchors.top: parent.top - anchors.bottom: parent.bottom - visible: _activeVehicle && _activeVehicle.messageCount - - Image { - id: criticalMessageIcon - anchors.fill: parent - source: "/qmlimages/Yield.svg" - sourceSize.height: height - fillMode: Image.PreserveAspectFit - cache: false - visible: _activeVehicle && _activeVehicle.messageCount > 0 && isMessageImportant - } - - QGCColoredImage { - anchors.fill: parent - source: "/qmlimages/Megaphone.svg" - sourceSize.height: height - fillMode: Image.PreserveAspectFit - color: getMessageColor() - visible: !criticalMessageIcon.visible - } - - MouseArea { - anchors.fill: parent - onClicked: mainWindow.showMessageArea() - } - } - - //------------------------------------------------------------------------- - //-- GPS Indicator - Item { - id: satelitte - width: (gpsValuesColumn.x + gpsValuesColumn.width) * 1.1 - anchors.top: parent.top - anchors.bottom: parent.bottom - - QGCColoredImage { - id: gpsIcon - width: height - anchors.top: parent.top - anchors.bottom: parent.bottom - source: "/qmlimages/Gps.svg" - fillMode: Image.PreserveAspectFit - sourceSize.height: height - opacity: (_activeVehicle && _activeVehicle.gps.count.value >= 0) ? 1 : 0.5 - color: qgcPal.buttonText - } - - Column { - id: gpsValuesColumn - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: ScreenTools.defaultFontPixelWidth / 2 - anchors.left: gpsIcon.right - - QGCLabel { - anchors.horizontalCenter: hdopValue.horizontalCenter - visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) - color: qgcPal.buttonText - text: _activeVehicle ? _activeVehicle.gps.count.valueString : "" - } - - QGCLabel { - id: hdopValue - visible: _activeVehicle && !isNaN(_activeVehicle.gps.hdop.value) - color: qgcPal.buttonText - text: _activeVehicle ? _activeVehicle.gps.hdop.value.toFixed(1) : "" - } - } // Column - - MouseArea { - anchors.fill: parent - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(gpsInfo, centerX) - } - } - } - - //------------------------------------------------------------------------- - //-- RC RSSI - Item { - id: rcRssi - width: rssiRow.width * 1.1 - anchors.top: parent.top - anchors.bottom: parent.bottom - visible: _activeVehicle ? _activeVehicle.supportsRadio : true - - Row { - id: rssiRow - anchors.top: parent.top - anchors.bottom: parent.bottom - spacing: ScreenTools.defaultFontPixelWidth - - QGCColoredImage { - width: height - anchors.top: parent.top - anchors.bottom: parent.bottom - sourceSize.height: height - source: "/qmlimages/RC.svg" - fillMode: Image.PreserveAspectFit - opacity: _activeVehicle ? (((_activeVehicle.rcRSSI < 0) || (_activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 - color: qgcPal.buttonText - } - - SignalStrength { - anchors.verticalCenter: parent.verticalCenter - size: parent.height * 0.5 - percent: _activeVehicle ? ((_activeVehicle.rcRSSI > 100) ? 0 : _activeVehicle.rcRSSI) : 0 - } - } - - MouseArea { - anchors.fill: parent - - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(rcRSSIInfo, centerX) - } - } - } - - //------------------------------------------------------------------------- - //-- Telemetry RSSI - QGCColoredImage { - anchors.top: parent.top - anchors.bottom: parent.bottom - sourceSize.height: height - source: "/qmlimages/TelemRSSI.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.buttonText - visible: _activeVehicle ? (_activeVehicle.telemetryLRSSI < 0) : false - - MouseArea { - anchors.fill: parent - - onClicked: { - var centerX = mapToItem(toolBar, x, y).x + (width / 2) - mainWindow.showPopUp(telemRSSIInfo, centerX) - } - } - } - - //------------------------------------------------------------------------- - //-- Battery Indicator - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: batteryIndicatorRow.width - - Row { - id: batteryIndicatorRow + visible: !communicationLost + Repeater { + model: QGroundControl.corePlugin.toolBarIndicators + Loader { anchors.top: parent.top anchors.bottom: parent.bottom - opacity: (_activeVehicle && _activeVehicle.battery.voltage.value >= 0) ? 1 : 0.5 - - QGCColoredImage { - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - sourceSize.width: width - source: "/qmlimages/Battery.svg" - fillMode: Image.PreserveAspectFit - color: qgcPal.text - } - - QGCLabel { - text: getBatteryPercentageText() - font.pointSize: ScreenTools.mediumFontPointSize - color: getBatteryColor() - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - anchors.fill: parent - onClicked: mainWindow.showPopUp(batteryInfo, mapToItem(toolBar, x, y).x + (width / 2)) + source: modelData; } } - - //------------------------------------------------------------------------- - //-- Mode Selector - QGCLabel { - id: flightModeSelector - text: _activeVehicle ? _activeVehicle.flightMode : qsTr("N/A", "No data to display") - font.pointSize: ScreenTools.mediumFontPointSize - color: qgcPal.buttonText - anchors.verticalCenter: parent.verticalCenter - - Menu { - id: flightModesMenu - } - - Component { - id: flightModeMenuItemComponent - - MenuItem { - onTriggered: _activeVehicle.flightMode = text - } - } - - property var flightModesMenuItems: [] - - function updateFlightModesMenu() { - if (_activeVehicle && _activeVehicle.flightModeSetAvailable) { - // Remove old menu items - for (var i = 0; i < flightModesMenuItems.length; i++) { - flightModesMenu.removeItem(flightModesMenuItems[i]) - } - flightModesMenuItems.length = 0 - // Add new items - for (var i = 0; i < _activeVehicle.flightModes.length; i++) { - var menuItem = flightModeMenuItemComponent.createObject(null, { "text": _activeVehicle.flightModes[i] }) - flightModesMenuItems.push(menuItem) - flightModesMenu.insertItem(i, menuItem) - } - } - } - - Component.onCompleted: flightModeSelector.updateFlightModesMenu() - - Connections { - target: QGroundControl.multiVehicleManager - onActiveVehicleChanged: flightModeSelector.updateFlightModesMenu() - } - - MouseArea { - visible: _activeVehicle && _activeVehicle.flightModeSetAvailable - anchors.fill: parent - onClicked: flightModesMenu.popup() - } - } // QGCLabel - Flight mode selector - } // Row - Vehicle indicators + } Image { anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom - visible: x > indicatorRow.width && !_communicationLost + visible: x > indicatorRow.width && !communicationLost fillMode: Image.PreserveAspectFit - source: _activeVehicle ? _activeVehicle.brandImage : "" + source: activeVehicle ? activeVehicle.brandImage : "" } Row { anchors.fill: parent layoutDirection: Qt.RightToLeft spacing: ScreenTools.defaultFontPixelWidth - visible: _communicationLost + visible: communicationLost QGCButton { id: disconnectButton anchors.verticalCenter: parent.verticalCenter text: qsTr("Disconnect") primary: true - onClicked: _activeVehicle.disconnectInactiveVehicle() + onClicked: activeVehicle.disconnectInactiveVehicle() } QGCLabel { @@ -574,5 +71,5 @@ Item { font.family: ScreenTools.demiboldFontFamily color: colorRed } - } // Row - Communication lost -} // Item + } +} diff --git a/src/ui/toolbar/MessageIndicator.qml b/src/ui/toolbar/MessageIndicator.qml new file mode 100644 index 000000000..aecd24614 --- /dev/null +++ b/src/ui/toolbar/MessageIndicator.qml @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- GPS Indicator +Item { + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + + function getMessageColor() { + if (activeVehicle) { + if (activeVehicle.messageTypeNone) + return colorGrey + if (activeVehicle.messageTypeNormal) + return colorBlue; + if (activeVehicle.messageTypeWarning) + return colorOrange; + if (activeVehicle.messageTypeError) + return colorRed; + // Cannot be so make make it obnoxious to show error + console.log("Invalid vehicle message type") + return "purple"; + } + //-- It can only get here when closing (vehicle gone while window active) + return "white"; + } + + Image { + id: criticalMessageIcon + anchors.fill: parent + source: "/qmlimages/Yield.svg" + sourceSize.height: height + fillMode: Image.PreserveAspectFit + cache: false + visible: activeVehicle && activeVehicle.messageCount > 0 && isMessageImportant + } + + QGCColoredImage { + anchors.fill: parent + source: "/qmlimages/Megaphone.svg" + sourceSize.height: height + fillMode: Image.PreserveAspectFit + color: getMessageColor() + visible: !criticalMessageIcon.visible + } + + MouseArea { + anchors.fill: parent + onClicked: mainWindow.showMessageArea() + } +} diff --git a/src/ui/toolbar/ModeIndicator.qml b/src/ui/toolbar/ModeIndicator.qml new file mode 100644 index 000000000..2d8ba7525 --- /dev/null +++ b/src/ui/toolbar/ModeIndicator.qml @@ -0,0 +1,63 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Mode Indicator +QGCLabel { + id: flightModeSelector + text: activeVehicle ? activeVehicle.flightMode : qsTr("N/A", "No data to display") + font.pointSize: ScreenTools.mediumFontPointSize + color: qgcPal.buttonText + anchors.verticalCenter: parent.verticalCenter + Menu { + id: flightModesMenu + } + Component { + id: flightModeMenuItemComponent + MenuItem { + onTriggered: activeVehicle.flightMode = text + } + } + property var flightModesMenuItems: [] + function updateFlightModesMenu() { + if (activeVehicle && activeVehicle.flightModeSetAvailable) { + // Remove old menu items + for (var i = 0; i < flightModesMenuItems.length; i++) { + flightModesMenu.removeItem(flightModesMenuItems[i]) + } + flightModesMenuItems.length = 0 + // Add new items + for (var i = 0; i < activeVehicle.flightModes.length; i++) { + var menuItem = flightModeMenuItemComponent.createObject(null, { "text": activeVehicle.flightModes[i] }) + flightModesMenuItems.push(menuItem) + flightModesMenu.insertItem(i, menuItem) + } + } + } + Component.onCompleted: flightModeSelector.updateFlightModesMenu() + Connections { + target: QGroundControl.multiVehicleManager + onActiveVehicleChanged: flightModeSelector.updateFlightModesMenu() + } + MouseArea { + visible: activeVehicle && activeVehicle.flightModeSetAvailable + anchors.fill: parent + onClicked: flightModesMenu.popup() + } +} diff --git a/src/ui/toolbar/RCRSSIIndicator.qml b/src/ui/toolbar/RCRSSIIndicator.qml new file mode 100644 index 000000000..64471abf2 --- /dev/null +++ b/src/ui/toolbar/RCRSSIIndicator.qml @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- GPS Indicator +Item { + width: rssiRow.width * 1.1 + anchors.top: parent.top + anchors.bottom: parent.bottom + visible: activeVehicle ? activeVehicle.supportsRadio : true + + Component { + id: rcRSSIInfo + + Rectangle { + width: rcrssiCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: rcrssiCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + + Column { + id: rcrssiCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(rcrssiGrid.width, rssiLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + + QGCLabel { + id: rssiLabel + text: activeVehicle ? (activeVehicle.rcRSSI != 255 ? qsTr("RC RSSI Status") : qsTr("RC RSSI Data Unavailable")) : qsTr("N/A", "No data available") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + + GridLayout { + id: rcrssiGrid + visible: activeVehicle && activeVehicle.rcRSSI != 255 + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + + QGCLabel { text: qsTr("RSSI:") } + QGCLabel { text: activeVehicle ? (activeVehicle.rcRSSI + "%") : 0 } + } + } + + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + + Row { + id: rssiRow + anchors.top: parent.top + anchors.bottom: parent.bottom + spacing: ScreenTools.defaultFontPixelWidth + + QGCColoredImage { + width: height + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/qmlimages/RC.svg" + fillMode: Image.PreserveAspectFit + opacity: activeVehicle ? (((activeVehicle.rcRSSI < 0) || (activeVehicle.rcRSSI > 100)) ? 0.5 : 1) : 0.5 + color: qgcPal.buttonText + } + + SignalStrength { + anchors.verticalCenter: parent.verticalCenter + size: parent.height * 0.5 + percent: activeVehicle ? ((activeVehicle.rcRSSI > 100) ? 0 : activeVehicle.rcRSSI) : 0 + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + var centerX = mapToItem(toolBar, x, y).x + (width / 2) + mainWindow.showPopUp(rcRSSIInfo, centerX) + } + } +} diff --git a/src/ui/toolbar/TelemetryRSSIIndicator.qml b/src/ui/toolbar/TelemetryRSSIIndicator.qml new file mode 100644 index 000000000..145fa6519 --- /dev/null +++ b/src/ui/toolbar/TelemetryRSSIIndicator.qml @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- Telemetry RSSI +QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + sourceSize.height: height + source: "/qmlimages/TelemRSSI.svg" + fillMode: Image.PreserveAspectFit + color: qgcPal.buttonText + visible: activeVehicle ? (activeVehicle.telemetryLRSSI < 0) : false + Component { + id: telemRSSIInfo + Rectangle { + width: telemCol.width + ScreenTools.defaultFontPixelWidth * 3 + height: telemCol.height + ScreenTools.defaultFontPixelHeight * 2 + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + border.color: qgcPal.text + Column { + id: telemCol + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + width: Math.max(telemGrid.width, telemLabel.width) + anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + QGCLabel { + id: telemLabel + text: qsTr("Telemetry RSSI Status") + font.family: ScreenTools.demiboldFontFamily + anchors.horizontalCenter: parent.horizontalCenter + } + GridLayout { + id: telemGrid + anchors.margins: ScreenTools.defaultFontPixelHeight + columnSpacing: ScreenTools.defaultFontPixelWidth + columns: 2 + anchors.horizontalCenter: parent.horizontalCenter + QGCLabel { text: qsTr("Local RSSI:") } + QGCLabel { text: activeVehicle.telemetryLRSSI + " dBm" } + QGCLabel { text: qsTr("Remote RSSI:") } + QGCLabel { text: activeVehicle.telemetryRRSSI + " dBm" } + QGCLabel { text: qsTr("RX Errors:") } + QGCLabel { text: activeVehicle.telemetryRXErrors } + QGCLabel { text: qsTr("Errors Fixed:") } + QGCLabel { text: activeVehicle.telemetryFixed } + QGCLabel { text: qsTr("TX Buffer:") } + QGCLabel { text: activeVehicle.telemetryTXBuffer } + QGCLabel { text: qsTr("Local Noise:") } + QGCLabel { text: activeVehicle.telemetryLNoise } + QGCLabel { text: qsTr("Remote Noise:") } + QGCLabel { text: activeVehicle.telemetryRNoise } + } + } + Component.onCompleted: { + var pos = mapFromItem(toolBar, centerX - (width / 2), toolBar.height) + x = pos.x + y = pos.y + ScreenTools.defaultFontPixelHeight + } + } + } + MouseArea { + anchors.fill: parent + onClicked: { + var centerX = mapToItem(toolBar, x, y).x + (width / 2) + mainWindow.showPopUp(telemRSSIInfo, centerX) + } + } +} -- GitLab From d0a6b775314287627cb638b9e4237a5371cf9e0c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 13:16:59 -0800 Subject: [PATCH 315/398] Add new SettingsGroup support --- qgroundcontrol.pro | 15 +- qgroundcontrol.qrc | 5 +- src/FactSystem/FactMetaData.cc | 38 ++-- src/FlightMap/MapScale.qml | 2 +- src/GAudioOutput.cc | 2 +- src/MissionEditor/MissionSettingsEditor.qml | 10 +- src/MissionManager/MissionCommandTree.cc | 4 +- src/MissionManager/MissionController.cc | 6 +- src/MissionManager/RallyPointController.cc | 2 +- src/MissionManager/SimpleMissionItem.cc | 2 +- src/MissionManager/SimpleMissionItemTest.cc | 4 +- src/Settings/AppSettings.cc | 129 +++++++++++ .../AppSettings.h} | 64 +----- src/Settings/AutoConnectSettings.cc | 21 ++ src/Settings/AutoConnectSettings.h | 27 +++ .../SettingsGroup.app.json} | 0 src/Settings/SettingsGroup.autoConnect.json | 2 + src/Settings/SettingsGroup.cc | 29 +++ src/Settings/SettingsGroup.h | 43 ++++ src/Settings/SettingsGroup.units.json | 2 + src/Settings/SettingsGroup.video.json | 2 + src/Settings/SettingsManager.cc | 34 +++ src/Settings/SettingsManager.h | 52 +++++ src/Settings/UnitsSettings.cc | 93 ++++++++ src/Settings/UnitsSettings.h | 68 ++++++ src/Settings/VideoSettings.cc | 21 ++ src/Settings/VideoSettings.h | 27 +++ src/SettingsManager.cc | 214 ------------------ src/Vehicle/MultiVehicleManager.cc | 4 +- src/Vehicle/Vehicle.cc | 20 +- src/api/QGCCorePlugin.cc | 8 + src/api/QGCCorePlugin.h | 5 + src/comm/MAVLinkProtocol.cc | 2 +- src/ui/preferences/GeneralSettings.qml | 126 ++++++----- 34 files changed, 699 insertions(+), 384 deletions(-) create mode 100644 src/Settings/AppSettings.cc rename src/{SettingsManager.h => Settings/AppSettings.h} (62%) create mode 100644 src/Settings/AutoConnectSettings.cc create mode 100644 src/Settings/AutoConnectSettings.h rename src/{SettingsManager.json => Settings/SettingsGroup.app.json} (100%) create mode 100644 src/Settings/SettingsGroup.autoConnect.json create mode 100644 src/Settings/SettingsGroup.cc create mode 100644 src/Settings/SettingsGroup.h create mode 100644 src/Settings/SettingsGroup.units.json create mode 100644 src/Settings/SettingsGroup.video.json create mode 100644 src/Settings/SettingsManager.cc create mode 100644 src/Settings/SettingsManager.h create mode 100644 src/Settings/UnitsSettings.cc create mode 100644 src/Settings/UnitsSettings.h create mode 100644 src/Settings/VideoSettings.cc create mode 100644 src/Settings/VideoSettings.h delete mode 100644 src/SettingsManager.cc diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 71fe844ff..f06292340 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -291,6 +291,7 @@ INCLUDEPATH += \ src/QmlControls \ src/QtLocationPlugin \ src/QtLocationPlugin/QMLControl \ + src/Settings \ src/VehicleSetup \ src/ViewWidgets \ src/audio \ @@ -474,7 +475,12 @@ HEADERS += \ src/QmlControls/RCChannelMonitorController.h \ src/QmlControls/ScreenToolsController.h \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ - src/SettingsManager.h \ + src/Settings/AppSettings.h \ + src/Settings/AutoConnectSettings.h \ + src/Settings/SettingsGroup.h \ + src/Settings/SettingsManager.h \ + src/Settings/UnitsSettings.h \ + src/Settings/VideoSettings.h \ src/Vehicle/MAVLinkLogManager.h \ src/VehicleSetup/JoystickConfigController.h \ src/audio/QGCAudioWorker.h \ @@ -636,7 +642,12 @@ SOURCES += \ src/QmlControls/RCChannelMonitorController.cc \ src/QmlControls/ScreenToolsController.cc \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ - src/SettingsManager.cc \ + src/Settings/AppSettings.cc \ + src/Settings/AutoConnectSettings.cc \ + src/Settings/SettingsGroup.cc \ + src/Settings/SettingsManager.cc \ + src/Settings/UnitsSettings.cc \ + src/Settings/VideoSettings.cc \ src/Vehicle/MAVLinkLogManager.cc \ src/VehicleSetup/JoystickConfigController.cc \ src/audio/QGCAudioWorker.cpp \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index b25a9fa69..e464e5137 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -170,7 +170,10 @@ src/Vehicle/GPSFact.json src/Vehicle/WindFact.json src/Vehicle/VibrationFact.json - src/SettingsManager.json + src/Settings/SettingsGroup.app.json + src/Settings/SettingsGroup.autoConnect.json + src/Settings/SettingsGroup.units.json + src/Settings/SettingsGroup.video.json src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index aa0575d19..dc9651422 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -40,21 +40,21 @@ const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] // Translations driven by app settings const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = { - { "m", "m", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "meters", "meters", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m/s", "m/s", true, SettingsManager::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m^2", "m^2", false, SettingsManager::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "meters", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m^2", "km^2", false, SettingsManager::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, - { "m^2", "ha", false, SettingsManager::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, - { "m^2", "ft^2", false, SettingsManager::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, - { "m^2", "ac", false, SettingsManager::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, - { "m^2", "mi^2", false, SettingsManager::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, - { "m/s", "ft/s", true, SettingsManager::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m/s", "mph", true, SettingsManager::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, - { "m/s", "km/h", true, SettingsManager::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, - { "m/s", "kn", true, SettingsManager::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, + { "m", "m", false, UnitsSettings::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "meters", "meters", false, UnitsSettings::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m/s", "m/s", true, UnitsSettings::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m^2", "m^2", false, UnitsSettings::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m", "ft", false, UnitsSettings::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "meters", "ft", false, UnitsSettings::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m^2", "km^2", false, UnitsSettings::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, + { "m^2", "ha", false, UnitsSettings::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, + { "m^2", "ft^2", false, UnitsSettings::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, + { "m^2", "ac", false, UnitsSettings::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, + { "m^2", "mi^2", false, UnitsSettings::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, + { "m/s", "ft/s", true, UnitsSettings::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m/s", "mph", true, UnitsSettings::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, + { "m/s", "km/h", true, UnitsSettings::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, + { "m/s", "kn", true, UnitsSettings::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, }; const char* FactMetaData::_decimalPlacesJsonKey = "decimalPlaces"; @@ -635,8 +635,8 @@ void FactMetaData::_setAppSettingsTranslators(void) const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == _rawUnits.toLower() && - ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->speedUnits()->rawValue().toUInt()) || - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt()))) { + ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->speedUnits()->rawValue().toUInt()) || + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt()))) { _cookedUnits = pAppSettingsTranslation->cookedUnits; setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator); return; @@ -651,7 +651,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt())) { + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt())) { return pAppSettingsTranslation; } } @@ -665,7 +665,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->areaUnits()->rawValue().toUInt()) + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt()) ) { return pAppSettingsTranslation; } diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index a861e41d5..8a265d863 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -115,7 +115,7 @@ Item { var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, scale.y)) var scaleLineMeters = Math.round(leftCoord.distanceTo(rightCoord)) - if (QGroundControl.settingsManager.distanceUnits.value == QGroundControl.settingsManager.DistanceUnitsFeet) { + if (QGroundControl.settingsManager.unitsSettings.distanceUnits.value == UnitsSettings.DistanceUnitsFeet) { calculateFeetRatio(scaleLineMeters, scaleLinePixelLength) } else { calculateMetersRatio(scaleLineMeters, scaleLinePixelLength) diff --git a/src/GAudioOutput.cc b/src/GAudioOutput.cc index c20f3a0b2..480e04ded 100644 --- a/src/GAudioOutput.cc +++ b/src/GAudioOutput.cc @@ -56,7 +56,7 @@ GAudioOutput::~GAudioOutput() bool GAudioOutput::say(const QString& inText) { - bool muted = qgcApp()->toolbox()->settingsManager()->audioMuted()->rawValue().toBool(); + bool muted = qgcApp()->toolbox()->settingsManager()->appSettings()->audioMuted()->rawValue().toBool(); muted |= qgcApp()->runningUnitTests(); if (!muted && !qgcApp()->runningUnitTests()) { #if defined __android__ diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index 3e711443a..729370c2a 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -122,7 +122,7 @@ Rectangle { Layout.fillWidth: true } FactComboBox { - fact: QGroundControl.settingsManager.offlineEditingFirmwareType + fact: QGroundControl.settingsManager.appSettings.offlineEditingFirmwareType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -146,7 +146,7 @@ Rectangle { } FactComboBox { id: offlineVehicleCombo - fact: QGroundControl.settingsManager.offlineEditingVehicleType + fact: QGroundControl.settingsManager.appSettings.offlineEditingVehicleType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -170,7 +170,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.settingsManager.offlineEditingCruiseSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingCruiseSpeed visible: _showCruiseSpeed Layout.preferredWidth: _fieldWidth } @@ -182,7 +182,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.settingsManager.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed visible: _showHoverSpeed Layout.preferredWidth: _fieldWidth } @@ -196,7 +196,7 @@ Rectangle { QGCLabel { text: qsTr("Hover speed:"); Layout.fillWidth: true } FactTextField { Layout.preferredWidth: _fieldWidth - fact: QGroundControl.settingsManager.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed } } diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index 31f69f52f..f494826b6 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -253,7 +253,7 @@ void MissionCommandTree::_baseVehicleInfo(Vehicle* vehicle, MAV_AUTOPILOT& baseF baseVehicleType = _baseVehicleType(vehicle->vehicleType()); } else { // No Vehicle means offline editing - baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)_settingsManager->offlineEditingFirmwareType()->rawValue().toInt()); - baseVehicleType = _baseVehicleType((MAV_TYPE)_settingsManager->offlineEditingVehicleType()->rawValue().toInt()); + baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)_settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()); + baseVehicleType = _baseVehicleType((MAV_TYPE)_settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()); } } diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 53a209823..39d31974a 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -437,13 +437,13 @@ bool MissionController::_loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObje return false; } if (json.contains(_jsonVehicleTypeKey) && vehicle->isOfflineEditingVehicle()) { - settingsManager->offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); + settingsManager->appSettings()->offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); } if (json.contains(_jsonCruiseSpeedKey)) { - settingsManager->offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); + settingsManager->appSettings()->offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); } if (json.contains(_jsonHoverSpeedKey)) { - settingsManager->offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); + settingsManager->appSettings()->offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); } SimpleMissionItem* homeItem = new SimpleMissionItem(vehicle, visualItems); diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 66e0b7667..adc43f62f 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -268,7 +268,7 @@ void RallyPointController::addPoint(QGeoCoordinate point) if (_points.count()) { defaultAlt = qobject_cast(_points[_points.count() - 1])->coordinate().altitude(); } else { - defaultAlt = qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble(); + defaultAlt = qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble(); } point.setAltitude(defaultAlt); RallyPoint* newPoint = new RallyPoint(point, this); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 3d6c0de22..21a04129f 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -530,7 +530,7 @@ void SimpleMissionItem::_syncFrameToAltitudeRelativeToHome(void) void SimpleMissionItem::setDefaultsForCommand(void) { // We set these global defaults first, then if there are param defaults they will get reset - _missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); + _missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble()); MAV_CMD command = (MAV_CMD)this->command(); const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); diff --git a/src/MissionManager/SimpleMissionItemTest.cc b/src/MissionManager/SimpleMissionItemTest.cc index cf453d34e..3339cedbc 100644 --- a/src/MissionManager/SimpleMissionItemTest.cc +++ b/src/MissionManager/SimpleMissionItemTest.cc @@ -141,7 +141,7 @@ void SimpleMissionItemTest::_testDefaultValues(void) item.missionItem().setCommand(MAV_CMD_NAV_WAYPOINT); item.missionItem().setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT); - QCOMPARE(item.missionItem().param7(), qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); + QCOMPARE(item.missionItem().param7(), qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble()); } void SimpleMissionItemTest::_testSignals(void) @@ -226,7 +226,7 @@ void SimpleMissionItemTest::_testSignals(void) // dirtyChanged // Check that changing to the same coordinate does not signal - simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble())); + simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble())); QVERIFY(multiSpy->checkNoSignals()); // Check that actually changing coordinate signals correctly diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc new file mode 100644 index 000000000..348baf896 --- /dev/null +++ b/src/Settings/AppSettings.cc @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * (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 "AppSettings.h" + +#include + +const char* AppSettings::appSettingsGroupName = "app"; +const char* AppSettings::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; +const char* AppSettings::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; +const char* AppSettings::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; +const char* AppSettings::offlineEditingHoverSpeedSettingsName = "OfflineEditingHoverSpeed"; +const char* AppSettings::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; +const char* AppSettings::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; +const char* AppSettings::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; +const char* AppSettings::promptFlightTelemetrySaveName = "PromptFLightDataSave"; +const char* AppSettings::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; +const char* AppSettings::audioMutedName = "AudioMuted"; + +AppSettings::AppSettings(QObject* parent) + : SettingsGroup(appSettingsGroupName, QString() /* root settings group */, parent) + , _offlineEditingFirmwareTypeFact(NULL) + , _offlineEditingVehicleTypeFact(NULL) + , _offlineEditingCruiseSpeedFact(NULL) + , _offlineEditingHoverSpeedFact(NULL) + , _batteryPercentRemainingAnnounceFact(NULL) + , _defaultMissionItemAltitudeFact(NULL) + , _missionAutoLoadDirFact(NULL) + , _promptFlightTelemetrySave(NULL) + , _promptFlightTelemetrySaveNotArmed(NULL) + , _audioMuted(NULL) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); +} + +Fact* AppSettings::offlineEditingFirmwareType(void) +{ + if (!_offlineEditingFirmwareTypeFact) { + _offlineEditingFirmwareTypeFact = _createSettingsFact(offlineEditingFirmwareTypeSettingsName); + } + + return _offlineEditingFirmwareTypeFact; +} + +Fact* AppSettings::offlineEditingVehicleType(void) +{ + if (!_offlineEditingVehicleTypeFact) { + _offlineEditingVehicleTypeFact = _createSettingsFact(offlineEditingVehicleTypeSettingsName); + } + + return _offlineEditingVehicleTypeFact; +} + +Fact* AppSettings::offlineEditingCruiseSpeed(void) +{ + if (!_offlineEditingCruiseSpeedFact) { + _offlineEditingCruiseSpeedFact = _createSettingsFact(offlineEditingCruiseSpeedSettingsName); + } + return _offlineEditingCruiseSpeedFact; +} + +Fact* AppSettings::offlineEditingHoverSpeed(void) +{ + if (!_offlineEditingHoverSpeedFact) { + _offlineEditingHoverSpeedFact = _createSettingsFact(offlineEditingHoverSpeedSettingsName); + } + return _offlineEditingHoverSpeedFact; +} + +Fact* AppSettings::batteryPercentRemainingAnnounce(void) +{ + if (!_batteryPercentRemainingAnnounceFact) { + _batteryPercentRemainingAnnounceFact = _createSettingsFact(batteryPercentRemainingAnnounceSettingsName); + } + + return _batteryPercentRemainingAnnounceFact; +} + +Fact* AppSettings::defaultMissionItemAltitude(void) +{ + if (!_defaultMissionItemAltitudeFact) { + _defaultMissionItemAltitudeFact = _createSettingsFact(defaultMissionItemAltitudeSettingsName); + } + + return _defaultMissionItemAltitudeFact; +} + +Fact* AppSettings::missionAutoLoadDir(void) +{ + if (!_missionAutoLoadDirFact) { + _missionAutoLoadDirFact = _createSettingsFact(missionAutoLoadDirSettingsName); + } + + return _missionAutoLoadDirFact; +} + +Fact* AppSettings::promptFlightTelemetrySave(void) +{ + if (!_promptFlightTelemetrySave) { + _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); + } + + return _promptFlightTelemetrySave; +} + +Fact* AppSettings::promptFlightTelemetrySaveNotArmed(void) +{ + if (!_promptFlightTelemetrySaveNotArmed) { + _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); + } + + return _promptFlightTelemetrySaveNotArmed; +} + +Fact* AppSettings::audioMuted(void) +{ + if (!_audioMuted) { + _audioMuted = _createSettingsFact(audioMutedName); + } + + return _audioMuted; +} diff --git a/src/SettingsManager.h b/src/Settings/AppSettings.h similarity index 62% rename from src/SettingsManager.h rename to src/Settings/AppSettings.h index 1df5d57a0..c9bbae84a 100644 --- a/src/SettingsManager.h +++ b/src/Settings/AppSettings.h @@ -7,60 +7,22 @@ * ****************************************************************************/ +#ifndef AppSettings_H +#define AppSettings_H -#ifndef SettingsManager_H -#define SettingsManager_H +#include "SettingsGroup.h" -#include "QGCLoggingCategory.h" -#include "Joystick.h" -#include "MultiVehicleManager.h" -#include "QGCToolbox.h" - -#include - -Q_DECLARE_LOGGING_CATEGORY(SettingsManagerLog) - -/// Provides access to all app settings -class SettingsManager : public QGCTool +class AppSettings : public SettingsGroup { Q_OBJECT public: - SettingsManager(QGCApplication* app); - - enum DistanceUnits { - DistanceUnitsFeet = 0, - DistanceUnitsMeters - }; - - enum AreaUnits { - AreaUnitsSquareFeet = 0, - AreaUnitsSquareMeters, - AreaUnitsSquareKilometers, - AreaUnitsHectares, - AreaUnitsAcres, - AreaUnitsSquareMiles, - }; - - enum SpeedUnits { - SpeedUnitsFeetPerSecond = 0, - SpeedUnitsMetersPerSecond, - SpeedUnitsMilesPerHour, - SpeedUnitsKilometersPerHour, - SpeedUnitsKnots, - }; - - Q_ENUMS(DistanceUnits) - Q_ENUMS(AreaUnits) - Q_ENUMS(SpeedUnits) + AppSettings(QObject* parent = NULL); Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) - Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) - Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) - Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) Q_PROPERTY(Fact* missionAutoLoadDir READ missionAutoLoadDir CONSTANT) @@ -72,9 +34,6 @@ public: Fact* offlineEditingVehicleType (void); Fact* offlineEditingCruiseSpeed (void); Fact* offlineEditingHoverSpeed (void); - Fact* distanceUnits (void); - Fact* areaUnits (void); - Fact* speedUnits (void); Fact* batteryPercentRemainingAnnounce (void); Fact* defaultMissionItemAltitude (void); Fact* missionAutoLoadDir (void); @@ -82,16 +41,12 @@ public: Fact* promptFlightTelemetrySaveNotArmed (void); Fact* audioMuted (void); - // Override from QGCTool - virtual void setToolbox(QGCToolbox *toolbox); + static const char* appSettingsGroupName; static const char* offlineEditingFirmwareTypeSettingsName; static const char* offlineEditingVehicleTypeSettingsName; static const char* offlineEditingCruiseSpeedSettingsName; static const char* offlineEditingHoverSpeedSettingsName; - static const char* distanceUnitsSettingsName; - static const char* areaUnitsSettingsName; - static const char* speedUnitsSettingsName; static const char* batteryPercentRemainingAnnounceSettingsName; static const char* defaultMissionItemAltitudeSettingsName; static const char* missionAutoLoadDirSettingsName; @@ -100,17 +55,10 @@ public: static const char* audioMutedName; private: - SettingsFact* _createSettingsFact(const QString& name); - - QMap _nameToMetaDataMap; - SettingsFact* _offlineEditingFirmwareTypeFact; SettingsFact* _offlineEditingVehicleTypeFact; SettingsFact* _offlineEditingCruiseSpeedFact; SettingsFact* _offlineEditingHoverSpeedFact; - SettingsFact* _distanceUnitsFact; - SettingsFact* _areaUnitsFact; - SettingsFact* _speedUnitsFact; SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; SettingsFact* _missionAutoLoadDirFact; diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc new file mode 100644 index 000000000..71de8257b --- /dev/null +++ b/src/Settings/AutoConnectSettings.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (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 "AutoConnectSettings.h" + +#include + +const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; + +AutoConnectSettings::AutoConnectSettings(QObject* parent) + : SettingsGroup(autoConnectSettingsGroupName, QString() /* root settings group */, parent) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AutoConnectSettings", "Reference only"); +} diff --git a/src/Settings/AutoConnectSettings.h b/src/Settings/AutoConnectSettings.h new file mode 100644 index 000000000..afcd32b63 --- /dev/null +++ b/src/Settings/AutoConnectSettings.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (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 AutoConnectSettings_H +#define AutoConnectSettings_H + +#include "SettingsGroup.h" + +class AutoConnectSettings : public SettingsGroup +{ + Q_OBJECT + +public: + AutoConnectSettings(QObject* parent = NULL); + + static const char* autoConnectSettingsGroupName; + +private: +}; + +#endif diff --git a/src/SettingsManager.json b/src/Settings/SettingsGroup.app.json similarity index 100% rename from src/SettingsManager.json rename to src/Settings/SettingsGroup.app.json diff --git a/src/Settings/SettingsGroup.autoConnect.json b/src/Settings/SettingsGroup.autoConnect.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.autoConnect.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsGroup.cc b/src/Settings/SettingsGroup.cc new file mode 100644 index 000000000..c29c42509 --- /dev/null +++ b/src/Settings/SettingsGroup.cc @@ -0,0 +1,29 @@ +/**************************************************************************** + * + * (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 "SettingsGroup.h" +#include "QGCCorePlugin.h" +#include "QGCApplication.h" + +SettingsGroup::SettingsGroup(const QString& name, const QString& settingsGroup, QObject* parent) + : QObject(parent) + , _name(name) + , _settingsGroup(settingsGroup) + , _visible(qgcApp()->toolbox()->corePlugin()->overrideSettingsGroupVisibility(name)) +{ + QString jsonNameFormat(":/json/SettingsGroup.%1.json"); + + _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(jsonNameFormat.arg(name), this); +} + +SettingsFact* SettingsGroup::_createSettingsFact(const QString& name) +{ + return new SettingsFact(_settingsGroup, _nameToMetaDataMap[name], this); +} + diff --git a/src/Settings/SettingsGroup.h b/src/Settings/SettingsGroup.h new file mode 100644 index 000000000..048c0b113 --- /dev/null +++ b/src/Settings/SettingsGroup.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * + * (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 SettingsGroup_H +#define SettingsGroup_H + +#include "QGCLoggingCategory.h" +#include "Joystick.h" +#include "MultiVehicleManager.h" +#include "QGCToolbox.h" + +#include + +/// Provides access to group of settings. The group is named and has a visible property associated with which can control whether the group +/// is shows in the ui. +class SettingsGroup : public QObject +{ + Q_OBJECT + +public: + /// @param name Name for this Settings group + /// @param settingsGroup Group to place settings in for QSettings::setGroup + SettingsGroup(const QString& name, const QString& settingsGroup, QObject* parent = NULL); + + Q_PROPERTY(bool visible MEMBER _visible CONSTANT) + +protected: + SettingsFact* _createSettingsFact(const QString& name); + + QString _name; + QString _settingsGroup; + bool _visible; + + QMap _nameToMetaDataMap; +}; + +#endif diff --git a/src/Settings/SettingsGroup.units.json b/src/Settings/SettingsGroup.units.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.units.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsGroup.video.json b/src/Settings/SettingsGroup.video.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.video.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc new file mode 100644 index 000000000..35f47cbc7 --- /dev/null +++ b/src/Settings/SettingsManager.cc @@ -0,0 +1,34 @@ +/**************************************************************************** + * + * (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 "SettingsManager.h" + +#include + +SettingsManager::SettingsManager(QGCApplication* app) + : QGCTool(app) + , _appSettings(NULL) + , _unitsSettings(NULL) + , _autoConnectSettings(NULL) + , _videoSettings(NULL) +{ + +} + +void SettingsManager::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only"); + + _unitsSettings = new UnitsSettings(this); // Must be first since AppSettings references it + _appSettings = new AppSettings(this); + _autoConnectSettings = new AutoConnectSettings(this); + _videoSettings = new VideoSettings(this); +} diff --git a/src/Settings/SettingsManager.h b/src/Settings/SettingsManager.h new file mode 100644 index 000000000..40e67555c --- /dev/null +++ b/src/Settings/SettingsManager.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * + * (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 SettingsManager_H +#define SettingsManager_H + +#include "QGCLoggingCategory.h" +#include "Joystick.h" +#include "MultiVehicleManager.h" +#include "QGCToolbox.h" +#include "AppSettings.h" +#include "UnitsSettings.h" +#include "AutoConnectSettings.h" +#include "VideoSettings.h" + +#include + +/// Provides access to all app settings +class SettingsManager : public QGCTool +{ + Q_OBJECT + +public: + SettingsManager(QGCApplication* app); + + Q_PROPERTY(QObject* appSettings READ appSettings CONSTANT) + Q_PROPERTY(QObject* unitsSettings READ unitsSettings CONSTANT) + Q_PROPERTY(QObject* autoConnectSettings READ autoConnectSettings CONSTANT) + Q_PROPERTY(QObject* videoSettings READ videoSettings CONSTANT) + + // Override from QGCTool + virtual void setToolbox(QGCToolbox *toolbox); + + AppSettings* appSettings (void) { return _appSettings; } + UnitsSettings* unitsSettings (void) { return _unitsSettings; } + AutoConnectSettings* autoConnectSettings (void) { return _autoConnectSettings; } + VideoSettings* videoSettings (void) { return _videoSettings; } + +private: + AppSettings* _appSettings; + UnitsSettings* _unitsSettings; + AutoConnectSettings* _autoConnectSettings; + VideoSettings* _videoSettings; +}; + +#endif diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc new file mode 100644 index 000000000..78c7d6b44 --- /dev/null +++ b/src/Settings/UnitsSettings.cc @@ -0,0 +1,93 @@ +/**************************************************************************** + * + * (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 "UnitsSettings.h" + +#include + +const char* UnitsSettings::unitsSettingsGroupName = "units"; +const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; +const char* UnitsSettings::areaUnitsSettingsName = "AreaUnits"; +const char* UnitsSettings::speedUnitsSettingsName = "SpeedUnits"; + +UnitsSettings::UnitsSettings(QObject* parent) + : SettingsGroup(unitsSettingsGroupName, QString() /* root settings group */, parent) + , _distanceUnitsFact(NULL) + , _areaUnitsFact(NULL) + , _speedUnitsFact(NULL) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "UnitsSettings", "Reference only"); +} + +Fact* UnitsSettings::distanceUnits(void) +{ + if (!_distanceUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet" << "Meters"; + enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(distanceUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(DistanceUnitsMeters); + + _distanceUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + + } + + return _distanceUnitsFact; + +} + +Fact* UnitsSettings::areaUnits(void) +{ + if (!_areaUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; + enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(areaUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(AreaUnitsSquareMeters); + + _areaUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _areaUnitsFact; + +} + +Fact* UnitsSettings::speedUnits(void) +{ + if (!_speedUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; + enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(speedUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); + + _speedUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _speedUnitsFact; +} diff --git a/src/Settings/UnitsSettings.h b/src/Settings/UnitsSettings.h new file mode 100644 index 000000000..3ede8765a --- /dev/null +++ b/src/Settings/UnitsSettings.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * + * (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 UnitsSettings_H +#define UnitsSettings_H + +#include "SettingsGroup.h" + +class UnitsSettings : public SettingsGroup +{ + Q_OBJECT + +public: + UnitsSettings(QObject* parent = NULL); + + enum DistanceUnits { + DistanceUnitsFeet = 0, + DistanceUnitsMeters + }; + + enum AreaUnits { + AreaUnitsSquareFeet = 0, + AreaUnitsSquareMeters, + AreaUnitsSquareKilometers, + AreaUnitsHectares, + AreaUnitsAcres, + AreaUnitsSquareMiles, + }; + + enum SpeedUnits { + SpeedUnitsFeetPerSecond = 0, + SpeedUnitsMetersPerSecond, + SpeedUnitsMilesPerHour, + SpeedUnitsKilometersPerHour, + SpeedUnitsKnots, + }; + + Q_ENUMS(DistanceUnits) + Q_ENUMS(AreaUnits) + Q_ENUMS(SpeedUnits) + + Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) + Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) + Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) + + Fact* distanceUnits (void); + Fact* areaUnits (void); + Fact* speedUnits (void); + + static const char* unitsSettingsGroupName; + + static const char* distanceUnitsSettingsName; + static const char* areaUnitsSettingsName; + static const char* speedUnitsSettingsName; + +private: + SettingsFact* _distanceUnitsFact; + SettingsFact* _areaUnitsFact; + SettingsFact* _speedUnitsFact; +}; + +#endif diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc new file mode 100644 index 000000000..f518056e1 --- /dev/null +++ b/src/Settings/VideoSettings.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (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 "VideoSettings.h" + +#include + +const char* VideoSettings::VideoSettingsGroupName = "video"; + +VideoSettings::VideoSettings(QObject* parent) + : SettingsGroup(VideoSettingsGroupName, QString() /* root settings group */, parent) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h new file mode 100644 index 000000000..e5bc1117d --- /dev/null +++ b/src/Settings/VideoSettings.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (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 VideoSettings_H +#define VideoSettings_H + +#include "SettingsGroup.h" + +class VideoSettings : public SettingsGroup +{ + Q_OBJECT + +public: + VideoSettings(QObject* parent = NULL); + + static const char* VideoSettingsGroupName; + +private: +}; + +#endif diff --git a/src/SettingsManager.cc b/src/SettingsManager.cc deleted file mode 100644 index 115c610c0..000000000 --- a/src/SettingsManager.cc +++ /dev/null @@ -1,214 +0,0 @@ -/**************************************************************************** - * - * (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 "SettingsManager.h" -#include "QGCApplication.h" - -QGC_LOGGING_CATEGORY(SettingsManagerLog, "SettingsManagerLog") - -const char* SettingsManager::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; -const char* SettingsManager::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; -const char* SettingsManager::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; -const char* SettingsManager::offlineEditingHoverSpeedSettingsName = "OfflineEditingHoverSpeed"; -const char* SettingsManager::distanceUnitsSettingsName = "DistanceUnits"; -const char* SettingsManager::areaUnitsSettingsName = "AreaUnits"; -const char* SettingsManager::speedUnitsSettingsName = "SpeedUnits"; -const char* SettingsManager::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; -const char* SettingsManager::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; -const char* SettingsManager::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; -const char* SettingsManager::promptFlightTelemetrySaveName = "PromptFLightDataSave"; -const char* SettingsManager::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; -const char* SettingsManager::audioMutedName = "AudioMuted"; - -SettingsManager::SettingsManager(QGCApplication* app) - : QGCTool(app) - , _offlineEditingFirmwareTypeFact(NULL) - , _offlineEditingVehicleTypeFact(NULL) - , _offlineEditingCruiseSpeedFact(NULL) - , _offlineEditingHoverSpeedFact(NULL) - , _distanceUnitsFact(NULL) - , _areaUnitsFact(NULL) - , _speedUnitsFact(NULL) - , _batteryPercentRemainingAnnounceFact(NULL) - , _defaultMissionItemAltitudeFact(NULL) - , _missionAutoLoadDirFact(NULL) - , _promptFlightTelemetrySave(NULL) - , _promptFlightTelemetrySaveNotArmed(NULL) - , _audioMuted(NULL) -{ - -} - -void SettingsManager::setToolbox(QGCToolbox *toolbox) -{ - QGCTool::setToolbox(toolbox); - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only"); - - _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(":/json/SettingsManager.json", this); -} - -SettingsFact* SettingsManager::_createSettingsFact(const QString& name) -{ - return new SettingsFact(QString() /* no settings group */, _nameToMetaDataMap[name], this); -} - -Fact* SettingsManager::offlineEditingFirmwareType(void) -{ - if (!_offlineEditingFirmwareTypeFact) { - _offlineEditingFirmwareTypeFact = _createSettingsFact(offlineEditingFirmwareTypeSettingsName); - } - - return _offlineEditingFirmwareTypeFact; -} - -Fact* SettingsManager::offlineEditingVehicleType(void) -{ - if (!_offlineEditingVehicleTypeFact) { - _offlineEditingVehicleTypeFact = _createSettingsFact(offlineEditingVehicleTypeSettingsName); - } - - return _offlineEditingVehicleTypeFact; -} - -Fact* SettingsManager::offlineEditingCruiseSpeed(void) -{ - if (!_offlineEditingCruiseSpeedFact) { - _offlineEditingCruiseSpeedFact = _createSettingsFact(offlineEditingCruiseSpeedSettingsName); - } - return _offlineEditingCruiseSpeedFact; -} - -Fact* SettingsManager::offlineEditingHoverSpeed(void) -{ - if (!_offlineEditingHoverSpeedFact) { - _offlineEditingHoverSpeedFact = _createSettingsFact(offlineEditingHoverSpeedSettingsName); - } - return _offlineEditingHoverSpeedFact; -} - -Fact* SettingsManager::batteryPercentRemainingAnnounce(void) -{ - if (!_batteryPercentRemainingAnnounceFact) { - _batteryPercentRemainingAnnounceFact = _createSettingsFact(batteryPercentRemainingAnnounceSettingsName); - } - - return _batteryPercentRemainingAnnounceFact; -} - -Fact* SettingsManager::defaultMissionItemAltitude(void) -{ - if (!_defaultMissionItemAltitudeFact) { - _defaultMissionItemAltitudeFact = _createSettingsFact(defaultMissionItemAltitudeSettingsName); - } - - return _defaultMissionItemAltitudeFact; -} - -Fact* SettingsManager::missionAutoLoadDir(void) -{ - if (!_missionAutoLoadDirFact) { - _missionAutoLoadDirFact = _createSettingsFact(missionAutoLoadDirSettingsName); - } - - return _missionAutoLoadDirFact; -} - -Fact* SettingsManager::promptFlightTelemetrySave(void) -{ - if (!_promptFlightTelemetrySave) { - _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); - } - - return _promptFlightTelemetrySave; -} - -Fact* SettingsManager::promptFlightTelemetrySaveNotArmed(void) -{ - if (!_promptFlightTelemetrySaveNotArmed) { - _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); - } - - return _promptFlightTelemetrySaveNotArmed; -} - -Fact* SettingsManager::audioMuted(void) -{ - if (!_audioMuted) { - _audioMuted = _createSettingsFact(audioMutedName); - } - - return _audioMuted; -} - -Fact* SettingsManager::distanceUnits(void) -{ - if (!_distanceUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "Feet" << "Meters"; - enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(distanceUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(DistanceUnitsMeters); - - _distanceUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - - } - - return _distanceUnitsFact; - -} - -Fact* SettingsManager::areaUnits(void) -{ - if (!_areaUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; - enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(areaUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(AreaUnitsSquareMeters); - - _areaUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - } - - return _areaUnitsFact; - -} - -Fact* SettingsManager::speedUnits(void) -{ - if (!_speedUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; - enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(speedUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); - - _speedUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - } - - return _speedUnitsFact; -} diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 45d3656c0..105da0786 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -64,8 +64,8 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo); SettingsManager* settingsManager = toolbox->settingsManager(); - _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->offlineEditingFirmwareType()->rawValue().toInt()), - static_cast(settingsManager->offlineEditingVehicleType()->rawValue().toInt()), + _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()), + static_cast(settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()), _firmwarePluginManager, this); } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index a98e094a5..1639e0519 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -101,8 +101,8 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _telemetryRRSSI(0) , _telemetryLRSSI(0) , _telemetryRXErrors(0) @@ -260,8 +260,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -323,10 +323,10 @@ void Vehicle::_commonInit(void) connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); // Offline editing vehicle tracks settings changes for offline editing settings - connect(_settingsManager->offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); - connect(_settingsManager->offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); - connect(_settingsManager->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); - connect(_settingsManager->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); // Build FactGroup object model @@ -791,7 +791,7 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) } _batteryFactGroup.percentRemaining()->setRawValue(sysStatus.battery_remaining); - if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _settingsManager->batteryPercentRemainingAnnounce()->rawValue().toInt()) { + if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _settingsManager->appSettings()->batteryPercentRemainingAnnounce()->rawValue().toInt()) { if (!_lowBatteryAnnounceTimer.isValid() || _lowBatteryAnnounceTimer.elapsed() > _lowBatteryAnnounceRepeatMSecs) { _lowBatteryAnnounceTimer.restart(); _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); @@ -1579,7 +1579,7 @@ void Vehicle::_parametersReady(bool parametersReady) { if (parametersReady && !_missionManagerInitialRequestSent) { _missionManagerInitialRequestSent = true; - QString missionAutoLoadDirPath = _settingsManager->missionAutoLoadDir()->rawValue().toString(); + QString missionAutoLoadDirPath = _settingsManager->appSettings()->missionAutoLoadDir()->rawValue().toString(); if (missionAutoLoadDirPath.isEmpty()) { _missionManager->requestMissionItems(); } else { diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index eab76cc0f..71653a72c 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -146,3 +146,11 @@ QVariant QGCCorePlugin::overrideSettingsDefault(QString name, QVariant defaultVa // No overrides for base plugin return defaultValue; } + +bool QGCCorePlugin::overrideSettingsGroupVisibility(QString name) +{ + Q_UNUSED(name); + + // Always show all + return true; +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index b668a9d7c..082c840f3 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -54,6 +54,11 @@ public: */ virtual QGCOptions* options (); + /// Allows the core plugin to override the default value for the specified setting + /// @param name - Setting group name + /// @return true: Show settings ui, false: Hide settings ui + virtual bool overrideSettingsGroupVisibility(QString name); + /// Allows the core plugin to override the default value for the specified setting /// @param name - Setting name /// @param defaultValue - Standard default value for setting diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 3ca92d845..0d031a4f8 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -424,7 +424,7 @@ void MAVLinkProtocol::_stopLogging(void) if (_closeLogFile()) { // If the signals are not connected it means we are running a unit test. In that case just delete log files SettingsManager* settingsManager = _app->toolbox()->settingsManager(); - if ((_vehicleWasArmed || settingsManager->promptFlightTelemetrySaveNotArmed()->rawValue().toBool()) && settingsManager->promptFlightTelemetrySave()->rawValue().toBool()) { + if ((_vehicleWasArmed || settingsManager->appSettings()->promptFlightTelemetrySaveNotArmed()->rawValue().toBool()) && settingsManager->appSettings()->promptFlightTelemetrySave()->rawValue().toBool()) { emit saveTempFlightDataLog(_tempLogFile.fileName()); } else { QFile::remove(_tempLogFile.fileName()); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 75fe1906c..ecaaeb9dd 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -32,8 +32,8 @@ QGCView { anchors.fill: parent anchors.margins: ScreenTools.defaultFontPixelWidth - property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.batteryPercentRemainingAnnounce - property Fact _autoLoadDir: QGroundControl.settingsManager.missionAutoLoadDir + property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.appSettings.batteryPercentRemainingAnnounce + property Fact _autoLoadDir: QGroundControl.settingsManager.appSettings.missionAutoLoadDir property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -41,14 +41,6 @@ QGCView { QGCPalette { id: qgcPal } - FileDialog { - id: fileDialog - title: "Choose a location to save video files." - folder: shortcuts.home - selectFolder: true - onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) - } - QGCViewPanel { id: panel anchors.fill: parent @@ -65,10 +57,11 @@ QGCView { //----------------------------------------------------------------- //-- Units Item { - width: qgcView.width * 0.8 - height: unitLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: unitLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.unitsSettings.visible QGCLabel { id: unitLabel text: qsTr("Units (Requires Restart)") @@ -76,11 +69,12 @@ QGCView { } } Rectangle { - height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.unitsSettings.visible Column { id: unitsCol spacing: ScreenTools.defaultFontPixelWidth @@ -95,7 +89,7 @@ QGCView { FactComboBox { id: distanceUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.distanceUnits + fact: QGroundControl.settingsManager.unitsSettings.distanceUnits indexModel: false } } @@ -109,7 +103,7 @@ QGCView { FactComboBox { id: areaUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.areaUnits + fact: QGroundControl.settingsManager.unitsSettings.areaUnits indexModel: false } } @@ -123,7 +117,7 @@ QGCView { FactComboBox { id: speedUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.speedUnits + fact: QGroundControl.settingsManager.unitsSettings.speedUnits indexModel: false } } @@ -132,10 +126,11 @@ QGCView { //----------------------------------------------------------------- //-- Miscellanous Item { - width: qgcView.width * 0.8 - height: miscLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: miscLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.appSettings.visible QGCLabel { id: miscLabel text: qsTr("Miscellaneous") @@ -143,11 +138,12 @@ QGCView { } } Rectangle { - height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.appSettings.visible Column { id: miscCol spacing: ScreenTools.defaultFontPixelWidth @@ -233,22 +229,21 @@ QGCView { //-- Audio preferences FactCheckBox { text: qsTr("Mute all audio output") - fact: QGroundControl.settingsManager.audioMuted + fact: QGroundControl.settingsManager.appSettings.audioMuted } //----------------------------------------------------------------- //-- Prompt Save Log - QGCCheckBox { + FactCheckBox { id: promptSaveLog text: qsTr("Prompt to save Flight Data Log after each flight") - checked: QGroundControl.settingsManager.promptFlightTelemetrySave - //fact: QGroundControl.settingsManager.promptFlightTelemetrySave + fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave visible: !ScreenTools.isMobile } //----------------------------------------------------------------- //-- Prompt Save even if not armed FactCheckBox { text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - fact: QGroundControl.settingsManager.promptFlightTelemetrySaveNotArmed + fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed visible: !ScreenTools.isMobile enabled: promptSaveLog.checked } @@ -320,7 +315,7 @@ QGCView { } FactTextField { id: defaultItemAltitudeField - fact: QGroundControl.settingsManager.defaultMissionItemAltitude + fact: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude } } //----------------------------------------------------------------- @@ -403,11 +398,11 @@ QGCView { //----------------------------------------------------------------- //-- Autoconnect settings Item { - width: qgcView.width * 0.8 - height: autoConnectLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - visible: QGroundControl.corePlugin.options.enableAutoConnectOptions + width: qgcView.width * 0.8 + height: autoConnectLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.autoConnectSettings.visible QGCLabel { id: autoConnectLabel text: qsTr("AutoConnect to the following devices:") @@ -415,12 +410,12 @@ QGCView { } } Rectangle { - height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - visible: QGroundControl.corePlugin.options.enableAutoConnectOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.autoConnectSettings.visible Column { id: autoConnectCol spacing: ScreenTools.defaultFontPixelWidth @@ -468,11 +463,11 @@ QGCView { //----------------------------------------------------------------- //-- Video Source Item { - width: qgcView.width * 0.8 - height: videoLabel.height - visible: QGroundControl.corePlugin.options.enableVideoSourceOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: videoLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.videoSettings.visible QGCLabel { id: videoLabel text: qsTr("Video (Requires Restart)") @@ -480,12 +475,12 @@ QGCView { } } Rectangle { - height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - visible: QGroundControl.corePlugin.options.enableVideoSourceOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.videoSettings.visible Column { id: videoCol spacing: ScreenTools.defaultFontPixelWidth @@ -566,8 +561,17 @@ QGCView { text: QGroundControl.videoManager.videoSavePath } QGCButton { - text: "Browse" - onClicked: fileDialog.visible = true + text: "Browse" + onClicked: videoLocationFileDialog.visible = true + + FileDialog { + id: videoLocationFileDialog + title: "Choose a location to save video files." + folder: shortcuts.home + selectFolder: true + onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) + } + } } } -- GitLab From b55c23fee86d66e6a5f84faa4ae85e85ae6dba6f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 14:11:59 -0800 Subject: [PATCH 316/398] Make Windows happy --- src/Settings/AppSettings.cc | 1 + src/Settings/AutoConnectSettings.cc | 1 + src/Settings/SettingsManager.cc | 1 + src/Settings/UnitsSettings.cc | 1 + src/Settings/VideoSettings.cc | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 348baf896..c0fb64fe8 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -10,6 +10,7 @@ #include "AppSettings.h" #include +#include const char* AppSettings::appSettingsGroupName = "app"; const char* AppSettings::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index 71de8257b..16a2c7647 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -10,6 +10,7 @@ #include "AutoConnectSettings.h" #include +#include const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc index 35f47cbc7..226976f28 100644 --- a/src/Settings/SettingsManager.cc +++ b/src/Settings/SettingsManager.cc @@ -10,6 +10,7 @@ #include "SettingsManager.h" #include +#include SettingsManager::SettingsManager(QGCApplication* app) : QGCTool(app) diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc index 78c7d6b44..608e45cfd 100644 --- a/src/Settings/UnitsSettings.cc +++ b/src/Settings/UnitsSettings.cc @@ -10,6 +10,7 @@ #include "UnitsSettings.h" #include +#include const char* UnitsSettings::unitsSettingsGroupName = "units"; const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index f518056e1..3b9ab513f 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -10,6 +10,7 @@ #include "VideoSettings.h" #include +#include const char* VideoSettings::VideoSettingsGroupName = "video"; -- GitLab From 39f67c63c1583fdf4ba9ae4a2ed58457768bd24f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 13:16:59 -0800 Subject: [PATCH 317/398] Add new SettingsGroup support --- qgroundcontrol.pro | 15 +- qgroundcontrol.qrc | 5 +- src/FactSystem/FactMetaData.cc | 38 ++-- src/FlightMap/MapScale.qml | 2 +- src/GAudioOutput.cc | 2 +- src/MissionEditor/MissionSettingsEditor.qml | 10 +- src/MissionManager/MissionCommandTree.cc | 4 +- src/MissionManager/MissionController.cc | 6 +- src/MissionManager/RallyPointController.cc | 2 +- src/MissionManager/SimpleMissionItem.cc | 2 +- src/MissionManager/SimpleMissionItemTest.cc | 4 +- src/Settings/AppSettings.cc | 129 +++++++++++ .../AppSettings.h} | 64 +----- src/Settings/AutoConnectSettings.cc | 21 ++ src/Settings/AutoConnectSettings.h | 27 +++ .../SettingsGroup.app.json} | 0 src/Settings/SettingsGroup.autoConnect.json | 2 + src/Settings/SettingsGroup.cc | 29 +++ src/Settings/SettingsGroup.h | 43 ++++ src/Settings/SettingsGroup.units.json | 2 + src/Settings/SettingsGroup.video.json | 2 + src/Settings/SettingsManager.cc | 34 +++ src/Settings/SettingsManager.h | 52 +++++ src/Settings/UnitsSettings.cc | 93 ++++++++ src/Settings/UnitsSettings.h | 68 ++++++ src/Settings/VideoSettings.cc | 21 ++ src/Settings/VideoSettings.h | 27 +++ src/SettingsManager.cc | 214 ------------------ src/Vehicle/MultiVehicleManager.cc | 4 +- src/Vehicle/Vehicle.cc | 20 +- src/api/QGCCorePlugin.cc | 8 + src/api/QGCCorePlugin.h | 5 + src/comm/MAVLinkProtocol.cc | 2 +- src/ui/preferences/GeneralSettings.qml | 126 ++++++----- 34 files changed, 699 insertions(+), 384 deletions(-) create mode 100644 src/Settings/AppSettings.cc rename src/{SettingsManager.h => Settings/AppSettings.h} (62%) create mode 100644 src/Settings/AutoConnectSettings.cc create mode 100644 src/Settings/AutoConnectSettings.h rename src/{SettingsManager.json => Settings/SettingsGroup.app.json} (100%) create mode 100644 src/Settings/SettingsGroup.autoConnect.json create mode 100644 src/Settings/SettingsGroup.cc create mode 100644 src/Settings/SettingsGroup.h create mode 100644 src/Settings/SettingsGroup.units.json create mode 100644 src/Settings/SettingsGroup.video.json create mode 100644 src/Settings/SettingsManager.cc create mode 100644 src/Settings/SettingsManager.h create mode 100644 src/Settings/UnitsSettings.cc create mode 100644 src/Settings/UnitsSettings.h create mode 100644 src/Settings/VideoSettings.cc create mode 100644 src/Settings/VideoSettings.h delete mode 100644 src/SettingsManager.cc diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 71fe844ff..f06292340 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -291,6 +291,7 @@ INCLUDEPATH += \ src/QmlControls \ src/QtLocationPlugin \ src/QtLocationPlugin/QMLControl \ + src/Settings \ src/VehicleSetup \ src/ViewWidgets \ src/audio \ @@ -474,7 +475,12 @@ HEADERS += \ src/QmlControls/RCChannelMonitorController.h \ src/QmlControls/ScreenToolsController.h \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.h \ - src/SettingsManager.h \ + src/Settings/AppSettings.h \ + src/Settings/AutoConnectSettings.h \ + src/Settings/SettingsGroup.h \ + src/Settings/SettingsManager.h \ + src/Settings/UnitsSettings.h \ + src/Settings/VideoSettings.h \ src/Vehicle/MAVLinkLogManager.h \ src/VehicleSetup/JoystickConfigController.h \ src/audio/QGCAudioWorker.h \ @@ -636,7 +642,12 @@ SOURCES += \ src/QmlControls/RCChannelMonitorController.cc \ src/QmlControls/ScreenToolsController.cc \ src/QtLocationPlugin/QMLControl/QGCMapEngineManager.cc \ - src/SettingsManager.cc \ + src/Settings/AppSettings.cc \ + src/Settings/AutoConnectSettings.cc \ + src/Settings/SettingsGroup.cc \ + src/Settings/SettingsManager.cc \ + src/Settings/UnitsSettings.cc \ + src/Settings/VideoSettings.cc \ src/Vehicle/MAVLinkLogManager.cc \ src/VehicleSetup/JoystickConfigController.cc \ src/audio/QGCAudioWorker.cpp \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 296df8deb..1f4bb7924 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -178,7 +178,10 @@ src/Vehicle/GPSFact.json src/Vehicle/WindFact.json src/Vehicle/VibrationFact.json - src/SettingsManager.json + src/Settings/SettingsGroup.app.json + src/Settings/SettingsGroup.autoConnect.json + src/Settings/SettingsGroup.units.json + src/Settings/SettingsGroup.video.json src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index aa0575d19..dc9651422 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -40,21 +40,21 @@ const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] // Translations driven by app settings const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = { - { "m", "m", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "meters", "meters", false, SettingsManager::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m/s", "m/s", true, SettingsManager::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m^2", "m^2", false, SettingsManager::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, - { "m", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "meters", "ft", false, SettingsManager::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m^2", "km^2", false, SettingsManager::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, - { "m^2", "ha", false, SettingsManager::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, - { "m^2", "ft^2", false, SettingsManager::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, - { "m^2", "ac", false, SettingsManager::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, - { "m^2", "mi^2", false, SettingsManager::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, - { "m/s", "ft/s", true, SettingsManager::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, - { "m/s", "mph", true, SettingsManager::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, - { "m/s", "km/h", true, SettingsManager::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, - { "m/s", "kn", true, SettingsManager::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, + { "m", "m", false, UnitsSettings::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "meters", "meters", false, UnitsSettings::DistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m/s", "m/s", true, UnitsSettings::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m^2", "m^2", false, UnitsSettings::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator }, + { "m", "ft", false, UnitsSettings::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "meters", "ft", false, UnitsSettings::DistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m^2", "km^2", false, UnitsSettings::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters }, + { "m^2", "ha", false, UnitsSettings::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters }, + { "m^2", "ft^2", false, UnitsSettings::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters }, + { "m^2", "ac", false, UnitsSettings::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters }, + { "m^2", "mi^2", false, UnitsSettings::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters }, + { "m/s", "ft/s", true, UnitsSettings::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters }, + { "m/s", "mph", true, UnitsSettings::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond }, + { "m/s", "km/h", true, UnitsSettings::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond }, + { "m/s", "kn", true, UnitsSettings::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, }; const char* FactMetaData::_decimalPlacesJsonKey = "decimalPlaces"; @@ -635,8 +635,8 @@ void FactMetaData::_setAppSettingsTranslators(void) const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == _rawUnits.toLower() && - ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->speedUnits()->rawValue().toUInt()) || - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt()))) { + ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->speedUnits()->rawValue().toUInt()) || + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt()))) { _cookedUnits = pAppSettingsTranslation->cookedUnits; setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator); return; @@ -651,7 +651,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->distanceUnits()->rawValue().toUInt())) { + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt())) { return pAppSettingsTranslation; } } @@ -665,7 +665,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; if (pAppSettingsTranslation->rawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->areaUnits()->rawValue().toUInt()) + (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt()) ) { return pAppSettingsTranslation; } diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index a861e41d5..8a265d863 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -115,7 +115,7 @@ Item { var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, scale.y)) var scaleLineMeters = Math.round(leftCoord.distanceTo(rightCoord)) - if (QGroundControl.settingsManager.distanceUnits.value == QGroundControl.settingsManager.DistanceUnitsFeet) { + if (QGroundControl.settingsManager.unitsSettings.distanceUnits.value == UnitsSettings.DistanceUnitsFeet) { calculateFeetRatio(scaleLineMeters, scaleLinePixelLength) } else { calculateMetersRatio(scaleLineMeters, scaleLinePixelLength) diff --git a/src/GAudioOutput.cc b/src/GAudioOutput.cc index c20f3a0b2..480e04ded 100644 --- a/src/GAudioOutput.cc +++ b/src/GAudioOutput.cc @@ -56,7 +56,7 @@ GAudioOutput::~GAudioOutput() bool GAudioOutput::say(const QString& inText) { - bool muted = qgcApp()->toolbox()->settingsManager()->audioMuted()->rawValue().toBool(); + bool muted = qgcApp()->toolbox()->settingsManager()->appSettings()->audioMuted()->rawValue().toBool(); muted |= qgcApp()->runningUnitTests(); if (!muted && !qgcApp()->runningUnitTests()) { #if defined __android__ diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index 3e711443a..729370c2a 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -122,7 +122,7 @@ Rectangle { Layout.fillWidth: true } FactComboBox { - fact: QGroundControl.settingsManager.offlineEditingFirmwareType + fact: QGroundControl.settingsManager.appSettings.offlineEditingFirmwareType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -146,7 +146,7 @@ Rectangle { } FactComboBox { id: offlineVehicleCombo - fact: QGroundControl.settingsManager.offlineEditingVehicleType + fact: QGroundControl.settingsManager.appSettings.offlineEditingVehicleType indexModel: false visible: _showOfflineEditingCombos Layout.preferredWidth: _fieldWidth @@ -170,7 +170,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.settingsManager.offlineEditingCruiseSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingCruiseSpeed visible: _showCruiseSpeed Layout.preferredWidth: _fieldWidth } @@ -182,7 +182,7 @@ Rectangle { Layout.fillWidth: true } FactTextField { - fact: QGroundControl.settingsManager.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed visible: _showHoverSpeed Layout.preferredWidth: _fieldWidth } @@ -196,7 +196,7 @@ Rectangle { QGCLabel { text: qsTr("Hover speed:"); Layout.fillWidth: true } FactTextField { Layout.preferredWidth: _fieldWidth - fact: QGroundControl.settingsManager.offlineEditingHoverSpeed + fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed } } diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index 31f69f52f..f494826b6 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -253,7 +253,7 @@ void MissionCommandTree::_baseVehicleInfo(Vehicle* vehicle, MAV_AUTOPILOT& baseF baseVehicleType = _baseVehicleType(vehicle->vehicleType()); } else { // No Vehicle means offline editing - baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)_settingsManager->offlineEditingFirmwareType()->rawValue().toInt()); - baseVehicleType = _baseVehicleType((MAV_TYPE)_settingsManager->offlineEditingVehicleType()->rawValue().toInt()); + baseFirmwareType = _baseFirmwareType((MAV_AUTOPILOT)_settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()); + baseVehicleType = _baseVehicleType((MAV_TYPE)_settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()); } } diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 53a209823..39d31974a 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -437,13 +437,13 @@ bool MissionController::_loadJsonMissionFileV2(Vehicle* vehicle, const QJsonObje return false; } if (json.contains(_jsonVehicleTypeKey) && vehicle->isOfflineEditingVehicle()) { - settingsManager->offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); + settingsManager->appSettings()->offlineEditingVehicleType()->setRawValue(json[_jsonVehicleTypeKey].toDouble()); } if (json.contains(_jsonCruiseSpeedKey)) { - settingsManager->offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); + settingsManager->appSettings()->offlineEditingCruiseSpeed()->setRawValue(json[_jsonCruiseSpeedKey].toDouble()); } if (json.contains(_jsonHoverSpeedKey)) { - settingsManager->offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); + settingsManager->appSettings()->offlineEditingHoverSpeed()->setRawValue(json[_jsonHoverSpeedKey].toDouble()); } SimpleMissionItem* homeItem = new SimpleMissionItem(vehicle, visualItems); diff --git a/src/MissionManager/RallyPointController.cc b/src/MissionManager/RallyPointController.cc index 66e0b7667..adc43f62f 100644 --- a/src/MissionManager/RallyPointController.cc +++ b/src/MissionManager/RallyPointController.cc @@ -268,7 +268,7 @@ void RallyPointController::addPoint(QGeoCoordinate point) if (_points.count()) { defaultAlt = qobject_cast(_points[_points.count() - 1])->coordinate().altitude(); } else { - defaultAlt = qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble(); + defaultAlt = qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble(); } point.setAltitude(defaultAlt); RallyPoint* newPoint = new RallyPoint(point, this); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 3d6c0de22..21a04129f 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -530,7 +530,7 @@ void SimpleMissionItem::_syncFrameToAltitudeRelativeToHome(void) void SimpleMissionItem::setDefaultsForCommand(void) { // We set these global defaults first, then if there are param defaults they will get reset - _missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); + _missionItem.setParam7(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble()); MAV_CMD command = (MAV_CMD)this->command(); const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); diff --git a/src/MissionManager/SimpleMissionItemTest.cc b/src/MissionManager/SimpleMissionItemTest.cc index cf453d34e..3339cedbc 100644 --- a/src/MissionManager/SimpleMissionItemTest.cc +++ b/src/MissionManager/SimpleMissionItemTest.cc @@ -141,7 +141,7 @@ void SimpleMissionItemTest::_testDefaultValues(void) item.missionItem().setCommand(MAV_CMD_NAV_WAYPOINT); item.missionItem().setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT); - QCOMPARE(item.missionItem().param7(), qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble()); + QCOMPARE(item.missionItem().param7(), qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble()); } void SimpleMissionItemTest::_testSignals(void) @@ -226,7 +226,7 @@ void SimpleMissionItemTest::_testSignals(void) // dirtyChanged // Check that changing to the same coordinate does not signal - simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, qgcApp()->toolbox()->settingsManager()->defaultMissionItemAltitude()->rawValue().toDouble())); + simpleMissionItem.setCoordinate(QGeoCoordinate(50.1234567, 60.1234567, qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble())); QVERIFY(multiSpy->checkNoSignals()); // Check that actually changing coordinate signals correctly diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc new file mode 100644 index 000000000..348baf896 --- /dev/null +++ b/src/Settings/AppSettings.cc @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * (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 "AppSettings.h" + +#include + +const char* AppSettings::appSettingsGroupName = "app"; +const char* AppSettings::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; +const char* AppSettings::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; +const char* AppSettings::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; +const char* AppSettings::offlineEditingHoverSpeedSettingsName = "OfflineEditingHoverSpeed"; +const char* AppSettings::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; +const char* AppSettings::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; +const char* AppSettings::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; +const char* AppSettings::promptFlightTelemetrySaveName = "PromptFLightDataSave"; +const char* AppSettings::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; +const char* AppSettings::audioMutedName = "AudioMuted"; + +AppSettings::AppSettings(QObject* parent) + : SettingsGroup(appSettingsGroupName, QString() /* root settings group */, parent) + , _offlineEditingFirmwareTypeFact(NULL) + , _offlineEditingVehicleTypeFact(NULL) + , _offlineEditingCruiseSpeedFact(NULL) + , _offlineEditingHoverSpeedFact(NULL) + , _batteryPercentRemainingAnnounceFact(NULL) + , _defaultMissionItemAltitudeFact(NULL) + , _missionAutoLoadDirFact(NULL) + , _promptFlightTelemetrySave(NULL) + , _promptFlightTelemetrySaveNotArmed(NULL) + , _audioMuted(NULL) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); +} + +Fact* AppSettings::offlineEditingFirmwareType(void) +{ + if (!_offlineEditingFirmwareTypeFact) { + _offlineEditingFirmwareTypeFact = _createSettingsFact(offlineEditingFirmwareTypeSettingsName); + } + + return _offlineEditingFirmwareTypeFact; +} + +Fact* AppSettings::offlineEditingVehicleType(void) +{ + if (!_offlineEditingVehicleTypeFact) { + _offlineEditingVehicleTypeFact = _createSettingsFact(offlineEditingVehicleTypeSettingsName); + } + + return _offlineEditingVehicleTypeFact; +} + +Fact* AppSettings::offlineEditingCruiseSpeed(void) +{ + if (!_offlineEditingCruiseSpeedFact) { + _offlineEditingCruiseSpeedFact = _createSettingsFact(offlineEditingCruiseSpeedSettingsName); + } + return _offlineEditingCruiseSpeedFact; +} + +Fact* AppSettings::offlineEditingHoverSpeed(void) +{ + if (!_offlineEditingHoverSpeedFact) { + _offlineEditingHoverSpeedFact = _createSettingsFact(offlineEditingHoverSpeedSettingsName); + } + return _offlineEditingHoverSpeedFact; +} + +Fact* AppSettings::batteryPercentRemainingAnnounce(void) +{ + if (!_batteryPercentRemainingAnnounceFact) { + _batteryPercentRemainingAnnounceFact = _createSettingsFact(batteryPercentRemainingAnnounceSettingsName); + } + + return _batteryPercentRemainingAnnounceFact; +} + +Fact* AppSettings::defaultMissionItemAltitude(void) +{ + if (!_defaultMissionItemAltitudeFact) { + _defaultMissionItemAltitudeFact = _createSettingsFact(defaultMissionItemAltitudeSettingsName); + } + + return _defaultMissionItemAltitudeFact; +} + +Fact* AppSettings::missionAutoLoadDir(void) +{ + if (!_missionAutoLoadDirFact) { + _missionAutoLoadDirFact = _createSettingsFact(missionAutoLoadDirSettingsName); + } + + return _missionAutoLoadDirFact; +} + +Fact* AppSettings::promptFlightTelemetrySave(void) +{ + if (!_promptFlightTelemetrySave) { + _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); + } + + return _promptFlightTelemetrySave; +} + +Fact* AppSettings::promptFlightTelemetrySaveNotArmed(void) +{ + if (!_promptFlightTelemetrySaveNotArmed) { + _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); + } + + return _promptFlightTelemetrySaveNotArmed; +} + +Fact* AppSettings::audioMuted(void) +{ + if (!_audioMuted) { + _audioMuted = _createSettingsFact(audioMutedName); + } + + return _audioMuted; +} diff --git a/src/SettingsManager.h b/src/Settings/AppSettings.h similarity index 62% rename from src/SettingsManager.h rename to src/Settings/AppSettings.h index 1df5d57a0..c9bbae84a 100644 --- a/src/SettingsManager.h +++ b/src/Settings/AppSettings.h @@ -7,60 +7,22 @@ * ****************************************************************************/ +#ifndef AppSettings_H +#define AppSettings_H -#ifndef SettingsManager_H -#define SettingsManager_H +#include "SettingsGroup.h" -#include "QGCLoggingCategory.h" -#include "Joystick.h" -#include "MultiVehicleManager.h" -#include "QGCToolbox.h" - -#include - -Q_DECLARE_LOGGING_CATEGORY(SettingsManagerLog) - -/// Provides access to all app settings -class SettingsManager : public QGCTool +class AppSettings : public SettingsGroup { Q_OBJECT public: - SettingsManager(QGCApplication* app); - - enum DistanceUnits { - DistanceUnitsFeet = 0, - DistanceUnitsMeters - }; - - enum AreaUnits { - AreaUnitsSquareFeet = 0, - AreaUnitsSquareMeters, - AreaUnitsSquareKilometers, - AreaUnitsHectares, - AreaUnitsAcres, - AreaUnitsSquareMiles, - }; - - enum SpeedUnits { - SpeedUnitsFeetPerSecond = 0, - SpeedUnitsMetersPerSecond, - SpeedUnitsMilesPerHour, - SpeedUnitsKilometersPerHour, - SpeedUnitsKnots, - }; - - Q_ENUMS(DistanceUnits) - Q_ENUMS(AreaUnits) - Q_ENUMS(SpeedUnits) + AppSettings(QObject* parent = NULL); Q_PROPERTY(Fact* offlineEditingFirmwareType READ offlineEditingFirmwareType CONSTANT) Q_PROPERTY(Fact* offlineEditingVehicleType READ offlineEditingVehicleType CONSTANT) Q_PROPERTY(Fact* offlineEditingCruiseSpeed READ offlineEditingCruiseSpeed CONSTANT) Q_PROPERTY(Fact* offlineEditingHoverSpeed READ offlineEditingHoverSpeed CONSTANT) - Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) - Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) - Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) Q_PROPERTY(Fact* batteryPercentRemainingAnnounce READ batteryPercentRemainingAnnounce CONSTANT) Q_PROPERTY(Fact* defaultMissionItemAltitude READ defaultMissionItemAltitude CONSTANT) Q_PROPERTY(Fact* missionAutoLoadDir READ missionAutoLoadDir CONSTANT) @@ -72,9 +34,6 @@ public: Fact* offlineEditingVehicleType (void); Fact* offlineEditingCruiseSpeed (void); Fact* offlineEditingHoverSpeed (void); - Fact* distanceUnits (void); - Fact* areaUnits (void); - Fact* speedUnits (void); Fact* batteryPercentRemainingAnnounce (void); Fact* defaultMissionItemAltitude (void); Fact* missionAutoLoadDir (void); @@ -82,16 +41,12 @@ public: Fact* promptFlightTelemetrySaveNotArmed (void); Fact* audioMuted (void); - // Override from QGCTool - virtual void setToolbox(QGCToolbox *toolbox); + static const char* appSettingsGroupName; static const char* offlineEditingFirmwareTypeSettingsName; static const char* offlineEditingVehicleTypeSettingsName; static const char* offlineEditingCruiseSpeedSettingsName; static const char* offlineEditingHoverSpeedSettingsName; - static const char* distanceUnitsSettingsName; - static const char* areaUnitsSettingsName; - static const char* speedUnitsSettingsName; static const char* batteryPercentRemainingAnnounceSettingsName; static const char* defaultMissionItemAltitudeSettingsName; static const char* missionAutoLoadDirSettingsName; @@ -100,17 +55,10 @@ public: static const char* audioMutedName; private: - SettingsFact* _createSettingsFact(const QString& name); - - QMap _nameToMetaDataMap; - SettingsFact* _offlineEditingFirmwareTypeFact; SettingsFact* _offlineEditingVehicleTypeFact; SettingsFact* _offlineEditingCruiseSpeedFact; SettingsFact* _offlineEditingHoverSpeedFact; - SettingsFact* _distanceUnitsFact; - SettingsFact* _areaUnitsFact; - SettingsFact* _speedUnitsFact; SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; SettingsFact* _missionAutoLoadDirFact; diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc new file mode 100644 index 000000000..71de8257b --- /dev/null +++ b/src/Settings/AutoConnectSettings.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (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 "AutoConnectSettings.h" + +#include + +const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; + +AutoConnectSettings::AutoConnectSettings(QObject* parent) + : SettingsGroup(autoConnectSettingsGroupName, QString() /* root settings group */, parent) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AutoConnectSettings", "Reference only"); +} diff --git a/src/Settings/AutoConnectSettings.h b/src/Settings/AutoConnectSettings.h new file mode 100644 index 000000000..afcd32b63 --- /dev/null +++ b/src/Settings/AutoConnectSettings.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (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 AutoConnectSettings_H +#define AutoConnectSettings_H + +#include "SettingsGroup.h" + +class AutoConnectSettings : public SettingsGroup +{ + Q_OBJECT + +public: + AutoConnectSettings(QObject* parent = NULL); + + static const char* autoConnectSettingsGroupName; + +private: +}; + +#endif diff --git a/src/SettingsManager.json b/src/Settings/SettingsGroup.app.json similarity index 100% rename from src/SettingsManager.json rename to src/Settings/SettingsGroup.app.json diff --git a/src/Settings/SettingsGroup.autoConnect.json b/src/Settings/SettingsGroup.autoConnect.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.autoConnect.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsGroup.cc b/src/Settings/SettingsGroup.cc new file mode 100644 index 000000000..c29c42509 --- /dev/null +++ b/src/Settings/SettingsGroup.cc @@ -0,0 +1,29 @@ +/**************************************************************************** + * + * (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 "SettingsGroup.h" +#include "QGCCorePlugin.h" +#include "QGCApplication.h" + +SettingsGroup::SettingsGroup(const QString& name, const QString& settingsGroup, QObject* parent) + : QObject(parent) + , _name(name) + , _settingsGroup(settingsGroup) + , _visible(qgcApp()->toolbox()->corePlugin()->overrideSettingsGroupVisibility(name)) +{ + QString jsonNameFormat(":/json/SettingsGroup.%1.json"); + + _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(jsonNameFormat.arg(name), this); +} + +SettingsFact* SettingsGroup::_createSettingsFact(const QString& name) +{ + return new SettingsFact(_settingsGroup, _nameToMetaDataMap[name], this); +} + diff --git a/src/Settings/SettingsGroup.h b/src/Settings/SettingsGroup.h new file mode 100644 index 000000000..048c0b113 --- /dev/null +++ b/src/Settings/SettingsGroup.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * + * (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 SettingsGroup_H +#define SettingsGroup_H + +#include "QGCLoggingCategory.h" +#include "Joystick.h" +#include "MultiVehicleManager.h" +#include "QGCToolbox.h" + +#include + +/// Provides access to group of settings. The group is named and has a visible property associated with which can control whether the group +/// is shows in the ui. +class SettingsGroup : public QObject +{ + Q_OBJECT + +public: + /// @param name Name for this Settings group + /// @param settingsGroup Group to place settings in for QSettings::setGroup + SettingsGroup(const QString& name, const QString& settingsGroup, QObject* parent = NULL); + + Q_PROPERTY(bool visible MEMBER _visible CONSTANT) + +protected: + SettingsFact* _createSettingsFact(const QString& name); + + QString _name; + QString _settingsGroup; + bool _visible; + + QMap _nameToMetaDataMap; +}; + +#endif diff --git a/src/Settings/SettingsGroup.units.json b/src/Settings/SettingsGroup.units.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.units.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsGroup.video.json b/src/Settings/SettingsGroup.video.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/src/Settings/SettingsGroup.video.json @@ -0,0 +1,2 @@ +[ +] diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc new file mode 100644 index 000000000..35f47cbc7 --- /dev/null +++ b/src/Settings/SettingsManager.cc @@ -0,0 +1,34 @@ +/**************************************************************************** + * + * (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 "SettingsManager.h" + +#include + +SettingsManager::SettingsManager(QGCApplication* app) + : QGCTool(app) + , _appSettings(NULL) + , _unitsSettings(NULL) + , _autoConnectSettings(NULL) + , _videoSettings(NULL) +{ + +} + +void SettingsManager::setToolbox(QGCToolbox *toolbox) +{ + QGCTool::setToolbox(toolbox); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only"); + + _unitsSettings = new UnitsSettings(this); // Must be first since AppSettings references it + _appSettings = new AppSettings(this); + _autoConnectSettings = new AutoConnectSettings(this); + _videoSettings = new VideoSettings(this); +} diff --git a/src/Settings/SettingsManager.h b/src/Settings/SettingsManager.h new file mode 100644 index 000000000..40e67555c --- /dev/null +++ b/src/Settings/SettingsManager.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * + * (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 SettingsManager_H +#define SettingsManager_H + +#include "QGCLoggingCategory.h" +#include "Joystick.h" +#include "MultiVehicleManager.h" +#include "QGCToolbox.h" +#include "AppSettings.h" +#include "UnitsSettings.h" +#include "AutoConnectSettings.h" +#include "VideoSettings.h" + +#include + +/// Provides access to all app settings +class SettingsManager : public QGCTool +{ + Q_OBJECT + +public: + SettingsManager(QGCApplication* app); + + Q_PROPERTY(QObject* appSettings READ appSettings CONSTANT) + Q_PROPERTY(QObject* unitsSettings READ unitsSettings CONSTANT) + Q_PROPERTY(QObject* autoConnectSettings READ autoConnectSettings CONSTANT) + Q_PROPERTY(QObject* videoSettings READ videoSettings CONSTANT) + + // Override from QGCTool + virtual void setToolbox(QGCToolbox *toolbox); + + AppSettings* appSettings (void) { return _appSettings; } + UnitsSettings* unitsSettings (void) { return _unitsSettings; } + AutoConnectSettings* autoConnectSettings (void) { return _autoConnectSettings; } + VideoSettings* videoSettings (void) { return _videoSettings; } + +private: + AppSettings* _appSettings; + UnitsSettings* _unitsSettings; + AutoConnectSettings* _autoConnectSettings; + VideoSettings* _videoSettings; +}; + +#endif diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc new file mode 100644 index 000000000..78c7d6b44 --- /dev/null +++ b/src/Settings/UnitsSettings.cc @@ -0,0 +1,93 @@ +/**************************************************************************** + * + * (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 "UnitsSettings.h" + +#include + +const char* UnitsSettings::unitsSettingsGroupName = "units"; +const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; +const char* UnitsSettings::areaUnitsSettingsName = "AreaUnits"; +const char* UnitsSettings::speedUnitsSettingsName = "SpeedUnits"; + +UnitsSettings::UnitsSettings(QObject* parent) + : SettingsGroup(unitsSettingsGroupName, QString() /* root settings group */, parent) + , _distanceUnitsFact(NULL) + , _areaUnitsFact(NULL) + , _speedUnitsFact(NULL) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "UnitsSettings", "Reference only"); +} + +Fact* UnitsSettings::distanceUnits(void) +{ + if (!_distanceUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet" << "Meters"; + enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(distanceUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(DistanceUnitsMeters); + + _distanceUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + + } + + return _distanceUnitsFact; + +} + +Fact* UnitsSettings::areaUnits(void) +{ + if (!_areaUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; + enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(areaUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(AreaUnitsSquareMeters); + + _areaUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _areaUnitsFact; + +} + +Fact* UnitsSettings::speedUnits(void) +{ + if (!_speedUnitsFact) { + // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; + enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(speedUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); + + _speedUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _speedUnitsFact; +} diff --git a/src/Settings/UnitsSettings.h b/src/Settings/UnitsSettings.h new file mode 100644 index 000000000..3ede8765a --- /dev/null +++ b/src/Settings/UnitsSettings.h @@ -0,0 +1,68 @@ +/**************************************************************************** + * + * (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 UnitsSettings_H +#define UnitsSettings_H + +#include "SettingsGroup.h" + +class UnitsSettings : public SettingsGroup +{ + Q_OBJECT + +public: + UnitsSettings(QObject* parent = NULL); + + enum DistanceUnits { + DistanceUnitsFeet = 0, + DistanceUnitsMeters + }; + + enum AreaUnits { + AreaUnitsSquareFeet = 0, + AreaUnitsSquareMeters, + AreaUnitsSquareKilometers, + AreaUnitsHectares, + AreaUnitsAcres, + AreaUnitsSquareMiles, + }; + + enum SpeedUnits { + SpeedUnitsFeetPerSecond = 0, + SpeedUnitsMetersPerSecond, + SpeedUnitsMilesPerHour, + SpeedUnitsKilometersPerHour, + SpeedUnitsKnots, + }; + + Q_ENUMS(DistanceUnits) + Q_ENUMS(AreaUnits) + Q_ENUMS(SpeedUnits) + + Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) + Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) + Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) + + Fact* distanceUnits (void); + Fact* areaUnits (void); + Fact* speedUnits (void); + + static const char* unitsSettingsGroupName; + + static const char* distanceUnitsSettingsName; + static const char* areaUnitsSettingsName; + static const char* speedUnitsSettingsName; + +private: + SettingsFact* _distanceUnitsFact; + SettingsFact* _areaUnitsFact; + SettingsFact* _speedUnitsFact; +}; + +#endif diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc new file mode 100644 index 000000000..f518056e1 --- /dev/null +++ b/src/Settings/VideoSettings.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (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 "VideoSettings.h" + +#include + +const char* VideoSettings::VideoSettingsGroupName = "video"; + +VideoSettings::VideoSettings(QObject* parent) + : SettingsGroup(VideoSettingsGroupName, QString() /* root settings group */, parent) +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h new file mode 100644 index 000000000..e5bc1117d --- /dev/null +++ b/src/Settings/VideoSettings.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * + * (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 VideoSettings_H +#define VideoSettings_H + +#include "SettingsGroup.h" + +class VideoSettings : public SettingsGroup +{ + Q_OBJECT + +public: + VideoSettings(QObject* parent = NULL); + + static const char* VideoSettingsGroupName; + +private: +}; + +#endif diff --git a/src/SettingsManager.cc b/src/SettingsManager.cc deleted file mode 100644 index 115c610c0..000000000 --- a/src/SettingsManager.cc +++ /dev/null @@ -1,214 +0,0 @@ -/**************************************************************************** - * - * (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 "SettingsManager.h" -#include "QGCApplication.h" - -QGC_LOGGING_CATEGORY(SettingsManagerLog, "SettingsManagerLog") - -const char* SettingsManager::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; -const char* SettingsManager::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; -const char* SettingsManager::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; -const char* SettingsManager::offlineEditingHoverSpeedSettingsName = "OfflineEditingHoverSpeed"; -const char* SettingsManager::distanceUnitsSettingsName = "DistanceUnits"; -const char* SettingsManager::areaUnitsSettingsName = "AreaUnits"; -const char* SettingsManager::speedUnitsSettingsName = "SpeedUnits"; -const char* SettingsManager::batteryPercentRemainingAnnounceSettingsName = "batteryPercentRemainingAnnounce"; -const char* SettingsManager::defaultMissionItemAltitudeSettingsName = "DefaultMissionItemAltitude"; -const char* SettingsManager::missionAutoLoadDirSettingsName = "MissionAutoLoadDir"; -const char* SettingsManager::promptFlightTelemetrySaveName = "PromptFLightDataSave"; -const char* SettingsManager::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; -const char* SettingsManager::audioMutedName = "AudioMuted"; - -SettingsManager::SettingsManager(QGCApplication* app) - : QGCTool(app) - , _offlineEditingFirmwareTypeFact(NULL) - , _offlineEditingVehicleTypeFact(NULL) - , _offlineEditingCruiseSpeedFact(NULL) - , _offlineEditingHoverSpeedFact(NULL) - , _distanceUnitsFact(NULL) - , _areaUnitsFact(NULL) - , _speedUnitsFact(NULL) - , _batteryPercentRemainingAnnounceFact(NULL) - , _defaultMissionItemAltitudeFact(NULL) - , _missionAutoLoadDirFact(NULL) - , _promptFlightTelemetrySave(NULL) - , _promptFlightTelemetrySaveNotArmed(NULL) - , _audioMuted(NULL) -{ - -} - -void SettingsManager::setToolbox(QGCToolbox *toolbox) -{ - QGCTool::setToolbox(toolbox); - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "SettingsManager", "Reference only"); - - _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(":/json/SettingsManager.json", this); -} - -SettingsFact* SettingsManager::_createSettingsFact(const QString& name) -{ - return new SettingsFact(QString() /* no settings group */, _nameToMetaDataMap[name], this); -} - -Fact* SettingsManager::offlineEditingFirmwareType(void) -{ - if (!_offlineEditingFirmwareTypeFact) { - _offlineEditingFirmwareTypeFact = _createSettingsFact(offlineEditingFirmwareTypeSettingsName); - } - - return _offlineEditingFirmwareTypeFact; -} - -Fact* SettingsManager::offlineEditingVehicleType(void) -{ - if (!_offlineEditingVehicleTypeFact) { - _offlineEditingVehicleTypeFact = _createSettingsFact(offlineEditingVehicleTypeSettingsName); - } - - return _offlineEditingVehicleTypeFact; -} - -Fact* SettingsManager::offlineEditingCruiseSpeed(void) -{ - if (!_offlineEditingCruiseSpeedFact) { - _offlineEditingCruiseSpeedFact = _createSettingsFact(offlineEditingCruiseSpeedSettingsName); - } - return _offlineEditingCruiseSpeedFact; -} - -Fact* SettingsManager::offlineEditingHoverSpeed(void) -{ - if (!_offlineEditingHoverSpeedFact) { - _offlineEditingHoverSpeedFact = _createSettingsFact(offlineEditingHoverSpeedSettingsName); - } - return _offlineEditingHoverSpeedFact; -} - -Fact* SettingsManager::batteryPercentRemainingAnnounce(void) -{ - if (!_batteryPercentRemainingAnnounceFact) { - _batteryPercentRemainingAnnounceFact = _createSettingsFact(batteryPercentRemainingAnnounceSettingsName); - } - - return _batteryPercentRemainingAnnounceFact; -} - -Fact* SettingsManager::defaultMissionItemAltitude(void) -{ - if (!_defaultMissionItemAltitudeFact) { - _defaultMissionItemAltitudeFact = _createSettingsFact(defaultMissionItemAltitudeSettingsName); - } - - return _defaultMissionItemAltitudeFact; -} - -Fact* SettingsManager::missionAutoLoadDir(void) -{ - if (!_missionAutoLoadDirFact) { - _missionAutoLoadDirFact = _createSettingsFact(missionAutoLoadDirSettingsName); - } - - return _missionAutoLoadDirFact; -} - -Fact* SettingsManager::promptFlightTelemetrySave(void) -{ - if (!_promptFlightTelemetrySave) { - _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); - } - - return _promptFlightTelemetrySave; -} - -Fact* SettingsManager::promptFlightTelemetrySaveNotArmed(void) -{ - if (!_promptFlightTelemetrySaveNotArmed) { - _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); - } - - return _promptFlightTelemetrySaveNotArmed; -} - -Fact* SettingsManager::audioMuted(void) -{ - if (!_audioMuted) { - _audioMuted = _createSettingsFact(audioMutedName); - } - - return _audioMuted; -} - -Fact* SettingsManager::distanceUnits(void) -{ - if (!_distanceUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "Feet" << "Meters"; - enumValues << QVariant::fromValue((uint32_t)DistanceUnitsFeet) << QVariant::fromValue((uint32_t)DistanceUnitsMeters); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(distanceUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(DistanceUnitsMeters); - - _distanceUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - - } - - return _distanceUnitsFact; - -} - -Fact* SettingsManager::areaUnits(void) -{ - if (!_areaUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "SquareFeet" << "SquareMeters" << "SquareKilometers" << "Hectares" << "Acres" << "SquareMiles"; - enumValues << QVariant::fromValue((uint32_t)AreaUnitsSquareFeet) << QVariant::fromValue((uint32_t)AreaUnitsSquareMeters) << QVariant::fromValue((uint32_t)AreaUnitsSquareKilometers) << QVariant::fromValue((uint32_t)AreaUnitsHectares) << QVariant::fromValue((uint32_t)AreaUnitsAcres) << QVariant::fromValue((uint32_t)AreaUnitsSquareMiles); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(areaUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(AreaUnitsSquareMeters); - - _areaUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - } - - return _areaUnitsFact; - -} - -Fact* SettingsManager::speedUnits(void) -{ - if (!_speedUnitsFact) { - // Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading. - QStringList enumStrings; - QVariantList enumValues; - - enumStrings << "Feet/second" << "Meters/second" << "Miles/hour" << "Kilometers/hour" << "Knots"; - enumValues << QVariant::fromValue((uint32_t)SpeedUnitsFeetPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMetersPerSecond) << QVariant::fromValue((uint32_t)SpeedUnitsMilesPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKilometersPerHour) << QVariant::fromValue((uint32_t)SpeedUnitsKnots); - - FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); - metaData->setName(speedUnitsSettingsName); - metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); - - _speedUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); - } - - return _speedUnitsFact; -} diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 45d3656c0..105da0786 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -64,8 +64,8 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo); SettingsManager* settingsManager = toolbox->settingsManager(); - _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->offlineEditingFirmwareType()->rawValue().toInt()), - static_cast(settingsManager->offlineEditingVehicleType()->rawValue().toInt()), + _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()), + static_cast(settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()), _firmwarePluginManager, this); } diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index a98e094a5..1639e0519 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -101,8 +101,8 @@ Vehicle::Vehicle(LinkInterface* link, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _telemetryRRSSI(0) , _telemetryLRSSI(0) , _telemetryRXErrors(0) @@ -260,8 +260,8 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _onboardControlSensorsUnhealthy(0) , _gpsRawIntMessageAvailable(false) , _globalPositionIntMessageAvailable(false) - , _cruiseSpeed(_settingsManager->offlineEditingCruiseSpeed()->rawValue().toDouble()) - , _hoverSpeed(_settingsManager->offlineEditingHoverSpeed()->rawValue().toDouble()) + , _cruiseSpeed(_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble()) + , _hoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _connectionLost(false) , _connectionLostEnabled(true) , _missionManager(NULL) @@ -323,10 +323,10 @@ void Vehicle::_commonInit(void) connect(_rallyPointManager, &RallyPointManager::error, this, &Vehicle::_rallyPointManagerError); // Offline editing vehicle tracks settings changes for offline editing settings - connect(_settingsManager->offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); - connect(_settingsManager->offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); - connect(_settingsManager->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); - connect(_settingsManager->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingFirmwareType(), &Fact::rawValueChanged, this, &Vehicle::_offlineFirmwareTypeSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingVehicleType(), &Fact::rawValueChanged, this, &Vehicle::_offlineVehicleTypeSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingCruiseSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineCruiseSpeedSettingChanged); + connect(_settingsManager->appSettings()->offlineEditingHoverSpeed(), &Fact::rawValueChanged, this, &Vehicle::_offlineHoverSpeedSettingChanged); // Build FactGroup object model @@ -791,7 +791,7 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) } _batteryFactGroup.percentRemaining()->setRawValue(sysStatus.battery_remaining); - if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _settingsManager->batteryPercentRemainingAnnounce()->rawValue().toInt()) { + if (sysStatus.battery_remaining > 0 && sysStatus.battery_remaining < _settingsManager->appSettings()->batteryPercentRemainingAnnounce()->rawValue().toInt()) { if (!_lowBatteryAnnounceTimer.isValid() || _lowBatteryAnnounceTimer.elapsed() > _lowBatteryAnnounceRepeatMSecs) { _lowBatteryAnnounceTimer.restart(); _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); @@ -1579,7 +1579,7 @@ void Vehicle::_parametersReady(bool parametersReady) { if (parametersReady && !_missionManagerInitialRequestSent) { _missionManagerInitialRequestSent = true; - QString missionAutoLoadDirPath = _settingsManager->missionAutoLoadDir()->rawValue().toString(); + QString missionAutoLoadDirPath = _settingsManager->appSettings()->missionAutoLoadDir()->rawValue().toString(); if (missionAutoLoadDirPath.isEmpty()) { _missionManager->requestMissionItems(); } else { diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index b59c274a3..aee94cde7 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -159,3 +159,11 @@ QVariantList& QGCCorePlugin::toolBarIndicators() } return _p->toolBarIndicatorList; } + +bool QGCCorePlugin::overrideSettingsGroupVisibility(QString name) +{ + Q_UNUSED(name); + + // Always show all + return true; +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 582821a3a..eb2ca44aa 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -53,6 +53,11 @@ public: /// @return An instance of QGCOptions virtual QGCOptions* options (); + /// Allows the core plugin to override the default value for the specified setting + /// @param name - Setting group name + /// @return true: Show settings ui, false: Hide settings ui + virtual bool overrideSettingsGroupVisibility(QString name); + /// Allows the core plugin to override the default value for the specified setting /// @param name - Setting name /// @param defaultValue - Standard default value for setting diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 3ca92d845..0d031a4f8 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -424,7 +424,7 @@ void MAVLinkProtocol::_stopLogging(void) if (_closeLogFile()) { // If the signals are not connected it means we are running a unit test. In that case just delete log files SettingsManager* settingsManager = _app->toolbox()->settingsManager(); - if ((_vehicleWasArmed || settingsManager->promptFlightTelemetrySaveNotArmed()->rawValue().toBool()) && settingsManager->promptFlightTelemetrySave()->rawValue().toBool()) { + if ((_vehicleWasArmed || settingsManager->appSettings()->promptFlightTelemetrySaveNotArmed()->rawValue().toBool()) && settingsManager->appSettings()->promptFlightTelemetrySave()->rawValue().toBool()) { emit saveTempFlightDataLog(_tempLogFile.fileName()); } else { QFile::remove(_tempLogFile.fileName()); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 75fe1906c..ecaaeb9dd 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -32,8 +32,8 @@ QGCView { anchors.fill: parent anchors.margins: ScreenTools.defaultFontPixelWidth - property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.batteryPercentRemainingAnnounce - property Fact _autoLoadDir: QGroundControl.settingsManager.missionAutoLoadDir + property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.appSettings.batteryPercentRemainingAnnounce + property Fact _autoLoadDir: QGroundControl.settingsManager.appSettings.missionAutoLoadDir property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -41,14 +41,6 @@ QGCView { QGCPalette { id: qgcPal } - FileDialog { - id: fileDialog - title: "Choose a location to save video files." - folder: shortcuts.home - selectFolder: true - onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) - } - QGCViewPanel { id: panel anchors.fill: parent @@ -65,10 +57,11 @@ QGCView { //----------------------------------------------------------------- //-- Units Item { - width: qgcView.width * 0.8 - height: unitLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: unitLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.unitsSettings.visible QGCLabel { id: unitLabel text: qsTr("Units (Requires Restart)") @@ -76,11 +69,12 @@ QGCView { } } Rectangle { - height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.unitsSettings.visible Column { id: unitsCol spacing: ScreenTools.defaultFontPixelWidth @@ -95,7 +89,7 @@ QGCView { FactComboBox { id: distanceUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.distanceUnits + fact: QGroundControl.settingsManager.unitsSettings.distanceUnits indexModel: false } } @@ -109,7 +103,7 @@ QGCView { FactComboBox { id: areaUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.areaUnits + fact: QGroundControl.settingsManager.unitsSettings.areaUnits indexModel: false } } @@ -123,7 +117,7 @@ QGCView { FactComboBox { id: speedUnitsCombo width: _editFieldWidth - fact: QGroundControl.settingsManager.speedUnits + fact: QGroundControl.settingsManager.unitsSettings.speedUnits indexModel: false } } @@ -132,10 +126,11 @@ QGCView { //----------------------------------------------------------------- //-- Miscellanous Item { - width: qgcView.width * 0.8 - height: miscLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: miscLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.appSettings.visible QGCLabel { id: miscLabel text: qsTr("Miscellaneous") @@ -143,11 +138,12 @@ QGCView { } } Rectangle { - height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.appSettings.visible Column { id: miscCol spacing: ScreenTools.defaultFontPixelWidth @@ -233,22 +229,21 @@ QGCView { //-- Audio preferences FactCheckBox { text: qsTr("Mute all audio output") - fact: QGroundControl.settingsManager.audioMuted + fact: QGroundControl.settingsManager.appSettings.audioMuted } //----------------------------------------------------------------- //-- Prompt Save Log - QGCCheckBox { + FactCheckBox { id: promptSaveLog text: qsTr("Prompt to save Flight Data Log after each flight") - checked: QGroundControl.settingsManager.promptFlightTelemetrySave - //fact: QGroundControl.settingsManager.promptFlightTelemetrySave + fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave visible: !ScreenTools.isMobile } //----------------------------------------------------------------- //-- Prompt Save even if not armed FactCheckBox { text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - fact: QGroundControl.settingsManager.promptFlightTelemetrySaveNotArmed + fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed visible: !ScreenTools.isMobile enabled: promptSaveLog.checked } @@ -320,7 +315,7 @@ QGCView { } FactTextField { id: defaultItemAltitudeField - fact: QGroundControl.settingsManager.defaultMissionItemAltitude + fact: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude } } //----------------------------------------------------------------- @@ -403,11 +398,11 @@ QGCView { //----------------------------------------------------------------- //-- Autoconnect settings Item { - width: qgcView.width * 0.8 - height: autoConnectLabel.height - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter - visible: QGroundControl.corePlugin.options.enableAutoConnectOptions + width: qgcView.width * 0.8 + height: autoConnectLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.autoConnectSettings.visible QGCLabel { id: autoConnectLabel text: qsTr("AutoConnect to the following devices:") @@ -415,12 +410,12 @@ QGCView { } } Rectangle { - height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - visible: QGroundControl.corePlugin.options.enableAutoConnectOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.autoConnectSettings.visible Column { id: autoConnectCol spacing: ScreenTools.defaultFontPixelWidth @@ -468,11 +463,11 @@ QGCView { //----------------------------------------------------------------- //-- Video Source Item { - width: qgcView.width * 0.8 - height: videoLabel.height - visible: QGroundControl.corePlugin.options.enableVideoSourceOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + width: qgcView.width * 0.8 + height: videoLabel.height + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.videoSettings.visible QGCLabel { id: videoLabel text: qsTr("Video (Requires Restart)") @@ -480,12 +475,12 @@ QGCView { } } Rectangle { - height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: qgcView.width * 0.8 - color: qgcPal.windowShade - visible: QGroundControl.corePlugin.options.enableVideoSourceOptions - anchors.margins: ScreenTools.defaultFontPixelWidth - anchors.horizontalCenter: parent.horizontalCenter + height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) + width: qgcView.width * 0.8 + color: qgcPal.windowShade + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: QGroundControl.settingsManager.videoSettings.visible Column { id: videoCol spacing: ScreenTools.defaultFontPixelWidth @@ -566,8 +561,17 @@ QGCView { text: QGroundControl.videoManager.videoSavePath } QGCButton { - text: "Browse" - onClicked: fileDialog.visible = true + text: "Browse" + onClicked: videoLocationFileDialog.visible = true + + FileDialog { + id: videoLocationFileDialog + title: "Choose a location to save video files." + folder: shortcuts.home + selectFolder: true + onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) + } + } } } -- GitLab From d8d3c0912fbe3fa024e18316bfde1de100317047 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 14:11:59 -0800 Subject: [PATCH 318/398] Make Windows happy --- src/Settings/AppSettings.cc | 1 + src/Settings/AutoConnectSettings.cc | 1 + src/Settings/SettingsManager.cc | 1 + src/Settings/UnitsSettings.cc | 1 + src/Settings/VideoSettings.cc | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 348baf896..c0fb64fe8 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -10,6 +10,7 @@ #include "AppSettings.h" #include +#include const char* AppSettings::appSettingsGroupName = "app"; const char* AppSettings::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index 71de8257b..16a2c7647 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -10,6 +10,7 @@ #include "AutoConnectSettings.h" #include +#include const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc index 35f47cbc7..226976f28 100644 --- a/src/Settings/SettingsManager.cc +++ b/src/Settings/SettingsManager.cc @@ -10,6 +10,7 @@ #include "SettingsManager.h" #include +#include SettingsManager::SettingsManager(QGCApplication* app) : QGCTool(app) diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc index 78c7d6b44..608e45cfd 100644 --- a/src/Settings/UnitsSettings.cc +++ b/src/Settings/UnitsSettings.cc @@ -10,6 +10,7 @@ #include "UnitsSettings.h" #include +#include const char* UnitsSettings::unitsSettingsGroupName = "units"; const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index f518056e1..3b9ab513f 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -10,6 +10,7 @@ #include "VideoSettings.h" #include +#include const char* VideoSettings::VideoSettingsGroupName = "video"; -- GitLab From 7a163e377b500a7a12a9b68d25d45868f90882fb Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 14:40:59 -0800 Subject: [PATCH 319/398] Fix typo --- src/api/QGCCorePlugin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index eb2ca44aa..76250aa2e 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -53,10 +53,10 @@ public: /// @return An instance of QGCOptions virtual QGCOptions* options (); - /// Allows the core plugin to override the default value for the specified setting + /// Allows the core plugin to override the visibility for a settings group /// @param name - Setting group name /// @return true: Show settings ui, false: Hide settings ui - virtual bool overrideSettingsGroupVisibility(QString name); + virtual bool overrideSettingsGroupVisibility (QString name); /// Allows the core plugin to override the default value for the specified setting /// @param name - Setting name -- GitLab From 0c8694e5d084317932404e6802d7966f89ebc2a8 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 15:29:58 -0800 Subject: [PATCH 320/398] Fix bad merge --- src/api/QGCCorePlugin.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index ca96241e5..76250aa2e 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -58,11 +58,6 @@ public: /// @return true: Show settings ui, false: Hide settings ui virtual bool overrideSettingsGroupVisibility (QString name); - /// Allows the core plugin to override the default value for the specified setting - /// @param name - Setting group name - /// @return true: Show settings ui, false: Hide settings ui - virtual bool overrideSettingsGroupVisibility(QString name); - /// Allows the core plugin to override the default value for the specified setting /// @param name - Setting name /// @param defaultValue - Standard default value for setting -- GitLab From c887002e5b144b53cdebbc0d081420bf1b6ea443 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 16:36:05 -0800 Subject: [PATCH 321/398] Fix switch bug --- src/MissionEditor/MissionEditor.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 936394e1e..070923ecb 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -851,7 +851,7 @@ QGCView { ] onClicked: { - switch (index == 0) { + switch (index) { case 0: _addWaypointOnClick = checked break -- GitLab From 4d119615a8e8a5131834b88180f5ae82f61a378b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 16:37:03 -0800 Subject: [PATCH 322/398] Change previous altitude semantics - Look backwards from insert point - Only pull altitude from _WAYPOINT items - Only set altitude on _WAYPOINT items --- src/MissionManager/MissionController.cc | 66 +++++++------------------ src/MissionManager/MissionController.h | 3 +- 2 files changed, 20 insertions(+), 49 deletions(-) diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 53a209823..5058026e2 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -204,15 +204,12 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) } newItem->setDefaultsForCommand(); if ((MAV_CMD)newItem->command() == MAV_CMD_NAV_WAYPOINT) { - double lastValue; - MAV_FRAME lastFrame; + double prevAltitude; + MAV_FRAME prevFrame; - if (_findLastAcceptanceRadius(&lastValue)) { - newItem->missionItem().setParam2(lastValue); - } - if (_findLastAltitude(&lastValue, &lastFrame)) { - newItem->missionItem().setFrame(lastFrame); - newItem->missionItem().setParam7(lastValue); + if (_findPreviousAltitude(i, &prevAltitude, &prevFrame)) { + newItem->missionItem().setFrame(prevFrame); + newItem->missionItem().setParam7(prevAltitude); } } _visualItems->insert(i, newItem); @@ -1349,61 +1346,36 @@ void MissionController::_inProgressChanged(bool inProgress) emit syncInProgressChanged(inProgress); } -bool MissionController::_findLastAltitude(double* lastAltitude, MAV_FRAME* frame) +bool MissionController::_findPreviousAltitude(int newIndex, double* prevAltitude, MAV_FRAME* prevFrame) { - bool found = false; - double foundAltitude; - MAV_FRAME foundFrame; + bool found = false; + double foundAltitude; + MAV_FRAME foundFrame; - // Don't use home position - for (int i=1; i<_visualItems->count(); i++) { + if (newIndex > _visualItems->count()) { + return false; + } + newIndex--; + + for (int i=newIndex; i>0; i--) { VisualMissionItem* visualItem = qobject_cast(_visualItems->get(i)); if (visualItem->specifiesCoordinate() && !visualItem->isStandaloneCoordinate()) { - if (visualItem->isSimpleItem()) { SimpleMissionItem* simpleItem = qobject_cast(visualItem); - if ((MAV_CMD)simpleItem->command() != MAV_CMD_NAV_TAKEOFF) { + if ((MAV_CMD)simpleItem->command() == MAV_CMD_NAV_WAYPOINT) { foundAltitude = simpleItem->exitCoordinate().altitude(); foundFrame = simpleItem->missionItem().frame(); found = true; + break; } } } } if (found) { - *lastAltitude = foundAltitude; - *frame = foundFrame; - } - - return found; -} - -bool MissionController::_findLastAcceptanceRadius(double* lastAcceptanceRadius) -{ - bool found = false; - double foundAcceptanceRadius; - - for (int i=0; i<_visualItems->count(); i++) { - VisualMissionItem* visualItem = qobject_cast(_visualItems->get(i)); - - if (visualItem->isSimpleItem()) { - SimpleMissionItem* simpleItem = qobject_cast(visualItem); - - if (simpleItem) { - if ((MAV_CMD)simpleItem->command() == MAV_CMD_NAV_WAYPOINT) { - foundAcceptanceRadius = simpleItem->missionItem().param2(); - found = true; - } - } else { - qWarning() << "isSimpleItem == true, yet not SimpleMissionItem"; - } - } - } - - if (found) { - *lastAcceptanceRadius = foundAcceptanceRadius; + *prevAltitude = foundAltitude; + *prevFrame = foundFrame; } return found; diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index f1b45fb34..130322928 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -148,8 +148,7 @@ private: void _setupActiveVehicle(Vehicle* activeVehicle, bool forceLoadFromVehicle); static void _calcPrevWaypointValues(double homeAlt, VisualMissionItem* currentItem, VisualMissionItem* prevItem, double* azimuth, double* distance, double* altDifference); static double _calcDistanceToHome(VisualMissionItem* currentItem, VisualMissionItem* homeItem); - bool _findLastAltitude(double* lastAltitude, MAV_FRAME* frame); - bool _findLastAcceptanceRadius(double* lastAcceptanceRadius); + bool _findPreviousAltitude(int newIndex, double* prevAltitude, MAV_FRAME* prevFrame); static double _normalizeLat(double lat); static double _normalizeLon(double lon); static void _addPlannedHomePosition(Vehicle* vehicle, QmlObjectListModel* visualItems, bool addToCenter); -- GitLab From 5c1f56dd6cb83250d26ba781a2078a748591d149 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 20 Feb 2017 18:29:37 -0800 Subject: [PATCH 323/398] Moving more settings to new SettingGroup mechanism --- src/Settings/AutoConnectSettings.cc | 72 +++++++++++++++- src/Settings/AutoConnectSettings.h | 29 +++++++ src/Settings/SettingsGroup.autoConnect.json | 42 +++++++++ src/comm/LinkManager.cc | 96 +++------------------ src/comm/LinkManager.h | 52 ++--------- src/ui/preferences/GeneralSettings.qml | 30 +++---- 6 files changed, 170 insertions(+), 151 deletions(-) diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index 16a2c7647..008c08e04 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -8,15 +8,85 @@ ****************************************************************************/ #include "AutoConnectSettings.h" +#include "LinkManager.h" #include #include +const char* AutoConnectSettings::_settingsGroup = "LinkManager"; + +const char* AutoConnectSettings:: autoConnectUDPSettingsName = "AutoconnectUDP"; +const char* AutoConnectSettings:: autoConnectPixhawkSettingsName = "AutoconnectPixhawk"; +const char* AutoConnectSettings:: autoConnectSiKRadioSettingsName = "Autoconnect3DRRadio"; +const char* AutoConnectSettings:: autoConnectPX4FlowSettingsName = "AutoconnectPX4Flow"; +const char* AutoConnectSettings:: autoConnectRTKGPSSettingsName = "AutoconnectRTKGPS"; +const char* AutoConnectSettings:: autoConnectLibrePilotSettingsName = "AutoconnectLibrePilot"; + const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; AutoConnectSettings::AutoConnectSettings(QObject* parent) - : SettingsGroup(autoConnectSettingsGroupName, QString() /* root settings group */, parent) + : SettingsGroup(autoConnectSettingsGroupName, _settingsGroup, parent) + , _autoConnectUDPFact(NULL) + , _autoConnectPixhawkFact(NULL) + , _autoConnectSiKRadioFact(NULL) + , _autoConnectPX4FlowFact(NULL) + , _autoConnectRTKGPSFact(NULL) + , _autoConnectLibrePilotFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AutoConnectSettings", "Reference only"); } + +Fact* AutoConnectSettings::autoConnectUDP(void) +{ + if (!_autoConnectUDPFact) { + _autoConnectUDPFact = _createSettingsFact(autoConnectUDPSettingsName); + } + + return _autoConnectUDPFact; +} + +Fact* AutoConnectSettings::autoConnectPixhawk(void) +{ + if (!_autoConnectPixhawkFact) { + _autoConnectPixhawkFact = _createSettingsFact(autoConnectPixhawkSettingsName); + } + + return _autoConnectPixhawkFact; +} + +Fact* AutoConnectSettings::autoConnectSiKRadio(void) +{ + if (!_autoConnectSiKRadioFact) { + _autoConnectSiKRadioFact = _createSettingsFact(autoConnectSiKRadioSettingsName); + } + + return _autoConnectSiKRadioFact; +} + +Fact* AutoConnectSettings::autoConnectPX4Flow(void) +{ + if (!_autoConnectPX4FlowFact) { + _autoConnectPX4FlowFact = _createSettingsFact(autoConnectPX4FlowSettingsName); + } + + return _autoConnectPX4FlowFact; +} + +Fact* AutoConnectSettings::autoConnectRTKGPS(void) +{ + if (!_autoConnectRTKGPSFact) { + _autoConnectRTKGPSFact = _createSettingsFact(autoConnectRTKGPSSettingsName); + } + + return _autoConnectRTKGPSFact; +} + +Fact* AutoConnectSettings::autoConnectLibrePilot(void) +{ + if (!_autoConnectLibrePilotFact) { + _autoConnectLibrePilotFact = _createSettingsFact(autoConnectLibrePilotSettingsName); + } + + return _autoConnectLibrePilotFact; +} diff --git a/src/Settings/AutoConnectSettings.h b/src/Settings/AutoConnectSettings.h index afcd32b63..83829fdfe 100644 --- a/src/Settings/AutoConnectSettings.h +++ b/src/Settings/AutoConnectSettings.h @@ -19,9 +19,38 @@ class AutoConnectSettings : public SettingsGroup public: AutoConnectSettings(QObject* parent = NULL); + Q_PROPERTY(Fact* autoConnectUDP READ autoConnectUDP CONSTANT) + Q_PROPERTY(Fact* autoConnectPixhawk READ autoConnectPixhawk CONSTANT) + Q_PROPERTY(Fact* autoConnectSiKRadio READ autoConnectSiKRadio CONSTANT) + Q_PROPERTY(Fact* autoConnectPX4Flow READ autoConnectPX4Flow CONSTANT) + Q_PROPERTY(Fact* autoConnectRTKGPS READ autoConnectRTKGPS CONSTANT) + Q_PROPERTY(Fact* autoConnectLibrePilot READ autoConnectLibrePilot CONSTANT) + + Fact* autoConnectUDP (void); + Fact* autoConnectPixhawk (void); + Fact* autoConnectSiKRadio (void); + Fact* autoConnectPX4Flow (void); + Fact* autoConnectRTKGPS (void); + Fact* autoConnectLibrePilot (void); + static const char* autoConnectSettingsGroupName; + static const char* autoConnectUDPSettingsName; + static const char* autoConnectPixhawkSettingsName; + static const char* autoConnectSiKRadioSettingsName; + static const char* autoConnectPX4FlowSettingsName; + static const char* autoConnectRTKGPSSettingsName; + static const char* autoConnectLibrePilotSettingsName; + private: + SettingsFact* _autoConnectUDPFact; + SettingsFact* _autoConnectPixhawkFact; + SettingsFact* _autoConnectSiKRadioFact; + SettingsFact* _autoConnectPX4FlowFact; + SettingsFact* _autoConnectRTKGPSFact; + SettingsFact* _autoConnectLibrePilotFact; + + static const char* _settingsGroup; }; #endif diff --git a/src/Settings/SettingsGroup.autoConnect.json b/src/Settings/SettingsGroup.autoConnect.json index 0d4f101c7..afce2047f 100644 --- a/src/Settings/SettingsGroup.autoConnect.json +++ b/src/Settings/SettingsGroup.autoConnect.json @@ -1,2 +1,44 @@ [ +{ + "name": "AutoconnectUDP", + "shortDescription": "Automatically open a connection over UDP", + "longDescription": "If this option is enabled GroundControl will automatically connect to a vehicle which is detected on a UDP communication link.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "AutoconnectPixhawk", + "shortDescription": "Automatically connect to a Pixhawk board", + "longDescription": "If this option is enabled GroundControl will automatically connect to a Pixhawk board which is connected via USB.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "Autoconnect3DRRadio", + "shortDescription": "Automatically connect to a SiK Radio", + "longDescription": "If this option is enabled GroundControl will automatically connect to a vehicle which is detected on a SiK Radio communication link.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "AutoconnectPX4Flow", + "shortDescription": "Automatically connect to a P4 Flow", + "longDescription": "If this option is enabled GroundControl will automatically connect to a PX4 Flow board which is connected via USB.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "AutoconnectRTKGPS", + "shortDescription": "Automatically connect to an RTK GPS", + "longDescription": "If this option is enabled GroundControl will automatically connect to an RTK GPS which is connected via USB.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "AutoconnectLibrePilot", + "shortDescription": "Automatically connect to a LibrePilot", + "longDescription": "If this option is enabled GroundControl will automatically connect to a LibrePilot board which is connected via USB.", + "type": "bool", + "defaultValue": true +} ] diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 09233d44c..0f6868495 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -20,6 +20,7 @@ #include "QGCApplication.h" #include "UDPLink.h" #include "TCPLink.h" +#include "SettingsManager.h" #ifdef QGC_ENABLE_BLUETOOTH #include "BluetoothLink.h" #endif @@ -31,13 +32,6 @@ QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog") QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog") -const char* LinkManager::_settingsGroup = "LinkManager"; -const char* LinkManager::_autoconnectUDPKey = "AutoconnectUDP"; -const char* LinkManager::_autoconnectPixhawkKey = "AutoconnectPixhawk"; -const char* LinkManager::_autoconnect3DRRadioKey = "Autoconnect3DRRadio"; -const char* LinkManager::_autoconnectPX4FlowKey = "AutoconnectPX4Flow"; -const char* LinkManager::_autoconnectRTKGPSKey = "AutoconnectRTKGPS"; -const char* LinkManager::_autoconnectLibrePilotKey = "AutoconnectLibrePilot"; const char* LinkManager::_defaultUPDLinkName = "UDP Link (AutoConnect)"; const int LinkManager::_autoconnectUpdateTimerMSecs = 1000; @@ -49,33 +43,18 @@ const int LinkManager::_autoconnectConnectDelayMSecs = 1000; #endif LinkManager::LinkManager(QGCApplication* app) - : QGCTool(app) + : QGCTool(app) , _configUpdateSuspended(false) , _configurationsLoaded(false) , _connectionsSuspended(false) , _mavlinkChannelsUsedBitMask(1) // We never use channel 0 to avoid sequence numbering problems + , _autoConnectSettings(NULL) , _mavlinkProtocol(NULL) - , _autoconnectUDP(true) - , _autoconnectPixhawk(true) - , _autoconnect3DRRadio(true) - , _autoconnectPX4Flow(true) - , _autoconnectRTKGPS(true) - , _autoconnectLibrePilot(true) { qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkManager", "Reference only"); qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkConfiguration", "Reference only"); qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkInterface", "Reference only"); - QSettings settings; - - settings.beginGroup(_settingsGroup); - _autoconnectUDP = settings.value(_autoconnectUDPKey, true).toBool(); - _autoconnectPixhawk = settings.value(_autoconnectPixhawkKey, true).toBool(); - _autoconnect3DRRadio = settings.value(_autoconnect3DRRadioKey, true).toBool(); - _autoconnectPX4Flow = settings.value(_autoconnectPX4FlowKey, true).toBool(); - _autoconnectRTKGPS = settings.value(_autoconnectRTKGPSKey, true).toBool(); - _autoconnectLibrePilot = settings.value(_autoconnectLibrePilotKey, true).toBool(); - #ifndef NO_SERIAL_LINK _activeLinkCheckTimer.setInterval(_activeLinkCheckTimeoutMSecs); _activeLinkCheckTimer.setSingleShot(false); @@ -92,6 +71,7 @@ void LinkManager::setToolbox(QGCToolbox *toolbox) { QGCTool::setToolbox(toolbox); + _autoConnectSettings = toolbox->settingsManager()->autoConnectSettings(); _mavlinkProtocol = _toolbox->mavlinkProtocol(); connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks); @@ -485,7 +465,7 @@ void LinkManager::_updateAutoConnectLinks(void) break; } } - if (!foundUDP && _autoconnectUDP) { + if (!foundUDP && _autoConnectSettings->autoConnectUDP()->rawValue().toBool()) { qCDebug(LinkManagerLog) << "New auto-connect UDP port added"; UDPConfiguration* udpConfig = new UDPConfiguration(_defaultUPDLinkName); udpConfig->setLocalPort(QGC_UDP_LOCAL_PORT); @@ -549,29 +529,29 @@ void LinkManager::_updateAutoConnectLinks(void) switch (boardType) { case QGCSerialPortInfo::BoardTypePixhawk: - if (_autoconnectPixhawk) { + if (_autoConnectSettings->autoConnectPixhawk()->rawValue().toBool()) { pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); pSerialConfig->setUsbDirect(true); } break; case QGCSerialPortInfo::BoardTypePX4Flow: - if (_autoconnectPX4Flow) { + if (_autoConnectSettings->autoConnectPX4Flow()->rawValue().toBool()) { pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; case QGCSerialPortInfo::BoardTypeSiKRadio: - if (_autoconnect3DRRadio) { + if (_autoConnectSettings->autoConnectSiKRadio()->rawValue().toBool()) { pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; case QGCSerialPortInfo::BoardTypeOpenPilot: - if (_autoconnectLibrePilot) { + if (_autoConnectSettings->autoConnectLibrePilot()->rawValue().toBool()) { pSerialConfig = new SerialConfiguration(tr("%1 on %2 (AutoConnect)").arg(boardName).arg(portInfo.portName().trimmed())); } break; #ifndef __mobile__ case QGCSerialPortInfo::BoardTypeRTKGPS: - if (_autoconnectRTKGPS && !_toolbox->gpsManager()->connected()) { + if (_autoConnectSettings->autoConnectRTKGPS()->rawValue().toBool() && !_toolbox->gpsManager()->connected()) { qCDebug(LinkManagerLog) << "RTK GPS auto-connected"; _toolbox->gpsManager()->connectGPS(portInfo.systemLocation()); } @@ -647,62 +627,6 @@ void LinkManager::shutdown(void) disconnectAll(); } -bool LinkManager::_setAutoconnectWorker(bool& currentAutoconnect, bool newAutoconnect, const char* autoconnectKey) -{ - if (currentAutoconnect != newAutoconnect) { - QSettings settings; - - settings.beginGroup(_settingsGroup); - settings.setValue(autoconnectKey, newAutoconnect); - currentAutoconnect = newAutoconnect; - return true; - } - - return false; -} - -void LinkManager::setAutoconnectUDP(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnectUDP, autoconnect, _autoconnectUDPKey)) { - emit autoconnectUDPChanged(autoconnect); - } -} - -void LinkManager::setAutoconnectPixhawk(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnectPixhawk, autoconnect, _autoconnectPixhawkKey)) { - emit autoconnectPixhawkChanged(autoconnect); - } -} - -void LinkManager::setAutoconnect3DRRadio(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnect3DRRadio, autoconnect, _autoconnect3DRRadioKey)) { - emit autoconnect3DRRadioChanged(autoconnect); - } -} - -void LinkManager::setAutoconnectPX4Flow(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnectPX4Flow, autoconnect, _autoconnectPX4FlowKey)) { - emit autoconnectPX4FlowChanged(autoconnect); - } -} - -void LinkManager::setAutoconnectRTKGPS(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnectRTKGPS, autoconnect, _autoconnectRTKGPSKey)) { - emit autoconnectRTKGPSChanged(autoconnect); - } -} - -void LinkManager::setAutoconnectLibrePilot(bool autoconnect) -{ - if (_setAutoconnectWorker(_autoconnectLibrePilot, autoconnect, _autoconnectLibrePilotKey)) { - emit autoconnectLibrePilotChanged(autoconnect); - } -} - QStringList LinkManager::linkTypeStrings(void) const { //-- Must follow same order as enum LinkType in LinkConfiguration.h diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 8a2031b86..9341b5625 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -37,12 +37,12 @@ #include "MockLink.h" #endif -class UDPConfiguration; - Q_DECLARE_LOGGING_CATEGORY(LinkManagerLog) Q_DECLARE_LOGGING_CATEGORY(LinkManagerVerboseLog) class QGCApplication; +class UDPConfiguration; +class AutoConnectSettings; /// Manage communication links /// @@ -61,14 +61,7 @@ public: LinkManager(QGCApplication* app); ~LinkManager(); - Q_PROPERTY(bool autoconnectUDP READ autoconnectUDP WRITE setAutoconnectUDP NOTIFY autoconnectUDPChanged) - Q_PROPERTY(bool autoconnectPixhawk READ autoconnectPixhawk WRITE setAutoconnectPixhawk NOTIFY autoconnectPixhawkChanged) - Q_PROPERTY(bool autoconnect3DRRadio READ autoconnect3DRRadio WRITE setAutoconnect3DRRadio NOTIFY autoconnect3DRRadioChanged) - Q_PROPERTY(bool autoconnectPX4Flow READ autoconnectPX4Flow WRITE setAutoconnectPX4Flow NOTIFY autoconnectPX4FlowChanged) - Q_PROPERTY(bool autoconnectRTKGPS READ autoconnectRTKGPS WRITE setAutoconnectRTKGPS NOTIFY autoconnectRTKGPSChanged) - Q_PROPERTY(bool autoconnectLibrePilot READ autoconnectLibrePilot WRITE setAutoconnectLibrePilot NOTIFY autoconnectLibrePilotChanged) Q_PROPERTY(bool isBluetoothAvailable READ isBluetoothAvailable CONSTANT) - Q_PROPERTY(QmlObjectListModel* linkConfigurations READ _qmlLinkConfigurations NOTIFY linkConfigurationsChanged) Q_PROPERTY(QStringList linkTypeStrings READ linkTypeStrings CONSTANT) Q_PROPERTY(QStringList serialBaudRates READ serialBaudRates CONSTANT) @@ -85,12 +78,6 @@ public: // Property accessors - bool autoconnectUDP (void) { return _autoconnectUDP; } - bool autoconnectPixhawk (void) { return _autoconnectPixhawk; } - bool autoconnect3DRRadio (void) { return _autoconnect3DRRadio; } - bool autoconnectPX4Flow (void) { return _autoconnectPX4Flow; } - bool autoconnectRTKGPS (void) { return _autoconnectRTKGPS; } - bool autoconnectLibrePilot (void) { return _autoconnectLibrePilot; } bool isBluetoothAvailable (void); QList links (void); @@ -99,13 +86,6 @@ public: QStringList serialPortStrings (void); QStringList serialPorts (void); - void setAutoconnectUDP (bool autoconnect); - void setAutoconnectPixhawk (bool autoconnect); - void setAutoconnect3DRRadio (bool autoconnect); - void setAutoconnectPX4Flow (bool autoconnect); - void setAutoconnectRTKGPS (bool autoconnect); - void setAutoconnectLibrePilot (bool autoconnect); - /// Load list of link configurations from disk void loadLinkConfigurationList(); @@ -173,15 +153,9 @@ public: void startAutoConnectedLinks(void); -signals: - void autoconnectUDPChanged (bool autoconnect); - void autoconnectPixhawkChanged (bool autoconnect); - void autoconnect3DRRadioChanged (bool autoconnect); - void autoconnectPX4FlowChanged (bool autoconnect); - void autoconnectRTKGPSChanged (bool autoconnect); - void autoconnectLibrePilotChanged (bool autoconnect); - + static const char* settingsGroup; +signals: void newLink(LinkInterface* link); // Link has been deleted. You may not necessarily get a linkInactive before the link is deleted. @@ -217,7 +191,6 @@ private: void _updateAutoConnectLinks(void); void _updateSerialPorts(); void _fixUnnamed(LinkConfiguration* config); - bool _setAutoconnectWorker(bool& currentAutoconnect, bool newAutoconnect, const char* autoconnectKey); void _removeConfiguration(LinkConfiguration* config); #ifndef NO_SERIAL_LINK @@ -231,8 +204,8 @@ private: QTimer _portListTimer; uint32_t _mavlinkChannelsUsedBitMask; - MAVLinkProtocol* _mavlinkProtocol; - + AutoConnectSettings* _autoConnectSettings; + MAVLinkProtocol* _mavlinkProtocol; QList _sharedLinks; QList _sharedConfigurations; @@ -243,25 +216,12 @@ private: QStringList _commPortList; QStringList _commPortDisplayList; - bool _autoconnectUDP; - bool _autoconnectPixhawk; - bool _autoconnect3DRRadio; - bool _autoconnectPX4Flow; - bool _autoconnectRTKGPS; - bool _autoconnectLibrePilot; #ifndef NO_SERIAL_LINK QTimer _activeLinkCheckTimer; ///< Timer which checks for a vehicle showing up on a usb direct link QList _activeLinkCheckList; ///< List of links we are waiting for a vehicle to show up on static const int _activeLinkCheckTimeoutMSecs = 15000; ///< Amount of time to wait for a heatbeat. Keep in mind ArduPilot stack heartbeat is slow to come. #endif - static const char* _settingsGroup; - static const char* _autoconnectUDPKey; - static const char* _autoconnectPixhawkKey; - static const char* _autoconnect3DRRadioKey; - static const char* _autoconnectPX4FlowKey; - static const char* _autoconnectRTKGPSKey; - static const char* _autoconnectLibrePilotKey; static const char* _defaultUPDLinkName; static const int _autoconnectUpdateTimerMSecs; static const int _autoconnectConnectDelayMSecs; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index ecaaeb9dd..9a14487f7 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -424,38 +424,32 @@ QGCView { //-- Autoconnect settings Row { spacing: ScreenTools.defaultFontPixelWidth * 2 - QGCCheckBox { + FactCheckBox { text: qsTr("Pixhawk") + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectPixhawk visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnectPixhawk - onClicked: QGroundControl.linkManager.autoconnectPixhawk = checked } - QGCCheckBox { + FactCheckBox { text: qsTr("SiK Radio") + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectSiKRadio visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnect3DRRadio - onClicked: QGroundControl.linkManager.autoconnect3DRRadio = checked } - QGCCheckBox { + FactCheckBox { text: qsTr("PX4 Flow") + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectPX4Flow visible: !ScreenTools.isiOS - checked: QGroundControl.linkManager.autoconnectPX4Flow - onClicked: QGroundControl.linkManager.autoconnectPX4Flow = checked } - QGCCheckBox { + FactCheckBox { text: qsTr("LibrePilot") - checked: QGroundControl.linkManager.autoconnectLibrePilot - onClicked: QGroundControl.linkManager.autoconnectLibrePilot = checked + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectLibrePilot } - QGCCheckBox { + FactCheckBox { text: qsTr("UDP") - checked: QGroundControl.linkManager.autoconnectUDP - onClicked: QGroundControl.linkManager.autoconnectUDP = checked + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectUDP } - QGCCheckBox { + FactCheckBox { text: qsTr("RTK GPS") - checked: QGroundControl.linkManager.autoconnectRTKGPS - onClicked: QGroundControl.linkManager.autoconnectRTKGPS = checked + fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectRTKGPS } } } -- GitLab From 9b79c3cf129f9e388d84f7fb985b46c2eda33ccf Mon Sep 17 00:00:00 2001 From: Sebastian Verling Date: Tue, 21 Feb 2017 10:32:24 +0100 Subject: [PATCH 324/398] fixed uploading of bin files --- src/VehicleSetup/FirmwareImage.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/VehicleSetup/FirmwareImage.cc b/src/VehicleSetup/FirmwareImage.cc index d947991e0..20841df74 100644 --- a/src/VehicleSetup/FirmwareImage.cc +++ b/src/VehicleSetup/FirmwareImage.cc @@ -51,9 +51,8 @@ bool FirmwareImage::load(const QString& imageFilename, uint32_t boardId) _boardId = boardId; if (imageFilename.endsWith(".bin")) { - return _binLoad(imageFilename); _binFormat = true; - return true; + return _binLoad(imageFilename); } else if (imageFilename.endsWith(".px4")) { _binFormat = true; return _px4Load(imageFilename); -- GitLab From b3daf09856bc9f1734a15061ca47bdaa273b0e18 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 09:53:07 -0800 Subject: [PATCH 325/398] Remove serial ids These are now stored in json file --- src/comm/QGCSerialPortInfo.h | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/comm/QGCSerialPortInfo.h b/src/comm/QGCSerialPortInfo.h index 71398d500..0148da2f0 100644 --- a/src/comm/QGCSerialPortInfo.h +++ b/src/comm/QGCSerialPortInfo.h @@ -35,39 +35,6 @@ public: BoardTypeUnknown } BoardType_t; - // Vendor and products ids for the boards we care about - - static const int px4VendorId = 9900; ///< Vendor ID for all Pixhawk boards and PX4 Flow - static const int pixhawkFMUV4ProductId = 18; ///< Product ID for Pixhawk V4 board - static const int pixhawkFMUV4ProProductId = 19; ///< Product ID for Pixhawk V4 Pro board - static const int pixhawkFMUV2ProductId = 17; ///< Product ID for Pixhawk V2 board - static const int pixhawkFMUV2OldBootloaderProductId = 22; ///< Product ID for Bootloader on older Pixhawk V2 boards - static const int pixhawkFMUV1ProductId = 16; ///< Product ID for PX4 FMU V1 board - static const int auavx2_1FMUV2ProductId = 33; ///< Product ID for AUAV X2.1 FMU V2 board - - static const int AeroCoreProductId = 4097; ///< Product ID for the AeroCore board - - static const int px4FlowProductId = 21; ///< Product ID for PX4 Flow board - - static const int MindPXFMUV2ProductId = 48; ///< Product ID for the MindPX V2 board - static const int TAPV1ProductId = 64; ///< Product ID for the TAP V1 board - static const int ASCV1ProductId = 65; ///< Product ID for the ASC V1 board - - static const int threeDRRadioVendorId = 1027; ///< Vendor ID for 3DR Radio - static const int threeDRRadioProductId = 24597; ///< Product ID for 3DR Radio - - static const int siLabsRadioVendorId = 0x10c4; ///< Vendor ID for SILabs Radio - static const int siLabsRadioProductId = 0xea60; ///< Product ID for SILabs Radio - - static const int ubloxRTKVendorId = 5446; ///< Vendor ID for ublox RTK - static const int ubloxRTKProductId = 424; ///< Product ID for ublox RTK - - static const int openpilotVendorId = 0x20A0; ///< Vendor ID for OpenPilot - static const int revolutionProductId = 0x415E; ///< Product ID for OpenPilot Revolution - static const int oplinkProductId = 0x415C; ///< Product ID for OpenPilot OPLink - static const int sparky2ProductId = 0x41D0; ///< Product ID for Taulabs Sparky2 - static const int CC3DProductId = 0x415D; ///< Product ID for OpenPilot CC3D - QGCSerialPortInfo(void); QGCSerialPortInfo(const QSerialPort & port); -- GitLab From 321eb344398b4a360750d479834fd5a21235d76c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 10:54:00 -0800 Subject: [PATCH 326/398] Fix typo --- src/Settings/VideoSettings.cc | 4 ++-- src/Settings/VideoSettings.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index 3b9ab513f..cd0228e57 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -12,10 +12,10 @@ #include #include -const char* VideoSettings::VideoSettingsGroupName = "video"; +const char* VideoSettings::videoSettingsGroupName = "video"; VideoSettings::VideoSettings(QObject* parent) - : SettingsGroup(VideoSettingsGroupName, QString() /* root settings group */, parent) + : SettingsGroup(videoSettingsGroupName, QString() /* root settings group */, parent) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index e5bc1117d..04bfc6bf8 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -19,7 +19,7 @@ class VideoSettings : public SettingsGroup public: VideoSettings(QObject* parent = NULL); - static const char* VideoSettingsGroupName; + static const char* videoSettingsGroupName; private: }; -- GitLab From 23df58ced35a61d407ef29d6d1d6b56559c494ab Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 10:54:07 -0800 Subject: [PATCH 327/398] Correct ownership --- src/FactSystem/SettingsFact.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FactSystem/SettingsFact.cc b/src/FactSystem/SettingsFact.cc index d4915cd0d..1f673d938 100644 --- a/src/FactSystem/SettingsFact.cc +++ b/src/FactSystem/SettingsFact.cc @@ -17,7 +17,7 @@ SettingsFact::SettingsFact(QObject* parent) : Fact(parent) { - + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } SettingsFact::SettingsFact(QString settingGroup, FactMetaData* metaData, QObject* parent) -- GitLab From c9ef68f31155d4b9176b65822bf251b2a1d1c916 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 14 Feb 2017 17:34:58 -0500 Subject: [PATCH 328/398] Fix Sub safety summary descriptions --- .../APM/APMSafetyComponentSummarySub.qml | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml b/src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml index 7bab272be..1ab63b7c4 100644 --- a/src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml +++ b/src/AutoPilotPlugins/APM/APMSafetyComponentSummarySub.qml @@ -25,34 +25,6 @@ FactPanel { property Fact _armingCheck: controller.getParameterFact(-1, "ARMING_CHECK") - property string _failsafeGCSEnableText - - Component.onCompleted: { - setFailsafeGCSEnableText() - } - - Connections { - target: _failsafeGCSEnable - - onValueChanged: setFailsafeGCSEnableText() - } - - function setFailsafeGCSEnableText() { - switch (_failsafeGCSEnable.value) { - case 0: - _failsafeGCSEnableText = qsTr("Disabled") - break - case 1: - _failsafeGCSEnableText = qsTr("Always RTL") - break - case 2: - _failsafeGCSEnableText = qsTr("Continue with Mission in Auto Mode") - break - default: - _failsafeGCSEnableText = qsTr("Unknown") - } - } - Column { anchors.fill: parent @@ -63,17 +35,17 @@ FactPanel { VehicleSummaryRow { labelText: qsTr("GCS failsafe:") - valueText: _failsafeGCSEnableText + valueText: _failsafeGCSEnable.enumOrValueString } VehicleSummaryRow { labelText: qsTr("Leak failsafe:") - valueText: _failsafeLeakEnable.value ? qsTr("Enabled") : qsTr("Disabled") + valueText: _failsafeLeakEnable.enumOrValueString } VehicleSummaryRow { labelText: qsTr("Leak detector:") - valueText: _leakPin.value > 0 ? qsTr("Enabled") : qsTr("Disabled") + valueText: _leakPin.enumOrValueString } VehicleSummaryRow { -- GitLab From 427836432d7d550f0e703e7fd7a413a7aeea7f66 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 14 Dec 2016 16:20:49 -0500 Subject: [PATCH 329/398] Add calibrate pressure button in sensors page, enabled only for ArduSub --- .../APM/APMSensorsComponent.qml | 36 +++++++++++++++++++ .../APM/APMSensorsComponentController.cc | 31 ++++++++++++++++ .../APM/APMSensorsComponentController.h | 4 +++ .../APM/ArduSubFirmwarePlugin.cc | 5 +++ .../APM/ArduSubFirmwarePlugin.h | 2 ++ src/FirmwarePlugin/FirmwarePlugin.cc | 5 +++ src/FirmwarePlugin/FirmwarePlugin.h | 4 +++ src/Vehicle/Vehicle.cc | 5 +++ src/Vehicle/Vehicle.h | 2 ++ src/uas/UAS.cc | 6 +++- src/uas/UASInterface.h | 1 + 11 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index 49aaa2b95..51b5b4ead 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -14,6 +14,7 @@ import QtQuick.Controls.Styles 1.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.2 +import QGroundControl 1.0 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 @@ -120,6 +121,7 @@ SetupPage { accelButton: accelButton compassMotButton: motorInterferenceButton levelButton: levelHorizonButton + calibratePressureButton: calibratePressureButton nextButton: nextButton cancelButton: cancelButton setOrientationsButton: setOrientationsButton @@ -445,6 +447,26 @@ SetupPage { } // QGCViewDialog } // Component - levelHorizonDialogComponent + Component { + id: calibratePressureDialogComponent + + QGCViewDialog { + id: calibratePressureDialog + + function accept() { + controller.calibratePressure() + calibratePressureDialog.hideDialog() + } + + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("Pressure calibration will set the depth to zero at the current pressure reading.") + } + } // QGCViewDialog + } // Component - calibratePressureDialogComponent + /// Left button column Column { spacing: ScreenTools.defaultFontPixelHeight / 2 @@ -492,6 +514,20 @@ SetupPage { } } + QGCButton { + id: calibratePressureButton + width: parent.buttonWidth + text: _calibratePressureText + visible: _activeVehicle ? _activeVehicle.supportsCalibratePressure : false + + readonly property string _calibratePressureText: qsTr("Calibrate Pressure") + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + onClicked: { + showDialog(calibratePressureDialogComponent, _calibratePressureText, qgcView.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Ok) + } + } + QGCButton { id: motorInterferenceButton width: parent.buttonWidth diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index 7cc06c590..c951904a0 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -30,6 +30,7 @@ APMSensorsComponentController::APMSensorsComponentController(void) , _accelButton(NULL) , _compassMotButton(NULL) , _levelButton(NULL) + , _calibratePressureButton(NULL) , _nextButton(NULL) , _cancelButton(NULL) , _setOrientationsButton(NULL) @@ -101,6 +102,7 @@ void APMSensorsComponentController::_startLogCalibration(void) _accelButton->setEnabled(false); _compassMotButton->setEnabled(false); _levelButton->setEnabled(false); + _calibratePressureButton->setEnabled(false); _setOrientationsButton->setEnabled(false); if (_calTypeInProgress == CalTypeAccel || _calTypeInProgress == CalTypeCompassMot) { _nextButton->setEnabled(true); @@ -114,6 +116,7 @@ void APMSensorsComponentController::_startVisualCalibration(void) _accelButton->setEnabled(false); _compassMotButton->setEnabled(false); _levelButton->setEnabled(false); + _calibratePressureButton->setEnabled(false); _setOrientationsButton->setEnabled(false); _cancelButton->setEnabled(true); @@ -158,6 +161,7 @@ void APMSensorsComponentController::_stopCalibration(APMSensorsComponentControll _accelButton->setEnabled(true); _compassMotButton->setEnabled(true); _levelButton->setEnabled(true); + _calibratePressureButton->setEnabled(true); _setOrientationsButton->setEnabled(true); _nextButton->setEnabled(false); _cancelButton->setEnabled(false); @@ -318,6 +322,15 @@ void APMSensorsComponentController::levelHorizon(void) _uas->startCalibration(UASInterface::StartCalibrationLevel); } +void APMSensorsComponentController::calibratePressure(void) +{ + _calTypeInProgress = CalTypePressure; + _vehicle->setConnectionLostEnabled(false); + _startLogCalibration(); + _appendStatusLog(tr("Requesting pressure calibration...")); + _uas->startCalibration(UASInterface::StartCalibrationPressure); +} + void APMSensorsComponentController::_handleUASTextMessage(int uasId, int compId, int severity, QString text) { Q_UNUSED(compId); @@ -628,6 +641,24 @@ void APMSensorsComponentController::_handleCommandAck(mavlink_message_t& message } } } + + if (_calTypeInProgress == CalTypePressure) { + mavlink_command_ack_t commandAck; + mavlink_msg_command_ack_decode(&message, &commandAck); + + if (commandAck.command == MAV_CMD_PREFLIGHT_CALIBRATION) { + switch (commandAck.result) { + case MAV_RESULT_ACCEPTED: + _appendStatusLog(tr("Pressure calibration success")); + _stopCalibration(StopCalibrationSuccessShowLog); + break; + default: + _appendStatusLog(tr("Pressure calibration fail")); + _stopCalibration(StopCalibrationFailed); + break; + } + } + } } void APMSensorsComponentController::_handleMagCalProgress(mavlink_message_t& message) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h index 16129f6c9..16e0fd23e 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.h +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.h @@ -38,6 +38,7 @@ public: Q_PROPERTY(QQuickItem* accelButton MEMBER _accelButton) Q_PROPERTY(QQuickItem* compassMotButton MEMBER _compassMotButton) Q_PROPERTY(QQuickItem* levelButton MEMBER _levelButton) + Q_PROPERTY(QQuickItem* calibratePressureButton MEMBER _calibratePressureButton) Q_PROPERTY(QQuickItem* nextButton MEMBER _nextButton) Q_PROPERTY(QQuickItem* cancelButton MEMBER _cancelButton) Q_PROPERTY(QQuickItem* setOrientationsButton MEMBER _setOrientationsButton) @@ -90,6 +91,7 @@ public: Q_INVOKABLE void calibrateAccel(void); Q_INVOKABLE void calibrateMotorInterference(void); Q_INVOKABLE void levelHorizon(void); + Q_INVOKABLE void calibratePressure(void); Q_INVOKABLE void cancelCalibration(void); Q_INVOKABLE void nextClicked(void); Q_INVOKABLE bool usingUDPLink(void); @@ -103,6 +105,7 @@ public: CalTypeOffboardCompass, CalTypeLevelHorizon, CalTypeCompassMot, + CalTypePressure, CalTypeNone } CalType_t; Q_ENUM(CalType_t) @@ -169,6 +172,7 @@ private: QQuickItem* _accelButton; QQuickItem* _compassMotButton; QQuickItem* _levelButton; + QQuickItem* _calibratePressureButton; QQuickItem* _nextButton; QQuickItem* _cancelButton; QQuickItem* _setOrientationsButton; diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc index e6dd01b74..8f9d2d51d 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc @@ -72,3 +72,8 @@ bool ArduSubFirmwarePlugin::supportsJSButton(void) { return true; } + +bool ArduSubFirmwarePlugin::supportsCalibratePressure(void) +{ + return true; +} diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index d9911f703..c585ba444 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -79,6 +79,8 @@ public: bool supportsJSButton(void); + bool supportsCalibratePressure(void); + QString brandImage(const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImageSub"); } }; diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index fb6d02816..1ccaea101 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -127,6 +127,11 @@ bool FirmwarePlugin::supportsRadio(void) return true; } +bool FirmwarePlugin::supportsCalibratePressure(void) +{ + return false; +} + bool FirmwarePlugin::supportsJSButton(void) { return false; diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 81c7dd623..1ec3d1e93 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -163,6 +163,10 @@ public: /// to be assigned via parameters in firmware. Default is false. virtual bool supportsJSButton(void); + /// Returns true if the firmware supports calibrating the pressure sensor so the altitude will read + /// zero at the current pressure. Default is false. + virtual bool supportsCalibratePressure(void); + /// Called before any mavlink message is processed by Vehicle such that the firmwre plugin /// can adjust any message characteristics. This is handy to adjust or differences in mavlink /// spec implementations such that the base code can remain mavlink generic. diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 1639e0519..fd27daee3 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1742,6 +1742,11 @@ bool Vehicle::supportsJSButton(void) const return _firmwarePlugin->supportsJSButton(); } +bool Vehicle::supportsCalibratePressure(void) const +{ + return _firmwarePlugin->supportsCalibratePressure(); +} + void Vehicle::_setCoordinateValid(bool coordinateValid) { if (coordinateValid != _coordinateValid) { diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 2963c1b58..9b634e0be 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -253,6 +253,7 @@ public: Q_PROPERTY(bool supportsThrottleModeCenterZero READ supportsThrottleModeCenterZero CONSTANT) Q_PROPERTY(bool supportsJSButton READ supportsJSButton CONSTANT) Q_PROPERTY(bool supportsRadio READ supportsRadio CONSTANT) + Q_PROPERTY(bool supportsCalibratePressure READ supportsCalibratePressure CONSTANT) Q_PROPERTY(bool autoDisconnect MEMBER _autoDisconnect NOTIFY autoDisconnectChanged) Q_PROPERTY(QString prearmError READ prearmError WRITE setPrearmError NOTIFY prearmErrorChanged) Q_PROPERTY(int motorCount READ motorCount CONSTANT) @@ -474,6 +475,7 @@ public: bool supportsThrottleModeCenterZero(void) const; bool supportsRadio(void) const; bool supportsJSButton(void) const; + bool supportsCalibratePressure(void) const; void setFlying(bool flying); void setGuidedMode(bool guidedMode); diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index ebe066245..eb23c566a 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -528,6 +528,7 @@ void UAS::startCalibration(UASInterface::StartCalibrationType calType) int airspeedCal = 0; int radioCal = 0; int accelCal = 0; + int pressureCal = 0; int escCal = 0; switch (calType) { @@ -552,6 +553,9 @@ void UAS::startCalibration(UASInterface::StartCalibrationType calType) case StartCalibrationLevel: accelCal = 2; break; + case StartCalibrationPressure: + pressureCal = 1; + break; case StartCalibrationEsc: escCal = 1; break; @@ -576,7 +580,7 @@ void UAS::startCalibration(UASInterface::StartCalibrationType calType) 0, // 0=first transmission of command gyroCal, // gyro cal magCal, // mag cal - 0, // ground pressure + pressureCal, // ground pressure radioCal, // radio cal accelCal, // accel cal airspeedCal, // PX4: airspeed cal, ArduPilot: compass mot diff --git a/src/uas/UASInterface.h b/src/uas/UASInterface.h index 515fb599b..5f35f1408 100644 --- a/src/uas/UASInterface.h +++ b/src/uas/UASInterface.h @@ -111,6 +111,7 @@ public: StartCalibrationAirspeed, StartCalibrationAccel, StartCalibrationLevel, + StartCalibrationPressure, StartCalibrationEsc, StartCalibrationCopyTrims, StartCalibrationUavcanEsc, -- GitLab From ec0450ce0fdc341732bf5ce4fa22422392b9257b Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Mon, 30 Jan 2017 17:30:53 -0500 Subject: [PATCH 330/398] Put temperature readings in vehicle values widget --- qgroundcontrol.qrc | 1 + src/Vehicle/TemperatureFact.json | 23 ++++++++++++++ src/Vehicle/Vehicle.cc | 51 ++++++++++++++++++++++++++++++++ src/Vehicle/Vehicle.h | 44 ++++++++++++++++++++++++--- 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 src/Vehicle/TemperatureFact.json diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 1f4bb7924..59331e22a 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -186,6 +186,7 @@ src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json src/comm/USBBoardInfo.json + src/Vehicle/TemperatureFact.json src/comm/APMArduCopterMockLink.params diff --git a/src/Vehicle/TemperatureFact.json b/src/Vehicle/TemperatureFact.json new file mode 100644 index 000000000..01cfb25e7 --- /dev/null +++ b/src/Vehicle/TemperatureFact.json @@ -0,0 +1,23 @@ +[ +{ + "name": "temperature1", + "shortDescription": "Temperature (1)", + "type": "double", + "decimalPlaces": 2, + "units": "C" +}, +{ + "name": "temperature2", + "shortDescription": "Temperature (2)", + "type": "double", + "decimalPlaces": 2, + "units": "C" +}, +{ + "name": "temperature3", + "shortDescription": "Temperature (3)", + "type": "double", + "decimalPlaces": 2, + "units": "C" +} +] diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 1639e0519..6c9ccaab0 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -54,6 +54,7 @@ const char* Vehicle::_gpsFactGroupName = "gps"; const char* Vehicle::_batteryFactGroupName = "battery"; const char* Vehicle::_windFactGroupName = "wind"; const char* Vehicle::_vibrationFactGroupName = "vibration"; +const char* Vehicle::_temperatureFactGroupName = "temperature"; const int Vehicle::_lowBatteryAnnounceRepeatMSecs = 30 * 1000; @@ -149,6 +150,7 @@ Vehicle::Vehicle(LinkInterface* link, , _batteryFactGroup(this) , _windFactGroup(this) , _vibrationFactGroup(this) + , _temperatureFactGroup(this) { _addLink(link); @@ -343,6 +345,7 @@ void Vehicle::_commonInit(void) _addFactGroup(&_batteryFactGroup, _batteryFactGroupName); _addFactGroup(&_windFactGroup, _windFactGroupName); _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName); + _addFactGroup(&_temperatureFactGroup, _temperatureFactGroupName); } Vehicle::~Vehicle() @@ -561,6 +564,15 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes case MAVLINK_MSG_ID_VFR_HUD: _handleVfrHud(message); break; + case MAVLINK_MSG_ID_SCALED_PRESSURE: + _handleScaledPressure(message); + break; + case MAVLINK_MSG_ID_SCALED_PRESSURE2: + _handleScaledPressure2(message); + break; + case MAVLINK_MSG_ID_SCALED_PRESSURE3: + _handleScaledPressure3(message); + break; // Following are ArduPilot dialect messages @@ -990,6 +1002,24 @@ void Vehicle::_handleRCChannelsRaw(mavlink_message_t& message) emit rcChannelsChanged(channelCount, pwmValues); } +void Vehicle::_handleScaledPressure(mavlink_message_t& message) { + mavlink_scaled_pressure_t pressure; + mavlink_msg_scaled_pressure_decode(&message, &pressure); + _temperatureFactGroup.temperature1()->setRawValue(pressure.temperature / 100.0); +} + +void Vehicle::_handleScaledPressure2(mavlink_message_t& message) { + mavlink_scaled_pressure2_t pressure; + mavlink_msg_scaled_pressure2_decode(&message, &pressure); + _temperatureFactGroup.temperature2()->setRawValue(pressure.temperature / 100.0); +} + +void Vehicle::_handleScaledPressure3(mavlink_message_t& message) { + mavlink_scaled_pressure3_t pressure; + mavlink_msg_scaled_pressure3_decode(&message, &pressure); + _temperatureFactGroup.temperature3()->setRawValue(pressure.temperature / 100.0); +} + bool Vehicle::_containsLink(LinkInterface* link) { return _links.contains(link); @@ -2389,3 +2419,24 @@ VehicleVibrationFactGroup::VehicleVibrationFactGroup(QObject* parent) _yAxisFact.setRawValue(std::numeric_limits::quiet_NaN()); _zAxisFact.setRawValue(std::numeric_limits::quiet_NaN()); } + + +const char* VehicleTemperatureFactGroup::_temperature1FactName = "temperature1"; +const char* VehicleTemperatureFactGroup::_temperature2FactName = "temperature2"; +const char* VehicleTemperatureFactGroup::_temperature3FactName = "temperature3"; + +VehicleTemperatureFactGroup::VehicleTemperatureFactGroup(QObject* parent) + : FactGroup(1000, ":/json/Vehicle/TemperatureFact.json", parent) + , _temperature1Fact (0, _temperature1FactName, FactMetaData::valueTypeDouble) + , _temperature2Fact (0, _temperature2FactName, FactMetaData::valueTypeDouble) + , _temperature3Fact (0, _temperature3FactName, FactMetaData::valueTypeDouble) +{ + _addFact(&_temperature1Fact, _temperature1FactName); + _addFact(&_temperature2Fact, _temperature2FactName); + _addFact(&_temperature3Fact, _temperature3FactName); + + // Start out as not available "--.--" + _temperature1Fact.setRawValue (std::numeric_limits::quiet_NaN()); + _temperature2Fact.setRawValue (std::numeric_limits::quiet_NaN()); + _temperature3Fact.setRawValue (std::numeric_limits::quiet_NaN()); +} diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 2963c1b58..50f587d62 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -185,6 +185,35 @@ private: Fact _cellCountFact; }; +class VehicleTemperatureFactGroup : public FactGroup +{ + Q_OBJECT + +public: + VehicleTemperatureFactGroup(QObject* parent = NULL); + + Q_PROPERTY(Fact* temperature1 READ temperature1 CONSTANT) + Q_PROPERTY(Fact* temperature2 READ temperature2 CONSTANT) + Q_PROPERTY(Fact* temperature3 READ temperature3 CONSTANT) + + Fact* temperature1 (void) { return &_temperature1Fact; } + Fact* temperature2 (void) { return &_temperature2Fact; } + Fact* temperature3 (void) { return &_temperature3Fact; } + + static const char* _temperature1FactName; + static const char* _temperature2FactName; + static const char* _temperature3FactName; + + static const char* _settingsGroup; + + static const double _temperatureUnavailable; + +private: + Fact _temperature1Fact; + Fact _temperature2Fact; + Fact _temperature3Fact; +}; + class Vehicle : public FactGroup { Q_OBJECT @@ -305,10 +334,11 @@ public: Q_PROPERTY(Fact* altitudeRelative READ altitudeRelative CONSTANT) Q_PROPERTY(Fact* altitudeAMSL READ altitudeAMSL CONSTANT) - Q_PROPERTY(FactGroup* gps READ gpsFactGroup CONSTANT) - Q_PROPERTY(FactGroup* battery READ batteryFactGroup CONSTANT) - Q_PROPERTY(FactGroup* wind READ windFactGroup CONSTANT) - Q_PROPERTY(FactGroup* vibration READ vibrationFactGroup CONSTANT) + Q_PROPERTY(FactGroup* gps READ gpsFactGroup CONSTANT) + Q_PROPERTY(FactGroup* battery READ batteryFactGroup CONSTANT) + Q_PROPERTY(FactGroup* wind READ windFactGroup CONSTANT) + Q_PROPERTY(FactGroup* vibration READ vibrationFactGroup CONSTANT) + Q_PROPERTY(FactGroup* temperature READ temperatureFactGroup CONSTANT) Q_PROPERTY(int firmwareMajorVersion READ firmwareMajorVersion NOTIFY firmwareMajorVersionChanged) Q_PROPERTY(int firmwareMinorVersion READ firmwareMinorVersion NOTIFY firmwareMinorVersionChanged) @@ -558,6 +588,7 @@ public: FactGroup* batteryFactGroup (void) { return &_batteryFactGroup; } FactGroup* windFactGroup (void) { return &_windFactGroup; } FactGroup* vibrationFactGroup (void) { return &_vibrationFactGroup; } + FactGroup* temperatureFactGroup (void) { return &_temperatureFactGroup; } void setConnectionLostEnabled(bool connectionLostEnabled); @@ -752,6 +783,9 @@ private: void _handleGlobalPositionInt(mavlink_message_t& message); void _handleAltitude(mavlink_message_t& message); void _handleVfrHud(mavlink_message_t& message); + void _handleScaledPressure(mavlink_message_t& message); + void _handleScaledPressure2(mavlink_message_t& message); + void _handleScaledPressure3(mavlink_message_t& message); void _missionManagerError(int errorCode, const QString& errorMsg); void _geoFenceManagerError(int errorCode, const QString& errorMsg); void _rallyPointManagerError(int errorCode, const QString& errorMsg); @@ -922,6 +956,7 @@ private: VehicleBatteryFactGroup _batteryFactGroup; VehicleWindFactGroup _windFactGroup; VehicleVibrationFactGroup _vibrationFactGroup; + VehicleTemperatureFactGroup _temperatureFactGroup; static const char* _rollFactName; static const char* _pitchFactName; @@ -936,6 +971,7 @@ private: static const char* _batteryFactGroupName; static const char* _windFactGroupName; static const char* _vibrationFactGroupName; + static const char* _temperatureFactGroupName; static const int _vehicleUIUpdateRateMSecs = 100; -- GitLab From 12fdb09724579859febe705948ad20a13c9757d9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 12:48:40 -0800 Subject: [PATCH 331/398] Fix typo --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 814a12ac9..31aa6a99a 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -256,7 +256,7 @@ QString PX4FirmwarePlugin::missionCommandOverrides(MAV_TYPE vehicleType) const return QStringLiteral(":/json/PX4/MavCmdInfoMultiRotor.json"); break; case MAV_TYPE_VTOL_QUADROTOR: - return QStringLiteral(":/json/APM/MavCmdInfoVTOL.json"); + return QStringLiteral(":/json/PX4/MavCmdInfoVTOL.json"); break; case MAV_TYPE_SUBMARINE: return QStringLiteral(":/json/PX4/MavCmdInfoSub.json"); -- GitLab From 72cd5d1ca6b714c109e9e37bb4dec6b65e0d8de1 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 12:48:56 -0800 Subject: [PATCH 332/398] Add support for FirmwarePluginFactory::supportedVehicleTypes --- .../APM/APMFirmwarePluginFactory.cc | 2 +- .../APM/APMFirmwarePluginFactory.h | 2 +- src/FirmwarePlugin/FirmwarePlugin.cc | 7 +++ src/FirmwarePlugin/FirmwarePlugin.h | 7 ++- src/FirmwarePlugin/FirmwarePluginManager.cc | 63 ++++++++++++++----- src/FirmwarePlugin/FirmwarePluginManager.h | 16 +++-- .../PX4/PX4FirmwarePluginFactory.cc | 2 +- .../PX4/PX4FirmwarePluginFactory.h | 2 +- src/MissionManager/MissionCommandTree.cc | 4 +- src/QmlControls/QGroundControlQmlGlobal.cc | 2 +- 10 files changed, 79 insertions(+), 28 deletions(-) diff --git a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc index e82523e8d..8c78ea79d 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.cc @@ -24,7 +24,7 @@ APMFirmwarePluginFactory::APMFirmwarePluginFactory(void) } -QList APMFirmwarePluginFactory::knownFirmwareTypes(void) const +QList APMFirmwarePluginFactory::supportedFirmwareTypes(void) const { QList list; diff --git a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h index 7ca1e0f03..3ef5b3615 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePluginFactory.h @@ -24,7 +24,7 @@ class APMFirmwarePluginFactory : public FirmwarePluginFactory public: APMFirmwarePluginFactory(void); - QList knownFirmwareTypes (void) const final; + QList supportedFirmwareTypes (void) const final; FirmwarePlugin* firmwarePluginForAutopilot (MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) final; private: diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index fb6d02816..33d7d1119 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -24,6 +24,13 @@ FirmwarePluginFactory::FirmwarePluginFactory(void) FirmwarePluginFactoryRegister::instance()->registerPluginFactory(this); } +QList FirmwarePluginFactory::supportedVehicleTypes(void) const +{ + QList vehicleTypes; + vehicleTypes << MAV_TYPE_FIXED_WING << MAV_TYPE_QUADROTOR << MAV_TYPE_VTOL_QUADROTOR << MAV_TYPE_GROUND_ROVER << MAV_TYPE_SUBMARINE; + return vehicleTypes; +} + FirmwarePluginFactoryRegister* FirmwarePluginFactoryRegister::instance(void) { if (!_instance) { diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 81c7dd623..3e1c2e76d 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -265,8 +265,11 @@ public: /// @return Singleton FirmwarePlugin instance for the specified MAV_AUTOPILOT. virtual FirmwarePlugin* firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) = 0; - /// @return List of autopilot types this plugin supports. - virtual QList knownFirmwareTypes(void) const = 0; + /// @return List of firmware types this plugin supports. + virtual QList supportedFirmwareTypes(void) const = 0; + + /// @return List of vehicle types this plugin supports. + virtual QList supportedVehicleTypes(void) const; }; class FirmwarePluginFactoryRegister : public QObject diff --git a/src/FirmwarePlugin/FirmwarePluginManager.cc b/src/FirmwarePlugin/FirmwarePluginManager.cc index 4cbe2b34f..6dd4264dc 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.cc +++ b/src/FirmwarePlugin/FirmwarePluginManager.cc @@ -26,33 +26,68 @@ FirmwarePluginManager::~FirmwarePluginManager() delete _genericFirmwarePlugin; } -QList FirmwarePluginManager::knownFirmwareTypes(void) +QList FirmwarePluginManager::supportedFirmwareTypes(void) { - if (_knownFirmwareTypes.isEmpty()) { + if (_supportedFirmwareTypes.isEmpty()) { QList factoryList = FirmwarePluginFactoryRegister::instance()->pluginFactories(); for (int i = 0; i < factoryList.count(); i++) { - _knownFirmwareTypes.append(factoryList[i]->knownFirmwareTypes()); + _supportedFirmwareTypes.append(factoryList[i]->supportedFirmwareTypes()); } - _knownFirmwareTypes.append(MAV_AUTOPILOT_GENERIC); + _supportedFirmwareTypes.append(MAV_AUTOPILOT_GENERIC); } - return _knownFirmwareTypes; + return _supportedFirmwareTypes; } -FirmwarePlugin* FirmwarePluginManager::firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) +QList FirmwarePluginManager::supportedVehicleTypes(MAV_AUTOPILOT firmwareType) +{ + QList vehicleTypes; + + FirmwarePluginFactory* factory = _findPluginFactory(firmwareType); + + if (factory) { + vehicleTypes = factory->supportedVehicleTypes(); + } else if (firmwareType == MAV_AUTOPILOT_GENERIC) { + vehicleTypes << MAV_TYPE_FIXED_WING << MAV_TYPE_QUADROTOR << MAV_TYPE_VTOL_QUADROTOR << MAV_TYPE_GROUND_ROVER << MAV_TYPE_SUBMARINE; + } else { + qWarning() << "Request for unknown firmware plugin factory" << firmwareType; + } + + return vehicleTypes; +} + +FirmwarePlugin* FirmwarePluginManager::firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType) +{ + FirmwarePluginFactory* factory = _findPluginFactory(firmwareType); + FirmwarePlugin* plugin = NULL; + + if (factory) { + plugin = factory->firmwarePluginForAutopilot(firmwareType, vehicleType); + } else if (firmwareType != MAV_AUTOPILOT_GENERIC) { + qWarning() << "Request for unknown firmware plugin factory" << firmwareType; + } + + if (!plugin) { + // Default plugin fallback + if (!_genericFirmwarePlugin) { + _genericFirmwarePlugin = new FirmwarePlugin; + } + plugin = _genericFirmwarePlugin; + } + + return plugin; +} + +FirmwarePluginFactory* FirmwarePluginManager::_findPluginFactory(MAV_AUTOPILOT firmwareType) { - FirmwarePlugin* _plugin = NULL; QList factoryList = FirmwarePluginFactoryRegister::instance()->pluginFactories(); // Find the plugin which supports this vehicle for (int i=0; ifirmwarePluginForAutopilot(autopilotType, vehicleType))) { - return _plugin; + FirmwarePluginFactory* factory = factoryList[i]; + if (factory->supportedFirmwareTypes().contains(firmwareType)) { + return factory; } } - // Default plugin fallback - if (!_genericFirmwarePlugin) { - _genericFirmwarePlugin = new FirmwarePlugin; - } - return _genericFirmwarePlugin; + return NULL; } diff --git a/src/FirmwarePlugin/FirmwarePluginManager.h b/src/FirmwarePlugin/FirmwarePluginManager.h index 0e5f1a4f0..bc1607fe8 100644 --- a/src/FirmwarePlugin/FirmwarePluginManager.h +++ b/src/FirmwarePlugin/FirmwarePluginManager.h @@ -32,17 +32,23 @@ public: FirmwarePluginManager(QGCApplication* app); ~FirmwarePluginManager(); - QList knownFirmwareTypes(void); + /// Returns list of firmwares which are supported by the system + QList supportedFirmwareTypes(void); + + /// Returns the list of supported vehicle types for the specified firmware + QList supportedVehicleTypes(MAV_AUTOPILOT firmwareType); /// Returns appropriate plugin for autopilot type. - /// @param autopilotType Type of autopilot to return plugin for. - /// @param vehicleType Vehicle type of autopilot to return plugin for. + /// @param firmwareType Type of firmwware to return plugin for. + /// @param vehicleType Vehicle type to return plugin for. /// @return Singleton FirmwarePlugin instance for the specified MAV_AUTOPILOT. - FirmwarePlugin* firmwarePluginForAutopilot(MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType); + FirmwarePlugin* firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType); private: + FirmwarePluginFactory* _findPluginFactory(MAV_AUTOPILOT firmwareType); + FirmwarePlugin* _genericFirmwarePlugin; - QList _knownFirmwareTypes; + QList _supportedFirmwareTypes; }; #endif diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc index 4b742f90b..9b667ee45 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.cc @@ -21,7 +21,7 @@ PX4FirmwarePluginFactory::PX4FirmwarePluginFactory(void) } -QList PX4FirmwarePluginFactory::knownFirmwareTypes(void) const +QList PX4FirmwarePluginFactory::supportedFirmwareTypes(void) const { QList list; diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h index 254cf7b64..4edbcef77 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePluginFactory.h @@ -21,7 +21,7 @@ class PX4FirmwarePluginFactory : public FirmwarePluginFactory public: PX4FirmwarePluginFactory(void); - QList knownFirmwareTypes (void) const final; + QList supportedFirmwareTypes (void) const final; FirmwarePlugin* firmwarePluginForAutopilot (MAV_AUTOPILOT autopilotType, MAV_TYPE vehicleType) final; private: diff --git a/src/MissionManager/MissionCommandTree.cc b/src/MissionManager/MissionCommandTree.cc index f494826b6..cbb41630d 100644 --- a/src/MissionManager/MissionCommandTree.cc +++ b/src/MissionManager/MissionCommandTree.cc @@ -46,7 +46,7 @@ void MissionCommandTree::setToolbox(QGCToolbox* toolbox) } else { #endif // Load all levels of hierarchy - foreach (MAV_AUTOPILOT firmwareType, _toolbox->firmwarePluginManager()->knownFirmwareTypes()) { + foreach (MAV_AUTOPILOT firmwareType, _toolbox->firmwarePluginManager()->supportedFirmwareTypes()) { FirmwarePlugin* plugin = _toolbox->firmwarePluginManager()->firmwarePluginForAutopilot(firmwareType, MAV_TYPE_QUADROTOR); QList vehicleTypes; @@ -66,7 +66,7 @@ void MissionCommandTree::setToolbox(QGCToolbox* toolbox) MAV_AUTOPILOT MissionCommandTree::_baseFirmwareType(MAV_AUTOPILOT firmwareType) const { - if (qgcApp()->toolbox()->firmwarePluginManager()->knownFirmwareTypes().contains(firmwareType)) { + if (qgcApp()->toolbox()->firmwarePluginManager()->supportedFirmwareTypes().contains(firmwareType)) { return firmwareType; } else { return MAV_AUTOPILOT_GENERIC; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 66e65e167..a3fb27497 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -197,7 +197,7 @@ void QGroundControlQmlGlobal::setBaseFontPointSize(qreal size) int QGroundControlQmlGlobal::supportedFirmwareCount() { - return _firmwarePluginManager->knownFirmwareTypes().count(); + return _firmwarePluginManager->supportedFirmwareTypes().count(); } -- GitLab From ec7c0608e140a6dcd6e94c20da68eb79512c0291 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Tue, 21 Feb 2017 13:45:33 -0500 Subject: [PATCH 333/398] Update parameter metadata for Sub --- src/FirmwarePlugin/APM/APMFirmwarePlugin.cc | 6 +- .../APM/APMParameterFactMetaData.Sub.3.4.xml | 1210 +-- .../APM/APMParameterFactMetaData.Sub.3.5.xml | 7683 +++++++++++++++++ src/FirmwarePlugin/APM/APMResources.qrc | 1 + .../APM/ArduSubFirmwarePlugin.cc | 28 +- .../APM/ArduSubFirmwarePlugin.h | 6 + 6 files changed, 8353 insertions(+), 581 deletions(-) create mode 100644 src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.5.xml diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index 1a7975490..ebe40c083 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -797,7 +797,11 @@ QString APMFirmwarePlugin::internalParameterMetaDataFile(Vehicle* vehicle) } return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml"); case MAV_TYPE_SUBMARINE: - return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml"); + if (vehicle->firmwareMajorVersion() < 3 || (vehicle->firmwareMajorVersion() == 3 && vehicle->firmwareMinorVersion() <= 4)) { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml"); + } else { + return QStringLiteral(":/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.5.xml"); + } default: return QString(); } diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml index ac42cc782..e8f2e01eb 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.4.xml @@ -16,6 +16,7 @@ AntennaTracker Copter Rover +ArduSub @@ -38,29 +39,6 @@ .5 Hz - -0.0 1000.0 -10 -Centimeters - - -0.0 500.0 -10 - - -0:Feedback from mid stick,1:High throttle cancels landing,2:Disarm on land detection - -None -Feedback from mid stick -High throttle cancels landing -Disarm on land detection - - - -0 10 -1 -seconds - 0:Roll,1:Pitch,2:Yaw @@ -70,25 +48,6 @@ Yaw - -0 8000 -1 -Centimeters - - -0.5 10.0 - -Disabled -Shallow -Steep - -.1 - - -0 2000 -50 -cm/s - 0.01 2.0 0.01 @@ -161,84 +120,6 @@ Enabled - - -Disabled -Mode1 -Mode2 -Mode1+2 -Mode3 -Mode1+3 -Mode2+3 -Mode1+2+3 -Mode4 -Mode1+4 -Mode2+4 -Mode1+2+4 -Mode3+4 -Mode1+3+4 -Mode2+3+4 -Mode1+2+3+4 -Mode5 -Mode1+5 -Mode2+5 -Mode1+2+5 -Mode3+5 -Mode1+3+5 -Mode2+3+5 -Mode1+2+3+5 -Mode4+5 -Mode1+4+5 -Mode2+4+5 -Mode1+2+4+5 -Mode3+4+5 -Mode1+3+4+5 -Mode2+3+4+5 -Mode1+2+3+4+5 -Mode6 -Mode1+6 -Mode2+6 -Mode1+2+6 -Mode3+6 -Mode1+3+6 -Mode2+3+6 -Mode1+2+3+6 -Mode4+6 -Mode1+4+6 -Mode2+4+6 -Mode1+2+4+6 -Mode3+4+6 -Mode1+3+4+6 -Mode2+3+4+6 -Mode1+2+3+4+6 -Mode5+6 -Mode1+5+6 -Mode2+5+6 -Mode1+2+5+6 -Mode3+5+6 -Mode1+3+5+6 -Mode2+3+5+6 -Mode1+2+3+5+6 -Mode4+5+6 -Mode1+4+5+6 -Mode2+4+5+6 -Mode1+2+4+5+6 -Mode3+4+5+6 -Mode1+3+4+5+6 -Mode2+3+4+5+6 -Mode1+2+3+4+5+6 - - - --1 1000 -1 -Centimeters - - -0 3000 -10 -Centimeters - Never change yaw @@ -247,21 +128,6 @@ Face along GPS course - -0 60000 -1000 -ms - - -30 200 -10 -cm/s - - -0 500 -10 -cm/s - 50 500 10 @@ -272,19 +138,6 @@ 10 cm/s/s - - -Disabled -Enabled always RTL -Enabled Continue with Mission in Auto Mode -Enabled always LAND - - - -925 1100 -1 -pwm - 0 300 1 @@ -332,8 +185,6 @@ Manual - - 0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW @@ -405,17 +256,6 @@ 0 32767 - - -Plus -X -V -H -V-Tail -A-Tail -Y6B (New) - - Do Nothing @@ -433,7 +273,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -470,7 +310,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -507,7 +347,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -544,7 +384,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -581,7 +421,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -618,7 +458,7 @@ Auto AutoTune Land -EPM +Gripper Parachute Enable Parachute Release Parachute 3pos @@ -671,20 +511,6 @@ 10 - -4 12 -deg/sec - - -2000 4500 -Centi-degrees - - - -No repositioning -Repositioning - - Land @@ -701,6 +527,30 @@ Enabled + +0.1 1.0 + + +0.2 1.0 + + +0.1 0.8 + + +1 10 + + +30 400 + + +30 400 + + +0.5 4.0 + + +1000 2000 + 50 490 1 @@ -750,6 +600,11 @@ 10 cm/s/s + +0 4500 +10 +cm/s/s + 1.000 8.000 @@ -767,7 +622,7 @@ 0.000 0.400 - + 1.000 100.000 Hz @@ -795,19 +650,13 @@ 0.001 0.006 - - -Stopped -Running - - Do Not Use in RTL and Land Use in RTL and Land - + 0 5 @@ -837,14 +686,18 @@ None -MAVlink1 +MAVLink1 MAVLink2 -Frsky D-PORT -Frsky S-PORT +Frsky D +Frsky SPort GPS Alexmos Gimbal Serial SToRM32 Gimbal Serial Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon @@ -866,14 +719,18 @@ None -MAVlink1 +MAVLink1 MAVLink2 -Frsky D-PORT -Frsky S-PORT +Frsky D +Frsky SPort GPS Alexmos Gimbal Serial SToRM32 Gimbal Serial Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon @@ -895,14 +752,18 @@ None -MAVlink1 +MAVLink1 MAVLink2 -Frsky D-PORT -Frsky S-PORT +Frsky D +Frsky SPort GPS Alexmos Gimbal Serial SToRM32 Gimbal Serial Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon @@ -924,14 +785,18 @@ None -MAVlink1 +MAVLink1 MAVLink2 -Frsky D-PORT -Frsky S-PORT +Frsky D +Frsky SPort GPS Alexmos Gimbal Serial SToRM32 Gimbal Serial Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon @@ -953,14 +818,18 @@ None -MAVlink1 +MAVLink1 MAVLink2 -Frsky D-PORT -Frsky S-PORT +Frsky D +Frsky SPort GPS Alexmos Gimbal Serial SToRM32 Gimbal Serial Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon @@ -1141,7 +1010,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1170,6 +1039,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1226,7 +1099,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1255,6 +1128,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1311,7 +1188,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1340,6 +1217,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1396,7 +1277,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1425,6 +1306,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1481,7 +1366,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1510,6 +1395,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1566,7 +1455,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1595,6 +1484,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1651,7 +1544,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1680,6 +1573,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1736,7 +1633,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1765,6 +1662,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1821,7 +1722,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1850,6 +1751,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -1906,7 +1811,7 @@ Flaperon2 GroundSteering Parachute -EPM +Gripper LandingGear EngineRunEnable HeliRSC @@ -1935,6 +1840,10 @@ RCIN14 RCIN15 RCIN16 +Ignition +Choke +Starter +Throttle @@ -3678,92 +3587,18 @@ - - - -Disabled -Enabled - - - -1000 2000 - - -1000 2000 - - -1000 2000 - - - -Never -every 15 seconds -every 30 seconds -once per minute - - - - - - -Disabled -Enabled - - - - -First Relay -Second Relay -Third Relay -Fourth Relay -Servo - - - -1000 2000 -1 -pwm - - -1000 2000 -1 -pwm - - -0 32000 -1 -Meters - - -0 5000 -1 -Milliseconds - - - - -1000 2000 -1 -pwm - - -1000 2000 -1 -pwm - - - + -400 400 1 milligauss - + -400 400 1 milligauss - + -400 400 1 milligauss @@ -3799,17 +3634,17 @@ Use Current - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle @@ -3864,32 +3699,32 @@ ForcedExternal - + -400 400 1 milligauss - + -400 400 1 milligauss - + -400 400 1 milligauss - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle @@ -3901,41 +3736,41 @@ ThirdCompass - + -400 400 1 milligauss - + -400 400 1 milligauss - + -400 400 1 milligauss - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle - + -1000 1000 1 Offset per Amp or at Full Throttle - + - + - + @@ -4049,60 +3884,55 @@ ForcedExternal - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + -4 20 +4 32 + +Very Strict +Default +Relaxed +Very Relaxed + 0.1 - - -Unknown -unused -unused -unused -SITL -PX4v1 -PX4v2 -unused -Linux - + rad/s @@ -4243,6 +4073,53 @@ IMU 3 + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +True + + +True + + +True + + +True + + +True + + +True + + + @@ -4291,10 +4168,16 @@ cm/s/s -100 981 +25 250 1 cm/s/s + + +Disable +Enable + + @@ -4325,6 +4208,17 @@ 1000 Centi-Degrees/Sec/Sec + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + Disabled @@ -4388,7 +4282,7 @@ 0.0 0.02 0.001 - + 1 100 1 Hz @@ -4410,7 +4304,7 @@ 0.0 0.02 0.001 - + 1 100 1 Hz @@ -4432,7 +4326,7 @@ 0.000 0.02 0.001 - + 1 100 1 Hz @@ -4444,8 +4338,8 @@ 0.5 0.9 - - + + 0.5 5 0.1 Hz @@ -4497,11 +4391,6 @@ 1 Hz - -0 50 -1 -Hz - @@ -4549,11 +4438,6 @@ 1 Hz - -0 50 -1 -Hz - @@ -4601,11 +4485,6 @@ 1 Hz - -0 50 -1 -Hz - @@ -4653,14 +4532,9 @@ 1 Hz - -0 50 -1 -Hz - - + 0.0 1.0 .01 @@ -4670,25 +4544,25 @@ Enabled - + 0.1 0.4 .01 - + 0.1 0.4 .01 - + 0 127 1 m/s - + -0.1745 +0.1745 0.01 Radians - + -0.1745 +0.1745 0.01 Radians @@ -5078,6 +4952,12 @@ Enabled + + +Disabled +Enabled + + @@ -5172,7 +5052,7 @@ - + No PWMs Two PWMs @@ -5182,7 +5062,7 @@ True - + Disabled Enabled @@ -5190,7 +5070,7 @@ True - + Disabled Enabled @@ -5198,14 +5078,14 @@ True - + Disabled Enabled True - + Disabled 50Hz @@ -5221,73 +5101,74 @@ -32767 32768 - + Disabled Enabled +Dynamic ID/Update - -0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8 + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 Disabled Enabled True - -degreesC + -1 80 +degreesC - - - - -Disabled -Enabled + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 - - -0 100 -percentage - - -1000 2000 -ms - - -0 1000 -cm/s - - -0 100 -percentage +True - + True True 1 pascals - + True True 1 degrees celsius - -meters + 0.1 +meters - + FirstBaro 2ndBaro 3rdBaro + + +Disabled +Bus0 + + 1.0:Fresh Water, 1.024:Salt Water @@ -5303,7 +5184,7 @@ - + None AUTO @@ -5317,10 +5198,14 @@ PX4-UAVCAN SBF GSOF +QURT +ERB +MAV +NOVA True - + None AUTO @@ -5337,7 +5222,7 @@ True - + Portable Stationary @@ -5374,7 +5259,7 @@ -100 90 Degrees - + send to first GPS send to 2nd GPS @@ -5388,7 +5273,7 @@ External only - + Disabled log every sample @@ -5439,6 +5324,38 @@ Enable automatic configuration + + +10Hz +8Hz +5Hz + + + + +10Hz +8Hz +5Hz + + + +m + + +m + + +m + + +m + + +m + + +m + @@ -5572,9 +5489,12 @@ +0:StopAtFence,1:UseProximitySensor None StopAtFence +UseProximitySensor +All @@ -5645,6 +5565,69 @@ 0.0 1.5 0.1 + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 +Brushed16kHz + +True + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + +PWM enabled while disarmed +PWM disabled while disarmed + + @@ -5850,12 +5833,12 @@ Enabled - + GPS 3D Vel and 2D Pos GPS 2D vel and 2D pos GPS 2D pos -No GPS use optical flow +No GPS @@ -5891,11 +5874,12 @@ 10 msec - + Use Baro Use Range Finder Use GPS +Use Range Beacon @@ -6041,68 +6025,49 @@ 0.00001 0.01 gauss/s + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +msec + 0 32766 1 - + Resume Mission Restart Mission - - - -Disabled -AnalogPin -RCChannelPwmValue - - - - -APM2 A0 -APM2 A1 -APM2 A13 -Pixracer -Pixhawk ADC4 -Pixhawk ADC3 - Pixhawk ADC6 -Pixhawk SBUS - - - -0 5.0 -0.01 -Volt - - -0 5.0 -0.01 -Volt - - - - - -0 2000 -Microseconds - - -0 2000 -Microseconds - - None Analog -APM2-MaxbotixI2C -APM2-PulsedLightI2C +MaxbotixI2C +PulsedLightI2C PX4-I2C PX4-PWM BBB-PRU @@ -6110,6 +6075,9 @@ LightWareSerial Bebop MAVLink +uLanding +LeddarOne +MaxbotixSerial @@ -6189,12 +6157,25 @@ 1 centimeters + +0 127 +1 + + +m + + +m + + +m + None Analog -APM2-MaxbotixI2C -APM2-PulsedLightI2C +MaxbotixI2C +PulsedLightI2C PX4-I2C PX4-PWM BBB-PRU @@ -6202,6 +6183,9 @@ LightWareSerial Bebop MAVLink +uLanding +LeddarOne +MaxbotixSerial @@ -6277,15 +6261,20 @@ 1 centimeters - + 0 127 1 - -0 127 -1 + +m - + +m + + +m + + None Analog @@ -6298,9 +6287,12 @@ LightWareSerial Bebop MAVLink +uLanding +LeddarOne +MaxbotixSerial - + Not Used APM2-A0 @@ -6318,30 +6310,30 @@ APM1-airspeed port - -meters/Volt + 0.001 +meters/Volt - -Volts + 0.001 +Volts - + Linear Inverted Hyperbolic - -centimeters + 1 - - centimeters + + 1 +centimeters - + Not Used Pixhawk AUXOUT1 @@ -6358,26 +6350,35 @@ PX4IO ACC2 - -milliseconds + 1 +milliseconds - + No Yes - + 0 127 1 centimeters - + 0 127 1 - + +m + + +m + + +m + + None Analog @@ -6390,9 +6391,12 @@ LightWareSerial Bebop MAVLink +uLanding +LeddarOne +MaxbotixSerial - + Not Used APM2-A0 @@ -6410,30 +6414,30 @@ APM1-airspeed port - -meters/Volt + 0.001 +meters/Volt - -Volts + 0.001 +Volts - + Linear Inverted Hyperbolic - -centimeters + 1 - - centimeters + + 1 +centimeters - + Not Used Pixhawk AUXOUT1 @@ -6450,36 +6454,45 @@ PX4IO ACC2 - -milliseconds + 1 +milliseconds - + No Yes - + 0 127 1 centimeters - + 0 127 1 + +m + + +m + + +m + - + Disable Enable - -meters + 1 +meters @@ -6501,126 +6514,167 @@ -18000 +18000 1 - - - - -Disabled -Enabled Always Land -Enabled Strict - + +m - - -None -CompanionComputer -IRLock - + +m + + +m + + +0 255 - + None PX4-PWM - + 0.001 - + 1 - + 1 - + 0.1 - + None PX4-PWM - + 0.001 - - + + -Disabled -Enabled +Off +Low +Medium +High - + -None -Loiter -LoiterAndDescend +Disable +Enable - -1 100 - - -1 100000 + + +Disable +Enable + - --1 16777215 + + + + +None +LightWareSF40C + - + -NoInfo -Light -Small -Large -HighVortexlarge -Heavy -HighlyManuv -Rotocraft -RESERVED -Glider -LightAir -Parachute -UltraLight -RESERVED -UAV -Space -RESERVED -EmergencySurface -ServiceSurface -PointObstacle +Default +Upside Down - + +-180 180 - + +0 360 - + +0 45 - - - + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + +0 360 + + +0 45 + + -Off -Low -Medium -High +None +LightWareSF40C - + -Disable -Enable +Default +Upside Down - + +-180 180 + + + + -Disable -Enable +Disabled +Enabled + + +None +Servo +EPM + + + +1000 2000 + + +1000 2000 + + +1000 2000 + + +0 255 +seconds + + +0 255 + diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.5.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.5.xml new file mode 100644 index 000000000..2ee76518b --- /dev/null +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Sub.3.5.xml @@ -0,0 +1,7683 @@ + + + + + + +-100 0 + + +True + + +True + +ArduPlane +AntennaTracker +Copter +Rover +ArduSub + + + +1 255 + + + +Mission Planner and DroidPlanner + AP Planner 2 + + + + +Disabled +Enabled + + + +0 10 +.5 +Hz + + +0:Roll,1:Pitch,2:Yaw + +None +Roll +Pitch +Yaw + + + +0.01 2.0 +0.01 + + + +Disabled + + + +0.1 +Volts + + +50 +mAh + + + +Disabled +Warn only +Disarm +Enter depth hold mode +Enter surface mode + + + + +Disabled +Warn only +Enter surface mode + + + + +Disabled +Warn only + + + + +Disabled +Warn only + + + +Pascal + + +Degrees Centigrade + + + +Disarm +Hold Position +Surface + + + +10 90 + + +100 900 + + + +Disabled +Enabled + + + + +Never change yaw +Face next waypoint +Face next waypoint except RTL +Face along GPS course + + + +50 500 +10 +Centimeters/Second + + +50 500 +10 +cm/s/s + + +0 300 +1 +pwm + + + +Stabilize +DepthHold +Manual + + + + +Stabilize +DepthHold +Manual + + + + +Stabilize +DepthHold +Manual + + + + +Stabilize +DepthHold +Manual + + + + +Stabilize +DepthHold +Manual + + + + +Stabilize +DepthHold +Manual + + + +0:ATTITUDE_FAST,1:ATTITUDE_MED,2:GPS,3:PM,4:CTUN,5:NTUN,6:RCIN,7:IMU,8:CMD,9:CURRENT,10:RCOUT,11:OPTFLOW,12:PID,13:COMPASS,14:INAV,15:CAMERA,17:MOTBATT,18:IMU_FAST,19:IMU_RAW + +Default +Default+RCIN +Default+IMU +Default+Motors +NearlyAll-AC315 +NearlyAll +All+FastATT +All+MotBatt +All+FastIMU +All+FastIMU+PID +All+FullIMU +Disabled + + + + +Normal Start-up +Start-up in ESC Calibration mode if throttle high +Start-up in ESC Calibration mode regardless of throttle +Disabled + + + + +None +Stab Roll/Pitch kP +Rate Roll/Pitch kP +Rate Roll/Pitch kI +Rate Roll/Pitch kD +Stab Yaw kP +Rate Yaw kP +Rate Yaw kD +Altitude Hold kP +Throttle Rate kP +Throttle Accel kP +Throttle Accel kI +Throttle Accel kD +Loiter Speed +Loiter Pos kP +Velocity XY kP +Velocity XY kI +WP Speed +Acro RollPitch kP +Acro Yaw kP +Heli Ext Gyro +OF Loiter kP +OF Loiter kI +OF Loiter kD +Declination +Circle Rate +RangeFinder Gain +Rate Pitch kP +Rate Pitch kI +Rate Pitch kD +Rate Roll kP +Rate Roll kI +Rate Roll kD +Rate Pitch FF +Rate Roll FF +Rate Yaw FF + + + +0 32767 + + +0 32767 + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + + +Do Nothing +Flip +Simple Mode +RTL +Save Trim +Save WP +Camera Trigger +RangeFinder +Fence +ResetToArmedYaw +Super Simple Mode +Acro Trainer +Auto +AutoTune +Land +Gripper +Parachute Enable +Parachute Release +Parachute 3pos +Auto Mission Reset +AttCon Feed Forward +AttCon Accel Limits +Retract Mount +Relay On/Off +Relay2 On/Off +Relay3 On/Off +Relay4 On/Off +Landing Gear +Lost vehicle Sound +Motor Emergency Stop +Motor Interlock +Brake +Throw + + + +0:All,1:Baro,2:Compass,3:GPS,4:INS,5:Parameters+Rangefinder,6:RC,7:Voltage + +Disabled +Enabled +Skip Baro +Skip Compass +Skip GPS +Skip INS +Skip Params/Rangefinder +Skip RC +Skip Voltage + + + +0 127 +Seconds + + +1000 8000 +Centi-degrees + + +0 100 + +Very Soft +Soft +Medium +Crisp +Very Crisp + +10 + + + +Disabled + + + +0.6:Strict, 0.8:Default, 1.0:Relaxed + + + +Disabled + + + +0.1 1.0 + + +0.2 1.0 + + +0.1 0.8 + + +1 10 + + +30 400 + + +30 400 + + +0.5 4.0 + + +1000 2000 + + + +BlueROV1 +Vectored +Vectored_6DOF +Vectored_6DOF_90 +SimpleROV-3 +SimpleROV-4 +SimpleROV-5 +Custom + +True + + +50 490 +1 +Hz + + +1 10 + + +1 10 + + +0 3 +0.1 + + +0 3 +0.1 + + + +Disabled +Leveling +Leveling and Limited + + + + +Disabled +Very Low +Low +Medium +High +Very High + + + +0.1 6.0 +0.1 + + +0.02 1.00 +0.01 + + +0 4500 +10 +cm/s/s + + +0 4500 +10 +cm/s/s + + +1.000 8.000 + + +0.500 1.500 +0.05 + + +0.000 3.000 + + +0 1000 +Percent*10 + + +0.000 0.400 + + +1.000 100.000 +Hz + + +1.000 3.000 + + +0.500 2.000 + + +0:Roll,1:Pitch,2:Yaw + +All +Roll Only +Pitch Only +Yaw Only +Roll and Pitch +Roll and Yaw +Pitch and Yaw + + + +0.05 0.10 + + +0.001 0.006 + + + +Do Not Use in RTL and Land +Use in RTL and Land + + + +0 5 + + + + +0.0 1.0 +.01 + + + +Disabled +Enabled + + + +0.1 0.4 +.01 + + +0.1 0.4 +.01 + + +0 127 +1 +m/s + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + +-0.1745 +0.1745 +0.01 +Radians + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 + + + +0.001 0.5 +.01 + + +0 10 +1 + + + +Disabled +Enable EKF2 +Enable EKF3 + + + + + +500 18000 +100 +Centi-Degrees/Sec + + +0 72000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + +0 180000 + +Disabled +Slow +Medium +Fast + +1000 +Centi-Degrees/Sec/Sec + + + +Disabled +Enabled + + + +3.000 12.000 + + +3.000 12.000 + + +3.000 6.000 + + +0.5 10.0 + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.08 0.30 +0.005 + + +0.01 0.5 +0.01 + + +0 1 +0.01 +Percent + + +0.0 0.02 +0.001 + + +1 100 +1 +Hz + + +0.10 0.50 +0.005 + + +0.010 0.05 +0.01 + + +0 1 +0.01 +Percent + + +0.000 0.02 +0.001 + + +1 100 +1 +Hz + + +0.1 0.25 + + +0.5 0.9 + + +0.5 0.9 + + + + +0:StopAtFence,1:UseProximitySensor + +None +StopAtFence +UseProximitySensor +All + + + +0 4500 + + +3 30 +meters + + + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Watts + + + +Disabled +Analog Voltage Only +Analog Voltage and Current +Solo +Bebop +SMBus-Maxell + + + + +Disabled +A0 +A1 +Pixhawk +A13 +PX4 + + + + +Disabled +A1 +A2 +Pixhawk +A12 +PX4 + + + + + +Amps/Volt + + +Volts + + +50 +mAh + + +1 +Amps + + + + + +No PWMs +Two PWMs +Four PWMs +Six PWMs +Three PWMs and One Capture + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled +Auto + +True + + + +Disabled +Enabled + +True + + + +Disabled +50Hz +75Hz +100Hz +150Hz +200Hz +250Hz +300Hz + +True + + +-32767 32768 + + + +Disabled +Enabled +Dynamic ID/Update + + + +0:Ch1,1:Ch2,2:Ch3,3:Ch4,4:Ch5,5:Ch6,6:Ch7,7:Ch8,8:Ch9,9:Ch10,10:Ch11,11:Ch12,12:Ch13,13:Ch14 + +Disabled +Enabled + +True + + +-1 80 +degreesC + + + +AUTO +PX4V1 +Pixhawk +Pixhawk2 +Pixracer +PixhawkMini +Pixhawk2Slim +VRBrain 5.1 +VRBrain 5.2 +VR Micro Brain 5.1 +VR Micro Brain 5.2 +VRBrain Core 1.0 +VRBrain 5.4 + +True + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + +Disabled +shift +arm_toggle +arm +disarm +mode_toggle +enter_mode_1 +enter_mode_2 +enter_mode_3 +enter_mode_4 +enter_mode_5 +enter_mode_6 +mount_center +mount_tilt_up +mount_tilt_down +camera_trigger +camera_source_toggle +mount_pan_right +mount_pan_left +lights1_cycle +lights1_brighter +lights1_dimmer +lights2_cycle +lights2_brighter +lights2_dimmer +gain_toggle +gain_inc +gain_dec +trim_roll_inc +trim_roll_dec +trim_pitch_inc +trim_pitch_dec +input_hold_toggle +relay_1_on +relay_1_off +relay_1_toggle +relay_2_on +relay_2_off +relay_2_toggle +custom_1 +custom_2 +custom_3 +custom_4 +custom_5 +custom_6 + + + + + + +Servo +Relay + + + +0 50 +deciseconds + + +1000 2000 +pwm + + +1000 2000 +pwm + + +0 1000 +meters + + + +Low +High + + + +0 10000 +milliseconds + + +0 180 +Degrees + + + +Disabled +PX4 AUX1 +PX4 AUX2 +PX4 AUX3 +PX4 AUX4(fast capture) +PX4 AUX5 +PX4 AUX6 + + + + +TriggerLow +TriggerHigh + + + + + +0 10000 +100 +cm + + +-90 90 +1 +deg/s + + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-3.142 3.142 +0.01 +Radians + + + +Disabled +Internal-Learning +EKF-Learning + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Use Throttle +Use Current + + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + +FirstCompass +SecondCompass +ThirdCompass + + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-400 400 +1 +milligauss + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + +-1000 1000 +1 +Offset per Amp or at Full Throttle + + + + + + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + +Disabled +Enabled + + + + +None +Yaw45 +Yaw90 +Yaw135 +Yaw180 +Yaw225 +Yaw270 +Yaw315 +Roll180 +Roll180Yaw45 +Roll180Yaw90 +Roll180Yaw135 +Pitch180 +Roll180Yaw225 +Roll180Yaw270 +Roll180Yaw315 +Roll90 +Roll90Yaw45 +Roll90Yaw90 +Roll90Yaw135 +Roll270 +Roll270Yaw45 +Roll270Yaw90 +Roll270Yaw136 +Pitch90 +Pitch270 +Pitch180Yaw90 +Pitch180Yaw270 +Roll90Pitch90 +Roll180Pitch90 +Roll270Pitch90 +Roll90Pitch180 +Roll270Pitch180 +Roll90Pitch270 +Roll180Pitch270 +Roll270Pitch270 +Roll90Pitch180Yaw90 +Roll90Yaw270 +Yaw293Pitch68Roll90 + + + + +Internal +External +ForcedExternal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 32 + +Very Strict +Strict +Default +Relaxed + +0.1 + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + +0 250 +10 +milliseconds + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.000001 0.001 +1/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds + + +2.0 6.0 +0.5 +m/s + + +0:FirstEKF,1:SecondEKF,2:ThirdEKF,3:FourthEKF,4:FifthEKF,5:SixthEKF + + + + + +Disabled +Enabled + + + + +GPS 3D Vel and 2D Pos +GPS 2D vel and 2D pos +GPS 2D pos +No GPS + + + +0.05 5.0 +0.05 +m/s + + +0.05 5.0 +0.05 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +10 100 +5 +m + + + +Use Baro +Use Range Finder +Use GPS +Use Range Beacon + + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.01 0.5 +0.01 +gauss + + + +When flying +When manoeuvring +Never +After first climb yaw reset +Always + + + +100 1000 +25 + + +0.5 5.0 +0.1 +m/s + + +100 1000 +25 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +1.0 4.0 +0.1 +rad/s + + +0.05 1.0 +0.05 +rad/s + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +0.0001 0.1 +0.0001 +rad/s + + +0.01 1.0 +0.01 +m/s/s + + +0.00001 0.001 +rad/s/s + + +0.00001 0.001 +m/s/s/s + + +0.01 1.0 +0.1 +m/s/s + + +0.0 1.0 +0.1 + + +0:NSats,1:HDoP,2:speed error,3:horiz pos error,4:yaw error,5:pos drift,6:vert speed,7:horiz speed + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +50 200 +% + + +0.5 50.0 +m + + +0:FirstIMU,1:SecondIMU,2:ThirdIMU,3:FourthIMU,4:FifthIMU,5:SixthIMU + + +0.05 1.0 +0.05 +rad + + +100 1000 +25 + + +10 50 +5 +cs + + +0.00001 0.01 +gauss/s + + +0.00001 0.01 +gauss/s + + +-1 70 +1 +% + + +0 0.2 +0.01 + + +0.1 10.0 +0.1 +m + + +100 1000 +25 + + +0 250 +10 +milliseconds +True + + +2.0 6.0 +0.5 +m/s + + +0.5 2.5 +0.1 +m/s/s + + +0:FirstEKF,1:SecondEKF,2:ThirdEKF,3:FourthEKF,4:FifthEKF,5:SixthEKF + + + + + +Disabled +Enabled + + + +0:Altitude,1:Circle,2:Polygon + +None +Altitude +Circle +Altitude and Circle +Polygon +Altitude and Polygon +Circle and Polygon +All + + + + +Report Only +RTL or Land + + + +10 1000 +1 +Meters + + +30 10000 +Meters + + +1 10 +Meters + + +1 20 + + +-100 100 +1 +Meters + + + + + +Disabled +Enabled + + + +-200 +200 +1 + + +-200 +200 +1 + + +-18000 +18000 +1 + + +m + + +m + + +m + + +0 255 + + + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +0.1 +meters + + + +FirstBaro +2ndBaro +3rdBaro + + + + +Disabled +Bus0 + + + +1.0:Freshwater,1.024:Saltwater + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + +True +True +1 +pascals + + +True +True +1 +degrees celsius + + + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +None +AUTO +uBlox +MTK +MTK19 +NMEA +SiRF +HIL +SwiftNav +PX4-UAVCAN +SBF +GSOF +QURT +ERB +MAV +NOVA + +True + + + +Portable +Stationary +Pedestrian +Automotive +Sea +Airborne1G +Airborne2G +Airborne4G + + + + +Disabled +Enabled + + + + +Any +FloatRTK +IntegerRTK + +True + + + +Disabled +Enabled +NoChange + + + +-100 90 +Degrees + + + +send to first GPS +send to 2nd GPS +send to all + + + + +None +All +External only + + + + +Disabled +log every sample +log every 5 samples + +True + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Do not save config +Save config +Save only when needed + + + +0:GPS,1:SBAS,2:Galileo,3:Beidou,4:IMES,5:QZSS,6:GLOSNASS + +Leave as currently configured +GPS-NoSBAS +GPS+SBAS +Galileo-NoSBAS +Galileo+SBAS +Beidou +GPS+IMES+QZSS+SBAS (Japan Only) +GLONASS +GLONASS+SBAS +GPS+GLONASS+SBAS + + + + +Disables automatic configuration +Enable automatic configuration + + + + +10Hz +8Hz +5Hz + +milliseconds + + + +10Hz +8Hz +5Hz + +milliseconds + + +m + + +m + + +m + + +m + + +m + + +m + + +0 250 +milliseconds + + +0 250 +milliseconds + + + + + +Disabled +Enabled + + + + +None +Servo +EPM + + + +1000 2000 +PWM + + +1000 2000 +PWM + + +1000 2000 +PWM + + +0 255 +seconds + + +0 255 + + + + + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +rad/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0.8 1.2 + + +0.8 1.2 + + +0.8 1.2 + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +-3.5 3.5 +m/s/s + + +0 127 +Hz + + +0 127 +Hz + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + +0.05 50 + + + +Never +Start-up only + + + + +Don't adjust the trims +Assume first orientation was level +Assume ACC_BODYFIX is perfectly aligned to the vehicle + + + + +IMU 1 +IMU 2 +IMU 3 + + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +m + + +True + + +True + + +True + + +True + + +True + + +True + + + + + + + +Disabled +Pixhawk Aux1 +Pixhawk Aux2 +Pixhawk Aux3 +Pixhawk Aux4 +Pixhawk Aux5 +Pixhawk Aux6 +Pixhawk 3.3ADC1 +Pixhawk 3.3ADC2 +Pixhawk 6.6ADC + + + + +Low +High + + + + +Disabled +Pixhawk Aux1 +Pixhawk Aux2 +Pixhawk Aux3 +Pixhawk Aux4 +Pixhawk Aux5 +Pixhawk Aux6 +Pixhawk 3.3ADC1 +Pixhawk 3.3ADC2 +Pixhawk 6.6ADC + + + + +Low +High + + + + +Disabled +Pixhawk Aux1 +Pixhawk Aux2 +Pixhawk Aux3 +Pixhawk Aux4 +Pixhawk Aux5 +Pixhawk Aux6 +Pixhawk 3.3ADC1 +Pixhawk 3.3ADC2 +Pixhawk 6.6ADC + + + + +Low +High + + + + + + +None +File +MAVLink +BothFileAndMAVLink + + + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + + +0 32766 +1 + + + +Resume Mission +Restart Mission + + + + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0 100 +1 + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + +True + + + +Retracted +Neutral +MavLink Targeting +RC Targeting +GPS Point + + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + +-180.00 179.99 +1 +Degrees + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +Enabled + + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + + +Disabled +RC5 +RC6 +RC7 +RC8 +RC9 +RC10 +RC11 +RC12 + + + +-18000 17999 +1 +Centi-Degrees + + +-18000 17999 +1 +Centi-Degrees + + +0.0 0.2 +.005 +Seconds + + +0.0 0.2 +.005 +Seconds + + + +None +Servo +3DR Solo +Alexmos Serial +SToRM32 MAVLink +SToRM32 Serial + + + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + + +normal +reverse + + + +0.0 1.5 +0.1 + + +0 500 +pwm + + +0.25 0.8 + + +0.9:Low, 0.95:Default, 1.0:High + + +6 35 +Volts + + +6 35 +Volts + + +0 200 +Amps + + + +Normal +OneShot +OneShot125 +Brushed16kHz + +True + + +0 2000 + + +0 2000 + + +0.0:Low, 0.15:Default, 0.3:High + + +0.0:Low, 0.1:Default, 0.2:High + + +0 10 +Seconds + + +0.2 0.8 + + + +Disabled +Learn +LearnAndSave + + + + +PWM enabled while disarmed +PWM disabled while disarmed + + + +5 80 +1 +Degrees + + +0 2 +0.1 +Seconds + + + + + +Off +Low +Medium +High + + + + +Disable +Enable + + + + +Disable +Enable + + + + +Disable +ssd1306 +sh1106 + + + + + + +None +LightWareSF40C +MAVLink +TeraRangerTower + + + + +Default +Upside Down + + + +-180 180 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + +0 360 +degrees + + +0 45 +degrees + + + +None +LightWareSF40C +MAVLink + + + + +Default +Upside Down + + + +-180 180 +degrees + + + + +0.5 5 +0.1 +Hz + + + + + + +0.1 +kilometers + + + +DoNotIncludeHome +IncludeHome + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + +0 200 +pwm + + + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + +1 8 +1 +True + + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Disabled +APM2 A9 pin +APM1 relay +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + + +Off +On +NoChange + + + + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 32767 +meters + + +5 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +MaxbotixI2C +PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial +TrOneI2C + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + +None +Analog +APM2-MaxbotixI2C +APM2-PulsedLightI2C +PX4-PWM +BBB-PRU +LightWareI2C +LightWareSerial +Bebop +MAVLink +uLanding +LeddarOne +MaxbotixSerial + + + + +Not Used +APM2-A0 +APM2-A1 +APM2-A2 +APM2-A3 +APM2-A4 +APM2-A5 +APM2-A6 +APM2-A7 +APM2-A8 +APM2-A9 +PX4-airspeed port +Pixhawk-airspeed port +APM1-airspeed port + + + +0.001 +meters/Volt + + +0.001 +Volts + + + +Linear +Inverted +Hyperbolic + + + +1 +centimeters + + +1 +centimeters + + + +Not Used +Pixhawk AUXOUT1 +Pixhawk AUXOUT2 +Pixhawk AUXOUT3 +Pixhawk AUXOUT4 +Pixhawk AUXOUT5 +Pixhawk AUXOUT6 +PX4 FMU Relay1 +PX4 FMU Relay2 +PX4IO Relay1 +PX4IO Relay2 +PX4IO ACC1 +PX4IO ACC2 + + + +1 +milliseconds + + + +No +Yes + + + +0 127 +1 +centimeters + + +0 127 +1 + + +m + + +m + + +m + + + + + +None +PX4-PWM + + + +0.001 + + +1 + + +1 + + +0.1 + + + +None +PX4-PWM + + + +0.001 + + + + + +Disabled +ShowSlips +ShowOverruns + + + + +50Hz +100Hz +200Hz +250Hz +300Hz +400Hz + +True + + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +MAVlink1 +MAVLink2 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + +None +MAVLink1 +MAVLink2 +Frsky D +Frsky SPort +GPS +Alexmos Gimbal Serial +SToRM32 Gimbal Serial +Lidar +FrSky SPort Passthrough (OpenTX) +Lidar360 +Aerotenna uLanding +Pozyx Beacon + + + + +1200 +2400 +4800 +9600 +19200 +38400 +57600 +111100 +115200 +500000 +921600 +1500000 + + + + + + +Disable +Enable + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + +800 2200 +1 +pwm + + + +Normal +Reversed + + + + +Disabled +RCPassThru +Flap +Flap_auto +Aileron +mount_pan +mount_tilt +mount_roll +mount_open +camera_trigger +release +mount2_pan +mount2_tilt +mount2_roll +mount2_open +DifferentialSpoiler1 +DifferentialSpoiler2 +AileronWithInput +Elevator +ElevatorWithInput +Rudder +Flaperon1 +Flaperon2 +GroundSteering +Parachute +EPM +LandingGear +EngineRunEnable +HeliRSC +HeliTailRSC +Motor1 +Motor2 +Motor3 +Motor4 +Motor5 +Motor6 +Motor7 +Motor8 +RCIN1 +RCIN2 +RCIN3 +RCIN4 +RCIN5 +RCIN6 +RCIN7 +RCIN8 +RCIN9 +RCIN10 +RCIN11 +RCIN12 +RCIN13 +RCIN14 +RCIN15 +RCIN16 +Ignition +Choke +Starter +Throttle +TrackerYaw +TrackerPitch +ThrottleLeft +ThrottleRight + + + + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + +0 10 +1 +Hz + + + + + +Disable +Enable + + + +1 +meters + + + + +20 2000 +50 +cm/s + + +100 1000 +1 +cm + + +0 1000 +50 +cm/s + + +0 500 +10 +cm/s + + +0 2000 +50 +cm/s + + +50 500 +10 +cm/s/s + + +50 500 +10 +cm/s/s + + +500 5000 +1 +cm/s/s/s + + +100 981 +1 +cm/s/s + + +25 250 +1 +cm/s/s + + + +Disable +Enable + + + + diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc index 941a2a14a..82e2bfc89 100644 --- a/src/FirmwarePlugin/APM/APMResources.qrc +++ b/src/FirmwarePlugin/APM/APMResources.qrc @@ -50,6 +50,7 @@ APMParameterFactMetaData.Rover.3.0.xml APMParameterFactMetaData.Rover.3.2.xml APMParameterFactMetaData.Sub.3.4.xml + APMParameterFactMetaData.Sub.3.5.xml CopterGeoFenceEditor.qml PlaneGeoFenceEditor.qml Copter3.4.OfflineEditing.params diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc index e6dd01b74..aca9380ad 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc @@ -25,8 +25,9 @@ /// @author Rustom Jehangir #include "ArduSubFirmwarePlugin.h" -#include "QGCApplication.h" -#include "MissionManager.h" + +bool ArduSubFirmwarePlugin::_remapParamNameIntialized = false; +FirmwarePlugin::remapParamNameMajorVersionMap_t ArduSubFirmwarePlugin::_remapParamName; APMSubMode::APMSubMode(uint32_t mode, bool settable) : APMCustomMode(mode, settable) @@ -46,6 +47,29 @@ ArduSubFirmwarePlugin::ArduSubFirmwarePlugin(void) supportedFlightModes << APMSubMode(APMSubMode::STABILIZE ,true); supportedFlightModes << APMSubMode(APMSubMode::ALT_HOLD ,true); setSupportedModes(supportedFlightModes); + + if (!_remapParamNameIntialized) { + FirmwarePlugin::remapParamNameMap_t& remapV3_5 = _remapParamName[3][5]; + + remapV3_5["SERVO5_FUNCTION"] = QStringLiteral("RC5_FUNCTION"); + remapV3_5["SERVO6_FUNCTION"] = QStringLiteral("RC6_FUNCTION"); + remapV3_5["SERVO7_FUNCTION"] = QStringLiteral("RC7_FUNCTION"); + remapV3_5["SERVO8_FUNCTION"] = QStringLiteral("RC8_FUNCTION"); + remapV3_5["SERVO9_FUNCTION"] = QStringLiteral("RC9_FUNCTION"); + remapV3_5["SERVO10_FUNCTION"] = QStringLiteral("RC10_FUNCTION"); + remapV3_5["SERVO11_FUNCTION"] = QStringLiteral("RC11_FUNCTION"); + remapV3_5["SERVO12_FUNCTION"] = QStringLiteral("RC12_FUNCTION"); + remapV3_5["SERVO13_FUNCTION"] = QStringLiteral("RC13_FUNCTION"); + remapV3_5["SERVO14_FUNCTION"] = QStringLiteral("RC14_FUNCTION"); + + _remapParamNameIntialized = true; + } +} + +int ArduSubFirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const +{ + // Remapping supports up to 3.5 + return majorVersionNumber == 3 ? 5 : Vehicle::versionNotSetValue; } int ArduSubFirmwarePlugin::manualControlReservedButtonCount(void) diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index d9911f703..491c04547 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -80,6 +80,12 @@ public: bool supportsJSButton(void); QString brandImage(const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImageSub"); } + const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } + int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const final; + +private: + static bool _remapParamNameIntialized; + static FirmwarePlugin::remapParamNameMajorVersionMap_t _remapParamName; }; #endif -- GitLab From 89c2345bad09467bef9e0b71e8c5accc42284a84 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 15:37:37 -0800 Subject: [PATCH 334/398] More work on FixedWing Landing Pattern --- qgroundcontrol.qrc | 1 + src/MissionEditor/FWLandingPatternEditor.qml | 20 ++- .../FWLandingPatternMapVisual.qml | 64 ++++++++ .../FWLandingPattern.FactMetaData.json | 38 +++++ .../FixedWingLandingComplexItem.cc | 147 ++++++++++++------ .../FixedWingLandingComplexItem.h | 28 +++- src/MissionManager/MissionController.cc | 4 +- 7 files changed, 249 insertions(+), 53 deletions(-) create mode 100644 src/MissionEditor/FWLandingPatternMapVisual.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 1f4bb7924..c61200b5b 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -56,6 +56,7 @@ src/QmlControls/FactSliderPanel.qml src/QmlControls/FlightModeDropdown.qml src/QmlControls/FlightModeMenu.qml + src/MissionEditor/FWLandingPatternMapVisual.qml src/QmlControls/GuidedBar.qml src/QmlControls/IndicatorButton.qml src/QmlControls/JoystickThumbPad.qml diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml index e30049ade..6d82d6a46 100644 --- a/src/MissionEditor/FWLandingPatternEditor.qml +++ b/src/MissionEditor/FWLandingPatternEditor.qml @@ -31,11 +31,25 @@ Rectangle { //property real availableWidth ///< Width for control //property var missionItem ///< Mission Item for editor - property real _margin: ScreenTools.defaultFontPixelWidth * 0.25 + property real _margin: ScreenTools.defaultFontPixelWidth * 0.25 Column { - id: editorColumn + id: editorColumn + anchors.margins: _margin + anchors.left: parent.left + anchors.right: parent.right - QGCLabel { text: "WIP" } + QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" } + + FactTextFieldGrid { + anchors.left: parent.left + anchors.right: parent.right + factList: missionItem.textFieldFacts + } + + FactCheckBox { + text: missionItem.loiterClockwise.name + fact: missionItem.loiterClockwise + } } } diff --git a/src/MissionEditor/FWLandingPatternMapVisual.qml b/src/MissionEditor/FWLandingPatternMapVisual.qml new file mode 100644 index 000000000..5a6adc4f0 --- /dev/null +++ b/src/MissionEditor/FWLandingPatternMapVisual.qml @@ -0,0 +1,64 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtLocation 5.3 +import QtPositioning 5.2 + +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 + +/// Fixed Wing Landing Pattern map visuals +Item { + property var map ///< Map control to place item in + + property var _loiterPoint + property var _flightPath + + Component.onCompleted: { + _flightPath = flightPathComponent.createObject(map) + _loiterPoint = loiterComponent.createObject(map) + map.addMapItem(_flightPath) + map.addMapItem(_loiterPoint) + } + + Component.onDestruction: { + _loiterPoint.destroy() + _flightPath.destroy() + } + + // Flight path + Component { + id: flightPathComponent + + MapPolyline { + line.color: "white" + line.width: 2 + path: [ object.loiterCoordinate, object.exitCoordinate ] + } + } + + // Loiter point + Component { + id: loiterComponent + + MapQuickItem { + anchorPoint.x: sourceItem.width / 2 + anchorPoint.y: sourceItem.height / 2 + coordinate: object.loiterCoordinate + + sourceItem: + MissionItemIndexLabel { + label: "L" + } + } + } +} diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json index 0d4f101c7..7ceddbaa2 100644 --- a/src/MissionManager/FWLandingPattern.FactMetaData.json +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -1,2 +1,40 @@ [ +{ + "name": "Landing distance", + "shortDescription": "Distance between landing and loiter points.", + "type": "double", + "units": "m", + "decimalPlaces": 1, + "defaultValue": 100.0 +}, +{ + "name": "Landing heading", + "shortDescription": "Heading from land point to loiter point.", + "type": "double", + "units": "deg", + "decimalPlaces": 0 +}, +{ + "name": "Loiter altitude", + "shortDescription": "Altitude to loiter prior to landing.", + "type": "double", + "units": "m", + "decimalPlaces": 1, + "defaultValue": 40.0 +}, +{ + "name": "Loiter radius", + "shortDescription": "Loiter radius.", + "type": "double", + "decimalPlaces": 1, + "min": 0.1, + "units": "m", + "defaultValue": 75.0 +}, +{ + "name": "Clockwise loiter", + "shortDescription": "If true, loiter will be clockwise. False, loiter will be counter-clockwise.", + "type": "bool", + "defaultValue": true +} ] diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 55fd5dfb7..9303f653b 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -19,23 +19,57 @@ QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "FixedWingLandingComplexIte const char* FixedWingLandingComplexItem::jsonComplexItemTypeValue = "fwLandingPattern"; +const char* FixedWingLandingComplexItem::_loiterToLandDistanceName = "Landing distance"; +const char* FixedWingLandingComplexItem::_landingHeadingName = "Landing heading"; +const char* FixedWingLandingComplexItem::_loiterAltitudeName = "Loiter altitude"; +const char* FixedWingLandingComplexItem::_loiterRadiusName = "Loiter radius"; +const char* FixedWingLandingComplexItem::_loiterClockwiseName = "Clockwise loiter"; + QMap FixedWingLandingComplexItem::_metaDataMap; -FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent) +FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent) : ComplexMissionItem(vehicle, parent) , _sequenceNumber(0) , _dirty(false) + , _loiterToLandDistanceFact (0, _loiterToLandDistanceName, FactMetaData::valueTypeDouble) + , _loiterAltitudeFact (0, _loiterAltitudeName, FactMetaData::valueTypeDouble) + , _loiterRadiusFact (0, _loiterRadiusName, FactMetaData::valueTypeDouble) + , _loiterClockwiseFact (0, _loiterClockwiseName, FactMetaData::valueTypeBool) + , _landingHeadingFact (0, _landingHeadingName, FactMetaData::valueTypeDouble) { _editorQml = "qrc:/qml/FWLandingPatternEditor.qml"; if (_metaDataMap.isEmpty()) { _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/FWLandingPattern.FactMetaData.json"), NULL /* metaDataParent */); } + + _loiterToLandDistanceFact.setMetaData (_metaDataMap[_loiterToLandDistanceName]); + _loiterAltitudeFact.setMetaData (_metaDataMap[_loiterAltitudeName]); + _loiterRadiusFact.setMetaData (_metaDataMap[_loiterRadiusName]); + _loiterClockwiseFact.setMetaData (_metaDataMap[_loiterClockwiseName]); + _landingHeadingFact.setMetaData (_metaDataMap[_landingHeadingName]); + + _loiterToLandDistanceFact.setRawValue (_loiterToLandDistanceFact.rawDefaultValue()); + _loiterAltitudeFact.setRawValue (_loiterAltitudeFact.rawDefaultValue()); + _loiterRadiusFact.setRawValue (_loiterRadiusFact.rawDefaultValue()); + _loiterClockwiseFact.setRawValue (_loiterClockwiseFact.rawDefaultValue()); + _landingHeadingFact.setRawValue (_landingHeadingFact.rawDefaultValue()); + + connect(&_loiterToLandDistanceFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition); + connect(&_landingHeadingFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition); + + _textFieldFacts << QVariant::fromValue(&_loiterToLandDistanceFact) << QVariant::fromValue(&_loiterAltitudeFact) << QVariant::fromValue(&_loiterRadiusFact) << QVariant::fromValue(&_landingHeadingFact); + + // Exit coordinate is our land point + _exitCoordinate = mapClickCoordinate; + + _recalcLoiterPosition(); } int FixedWingLandingComplexItem::lastSequenceNumber(void) const { - return _sequenceNumber; + // land start, loiter, land + return _sequenceNumber + 2; } void FixedWingLandingComplexItem::setCoordinate(const QGeoCoordinate& coordinate) @@ -117,54 +151,46 @@ bool FixedWingLandingComplexItem::specifiesCoordinate(void) const QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const { - // FIXME: Need real implementation QmlObjectListModel* pMissionItems = new QmlObjectListModel; -#if 0 int seqNum = _sequenceNumber; - for (int i=0; i<_gridPoints.count(); i++) { - QGeoCoordinate coord = _gridPoints[i].value(); - double altitude = _gridAltitudeFact.rawValue().toDouble(); - - MissionItem* item = new MissionItem(seqNum++, // sequence number - MAV_CMD_NAV_WAYPOINT, // MAV_CMD - _gridAltitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, // MAV_FRAME - 0.0, 0.0, 0.0, 0.0, // param 1-4 - coord.latitude(), - coord.longitude(), - altitude, - true, // autoContinue - false, // isCurrentItem - pMissionItems); // parent - allow delete on pMissionItems to delete everthing - pMissionItems->append(item); - - if (_cameraTrigger && i == 0) { - // Turn on camera - MissionItem* item = new MissionItem(seqNum++, // sequence number - MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD - MAV_FRAME_MISSION, // MAV_FRAME - _cameraTriggerDistanceFact.rawValue().toDouble(), // trigger distance - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7 - true, // autoContinue - false, // isCurrentItem - pMissionItems); // parent - allow delete on pMissionItems to delete everthing - pMissionItems->append(item); - } - } - if (_cameraTrigger) { - // Turn off camera - MissionItem* item = new MissionItem(seqNum++, // sequence number - MAV_CMD_DO_SET_CAM_TRIGG_DIST, // MAV_CMD - MAV_FRAME_MISSION, // MAV_FRAME - 0.0, // trigger distance - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 2-7 - true, // autoContinue - false, // isCurrentItem - pMissionItems); // parent - allow delete on pMissionItems to delete everthing - pMissionItems->append(item); - } -#endif + MissionItem* item = new MissionItem(seqNum++, // sequence number + MAV_CMD_DO_LAND_START, // MAV_CMD + MAV_FRAME_GLOBAL_RELATIVE_ALT, // MAV_FRAME + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 1-7 + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); + + float loiterRadius = _loiterRadiusFact.rawValue().toDouble() * (_loiterClockwiseFact.rawValue().toBool() ? 1.0 : -1.0); + item = new MissionItem(seqNum++, + MAV_CMD_NAV_LOITER_TO_ALT, + MAV_FRAME_GLOBAL_RELATIVE_ALT, + 1.0, // Heading required = true + loiterRadius, // Loiter radius + 0.0, // param 3 - unused + 0.0, // Exit crosstrack - center of waypoint + _loiterCoordinate.latitude(), + _loiterCoordinate.longitude(), + _loiterCoordinate.altitude(), + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); + + item = new MissionItem(seqNum++, + MAV_CMD_NAV_LAND, + MAV_FRAME_GLOBAL_RELATIVE_ALT, + 0.0, 0.0, 0.0, 0.0, // param 1-4 + _exitCoordinate.latitude(), + _exitCoordinate.longitude(), + 0.0, // altitude + true, // autoContinue + false, // isCurrentItem + pMissionItems); // parent - allow delete on pMissionItems to delete everthing + pMissionItems->append(item); return pMissionItems; } @@ -180,3 +206,32 @@ void FixedWingLandingComplexItem::setCruiseSpeed(double cruiseSpeed) // FIXME: Need real implementation Q_UNUSED(cruiseSpeed); } + +void FixedWingLandingComplexItem::_recalcLoiterPosition(void) +{ + double north, east, down; + QGeoCoordinate tangentOrigin = _exitCoordinate; + + convertGeoToNed(_exitCoordinate, tangentOrigin, &north, &east, &down); + + QPointF originPoint(east, north); + north += _loiterToLandDistanceFact.rawValue().toDouble(); + QPointF loiterPoint(east, north); + QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, _landingHeadingFact.rawValue().toDouble()); + + convertNedToGeo(rotatedLoiterPoint.y(), rotatedLoiterPoint.x(), down, tangentOrigin, &_loiterCoordinate); + + emit loiterCoordinateChanged(_loiterCoordinate); + setCoordinate(_loiterCoordinate); +} + +QPointF FixedWingLandingComplexItem::_rotatePoint(const QPointF& point, const QPointF& origin, double angle) +{ + QPointF rotated; + double radians = (M_PI / 180.0) * angle; + + rotated.setX(((point.x() - origin.x()) * cos(radians)) - ((point.y() - origin.y()) * sin(radians)) + origin.x()); + rotated.setY(((point.x() - origin.x()) * sin(radians)) + ((point.y() - origin.y()) * cos(radians)) + origin.y()); + + return rotated; +} diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 26a5c4766..c7485c368 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -22,7 +22,13 @@ class FixedWingLandingComplexItem : public ComplexMissionItem Q_OBJECT public: - FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent = NULL); + FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent = NULL); + + Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT) + Q_PROPERTY(Fact* loiterClockwise READ loiterClockwise CONSTANT) + Q_PROPERTY(QGeoCoordinate loiterCoordinate MEMBER _loiterCoordinate NOTIFY loiterCoordinateChanged) + + Fact* loiterClockwise(void) { return &_loiterClockwiseFact; } // Overrides from ComplexMissionItem @@ -32,7 +38,7 @@ public: bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; double greatestDistanceTo (const QGeoCoordinate &other) const final; void setCruiseSpeed (double cruiseSpeed) final; - QString mapVisualQML (void) const final { return QString(); } + QString mapVisualQML (void) const final { return QStringLiteral("FWLandingPatternMapVisual.qml"); } // Overrides from VisualMissionItem @@ -60,18 +66,36 @@ public: static const char* jsonComplexItemTypeValue; signals: + void loiterCoordinateChanged(QGeoCoordinate coordinate); private slots: + void _recalcLoiterPosition(void); private: void _setExitCoordinate(const QGeoCoordinate& coordinate); + QPointF _rotatePoint(const QPointF& point, const QPointF& origin, double angle); int _sequenceNumber; bool _dirty; QGeoCoordinate _coordinate; QGeoCoordinate _exitCoordinate; + QGeoCoordinate _loiterCoordinate; + + Fact _loiterToLandDistanceFact; + Fact _loiterAltitudeFact; + Fact _loiterRadiusFact; + Fact _loiterClockwiseFact; + Fact _landingHeadingFact; static QMap _metaDataMap; + + QVariantList _textFieldFacts; + + static const char* _loiterToLandDistanceName; + static const char* _loiterAltitudeName; + static const char* _loiterRadiusName; + static const char* _loiterClockwiseName; + static const char* _landingHeadingName; }; #endif diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 2c6d53dc9..f6f949be1 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -226,14 +226,14 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate int sequenceNumber = _nextSequenceNumber(); if (itemName == _surveyMissionItemName) { newItem = new SurveyMissionItem(_activeVehicle, _visualItems); + newItem->setCoordinate(mapCenterCoordinate); } else if (itemName == _fwLandingMissionItemName) { - newItem = new FixedWingLandingComplexItem(_activeVehicle, _visualItems); + newItem = new FixedWingLandingComplexItem(_activeVehicle, mapCenterCoordinate, _visualItems); } else { qWarning() << "Internal error: Unknown complex item:" << itemName; return sequenceNumber; } newItem->setSequenceNumber(sequenceNumber); - newItem->setCoordinate(mapCenterCoordinate); _initVisualItem(newItem); _visualItems->insert(i, newItem); -- GitLab From 248ec0474a8c0f8e0ba2016b50cd0d7384198cd0 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 21 Feb 2017 15:45:13 -0800 Subject: [PATCH 335/398] Add default value --- src/MissionManager/FWLandingPattern.FactMetaData.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json index 7ceddbaa2..d606af684 100644 --- a/src/MissionManager/FWLandingPattern.FactMetaData.json +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -12,7 +12,8 @@ "shortDescription": "Heading from land point to loiter point.", "type": "double", "units": "deg", - "decimalPlaces": 0 + "decimalPlaces": 0, + "defaultValue": 0.0 }, { "name": "Loiter altitude", -- GitLab From fc2c0bbe83339e655dac595bd4b7340bd8cb298b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 22 Feb 2017 10:18:08 -0800 Subject: [PATCH 336/398] Remove deprecated methods These are covered by SettingsGroups now --- src/api/QGCOptions.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index dbe141d4f..c465d5d2b 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -26,8 +26,6 @@ public: Q_PROPERTY(bool combineSettingsAndSetup READ combineSettingsAndSetup CONSTANT) Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) - Q_PROPERTY(bool enableAutoConnectOptions READ enableAutoConnectOptions CONSTANT) - Q_PROPERTY(bool enableVideoSourceOptions READ enableVideoSourceOptions CONSTANT) Q_PROPERTY(bool definesVideo READ definesVideo CONSTANT) Q_PROPERTY(uint16_t videoUDPPort READ videoUDPPort CONSTANT) Q_PROPERTY(QString videoRSTPUrl READ videoRSTPUrl CONSTANT) @@ -47,16 +45,6 @@ public: @return false to disable Virtual Joysticks. */ virtual bool enableVirtualJoystick () { return true; } - //! Should QGC allow setting auto-connect options? - /*! - @return false to disable auto-connect options. - */ - virtual bool enableAutoConnectOptions () { return true; } - //! Should QGC allow setting video source options? - /*! - @return false to disable video source options. - */ - virtual bool enableVideoSourceOptions () { return true; } //! Does your plugin defines its on video source? /*! @return true to define your own video source. -- GitLab From 3387a14d823a6bd00a956744644d24bf4ecf19ed Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 22 Feb 2017 10:49:16 -0800 Subject: [PATCH 337/398] Add support for SettingsFact::visible --- src/FactSystem/SettingsFact.cc | 3 +- src/FactSystem/SettingsFact.h | 3 + src/api/QGCCorePlugin.cc | 16 ++-- src/api/QGCCorePlugin.h | 10 +-- src/ui/preferences/GeneralSettings.qml | 114 ++++++++++--------------- 5 files changed, 64 insertions(+), 82 deletions(-) diff --git a/src/FactSystem/SettingsFact.cc b/src/FactSystem/SettingsFact.cc index 1f673d938..eb4c4fc86 100644 --- a/src/FactSystem/SettingsFact.cc +++ b/src/FactSystem/SettingsFact.cc @@ -23,6 +23,7 @@ SettingsFact::SettingsFact(QObject* parent) SettingsFact::SettingsFact(QString settingGroup, FactMetaData* metaData, QObject* parent) : Fact(0, metaData->name(), metaData->type(), parent) , _settingGroup(settingGroup) + , _visible(true) { QSettings settings; @@ -31,7 +32,7 @@ SettingsFact::SettingsFact(QString settingGroup, FactMetaData* metaData, QObject } // Allow core plugin a chance to override the default value - metaData->setRawDefaultValue(qgcApp()->toolbox()->corePlugin()->overrideSettingsDefault(metaData->name(), metaData->rawDefaultValue())); + _visible = qgcApp()->toolbox()->corePlugin()->adjustSettingMetaData(*metaData); setMetaData(metaData); QVariant typedValue; diff --git a/src/FactSystem/SettingsFact.h b/src/FactSystem/SettingsFact.h index 0984d4764..cb6e8330f 100644 --- a/src/FactSystem/SettingsFact.h +++ b/src/FactSystem/SettingsFact.h @@ -28,11 +28,14 @@ public: const SettingsFact& operator=(const SettingsFact& other); + Q_PROPERTY(bool visible MEMBER _visible CONSTANT) + private slots: void _rawValueChanged(QVariant value); private: QString _settingGroup; + bool _visible; }; #endif diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index aee94cde7..78fa0150d 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -10,6 +10,7 @@ #include "QGCCorePlugin.h" #include "QGCOptions.h" #include "QGCSettings.h" +#include "FactMetaData.h" #include #include @@ -140,13 +141,6 @@ QGCOptions* QGCCorePlugin::options() return _p->defaultOptions; } -QVariant QGCCorePlugin::overrideSettingsDefault(QString name, QVariant defaultValue) -{ - Q_UNUSED(name); - // No overrides for base plugin - return defaultValue; -} - QVariantList& QGCCorePlugin::toolBarIndicators() { if(_p->toolBarIndicatorList.size() == 0) { @@ -163,7 +157,13 @@ QVariantList& QGCCorePlugin::toolBarIndicators() bool QGCCorePlugin::overrideSettingsGroupVisibility(QString name) { Q_UNUSED(name); - + // Always show all return true; } + +bool QGCCorePlugin::adjustSettingMetaData(FactMetaData& metaData) +{ + Q_UNUSED(metaData); // No mods to standard meta data + return true; // Show setting in ui +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 76250aa2e..e9f1a0a9f 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -24,6 +24,7 @@ class QGCApplication; class QGCOptions; class QGCSettings; class QGCCorePlugin_p; +class FactMetaData; class QGCCorePlugin : public QGCTool { @@ -58,11 +59,10 @@ public: /// @return true: Show settings ui, false: Hide settings ui virtual bool overrideSettingsGroupVisibility (QString name); - /// Allows the core plugin to override the default value for the specified setting - /// @param name - Setting name - /// @param defaultValue - Standard default value for setting - /// @return New default value for setting, if no override just return passed in defaultValue - virtual QVariant overrideSettingsDefault (QString name, QVariant defaultValue); + /// Allows the core plugin to override the setting meta data before the setting fact is created. + /// @param metaData - MetaData for setting fact + /// @return true: Setting should be visible in ui, false: Setting should not be shown in ui + virtual bool adjustSettingMetaData (FactMetaData& metaData); // Override from QGCTool void setToolbox (QGCToolbox *toolbox); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 9a14487f7..94f196821 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -54,6 +54,7 @@ QGCView { width: qgcView.width spacing: ScreenTools.defaultFontPixelHeight * 0.5 anchors.margins: ScreenTools.defaultFontPixelWidth + //----------------------------------------------------------------- //-- Units Item { @@ -79,50 +80,33 @@ QGCView { id: unitsCol spacing: ScreenTools.defaultFontPixelWidth anchors.centerIn: parent - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - width: _labelWidth - anchors.baseline: distanceUnitsCombo.baseline - text: qsTr("Distance:") - } - FactComboBox { - id: distanceUnitsCombo - width: _editFieldWidth - fact: QGroundControl.settingsManager.unitsSettings.distanceUnits - indexModel: false - } - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - width: _labelWidth - anchors.baseline: areaUnitsCombo.baseline - text: qsTr("Area:") - } - FactComboBox { - id: areaUnitsCombo - width: _editFieldWidth - fact: QGroundControl.settingsManager.unitsSettings.areaUnits - indexModel: false - } - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - width: _labelWidth - anchors.baseline: speedUnitsCombo.baseline - text: qsTr("Speed:") - } - FactComboBox { - id: speedUnitsCombo - width: _editFieldWidth - fact: QGroundControl.settingsManager.unitsSettings.speedUnits - indexModel: false + + Repeater { + id: unitsRepeater + model: [ QGroundControl.settingsManager.unitsSettings.distanceUnits, QGroundControl.settingsManager.unitsSettings.areaUnits, QGroundControl.settingsManager.unitsSettings.speedUnits ] + + property var names: [ qsTr("Distance:"), qsTr("Area:"), qsTr("Speed:") ] + + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: modelData.visible + + QGCLabel { + width: _labelWidth + anchors.baseline: factCombo.baseline + text: unitsRepeater.names[index] + } + FactComboBox { + id: factCombo + width: _editFieldWidth + fact: modelData + indexModel: false + } } } } } + //----------------------------------------------------------------- //-- Miscellanous Item { @@ -395,6 +379,7 @@ QGCView { } } } + //----------------------------------------------------------------- //-- Autoconnect settings Item { @@ -416,44 +401,37 @@ QGCView { anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter visible: QGroundControl.settingsManager.autoConnectSettings.visible + Column { id: autoConnectCol spacing: ScreenTools.defaultFontPixelWidth anchors.centerIn: parent - //----------------------------------------------------------------- - //-- Autoconnect settings + Row { spacing: ScreenTools.defaultFontPixelWidth * 2 - FactCheckBox { - text: qsTr("Pixhawk") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectPixhawk - visible: !ScreenTools.isiOS - } - FactCheckBox { - text: qsTr("SiK Radio") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectSiKRadio - visible: !ScreenTools.isiOS - } - FactCheckBox { - text: qsTr("PX4 Flow") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectPX4Flow - visible: !ScreenTools.isiOS - } - FactCheckBox { - text: qsTr("LibrePilot") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectLibrePilot - } - FactCheckBox { - text: qsTr("UDP") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectUDP - } - FactCheckBox { - text: qsTr("RTK GPS") - fact: QGroundControl.settingsManager.autoConnectSettings.autoConnectRTKGPS + + Repeater { + id: autoConnectRepeater + model: [ QGroundControl.settingsManager.autoConnectSettings.autoConnectPixhawk, + QGroundControl.settingsManager.autoConnectSettings.autoConnectSiKRadio, + QGroundControl.settingsManager.autoConnectSettings.autoConnectPX4Flow, + QGroundControl.settingsManager.autoConnectSettings.autoConnectLibrePilot, + QGroundControl.settingsManager.autoConnectSettings.autoConnectUDP, + QGroundControl.settingsManager.autoConnectSettings.autoConnectRTKGPS + ] + + property var names: [ qsTr("Pixhawk"), qsTr("SiK Radio"), qsTr("PX4 Flow"), qsTr("LibrePilot"), qsTr("UDP"), qsTr("RTK GPS") ] + + FactCheckBox { + text: autoConnectRepeater.names[index] + fact: modelData + visible: !ScreenTools.isiOS && modelData.visible + } } } } } + //----------------------------------------------------------------- //-- Video Source Item { -- GitLab From bd5e0329a5f7ca7f43c499f7551b69d0937601eb Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 22 Feb 2017 14:00:47 -0800 Subject: [PATCH 338/398] More work in Fixed Wing Pattern --- src/MissionEditor/FWLandingPatternEditor.qml | 15 +- .../FWLandingPatternMapVisual.qml | 216 +++++++++++++++++- src/MissionEditor/MissionEditor.qml | 38 +-- src/MissionEditor/SurveyComplexItem.qml | 48 +++- .../FixedWingLandingComplexItem.cc | 125 +++++++--- .../FixedWingLandingComplexItem.h | 36 +-- src/MissionManager/MissionController.cc | 2 +- 7 files changed, 378 insertions(+), 102 deletions(-) diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml index 6d82d6a46..a2f0c5cec 100644 --- a/src/MissionEditor/FWLandingPatternEditor.qml +++ b/src/MissionEditor/FWLandingPatternEditor.qml @@ -22,7 +22,7 @@ import QGroundControl.Palette 1.0 // Editor for Fixed Wing Landing Pattern complex mission item Rectangle { id: _root - height: visible ? (editorColumn.height + (_margin * 2)) : 0 + height: visible ? ((editorColumn.visible ? editorColumn.height : editorColumnNeedLandingPoint.height) + (_margin * 2)) : 0 width: availableWidth color: qgcPal.windowShadeDark radius: _radius @@ -38,6 +38,7 @@ Rectangle { anchors.margins: _margin anchors.left: parent.left anchors.right: parent.right + visible: missionItem.landingCoordSet QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" } @@ -52,4 +53,16 @@ Rectangle { fact: missionItem.loiterClockwise } } + + Column { + id: editorColumnNeedLandingPoint + anchors.margins: _margin + anchors.left: parent.left + anchors.right: parent.right + visible: !missionItem.landingCoordSet + + QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" } + + QGCLabel { text: qsTr("Click in map to set landing point.") } + } } diff --git a/src/MissionEditor/FWLandingPatternMapVisual.qml b/src/MissionEditor/FWLandingPatternMapVisual.qml index 5a6adc4f0..d2c01674b 100644 --- a/src/MissionEditor/FWLandingPatternMapVisual.qml +++ b/src/MissionEditor/FWLandingPatternMapVisual.qml @@ -12,6 +12,7 @@ import QtQuick.Controls 1.2 import QtLocation 5.3 import QtPositioning 5.2 +import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 @@ -20,19 +21,193 @@ import QGroundControl.Controls 1.0 Item { property var map ///< Map control to place item in + property var _missionItem: object + property var _mouseArea + property var _dragLoiter + property var _dragLand property var _loiterPoint + property var _landPoint property var _flightPath + function hideItemVisuals() { + if (_flightPath) { + _flightPath.destroy() + _flightPath = undefined + } + if (_loiterPoint) { + _loiterPoint.destroy() + _loiterPoint = undefined + } + if (_landPoint) { + _landPoint.destroy() + _landPoint = undefined + } + } + + function showItemVisuals() { + if (!_flightPath) { + _flightPath = flightPathComponent.createObject(map) + map.addMapItem(_flightPath) + } + if (!_loiterPoint) { + _loiterPoint = loiterPointComponent.createObject(map) + map.addMapItem(_loiterPoint) + } + if (!_landPoint) { + _landPoint = landPointComponent.createObject(map) + map.addMapItem(_landPoint) + } + } + + function hideMouseArea() { + if (_mouseArea) { + _mouseArea.destroy() + _mouseArea = undefined + } + } + + function showMouseArea() { + if (!_mouseArea) { + _mouseArea = mouseAreaComponent.createObject(map) + map.addMapItem(_mouseArea) + } + } + + function hideDragAreas() { + console.log("hideDragAreas") + if (_dragLoiter) { + _dragLoiter.destroy() + _dragLoiter = undefined + } + if (_dragLand) { + _dragLand.destroy() + _dragLand = undefined + } + } + + function showDragAreas() { + console.log("showDragAreas") + if (!_dragLoiter) { + _dragLoiter = dragAreaComponent.createObject(map, { "dragLoiter": true }) + } + if (!_dragLand) { + _dragLand = dragAreaComponent.createObject(map, { "dragLoiter": false }) + } + } + Component.onCompleted: { - _flightPath = flightPathComponent.createObject(map) - _loiterPoint = loiterComponent.createObject(map) - map.addMapItem(_flightPath) - map.addMapItem(_loiterPoint) + if (_missionItem.landingCoordSet) { + showItemVisuals() + if (_missionItem.isCurrentItem) { + showDragAreas() + } + } else if (_missionItem.isCurrentItem) { + showMouseArea() + } } Component.onDestruction: { - _loiterPoint.destroy() - _flightPath.destroy() + hideDragAreas() + hideMouseArea() + hideItemVisuals() + } + + Connections { + target: _missionItem + + onIsCurrentItemChanged: { + console.log("onIsCurrentItemChanged", _missionItem.isCurrentItem) + if (_missionItem.isCurrentItem) { + if (_missionItem.landingCoordSet) { + showDragAreas() + } else { + showMouseArea() + } + } else { + hideMouseArea() + hideDragAreas() + } + } + + onLandingCoordSetChanged: { + if (_missionItem.landingCoordSet) { + hideMouseArea() + showItemVisuals() + showDragAreas() + } else if (_missionItem.isCurrentItem) { + hideDragAreas() + showMouseArea() + } + } + } + + // Mouse area to capture landing point coordindate + Component { + id: mouseAreaComponent + + MouseArea { + anchors.fill: map + + onClicked: { + var coordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y)) + coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces) + coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces) + coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces) + _missionItem.landingCoordinate = coordinate + } + } + } + + // Control which is used to drag items + Component { + id: dragAreaComponent + + Rectangle { + id: itemDragger + x: mapQuickItem ? (mapQuickItem.x + mapQuickItem.anchorPoint.x - (itemDragger.width / 2)) : 100 + y: mapQuickItem ? (mapQuickItem.y + mapQuickItem.anchorPoint.y - (itemDragger.height / 2)) : 100 + width: ScreenTools.defaultFontPixelHeight * 2 + height: ScreenTools.defaultFontPixelHeight * 2 + color: "transparent" + z: QGroundControl.zOrderMapItems + 1 // Above item icons + + property bool dragLoiter + property var mapQuickItem: dragLoiter ? _loiterPoint : _landPoint + property bool _preventCoordinateBindingLoop: false + + onXChanged: liveDrag() + onYChanged: liveDrag() + + function liveDrag() { + if (!itemDragger._preventCoordinateBindingLoop && Drag.active) { + var point = Qt.point(itemDragger.x + (itemDragger.width / 2), itemDragger.y + (itemDragger.height / 2)) + var coordinate = map.toCoordinate(point) + itemDragger._preventCoordinateBindingLoop = true + if (dragLoiter) { + coordinate.altitude = _missionItem.loiterCoordinate.altitude + _missionItem.loiterCoordinate = coordinate + } else { + coordinate.altitude = _missionItem.landingCoordinate.altitude + _missionItem.landingCoordinate = coordinate + } + itemDragger._preventCoordinateBindingLoop = false + } + } + + Drag.active: itemDrag.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + + MouseArea { + id: itemDrag + anchors.fill: parent + drag.target: parent + drag.minimumX: 0 + drag.minimumY: 0 + drag.maximumX: itemDragger.parent.width - parent.width + drag.maximumY: itemDragger.parent.height - parent.height + } + } } // Flight path @@ -40,25 +215,44 @@ Item { id: flightPathComponent MapPolyline { + z: QGroundControl.zOrderMapItems - 1 // Under item indicators line.color: "white" line.width: 2 - path: [ object.loiterCoordinate, object.exitCoordinate ] + path: _missionItem.landingCoordSet ? [ _missionItem.loiterCoordinate, _missionItem.landingCoordinate ] : undefined } } // Loiter point Component { - id: loiterComponent + id: loiterPointComponent MapQuickItem { anchorPoint.x: sourceItem.width / 2 anchorPoint.y: sourceItem.height / 2 - coordinate: object.loiterCoordinate + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.loiterCoordinate sourceItem: MissionItemIndexLabel { - label: "L" - } + label: "P" + } + } + } + + // Land point + Component { + id: landPointComponent + + MapQuickItem { + anchorPoint.x: sourceItem.width / 2 + anchorPoint.y: sourceItem.height / 2 + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.landingCoordinate + + sourceItem: + MissionItemIndexLabel { + label: "L" + } } } } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index 070923ecb..c3fe618e2 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -454,42 +454,6 @@ QGCView { } } - // Add the complex mission item exit coordinates - MapItemView { - model: missionController.complexVisualItems - delegate: exitCoordinateComponent - } - - Component { - id: exitCoordinateComponent - - MissionItemIndicator { - coordinate: object.exitCoordinate - z: QGroundControl.zOrderMapItems - missionItem: object - sequenceNumber: object.lastSequenceNumber - visible: object.specifiesCoordinate - - // These are the non-coordinate child mission items attached to this item - Row { - anchors.top: parent.top - anchors.left: parent.right - - Repeater { - model: !object.isSimpleItem ? object.childItems : 0 - - delegate: MissionItemIndexLabel { - label: object.abbreviation - checked: object.isCurrentItem - z: 2 - - onClicked: setCurrentItem(object.sequenceNumber) - } - } - } - } - } - // Add the simple mission items to the map MapItemView { model: missionController.visualItems @@ -502,7 +466,7 @@ QGCView { MissionItemIndicator { id: itemIndicator coordinate: object.coordinate - visible: object.specifiesCoordinate + visible: object.isSimpleItem && object.specifiesCoordinate z: QGroundControl.zOrderMapItems missionItem: object sequenceNumber: object.sequenceNumber diff --git a/src/MissionEditor/SurveyComplexItem.qml b/src/MissionEditor/SurveyComplexItem.qml index d3a2382cb..64d261ec5 100644 --- a/src/MissionEditor/SurveyComplexItem.qml +++ b/src/MissionEditor/SurveyComplexItem.qml @@ -12,6 +12,7 @@ import QtQuick.Controls 1.2 import QtLocation 5.3 import QtPositioning 5.2 +import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 @@ -20,19 +21,28 @@ import QGroundControl.Controls 1.0 Item { property var map ///< Map control to place item in + property var _missionItem: object property var _polygon property var _grid + property var _entryCoordinate + property var _exitCoordinate Component.onCompleted: { _polygon = polygonComponent.createObject(map) _grid = gridComponent.createObject(map) + _entryCoordinate = entryPointComponent.createObject(map) + _exitCoordinate = exitPointComponent.createObject(map) map.addMapItem(_polygon) map.addMapItem(_grid) + map.addMapItem(_entryCoordinate) + map.addMapItem(_exitCoordinate) } Component.onDestruction: { _polygon.destroy() _grid.destroy() + _entryCoordinate.destroy() + _exitCoordinate.destroy() } // Survey area polygon @@ -42,7 +52,7 @@ Item { MapPolygon { color: "green" opacity: 0.5 - path: object.polygonPath + path: _missionItem.polygonPath } } @@ -53,7 +63,41 @@ Item { MapPolyline { line.color: "white" line.width: 2 - path: object.gridPoints + path: _missionItem.gridPoints + } + } + + // Entry point + Component { + id: entryPointComponent + + MapQuickItem { + anchorPoint.x: sourceItem.width / 2 + anchorPoint.y: sourceItem.height / 2 + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.coordinate + + sourceItem: + MissionItemIndexLabel { + label: "S" + } + } + } + + // Exit point + Component { + id: exitPointComponent + + MapQuickItem { + anchorPoint.x: sourceItem.width / 2 + anchorPoint.y: sourceItem.height / 2 + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.exitCoordinate + + sourceItem: + MissionItemIndexLabel { + label: "S" + } } } } diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 9303f653b..799683ece 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -27,10 +27,12 @@ const char* FixedWingLandingComplexItem::_loiterClockwiseName = "Clockwi QMap FixedWingLandingComplexItem::_metaDataMap; -FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent) +FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent) : ComplexMissionItem(vehicle, parent) , _sequenceNumber(0) , _dirty(false) + , _landingCoordSet(false) + , _ignoreRecalcSignals(false) , _loiterToLandDistanceFact (0, _loiterToLandDistanceName, FactMetaData::valueTypeDouble) , _loiterAltitudeFact (0, _loiterAltitudeName, FactMetaData::valueTypeDouble) , _loiterRadiusFact (0, _loiterRadiusName, FactMetaData::valueTypeDouble) @@ -55,15 +57,12 @@ FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QGeoC _loiterClockwiseFact.setRawValue (_loiterClockwiseFact.rawDefaultValue()); _landingHeadingFact.setRawValue (_landingHeadingFact.rawDefaultValue()); - connect(&_loiterToLandDistanceFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition); - connect(&_landingHeadingFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterPosition); + connect(&_loiterToLandDistanceFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts); + connect(&_landingHeadingFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts); + connect(this, &FixedWingLandingComplexItem::loiterCoordinateChanged, this, &FixedWingLandingComplexItem::_recalcFactsFromCoords); + connect(this, &FixedWingLandingComplexItem::landingCoordinateChanged, this, &FixedWingLandingComplexItem::_recalcFactsFromCoords); _textFieldFacts << QVariant::fromValue(&_loiterToLandDistanceFact) << QVariant::fromValue(&_loiterAltitudeFact) << QVariant::fromValue(&_loiterRadiusFact) << QVariant::fromValue(&_landingHeadingFact); - - // Exit coordinate is our land point - _exitCoordinate = mapClickCoordinate; - - _recalcLoiterPosition(); } int FixedWingLandingComplexItem::lastSequenceNumber(void) const @@ -72,14 +71,6 @@ int FixedWingLandingComplexItem::lastSequenceNumber(void) const return _sequenceNumber + 2; } -void FixedWingLandingComplexItem::setCoordinate(const QGeoCoordinate& coordinate) -{ - if (_coordinate != coordinate) { - _coordinate = coordinate; - emit coordinateChanged(_coordinate); - } -} - void FixedWingLandingComplexItem::setDirty(bool dirty) { if (_dirty != dirty) { @@ -136,14 +127,6 @@ double FixedWingLandingComplexItem::greatestDistanceTo(const QGeoCoordinate &oth return greatestDistance; } -void FixedWingLandingComplexItem::_setExitCoordinate(const QGeoCoordinate& coordinate) -{ - if (_exitCoordinate != coordinate) { - _exitCoordinate = coordinate; - emit exitCoordinateChanged(coordinate); - } -} - bool FixedWingLandingComplexItem::specifiesCoordinate(void) const { return true; @@ -184,8 +167,8 @@ QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const MAV_CMD_NAV_LAND, MAV_FRAME_GLOBAL_RELATIVE_ALT, 0.0, 0.0, 0.0, 0.0, // param 1-4 - _exitCoordinate.latitude(), - _exitCoordinate.longitude(), + _landingCoordinate.latitude(), + _landingCoordinate.longitude(), 0.0, // altitude true, // autoContinue false, // isCurrentItem @@ -207,22 +190,54 @@ void FixedWingLandingComplexItem::setCruiseSpeed(double cruiseSpeed) Q_UNUSED(cruiseSpeed); } -void FixedWingLandingComplexItem::_recalcLoiterPosition(void) +void FixedWingLandingComplexItem::setLandingCoordinate(const QGeoCoordinate& coordinate) { - double north, east, down; - QGeoCoordinate tangentOrigin = _exitCoordinate; + if (coordinate != _landingCoordinate) { + _landingCoordinate = coordinate; + if (_landingCoordSet) { + emit exitCoordinateChanged(coordinate); + emit landingCoordinateChanged(coordinate); + } else { + _ignoreRecalcSignals = true; + emit exitCoordinateChanged(coordinate); + emit landingCoordinateChanged(coordinate); + _ignoreRecalcSignals = false; + _landingCoordSet = true; + _recalcLoiterCoordFromFacts(); + emit landingCoordSetChanged(true); + } + } +} - convertGeoToNed(_exitCoordinate, tangentOrigin, &north, &east, &down); +void FixedWingLandingComplexItem::setLoiterCoordinate(const QGeoCoordinate& coordinate) +{ + if (coordinate != _loiterCoordinate) { + _loiterCoordinate = coordinate; + emit coordinateChanged(coordinate); + emit loiterCoordinateChanged(coordinate); + } +} - QPointF originPoint(east, north); - north += _loiterToLandDistanceFact.rawValue().toDouble(); - QPointF loiterPoint(east, north); - QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, _landingHeadingFact.rawValue().toDouble()); +void FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts(void) +{ + if (!_ignoreRecalcSignals && _landingCoordSet) { + double north, east, down; + QGeoCoordinate tangentOrigin = _landingCoordinate; - convertNedToGeo(rotatedLoiterPoint.y(), rotatedLoiterPoint.x(), down, tangentOrigin, &_loiterCoordinate); + convertGeoToNed(_landingCoordinate, tangentOrigin, &north, &east, &down); - emit loiterCoordinateChanged(_loiterCoordinate); - setCoordinate(_loiterCoordinate); + QPointF originPoint(east, north); + north += _loiterToLandDistanceFact.rawValue().toDouble(); + QPointF loiterPoint(east, north); + QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, _landingHeadingFact.rawValue().toDouble()); + + convertNedToGeo(rotatedLoiterPoint.y(), rotatedLoiterPoint.x(), down, tangentOrigin, &_loiterCoordinate); + + _ignoreRecalcSignals = true; + emit loiterCoordinateChanged(_loiterCoordinate); + emit coordinateChanged(_loiterCoordinate); + _ignoreRecalcSignals = false; + } } QPointF FixedWingLandingComplexItem::_rotatePoint(const QPointF& point, const QPointF& origin, double angle) @@ -235,3 +250,39 @@ QPointF FixedWingLandingComplexItem::_rotatePoint(const QPointF& point, const QP return rotated; } + +void FixedWingLandingComplexItem::_recalcFactsFromCoords(void) +{ + if (!_ignoreRecalcSignals && _landingCoordSet) { + + // Prevent signal recursion + _ignoreRecalcSignals = true; + + // Calc new distance + + double northLand, eastLand, down; + double northLoiter, eastLoiter; + QGeoCoordinate tangentOrigin = _landingCoordinate; + + convertGeoToNed(_landingCoordinate, tangentOrigin, &northLand, &eastLand, &down); + convertGeoToNed(_loiterCoordinate, tangentOrigin, &northLoiter, &eastLoiter, &down); + + double newDistance = sqrt(pow(eastLoiter - eastLand, 2.0) + pow(northLoiter - northLand, 2.0)); + _loiterToLandDistanceFact.setRawValue(newDistance); + + // Calc new heading + + QPointF vector(eastLoiter - eastLand, northLoiter - northLand); + double radians = atan2(vector.y(), vector.x()); + double degrees = qRadiansToDegrees(radians); + degrees -= 90; // north up + if (degrees < 0.0) { + degrees += 360.0; + } else if (degrees > 360.0) { + degrees -= 360.0; + } + _landingHeadingFact.setRawValue(degrees); + + _ignoreRecalcSignals = false; + } +} diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index c7485c368..4e12c8506 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -22,13 +22,20 @@ class FixedWingLandingComplexItem : public ComplexMissionItem Q_OBJECT public: - FixedWingLandingComplexItem(Vehicle* vehicle, QGeoCoordinate mapClickCoordinate, QObject* parent = NULL); + FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent = NULL); - Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT) - Q_PROPERTY(Fact* loiterClockwise READ loiterClockwise CONSTANT) - Q_PROPERTY(QGeoCoordinate loiterCoordinate MEMBER _loiterCoordinate NOTIFY loiterCoordinateChanged) + Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT) + Q_PROPERTY(Fact* loiterClockwise READ loiterClockwise CONSTANT) + Q_PROPERTY(QGeoCoordinate loiterCoordinate READ loiterCoordinate WRITE setLoiterCoordinate NOTIFY loiterCoordinateChanged) + Q_PROPERTY(QGeoCoordinate landingCoordinate READ landingCoordinate WRITE setLandingCoordinate NOTIFY landingCoordinateChanged) + Q_PROPERTY(bool landingCoordSet MEMBER _landingCoordSet NOTIFY landingCoordSetChanged) - Fact* loiterClockwise(void) { return &_loiterClockwiseFact; } + Fact* loiterClockwise (void) { return &_loiterClockwiseFact; } + QGeoCoordinate landingCoordinate (void) const { return _landingCoordinate; } + QGeoCoordinate loiterCoordinate (void) const { return _loiterCoordinate; } + + void setLandingCoordinate (const QGeoCoordinate& coordinate); + void setLoiterCoordinate (const QGeoCoordinate& coordinate); // Overrides from ComplexMissionItem @@ -49,8 +56,8 @@ public: QString commandDescription (void) const final { return "Landing Pattern"; } QString commandName (void) const final { return "Landing Pattern"; } QString abbreviation (void) const final { return "L"; } - QGeoCoordinate coordinate (void) const final { return _coordinate; } - QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } + QGeoCoordinate coordinate (void) const final { return _loiterCoordinate; } + QGeoCoordinate exitCoordinate (void) const final { return _landingCoordinate; } int sequenceNumber (void) const final { return _sequenceNumber; } double flightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } @@ -59,27 +66,30 @@ public: bool exitCoordinateSameAsEntry (void) const final { return true; } void setDirty (bool dirty) final; - void setCoordinate (const QGeoCoordinate& coordinate) final; + void setCoordinate (const QGeoCoordinate& coordinate) final { setLoiterCoordinate(coordinate); } void setSequenceNumber (int sequenceNumber) final; void save (QJsonObject& saveObject) const final; static const char* jsonComplexItemTypeValue; signals: - void loiterCoordinateChanged(QGeoCoordinate coordinate); + void loiterCoordinateChanged (QGeoCoordinate coordinate); + void landingCoordinateChanged (QGeoCoordinate coordinate); + void landingCoordSetChanged (bool landingCoordSet); private slots: - void _recalcLoiterPosition(void); + void _recalcLoiterCoordFromFacts(void); + void _recalcFactsFromCoords(void); private: - void _setExitCoordinate(const QGeoCoordinate& coordinate); QPointF _rotatePoint(const QPointF& point, const QPointF& origin, double angle); int _sequenceNumber; bool _dirty; - QGeoCoordinate _coordinate; - QGeoCoordinate _exitCoordinate; QGeoCoordinate _loiterCoordinate; + QGeoCoordinate _landingCoordinate; + bool _landingCoordSet; + bool _ignoreRecalcSignals; Fact _loiterToLandDistanceFact; Fact _loiterAltitudeFact; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index f6f949be1..ed4fcd7a1 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -228,7 +228,7 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate newItem = new SurveyMissionItem(_activeVehicle, _visualItems); newItem->setCoordinate(mapCenterCoordinate); } else if (itemName == _fwLandingMissionItemName) { - newItem = new FixedWingLandingComplexItem(_activeVehicle, mapCenterCoordinate, _visualItems); + newItem = new FixedWingLandingComplexItem(_activeVehicle, _visualItems); } else { qWarning() << "Internal error: Unknown complex item:" << itemName; return sequenceNumber; -- GitLab From 69965242d6bb2155c45e7bd9370ed9c99033c978 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 22 Feb 2017 17:23:44 -0800 Subject: [PATCH 339/398] Paring back OS X travis to installer only --- .travis.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3877ade68..8533f3c78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,18 +27,19 @@ matrix: language: android env: SPEC=android-g++ CONFIG=installer sudo: false - - os: osx - osx_image: xcode8 - env: SPEC=macx-clang CONFIG=debug - sudo: required - os: osx osx_image: xcode8 env: SPEC=macx-clang CONFIG=installer sudo: required - - os: osx - osx_image: xcode8 - env: SPEC=macx-ios-clang CONFIG=release - sudo: false +# OSX builds pared back to installer only since travis sucks so bad we can't afford more than one' +# - os: osx +# osx_image: xcode8 +# env: SPEC=macx-clang CONFIG=debug +# sudo: required +# - os: osx +# osx_image: xcode8 +# env: SPEC=macx-ios-clang CONFIG=release +# sudo: false android: components: -- GitLab From 0242d39ccb121e3fc5b4d8ea097cbc8320580b60 Mon Sep 17 00:00:00 2001 From: Daniel Agar Date: Thu, 23 Feb 2017 00:35:50 -0500 Subject: [PATCH 340/398] PX4 FW enable acro and rattitude --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 31aa6a99a..88fd03457 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -57,8 +57,8 @@ static const struct Modes2Name rgModes2Name[] = { //main_mode sub_mode name canBeSet FW MC { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::_manualFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::_stabilizedFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::_acroFlightMode, true, false, true }, - { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::_acroFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::_altCtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::_posCtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, -- GitLab From f8547877e1fe554ec2410bd901c1122757aa3700 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Thu, 23 Feb 2017 13:14:48 -0500 Subject: [PATCH 341/398] Added a root loader to help deep nested children to show an element in the middle of the root Window. --- src/FlightDisplay/FlightDisplayView.qml | 2 +- src/ui/MainWindowInner.qml | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 473136265..368279771 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -264,7 +264,7 @@ QGCView { anchors.top: parent.top anchors.bottom: parent.bottom width: height - radius: QGroundControl.videoManager.videoReceiver && QGroundControl.videoManager.videoReceiver.recording ? 0 : height + radius: QGroundControl.videoManager && QGroundControl.videoManager.videoReceiver && QGroundControl.videoManager.videoReceiver.recording ? 0 : height color: "red" } diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index dc374d571..0276d4055 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -530,5 +530,14 @@ Item { } } } + + //------------------------------------------------------------------------- + //-- Loader helper for any child, no matter how deep can display an element + // in the middle of the main window. + Loader { + id: rootLoader + anchors.centerIn: parent + } + } -- GitLab From 29e3c496ad8403d7aa10c76f51e49f57066a656e Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Thu, 23 Feb 2017 13:49:20 -0500 Subject: [PATCH 342/398] Disable CompassMot for Sub --- src/AutoPilotPlugins/APM/APMSensorsComponent.qml | 4 ++++ src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc | 5 +++++ src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h | 2 ++ src/FirmwarePlugin/FirmwarePlugin.cc | 5 +++++ src/FirmwarePlugin/FirmwarePlugin.h | 4 ++++ src/Vehicle/Vehicle.cc | 5 +++++ src/Vehicle/Vehicle.h | 4 +++- 7 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index 51b5b4ead..1f328647e 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -532,6 +532,10 @@ SetupPage { id: motorInterferenceButton width: parent.buttonWidth text: qsTr("CompassMot") + visible: _activeVehicle ? _activeVehicle.supportsMotorInterference : false + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + onClicked: showDialog(compassMotDialogComponent, qsTr("CompassMot - Compass Motor Interference Calibration"), qgcView.showDialogFullWidth, StandardButton.Cancel | StandardButton.Ok) } diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc index 74f0678f6..5b0dd15a0 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc @@ -101,3 +101,8 @@ bool ArduSubFirmwarePlugin::supportsCalibratePressure(void) { return true; } + +bool ArduSubFirmwarePlugin::supportsMotorInterference(void) +{ + return false; +} diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index 435795825..d44bacf43 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -81,6 +81,8 @@ public: bool supportsCalibratePressure(void); + bool supportsMotorInterference(void); + QString brandImage(const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/APM/BrandImageSub"); } const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } int remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const final; diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index 2974b73fe..5df28367d 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -139,6 +139,11 @@ bool FirmwarePlugin::supportsCalibratePressure(void) return false; } +bool FirmwarePlugin::supportsMotorInterference(void) +{ + return true; +} + bool FirmwarePlugin::supportsJSButton(void) { return false; diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index 9cf38be3b..d9aa61803 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -167,6 +167,10 @@ public: /// zero at the current pressure. Default is false. virtual bool supportsCalibratePressure(void); + /// Returns true if the firmware supports calibrating motor interference offsets for the compass + /// (CompassMot). Default is true. + virtual bool supportsMotorInterference(void); + /// Called before any mavlink message is processed by Vehicle such that the firmwre plugin /// can adjust any message characteristics. This is handy to adjust or differences in mavlink /// spec implementations such that the base code can remain mavlink generic. diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 0794337dc..9abd02ae7 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1777,6 +1777,11 @@ bool Vehicle::supportsCalibratePressure(void) const return _firmwarePlugin->supportsCalibratePressure(); } +bool Vehicle::supportsMotorInterference(void) const +{ + return _firmwarePlugin->supportsMotorInterference(); +} + void Vehicle::_setCoordinateValid(bool coordinateValid) { if (coordinateValid != _coordinateValid) { diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 89516ba87..c5e0d27d5 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -282,7 +282,8 @@ public: Q_PROPERTY(bool supportsThrottleModeCenterZero READ supportsThrottleModeCenterZero CONSTANT) Q_PROPERTY(bool supportsJSButton READ supportsJSButton CONSTANT) Q_PROPERTY(bool supportsRadio READ supportsRadio CONSTANT) - Q_PROPERTY(bool supportsCalibratePressure READ supportsCalibratePressure CONSTANT) + Q_PROPERTY(bool supportsCalibratePressure READ supportsCalibratePressure CONSTANT) + Q_PROPERTY(bool supportsMotorInterference READ supportsMotorInterference CONSTANT) Q_PROPERTY(bool autoDisconnect MEMBER _autoDisconnect NOTIFY autoDisconnectChanged) Q_PROPERTY(QString prearmError READ prearmError WRITE setPrearmError NOTIFY prearmErrorChanged) Q_PROPERTY(int motorCount READ motorCount CONSTANT) @@ -506,6 +507,7 @@ public: bool supportsRadio(void) const; bool supportsJSButton(void) const; bool supportsCalibratePressure(void) const; + bool supportsMotorInterference(void) const; void setFlying(bool flying); void setGuidedMode(bool guidedMode); -- GitLab From 026e6057505c358f7dfbceb808f20a374794d769 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 23 Feb 2017 14:39:02 -0800 Subject: [PATCH 343/398] Rename SettingsGroup json files --- qgroundcontrol.qrc | 8 ++++---- .../{SettingsGroup.app.json => App.SettingsGroup.json} | 0 src/Settings/AppSettings.cc | 2 +- ...up.autoConnect.json => AutoConnect.SettingsGroup.json} | 0 src/Settings/AutoConnectSettings.cc | 2 +- src/Settings/SettingsGroup.cc | 2 +- ...{SettingsGroup.units.json => Units.SettingsGroup.json} | 0 src/Settings/UnitsSettings.cc | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename src/Settings/{SettingsGroup.app.json => App.SettingsGroup.json} (100%) rename src/Settings/{SettingsGroup.autoConnect.json => AutoConnect.SettingsGroup.json} (100%) rename src/Settings/{SettingsGroup.units.json => Units.SettingsGroup.json} (100%) diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index c9620f62c..870f12057 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -179,10 +179,10 @@ src/Vehicle/GPSFact.json src/Vehicle/WindFact.json src/Vehicle/VibrationFact.json - src/Settings/SettingsGroup.app.json - src/Settings/SettingsGroup.autoConnect.json - src/Settings/SettingsGroup.units.json - src/Settings/SettingsGroup.video.json + src/Settings/App.SettingsGroup.json + src/Settings/AutoConnect.SettingsGroup.json + src/Settings/Units.SettingsGroup.json + src/Settings/Video.SettingsGroup.json src/MissionManager/RallyPoint.FactMetaData.json src/MissionManager/FWLandingPattern.FactMetaData.json src/MissionManager/Survey.FactMetaData.json diff --git a/src/Settings/SettingsGroup.app.json b/src/Settings/App.SettingsGroup.json similarity index 100% rename from src/Settings/SettingsGroup.app.json rename to src/Settings/App.SettingsGroup.json diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index c0fb64fe8..d2284a7b3 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -12,7 +12,7 @@ #include #include -const char* AppSettings::appSettingsGroupName = "app"; +const char* AppSettings::appSettingsGroupName = "App"; const char* AppSettings::offlineEditingFirmwareTypeSettingsName = "OfflineEditingFirmwareType"; const char* AppSettings::offlineEditingVehicleTypeSettingsName = "OfflineEditingVehicleType"; const char* AppSettings::offlineEditingCruiseSpeedSettingsName = "OfflineEditingCruiseSpeed"; diff --git a/src/Settings/SettingsGroup.autoConnect.json b/src/Settings/AutoConnect.SettingsGroup.json similarity index 100% rename from src/Settings/SettingsGroup.autoConnect.json rename to src/Settings/AutoConnect.SettingsGroup.json diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index 008c08e04..fc1247f84 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -22,7 +22,7 @@ const char* AutoConnectSettings:: autoConnectPX4FlowSettingsName = "Autocon const char* AutoConnectSettings:: autoConnectRTKGPSSettingsName = "AutoconnectRTKGPS"; const char* AutoConnectSettings:: autoConnectLibrePilotSettingsName = "AutoconnectLibrePilot"; -const char* AutoConnectSettings::autoConnectSettingsGroupName = "autoConnect"; +const char* AutoConnectSettings::autoConnectSettingsGroupName = "AutoConnect"; AutoConnectSettings::AutoConnectSettings(QObject* parent) : SettingsGroup(autoConnectSettingsGroupName, _settingsGroup, parent) diff --git a/src/Settings/SettingsGroup.cc b/src/Settings/SettingsGroup.cc index c29c42509..e0f1ac231 100644 --- a/src/Settings/SettingsGroup.cc +++ b/src/Settings/SettingsGroup.cc @@ -17,7 +17,7 @@ SettingsGroup::SettingsGroup(const QString& name, const QString& settingsGroup, , _settingsGroup(settingsGroup) , _visible(qgcApp()->toolbox()->corePlugin()->overrideSettingsGroupVisibility(name)) { - QString jsonNameFormat(":/json/SettingsGroup.%1.json"); + QString jsonNameFormat(":/json/%1.SettingsGroup.json"); _nameToMetaDataMap = FactMetaData::createMapFromJsonFile(jsonNameFormat.arg(name), this); } diff --git a/src/Settings/SettingsGroup.units.json b/src/Settings/Units.SettingsGroup.json similarity index 100% rename from src/Settings/SettingsGroup.units.json rename to src/Settings/Units.SettingsGroup.json diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc index 608e45cfd..0d5c6e0d6 100644 --- a/src/Settings/UnitsSettings.cc +++ b/src/Settings/UnitsSettings.cc @@ -12,7 +12,7 @@ #include #include -const char* UnitsSettings::unitsSettingsGroupName = "units"; +const char* UnitsSettings::unitsSettingsGroupName = "Units"; const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; const char* UnitsSettings::areaUnitsSettingsName = "AreaUnits"; const char* UnitsSettings::speedUnitsSettingsName = "SpeedUnits"; -- GitLab From 23f57eb907acc144cd1d77bf5e2b7d1532703454 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 23 Feb 2017 14:39:20 -0800 Subject: [PATCH 344/398] Move video settings to new SettingsGroup system --- src/FlightDisplay/VideoManager.cc | 193 +++++-------------------- src/FlightDisplay/VideoManager.h | 42 ++---- src/Settings/SettingsGroup.video.json | 2 - src/Settings/Video.SettingsGroup.json | 31 ++++ src/Settings/VideoSettings.cc | 83 ++++++++++- src/Settings/VideoSettings.h | 23 +++ src/VideoStreaming/VideoReceiver.cc | 20 +-- src/VideoStreaming/VideoReceiver.h | 2 - src/ui/preferences/GeneralSettings.qml | 57 +++----- 9 files changed, 209 insertions(+), 244 deletions(-) delete mode 100644 src/Settings/SettingsGroup.video.json create mode 100644 src/Settings/Video.SettingsGroup.json diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index 09a7c3216..e2cf39875 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -25,19 +25,7 @@ #include "QGCToolbox.h" #include "QGCCorePlugin.h" #include "QGCOptions.h" - -static const char* kVideoSourceKey = "VideoSource"; -static const char* kVideoUDPPortKey = "VideoUDPPort"; -static const char* kVideoRTSPUrlKey = "VideoRTSPUrl"; -static const char* kNoVideo = "No Video Available"; - -#if defined(QGC_GST_STREAMING) -#if defined(QGC_ENABLE_VIDEORECORDING) -static const char* kVideoSavePathKey= "VideoSavePath"; -#endif -static const char* kUDPStream = "UDP Video Stream"; -static const char* kRTSPStream = "RTSP Video Stream"; -#endif +#include "Settings/SettingsManager.h" QGC_LOGGING_CATEGORY(VideoManagerLog, "VideoManagerLog") @@ -47,8 +35,8 @@ VideoManager::VideoManager(QGCApplication* app) , _videoSurface(NULL) , _videoReceiver(NULL) , _videoRunning(false) - , _udpPort(5600) //-- Defalut Port 5600 == Solo UDP Port , _init(false) + , _videoSettings(NULL) { } @@ -65,31 +53,36 @@ VideoManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.VideoManager", 1, 0, "VideoManager", "Reference only"); - //-- Get saved settings + + _videoSettings = toolbox->settingsManager()->videoSettings(); + QString videoSource = _videoSettings->videoSource()->rawValue().toString(); + #if defined(QGC_GST_STREAMING) - QSettings settings; -#if defined(NO_UDP_VIDEO) - setVideoSource(settings.value(kVideoSourceKey, kRTSPStream).toString()); -#else - setVideoSource(settings.value(kVideoSourceKey, kUDPStream).toString()); -#endif - //-- Check if core plugin defines its own video requirements - if(qgcApp()->toolbox()->corePlugin()->options()->definesVideo()) { - if(qgcApp()->toolbox()->corePlugin()->options()->videoUDPPort()) { - setUdpPort(qgcApp()->toolbox()->corePlugin()->options()->videoUDPPort()); - setVideoSource(kUDPStream); - } else { - setVideoSource(kRTSPStream); - setRtspURL(qgcApp()->toolbox()->corePlugin()->options()->videoRSTPUrl()); - } - } else { - setUdpPort(settings.value(kVideoUDPPortKey, 5600).toUInt()); - setRtspURL(settings.value(kVideoRTSPUrlKey, "rtsp://192.168.42.1:554/live").toString()); //-- Example RTSP URL - } -#if defined(QGC_ENABLE_VIDEORECORDING) - setVideoSavePath(settings.value(kVideoSavePathKey, QDir::homePath()).toString()); +#ifndef QGC_DISABLE_UVC + // If we are using a UVC camera setup the device name + QList cameras = QCameraInfo::availableCameras(); + foreach (const QCameraInfo &cameraInfo, cameras) { + if(cameraInfo.description() == videoSource) { + _videoSourceID = cameraInfo.deviceName(); + emit videoSourceIDChanged(); + qCDebug(VideoManagerLog) << "Found USB source:" << _videoSourceID << " Name:" << videoSource; + break; + } + } #endif + + emit isGStreamerChanged(); + qCDebug(VideoManagerLog) << "New Video Source:" << videoSource; + + if(_videoReceiver) { + if(isGStreamer()) { + _videoReceiver->start(); + } else { + _videoReceiver->stop(); + } + } #endif + _init = true; #if defined(QGC_GST_STREAMING) _updateVideo(); @@ -105,7 +98,8 @@ VideoManager::hasVideo() #if defined(QGC_GST_STREAMING) return true; #endif - return !_videoSource.isEmpty(); + QString videoSource = _videoSettings->videoSource()->rawValue().toString(); + return !videoSource.isEmpty() && videoSource != VideoSettings::videoSourceNoVideo; } //----------------------------------------------------------------------------- @@ -113,7 +107,8 @@ bool VideoManager::isGStreamer() { #if defined(QGC_GST_STREAMING) - return _videoSource == kUDPStream || _videoSource == kRTSPStream; + QString videoSource = _videoSettings->videoSource()->rawValue().toString(); + return videoSource == VideoSettings::videoSourceUDP || videoSource == VideoSettings::videoSourceRTSP; #else return false; #endif @@ -128,118 +123,6 @@ VideoManager::uvcEnabled() } #endif -//----------------------------------------------------------------------------- -void -VideoManager::setVideoSource(QString vSource) -{ - if(vSource == kNoVideo) - return; - _videoSource = vSource; - QSettings settings; - settings.setValue(kVideoSourceKey, vSource); - emit videoSourceChanged(); -#ifndef QGC_DISABLE_UVC - QList cameras = QCameraInfo::availableCameras(); - foreach (const QCameraInfo &cameraInfo, cameras) { - if(cameraInfo.description() == vSource) { - _videoSourceID = cameraInfo.deviceName(); - emit videoSourceIDChanged(); - qCDebug(VideoManagerLog) << "Found USB source:" << _videoSourceID << " Name:" << _videoSource; - break; - } - } -#endif - emit isGStreamerChanged(); - qCDebug(VideoManagerLog) << "New Video Source:" << vSource; - /* - * Not working. Requires restart for now. (Undef KRTSP/kUDP above when enabling this) - if(isGStreamer()) - _updateVideo(); - */ - if(_videoReceiver) { - if(isGStreamer()) { - _videoReceiver->start(); - } else { - _videoReceiver->stop(); - } - } -} - -//----------------------------------------------------------------------------- -void -VideoManager::setUdpPort(quint16 port) -{ - _udpPort = port; - QSettings settings; - settings.setValue(kVideoUDPPortKey, port); - emit udpPortChanged(); - /* - * Not working. Requires restart for now. (Undef KRTSP/kUDP above when enabling this) - if(_videoSource == kUDPStream) - _updateVideo(); - */ -} - -//----------------------------------------------------------------------------- -void -VideoManager::setRtspURL(QString url) -{ - _rtspURL = url; - QSettings settings; - settings.setValue(kVideoRTSPUrlKey, url); - emit rtspURLChanged(); - /* - * Not working. Requires restart for now. (Undef KRTSP/kUDP above when enabling this) - if(_videoSource == kRTSPStream) - _updateVideo(); - */ -} - -void -VideoManager::setVideoSavePathByUrl(QUrl url) { -#if defined(QGC_ENABLE_VIDEORECORDING) - setVideoSavePath(url.toLocalFile()); -#else - Q_UNUSED(url); -#endif -} - -void -VideoManager::setVideoSavePath(QString path) -{ -#if defined(QGC_ENABLE_VIDEORECORDING) - _videoSavePath = path; - QSettings settings; - settings.setValue(kVideoSavePathKey, path); - if(_videoReceiver) - _videoReceiver->setVideoSavePath(_videoSavePath); - emit videoSavePathChanged(); -#else - Q_UNUSED(path); -#endif -} - -//----------------------------------------------------------------------------- -QStringList -VideoManager::videoSourceList() -{ - _videoSourceList.clear(); -#if defined(QGC_GST_STREAMING) - _videoSourceList.append(kUDPStream); - _videoSourceList.append(kRTSPStream); -#endif -#ifndef QGC_DISABLE_UVC - QList cameras = QCameraInfo::availableCameras(); - foreach (const QCameraInfo &cameraInfo, cameras) { - qCDebug(VideoManagerLog) << "UVC Video source ID:" << cameraInfo.deviceName() << " Name:" << cameraInfo.description(); - _videoSourceList.append(cameraInfo.description()); - } -#endif - if(_videoSourceList.count() == 0) - _videoSourceList.append(kNoVideo); - return _videoSourceList; -} - //----------------------------------------------------------------------------- void VideoManager::_updateTimer() { @@ -292,13 +175,11 @@ void VideoManager::_updateVideo() _videoReceiver = new VideoReceiver(this); #if defined(QGC_GST_STREAMING) _videoReceiver->setVideoSink(_videoSurface->videoSink()); - if(_videoSource == kUDPStream) - _videoReceiver->setUri(QStringLiteral("udp://0.0.0.0:%1").arg(_udpPort)); + QString videoSource = _videoSettings->videoSource()->rawValue().toString(); + if (_videoSettings->videoSource()->rawValue().toString() == VideoSettings::videoSourceUDP) + _videoReceiver->setUri(QStringLiteral("udp://0.0.0.0:%1").arg(_videoSettings->udpPort()->rawValue().toInt())); else - _videoReceiver->setUri(_rtspURL); -#if defined(QGC_ENABLE_VIDEORECORDING) - _videoReceiver->setVideoSavePath(_videoSavePath); -#endif + _videoReceiver->setUri(_videoSettings->rtspUrl()->rawValue().toString()); #endif _videoReceiver->start(); } diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index 912e57715..f96f1acb0 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -22,6 +22,8 @@ Q_DECLARE_LOGGING_CATEGORY(VideoManagerLog) +class VideoSettings; + class VideoManager : public QGCTool { Q_OBJECT @@ -33,28 +35,16 @@ public: Q_PROPERTY(bool hasVideo READ hasVideo NOTIFY hasVideoChanged) Q_PROPERTY(bool isGStreamer READ isGStreamer NOTIFY isGStreamerChanged) Q_PROPERTY(QString videoSourceID READ videoSourceID NOTIFY videoSourceIDChanged) - Q_PROPERTY(QString videoSource READ videoSource WRITE setVideoSource NOTIFY videoSourceChanged) - Q_PROPERTY(QStringList videoSourceList READ videoSourceList NOTIFY videoSourceListChanged) Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) - Q_PROPERTY(quint16 udpPort READ udpPort WRITE setUdpPort NOTIFY udpPortChanged) - Q_PROPERTY(QString rtspURL READ rtspURL WRITE setRtspURL NOTIFY rtspURLChanged) - Q_PROPERTY(QString videoSavePath READ videoSavePath NOTIFY videoSavePathChanged) Q_PROPERTY(bool uvcEnabled READ uvcEnabled CONSTANT) Q_PROPERTY(VideoSurface* videoSurface MEMBER _videoSurface CONSTANT) Q_PROPERTY(VideoReceiver* videoReceiver MEMBER _videoReceiver CONSTANT) Q_PROPERTY(bool recordingEnabled READ recordingEnabled CONSTANT) - Q_INVOKABLE void setVideoSavePathByUrl (QUrl url); - bool hasVideo (); bool isGStreamer (); bool videoRunning () { return _videoRunning; } QString videoSourceID () { return _videoSourceID; } - QString videoSource () { return _videoSource; } - QStringList videoSourceList (); - quint16 udpPort () { return _udpPort; } - QString rtspURL () { return _rtspURL; } - QString videoSavePath () { return _videoSavePath; } #if defined(QGC_DISABLE_UVC) bool uvcEnabled () { return false; } @@ -68,43 +58,29 @@ public: bool recordingEnabled () { return false; } #endif - void setVideoSource (QString vSource); - void setUdpPort (quint16 port); - void setRtspURL (QString url); - void setVideoSavePath (QString path); - // Override from QGCTool void setToolbox (QGCToolbox *toolbox); signals: void hasVideoChanged (); void videoRunningChanged (); - void videoSourceChanged (); - void videoSourceListChanged (); void isGStreamerChanged (); void videoSourceIDChanged (); - void udpPortChanged (); - void rtspURLChanged (); - void videoSavePathChanged (); private: void _updateTimer (); void _updateVideo (); private: - VideoSurface* _videoSurface; - VideoReceiver* _videoReceiver; - bool _videoRunning; + VideoSurface* _videoSurface; + VideoReceiver* _videoReceiver; + bool _videoRunning; #if defined(QGC_GST_STREAMING) - QTimer _frameTimer; + QTimer _frameTimer; #endif - QString _videoSource; - QString _videoSourceID; - QStringList _videoSourceList; - quint16 _udpPort; - QString _rtspURL; - QString _videoSavePath; - bool _init; + QString _videoSourceID; + bool _init; + VideoSettings* _videoSettings; }; #endif diff --git a/src/Settings/SettingsGroup.video.json b/src/Settings/SettingsGroup.video.json deleted file mode 100644 index 0d4f101c7..000000000 --- a/src/Settings/SettingsGroup.video.json +++ /dev/null @@ -1,2 +0,0 @@ -[ -] diff --git a/src/Settings/Video.SettingsGroup.json b/src/Settings/Video.SettingsGroup.json new file mode 100644 index 000000000..c9835ee05 --- /dev/null +++ b/src/Settings/Video.SettingsGroup.json @@ -0,0 +1,31 @@ +[ +{ + "name": "VideoSource", + "shortDescription": "Video source", + "longDescription": "Source for video. UDP, RTSP and UVC Cameras may be supported supported depending on Vehicle and QGroundControl version.", + "type": "string", + "defaultValue": "" +}, +{ + "name": "VideoUDPPort", + "shortDescription": "Video UDP Port", + "longDescription": "UDP port to bind to for video stream.", + "type": "uint16", + "min": 1025, + "defaultValue": 5600 +}, +{ + "name": "VideoRTSPUrl", + "shortDescription": "Video RTSP Url", + "longDescription": "RTSP url address and port to bind to for video stream. Example: rtsp://192.168.42.1:554/live", + "type": "string", + "defaultValue": "" +}, +{ + "name": "VideoSavePath", + "shortDescription": "Video save directory", + "longDescription": "Directory to save videos to.", + "type": "string", + "defaultValue": "" +} +] diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index cd0228e57..ca2a9edbe 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -11,12 +11,93 @@ #include #include +#include +#include -const char* VideoSettings::videoSettingsGroupName = "video"; +const char* VideoSettings::videoSettingsGroupName = "Video"; + +const char* VideoSettings::videoSourceName = "VideoSource"; +const char* VideoSettings::udpPortName = "VideoUDPPort"; +const char* VideoSettings::rtspUrlName = "VideoRTSPUrl"; +const char* VideoSettings::videoSavePathName = "VideoSavePath"; + +const char* VideoSettings::videoSourceNoVideo = "No Video Available"; +const char* VideoSettings::videoSourceUDP = "UDP Video Stream"; +const char* VideoSettings::videoSourceRTSP = "RTSP Video Stream"; VideoSettings::VideoSettings(QObject* parent) : SettingsGroup(videoSettingsGroupName, QString() /* root settings group */, parent) + , _videoSourceFact(NULL) + , _udpPortFact(NULL) + , _rtspUrlFact(NULL) + , _videoSavePathFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); + + // Setup enum values for videoSource settings into meta data + QStringList videoSourceList; +#ifdef QGC_GST_STREAMING +#ifndef NO_UDP_VIDEO + videoSourceList.append(videoSourceUDP); +#endif + videoSourceList.append(videoSourceRTSP); +#endif +#ifndef QGC_DISABLE_UVC + QList cameras = QCameraInfo::availableCameras(); + foreach (const QCameraInfo &cameraInfo, cameras) { + videoSourceList.append(cameraInfo.description()); + } +#endif + if (videoSourceList.count() == 0) { + videoSourceList.append(videoSourceNoVideo); + } + QVariantList videoSourceVarList; + foreach (const QString& videoSource, videoSourceList) { + videoSourceVarList.append(QVariant::fromValue(videoSource)); + } + _nameToMetaDataMap[videoSourceName]->setEnumInfo(videoSourceList, videoSourceVarList); + + // Set default value for videoSource +#if defined(NO_UDP_VIDEO) + _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoSourceRTSP); +#else + _nameToMetaDataMap[videoSourceName]->setRawDefaultValue(videoSourceUDP); +#endif +} + +Fact* VideoSettings::videoSource(void) +{ + if (!_videoSourceFact) { + _videoSourceFact = _createSettingsFact(videoSourceName); + } + + return _videoSourceFact; +} + +Fact* VideoSettings::udpPort(void) +{ + if (!_udpPortFact) { + _udpPortFact = _createSettingsFact(udpPortName); + } + + return _udpPortFact; +} + +Fact* VideoSettings::rtspUrl(void) +{ + if (!_rtspUrlFact) { + _rtspUrlFact = _createSettingsFact(rtspUrlName); + } + + return _rtspUrlFact; +} + +Fact* VideoSettings::videoSavePath(void) +{ + if (!_videoSavePathFact) { + _videoSavePathFact = _createSettingsFact(videoSavePathName); + } + + return _videoSavePathFact; } diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index 04bfc6bf8..df3821720 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -19,9 +19,32 @@ class VideoSettings : public SettingsGroup public: VideoSettings(QObject* parent = NULL); + Q_PROPERTY(Fact* videoSource READ videoSource CONSTANT) + Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) + Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) + Q_PROPERTY(Fact* videoSavePath READ videoSavePath CONSTANT) + + Fact* videoSource (void); + Fact* udpPort (void); + Fact* rtspUrl (void); + Fact* videoSavePath (void); + static const char* videoSettingsGroupName; + static const char* videoSourceName; + static const char* udpPortName; + static const char* rtspUrlName; + static const char* videoSavePathName; + + static const char* videoSourceNoVideo; + static const char* videoSourceUDP; + static const char* videoSourceRTSP; + private: + SettingsFact* _videoSourceFact; + SettingsFact* _udpPortFact; + SettingsFact* _rtspUrlFact; + SettingsFact* _videoSavePathFact; }; #endif diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 0a58ff8d0..383f92b66 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -15,6 +15,9 @@ */ #include "VideoReceiver.h" +#include "SettingsManager.h" +#include "QGCApplication.h" + #include #include #include @@ -348,16 +351,6 @@ void VideoReceiver::setUri(const QString & uri) _uri = uri; } -void VideoReceiver::setVideoSavePath(const QString& path) -{ -#if defined(QGC_ENABLE_VIDEORECORDING) - _path = path; - qCDebug(VideoReceiverLog) << "New Path:" << _path; -#else - Q_UNUSED(path); -#endif -} - #if defined(QGC_GST_STREAMING) void VideoReceiver::_shutdownPipeline() { if(!_pipeline) { @@ -467,8 +460,9 @@ void VideoReceiver::startRecording(void) return; } - if(_path.isEmpty()) { - qWarning() << "VideoReceiver::startRecording Empty Path!"; + QString savePath = qgcApp()->toolbox()->settingsManager()->videoSettings()->videoSavePath()->rawValue().toString(); + if(savePath.isEmpty()) { + qgcApp()->showMessage("Unabled to record video. Video save path must be specified in Settings."); return; } @@ -485,7 +479,7 @@ void VideoReceiver::startRecording(void) } QString videoFile; - videoFile = _path + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") + ".mkv"; + videoFile = savePath + "/QGC-" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") + ".mkv"; g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(videoFile), NULL); qCDebug(VideoReceiverLog) << "New video file:" << videoFile; diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 9ec600fed..2c294cf24 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -62,7 +62,6 @@ public slots: void start (); void stop (); void setUri (const QString& uri); - void setVideoSavePath (const QString& path); void stopRecording (); void startRecording (); @@ -105,7 +104,6 @@ private: #endif QString _uri; - QString _path; #if defined(QGC_GST_STREAMING) GstElement* _pipeline; diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 94f196821..a83ceb588 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -453,84 +453,68 @@ QGCView { anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter visible: QGroundControl.settingsManager.videoSettings.visible + Column { id: videoCol spacing: ScreenTools.defaultFontPixelWidth anchors.centerIn: parent + + Row { spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.settingsManager.videoSettings.videoSource.visible QGCLabel { anchors.baseline: videoSource.baseline text: qsTr("Video Source:") width: _labelWidth } - QGCComboBox { - id: videoSource - width: _editFieldWidth - model: QGroundControl.videoManager.videoSourceList - Component.onCompleted: { - var index = videoSource.find(QGroundControl.videoManager.videoSource) - if (index >= 0) { - videoSource.currentIndex = index - } - } - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.videoManager.videoSource = model[index] - } - } + FactComboBox { + id: videoSource + width: _editFieldWidth + indexModel: false + fact: QGroundControl.settingsManager.videoSettings.videoSource } } Row { spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 0 + visible: QGroundControl.settingsManager.videoSettings.udpPort.visible && QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 0 QGCLabel { anchors.baseline: udpField.baseline text: qsTr("UDP Port:") width: _labelWidth } - QGCTextField { + FactTextField { id: udpField width: _editFieldWidth - text: QGroundControl.videoManager.udpPort - validator: IntValidator {bottom: 1024; top: 65535;} - inputMethodHints: Qt.ImhDigitsOnly - onEditingFinished: { - QGroundControl.videoManager.udpPort = parseInt(text) - } + fact: QGroundControl.settingsManager.videoSettings.udpPort } } Row { spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 1 + visible: QGroundControl.settingsManager.videoSettings.rtspUrl.visible && QGroundControl.videoManager.isGStreamer && videoSource.currentIndex === 1 QGCLabel { anchors.baseline: rtspField.baseline text: qsTr("RTSP URL:") width: _labelWidth } - QGCTextField { + FactTextField { id: rtspField width: _editFieldWidth - text: QGroundControl.videoManager.rtspURL - onEditingFinished: { - QGroundControl.videoManager.rtspURL = text - } + fact: QGroundControl.settingsManager.videoSettings.rtspUrl } } Row { spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.recordingEnabled + visible: QGroundControl.settingsManager.videoSettings.videoSavePath.visible && QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.recordingEnabled QGCLabel { anchors.baseline: pathField.baseline text: qsTr("Save Path:") width: _labelWidth } - QGCTextField { + FactTextField { id: pathField width: _editFieldWidth - readOnly: true - text: QGroundControl.videoManager.videoSavePath + fact: QGroundControl.settingsManager.videoSettings.videoSavePath } QGCButton { text: "Browse" @@ -541,13 +525,12 @@ QGCView { title: "Choose a location to save video files." folder: shortcuts.home selectFolder: true - onAccepted: QGroundControl.videoManager.setVideoSavePathByUrl(fileDialog.fileUrl) + onAccepted: QGroundControl.settingsManager.videoSettings.videoSavePath.value = QGroundControl.urlToLocalFile(videoLocationFileDialog.fileUrl) } - } } } - } + } // Video Source - Rectangle QGCLabel { anchors.horizontalCenter: parent.horizontalCenter -- GitLab From aa2cade0867bdcc544c1a1322c07cc0347757dd5 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 23 Feb 2017 17:26:15 -0800 Subject: [PATCH 345/398] Fix include ifdef --- src/Settings/VideoSettings.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index ca2a9edbe..190c87fff 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -12,7 +12,10 @@ #include #include #include + +#ifndef QGC_DISABLE_UVC #include +#endif const char* VideoSettings::videoSettingsGroupName = "Video"; -- GitLab From 7a105693054bb39718f78468d2b020cef0d2195c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 23 Feb 2017 19:34:40 -0800 Subject: [PATCH 346/398] Add missing values and better ui --- src/MissionEditor/FWLandingPatternEditor.qml | 74 +++++++++++++++++-- .../FWLandingPattern.FactMetaData.json | 12 +-- .../FixedWingLandingComplexItem.cc | 19 ++--- .../FixedWingLandingComplexItem.h | 43 +++++++---- 4 files changed, 114 insertions(+), 34 deletions(-) diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml index a2f0c5cec..af6a9d5a2 100644 --- a/src/MissionEditor/FWLandingPatternEditor.qml +++ b/src/MissionEditor/FWLandingPatternEditor.qml @@ -31,7 +31,8 @@ Rectangle { //property real availableWidth ///< Width for control //property var missionItem ///< Mission Item for editor - property real _margin: ScreenTools.defaultFontPixelWidth * 0.25 + property real _margin: ScreenTools.defaultFontPixelWidth / 4 + property real _spacer: ScreenTools.defaultFontPixelWidth / 2 Column { id: editorColumn @@ -40,17 +41,78 @@ Rectangle { anchors.right: parent.right visible: missionItem.landingCoordSet - QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" } + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: "WIP (NOT FOR REAL FLIGHT!)" + } + + Item { width: 1; height: _margin } + + QGCLabel { text: qsTr("Loiter point") } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + } + + Item { width: 1; height: _spacer } + + FactTextFieldGrid { + anchors.left: parent.left + anchors.right: parent.right + factList: [ missionItem.loiterAltitude, missionItem.loiterRadius ] + } + + Item { width: 1; height: _spacer } + + QGCCheckBox { + id: loiterAltRelative + anchors.right: parent.right + text: qsTr("Altitude relative to home") + checked: missionItem.loiterAltitudeRelative + onClicked: missionItem.loiterAltitudeRelative = checked + } + + Item { width: 1; height: _spacer } + + QGCCheckBox { + anchors.left: loiterAltRelative.left + text: qsTr("Loiter clockwise") + checked: missionItem.loiterClockwise + onClicked: missionItem.loiterClockwise = checked + } + + Item { width: 1; height: ScreenTools.defaultFontPixelHeight / 2 } + + QGCLabel { text: qsTr("Landing point") } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: qgcPal.text + } + + Item { width: 1; height: _spacer } FactTextFieldGrid { anchors.left: parent.left anchors.right: parent.right - factList: missionItem.textFieldFacts + factList: [ missionItem.landingAltitude, missionItem.landingDistance, missionItem.landingHeading ] } - FactCheckBox { - text: missionItem.loiterClockwise.name - fact: missionItem.loiterClockwise + Item { width: 1; height: _spacer } + + QGCCheckBox { + anchors.right: parent.right + text: qsTr("Altitude relative to home") + checked: missionItem.landingAltitudeRelative + onClicked: missionItem.landingAltitudeRelative = checked } } diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json index d606af684..96e5657ed 100644 --- a/src/MissionManager/FWLandingPattern.FactMetaData.json +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -17,7 +17,7 @@ }, { "name": "Loiter altitude", - "shortDescription": "Altitude to loiter prior to landing.", + "shortDescription": "Aircraft will proceed to the loiter point and loiter until it reaches this altitude. Once altitude is reached the aircraft will proceed to land.", "type": "double", "units": "m", "decimalPlaces": 1, @@ -33,9 +33,11 @@ "defaultValue": 75.0 }, { - "name": "Clockwise loiter", - "shortDescription": "If true, loiter will be clockwise. False, loiter will be counter-clockwise.", - "type": "bool", - "defaultValue": true + "name": "Landing altitude", + "shortDescription": "Altitude for landing point.", + "type": "double", + "units": "m", + "decimalPlaces": 1, + "defaultValue": 0.0 } ] diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 799683ece..3603eba38 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -23,7 +23,7 @@ const char* FixedWingLandingComplexItem::_loiterToLandDistanceName = "Landing const char* FixedWingLandingComplexItem::_landingHeadingName = "Landing heading"; const char* FixedWingLandingComplexItem::_loiterAltitudeName = "Loiter altitude"; const char* FixedWingLandingComplexItem::_loiterRadiusName = "Loiter radius"; -const char* FixedWingLandingComplexItem::_loiterClockwiseName = "Clockwise loiter"; +const char* FixedWingLandingComplexItem::_landingAltitudeName = "Landing altitude"; QMap FixedWingLandingComplexItem::_metaDataMap; @@ -36,8 +36,11 @@ FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObje , _loiterToLandDistanceFact (0, _loiterToLandDistanceName, FactMetaData::valueTypeDouble) , _loiterAltitudeFact (0, _loiterAltitudeName, FactMetaData::valueTypeDouble) , _loiterRadiusFact (0, _loiterRadiusName, FactMetaData::valueTypeDouble) - , _loiterClockwiseFact (0, _loiterClockwiseName, FactMetaData::valueTypeBool) , _landingHeadingFact (0, _landingHeadingName, FactMetaData::valueTypeDouble) + , _landingAltitudeFact (0, _landingAltitudeName, FactMetaData::valueTypeDouble) + , _loiterClockwise(true) + , _loiterAltitudeRelative(true) + , _landingAltitudeRelative(true) { _editorQml = "qrc:/qml/FWLandingPatternEditor.qml"; @@ -48,21 +51,19 @@ FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObje _loiterToLandDistanceFact.setMetaData (_metaDataMap[_loiterToLandDistanceName]); _loiterAltitudeFact.setMetaData (_metaDataMap[_loiterAltitudeName]); _loiterRadiusFact.setMetaData (_metaDataMap[_loiterRadiusName]); - _loiterClockwiseFact.setMetaData (_metaDataMap[_loiterClockwiseName]); _landingHeadingFact.setMetaData (_metaDataMap[_landingHeadingName]); + _landingAltitudeFact.setMetaData (_metaDataMap[_landingAltitudeName]); _loiterToLandDistanceFact.setRawValue (_loiterToLandDistanceFact.rawDefaultValue()); _loiterAltitudeFact.setRawValue (_loiterAltitudeFact.rawDefaultValue()); _loiterRadiusFact.setRawValue (_loiterRadiusFact.rawDefaultValue()); - _loiterClockwiseFact.setRawValue (_loiterClockwiseFact.rawDefaultValue()); _landingHeadingFact.setRawValue (_landingHeadingFact.rawDefaultValue()); + _landingAltitudeFact.setRawValue (_landingAltitudeFact.rawDefaultValue()); connect(&_loiterToLandDistanceFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts); connect(&_landingHeadingFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts); connect(this, &FixedWingLandingComplexItem::loiterCoordinateChanged, this, &FixedWingLandingComplexItem::_recalcFactsFromCoords); connect(this, &FixedWingLandingComplexItem::landingCoordinateChanged, this, &FixedWingLandingComplexItem::_recalcFactsFromCoords); - - _textFieldFacts << QVariant::fromValue(&_loiterToLandDistanceFact) << QVariant::fromValue(&_loiterAltitudeFact) << QVariant::fromValue(&_loiterRadiusFact) << QVariant::fromValue(&_landingHeadingFact); } int FixedWingLandingComplexItem::lastSequenceNumber(void) const @@ -147,10 +148,10 @@ QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const pMissionItems); // parent - allow delete on pMissionItems to delete everthing pMissionItems->append(item); - float loiterRadius = _loiterRadiusFact.rawValue().toDouble() * (_loiterClockwiseFact.rawValue().toBool() ? 1.0 : -1.0); + float loiterRadius = _loiterRadiusFact.rawValue().toDouble() * (_loiterClockwise ? 1.0 : -1.0); item = new MissionItem(seqNum++, MAV_CMD_NAV_LOITER_TO_ALT, - MAV_FRAME_GLOBAL_RELATIVE_ALT, + _loiterAltitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, 1.0, // Heading required = true loiterRadius, // Loiter radius 0.0, // param 3 - unused @@ -165,7 +166,7 @@ QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const item = new MissionItem(seqNum++, MAV_CMD_NAV_LAND, - MAV_FRAME_GLOBAL_RELATIVE_ALT, + _landingAltitudeRelative ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, 0.0, 0.0, 0.0, 0.0, // param 1-4 _landingCoordinate.latitude(), _landingCoordinate.longitude(), diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 4e12c8506..a9be9ccfc 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -24,13 +24,23 @@ class FixedWingLandingComplexItem : public ComplexMissionItem public: FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent = NULL); - Q_PROPERTY(QVariantList textFieldFacts MEMBER _textFieldFacts CONSTANT) - Q_PROPERTY(Fact* loiterClockwise READ loiterClockwise CONSTANT) - Q_PROPERTY(QGeoCoordinate loiterCoordinate READ loiterCoordinate WRITE setLoiterCoordinate NOTIFY loiterCoordinateChanged) - Q_PROPERTY(QGeoCoordinate landingCoordinate READ landingCoordinate WRITE setLandingCoordinate NOTIFY landingCoordinateChanged) - Q_PROPERTY(bool landingCoordSet MEMBER _landingCoordSet NOTIFY landingCoordSetChanged) - - Fact* loiterClockwise (void) { return &_loiterClockwiseFact; } + Q_PROPERTY(Fact* loiterAltitude READ loiterAltitude CONSTANT) + Q_PROPERTY(Fact* loiterRadius READ loiterRadius CONSTANT) + Q_PROPERTY(Fact* landingAltitude READ landingAltitude CONSTANT) + Q_PROPERTY(Fact* landingDistance READ landingDistance CONSTANT) + Q_PROPERTY(Fact* landingHeading READ landingHeading CONSTANT) + Q_PROPERTY(bool loiterClockwise MEMBER _loiterClockwise NOTIFY loiterClockwiseChanged) + Q_PROPERTY(bool loiterAltitudeRelative MEMBER _loiterAltitudeRelative NOTIFY loiterAltitudeRelativeChanged) + Q_PROPERTY(bool landingAltitudeRelative MEMBER _landingAltitudeRelative NOTIFY landingAltitudeRelativeChanged) + Q_PROPERTY(QGeoCoordinate loiterCoordinate READ loiterCoordinate WRITE setLoiterCoordinate NOTIFY loiterCoordinateChanged) + Q_PROPERTY(QGeoCoordinate landingCoordinate READ landingCoordinate WRITE setLandingCoordinate NOTIFY landingCoordinateChanged) + Q_PROPERTY(bool landingCoordSet MEMBER _landingCoordSet NOTIFY landingCoordSetChanged) + + Fact* loiterAltitude (void) { return &_loiterAltitudeFact; } + Fact* loiterRadius (void) { return &_loiterRadiusFact; } + Fact* landingAltitude (void) { return &_landingAltitudeFact; } + Fact* landingDistance (void) { return &_loiterToLandDistanceFact; } + Fact* landingHeading (void) { return &_landingHeadingFact; } QGeoCoordinate landingCoordinate (void) const { return _landingCoordinate; } QGeoCoordinate loiterCoordinate (void) const { return _loiterCoordinate; } @@ -73,9 +83,12 @@ public: static const char* jsonComplexItemTypeValue; signals: - void loiterCoordinateChanged (QGeoCoordinate coordinate); - void landingCoordinateChanged (QGeoCoordinate coordinate); - void landingCoordSetChanged (bool landingCoordSet); + void loiterCoordinateChanged (QGeoCoordinate coordinate); + void landingCoordinateChanged (QGeoCoordinate coordinate); + void landingCoordSetChanged (bool landingCoordSet); + void loiterClockwiseChanged (bool loiterClockwise); + void loiterAltitudeRelativeChanged (bool loiterAltitudeRelative); + void landingAltitudeRelativeChanged (bool loiterAltitudeRelative); private slots: void _recalcLoiterCoordFromFacts(void); @@ -94,18 +107,20 @@ private: Fact _loiterToLandDistanceFact; Fact _loiterAltitudeFact; Fact _loiterRadiusFact; - Fact _loiterClockwiseFact; Fact _landingHeadingFact; + Fact _landingAltitudeFact; - static QMap _metaDataMap; + bool _loiterClockwise; + bool _loiterAltitudeRelative; + bool _landingAltitudeRelative; - QVariantList _textFieldFacts; + static QMap _metaDataMap; static const char* _loiterToLandDistanceName; static const char* _loiterAltitudeName; static const char* _loiterRadiusName; - static const char* _loiterClockwiseName; static const char* _landingHeadingName; + static const char* _landingAltitudeName; }; #endif -- GitLab From 3c7c9b746a1ff1a9dddfd73cb40588a251ed621a Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Fri, 24 Feb 2017 02:54:00 -0500 Subject: [PATCH 347/398] Added altitude to the Vehicle coordinate (QGeoCoordinate) --- src/Vehicle/Vehicle.cc | 20 +++++++++++++++----- src/Vehicle/Vehicle.h | 7 +++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 0794337dc..dfba32ecc 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -605,8 +605,11 @@ void Vehicle::_handleGpsRawInt(mavlink_message_t& message) if (gpsRawInt.fix_type >= GPS_FIX_TYPE_3D_FIX) { if (!_globalPositionIntMessageAvailable) { - setLatitude(gpsRawInt.lat / (double)1E7); - setLongitude(gpsRawInt.lon / (double)1E7); + //-- Set these here and emit a single signal instead of 3 for the same variable (_coordinate) + _coordinate.setLatitude(gpsRawInt.lat / (double)1E7); + _coordinate.setLongitude(gpsRawInt.lon / (double)1E7); + _coordinate.setAltitude(gpsRawInt.alt / 1000.0); + emit coordinateChanged(_coordinate); _altitudeAMSLFact.setRawValue(gpsRawInt.alt / 1000.0); } } @@ -628,9 +631,11 @@ void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message) mavlink_msg_global_position_int_decode(&message, &globalPositionInt); _globalPositionIntMessageAvailable = true; - - setLatitude(globalPositionInt.lat / (double)1E7); - setLongitude(globalPositionInt.lon / (double)1E7); + //-- Set these here and emit a single signal instead of 3 for the same variable (_coordinate) + _coordinate.setLatitude(globalPositionInt.lat / (double)1E7); + _coordinate.setLongitude(globalPositionInt.lon / (double)1E7); + _coordinate.setAltitude(globalPositionInt.alt / 1000.0); + emit coordinateChanged(_coordinate); _altitudeRelativeFact.setRawValue(globalPositionInt.relative_alt / 1000.0); _altitudeAMSLFact.setRawValue(globalPositionInt.alt / 1000.0); } @@ -1136,6 +1141,11 @@ void Vehicle::setLongitude(double longitude){ emit coordinateChanged(_coordinate); } +void Vehicle::setAltitude(double altitude){ + _coordinate.setAltitude(altitude); + emit coordinateChanged(_coordinate); +} + void Vehicle::_updateAttitude(UASInterface*, double roll, double pitch, double yaw, quint64) { if (qIsInf(roll)) { diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 89516ba87..91fd880d4 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -645,8 +645,11 @@ public: QString vehicleImageCompass () const; public slots: - void setLatitude(double latitude); - void setLongitude(double longitude); + /// Sets the firmware plugin instance data associated with this Vehicle. This object will be parented to the Vehicle + /// and destroyed when the vehicle goes away. + void setLatitude (double latitude); + void setLongitude (double longitude); + void setAltitude (double altitude); signals: void allLinksInactive(Vehicle* vehicle); -- GitLab From 15d62614b7685647c956914a77ed8136db821af9 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Thu, 23 Feb 2017 22:22:34 -0500 Subject: [PATCH 348/398] Add lights brightness steps to APMLightsComponent page --- .../APM/APMLightsComponent.qml | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMLightsComponent.qml b/src/AutoPilotPlugins/APM/APMLightsComponent.qml index aefb1d923..b2eac54bb 100644 --- a/src/AutoPilotPlugins/APM/APMLightsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMLightsComponent.qml @@ -42,6 +42,7 @@ SetupPage { property Fact _rc12Function: controller.getParameterFact(-1, "r.SERVO12_FUNCTION") property Fact _rc13Function: controller.getParameterFact(-1, "r.SERVO13_FUNCTION") property Fact _rc14Function: controller.getParameterFact(-1, "r.SERVO14_FUNCTION") + property Fact _stepSize: controller.getParameterFact(-1, "JS_LIGHTS_STEP") readonly property real _margins: ScreenTools.defaultFontPixelHeight readonly property int _rcFunctionDisabled: 0 @@ -52,6 +53,7 @@ SetupPage { Component.onCompleted: { calcLightOutValues() + calcCurrentStep() } /// Light output channels are stored in SERVO#_FUNCTION parameters. We need to loop through those @@ -85,6 +87,25 @@ SetupPage { } } + function calcCurrentStep() { + var i = 1 + for(i; i <= 10; i++) { + var stepSize = (1900-1100)/i + if(_stepSize.value >= stepSize) { + _stepSize.value = stepSize; + break; + } + } + if (_stepSize.value < 80) { + _stepSize.value = 80; + } + lightsLoader.lightsSteps = i + } + + function calcStepSize(steps) { + _stepSize.value = (1900-1100)/steps + } + // Whenever any SERVO#_FUNCTION parameters chagnes we need to go looking for light output channels again Connections { target: _rc5Function; onValueChanged: calcLightOutValues() } Connections { target: _rc6Function; onValueChanged: calcLightOutValues() } @@ -129,14 +150,14 @@ SetupPage { id: rectangle anchors.topMargin: _margins / 2 anchors.top: settingsLabel.bottom - width: lights1Combo.x + lights1Combo.width + _margins - height: lights2Combo.y + lights2Combo.height + _margins + width: lights1Combo.x + lights1Combo.width + lightsStepCombo.width + _margins + height: lights2Combo.y + lights2Combo.height + lightsStepCombo.height + 2*_margins color: palette.windowShade QGCLabel { id: lights1Label anchors.margins: _margins - anchors.left: parent.left + anchors.right: lights1Combo.left anchors.baseline: lights1Combo.baseline text: qsTr("Lights 1:") } @@ -145,7 +166,7 @@ SetupPage { id: lights1Combo anchors.margins: _margins anchors.top: parent.top - anchors.left: lights1Label.right + anchors.left: lightsStepLabel.right width: ScreenTools.defaultFontPixelWidth * 15 model: lightsOutModel currentIndex: lights1OutIndex @@ -156,7 +177,7 @@ SetupPage { QGCLabel { id: lights2Label anchors.margins: _margins - anchors.left: parent.left + anchors.right: lights2Combo.left anchors.baseline: lights2Combo.baseline text: qsTr("Lights 2:") } @@ -165,13 +186,33 @@ SetupPage { id: lights2Combo anchors.margins: _margins anchors.top: lights1Combo.bottom - anchors.left: lights2Label.right + anchors.left: lightsStepLabel.right width: lights1Combo.width model: lightsOutModel currentIndex: lights2OutIndex onActivated: setRCFunction(lightsOutModel.get(index).value, lights2Function) } + + QGCLabel { + id: lightsStepLabel + anchors.margins: _margins + anchors.left: parent.left + anchors.baseline: lightsStepCombo.baseline + text: qsTr("Brightness Steps:") + } + + QGCComboBox { + id: lightsStepCombo + anchors.margins: _margins + anchors.top: lights2Combo.bottom + anchors.left: lightsStepLabel.right + width: lights2Combo.width + model: [1,2,3,4,5,6,7,8,9,10] + currentIndex: lightsSteps-1 + + onActivated: calcStepSize(index+1) + } } // Rectangle } // Item } // Component - lightSettings @@ -184,6 +225,7 @@ SetupPage { property int lights2OutIndex: 0 property int lights1Function: _rcFunctionRCIN9 property int lights2Function: _rcFunctionRCIN10 + property int lightsSteps: 1 } } // Column } // Component -- GitLab From 204f64ef824703316e8b50acbfbd17d48dd1d347 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 24 Feb 2017 11:13:54 -0500 Subject: [PATCH 349/398] Cast rtspsrc timeout to expected 64bit type --- src/VideoStreaming/VideoReceiver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 0a58ff8d0..f54059df0 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -205,7 +205,7 @@ void VideoReceiver::start() } g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); } else { - g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, "udp-reconnect", 1, "timeout", 5000000, NULL); + g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, "udp-reconnect", 1, "timeout", static_cast(5000000), NULL); } if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) { -- GitLab From 2c30ce84d3be881580ad87f0752b0bdcec629769 Mon Sep 17 00:00:00 2001 From: Nate Weibley Date: Fri, 24 Feb 2017 11:14:58 -0500 Subject: [PATCH 350/398] Add imperceptible delay so jitterbuffer can help less ideal networks --- src/VideoStreaming/VideoReceiver.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index f54059df0..1c75e9c1c 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -205,7 +205,7 @@ void VideoReceiver::start() } g_object_set(G_OBJECT(dataSource), "uri", qPrintable(_uri), "caps", caps, NULL); } else { - g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 0, "udp-reconnect", 1, "timeout", static_cast(5000000), NULL); + g_object_set(G_OBJECT(dataSource), "location", qPrintable(_uri), "latency", 17, "udp-reconnect", 1, "timeout", static_cast(5000000), NULL); } if ((demux = gst_element_factory_make("rtph264depay", "rtp-h264-depacketizer")) == NULL) { -- GitLab From d89f6b7994d128720d16b566fd244f91fd476469 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 24 Feb 2017 09:55:20 -0800 Subject: [PATCH 351/398] Fix shutdown crash --- src/QGCApplication.cc | 11 ++++++++++- src/QGCApplication.h | 6 ++++++ src/main.cc | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index a92a4ed89..2e351302f 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -334,8 +334,12 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) _toolbox->setChildToolboxes(); } -QGCApplication::~QGCApplication() +void QGCApplication::_shutdown(void) { + // This code is specifically not in the destructor since the application object may not be available in the destructor. + // This cause problems for deleting object like settings which are in the toolbox which may have qml references. By + // moving them here and having main.cc call this prior to deleting the app object we make sure app object is still + // around while these things are shutting down. #ifndef __mobile__ MainWindow* mainWindow = MainWindow::instance(); if (mainWindow) { @@ -346,6 +350,11 @@ QGCApplication::~QGCApplication() delete _toolbox; } +QGCApplication::~QGCApplication() +{ + // Place shutdown code in _shutdown +} + void QGCApplication::_initCommon(void) { QSettings settings; diff --git a/src/QGCApplication.h b/src/QGCApplication.h index c080b2490..f5234c533 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -152,6 +152,12 @@ public: static QGCApplication* _app; ///< Our own singleton. Should be reference directly by qgcApp +public: + // Although public, these methods are internal and should only be called by UnitTest code + + /// Shutdown the application object + void _shutdown(void); + private slots: void _missingParamsDisplay(void); diff --git a/src/main.cc b/src/main.cc index 4dec260fa..8c2ff2ddc 100644 --- a/src/main.cc +++ b/src/main.cc @@ -253,6 +253,7 @@ int main(int argc, char *argv[]) exitCode = app->exec(); } + app->_shutdown(); delete app; //-- Shutdown Cache System destroyMapEngine(); -- GitLab From 1bdc7d606d17185068501a3ced5b92aa2bede274 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 24 Feb 2017 12:43:23 -0500 Subject: [PATCH 352/398] Disable scrolling on QGCComboBox --- src/QmlControls/QGCComboBox.qml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/QmlControls/QGCComboBox.qml b/src/QmlControls/QGCComboBox.qml index 116ffb0e6..8b00e725d 100644 --- a/src/QmlControls/QGCComboBox.qml +++ b/src/QmlControls/QGCComboBox.qml @@ -39,4 +39,24 @@ ComboBox { } } } + + // Capture Wheel events to disable scrolling options in ComboBox. + // As a side effect, this also prevents scrolling the page when + // mouse is over a ComboBox, but this would also the case when + // scrolling items in the ComboBox is enabled. + MouseArea { + anchors.fill: parent + onWheel: { + // do nothing + wheel.accepted = true; + } + onPressed: { + // propogate to ComboBox + mouse.accepted = false; + } + onReleased: { + // propogate to ComboBox + mouse.accepted = false; + } + } } -- GitLab From a5808707c987b65ade19357b6ec6fb09505013f0 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 24 Feb 2017 10:42:31 -0800 Subject: [PATCH 353/398] Correct init of _editorQml --- src/MissionManager/SimpleMissionItem.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 21a04129f..d206a0595 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -65,7 +65,7 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent) , _syncingAltitudeRelativeToHomeAndFrame (false) , _syncingHeadingDegreesAndParam4 (false) { - _editorQml = "qrc:/qml/SimpleItemEditor.qml"; + _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); _altitudeRelativeToHomeFact.setRawValue(true); @@ -97,6 +97,8 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, const MissionItem& missio , _syncingAltitudeRelativeToHomeAndFrame (false) , _syncingHeadingDegreesAndParam4 (false) { + _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); + _altitudeRelativeToHomeFact.setRawValue(true); _setupMetaData(); @@ -122,6 +124,8 @@ SimpleMissionItem::SimpleMissionItem(const SimpleMissionItem& other, QObject* pa , _syncingAltitudeRelativeToHomeAndFrame (false) , _syncingHeadingDegreesAndParam4 (false) { + _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml"); + _setupMetaData(); _connectSignals(); -- GitLab From 98fde8d017ce392aa4a9ba23e96c0cd36a0a06b6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 24 Feb 2017 12:41:08 -0800 Subject: [PATCH 354/398] Add Simple flight mode for multi-rotor --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 2 ++ src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 1 + src/FirmwarePlugin/PX4/px4_custom_mode.h | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 88fd03457..142f0f75a 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -50,6 +50,7 @@ const char* PX4FirmwarePlugin::_rattitudeFlightMode = "Rattitude"; const char* PX4FirmwarePlugin::_followMeFlightMode = "Follow Me"; const char* PX4FirmwarePlugin::_rtgsFlightMode = "Return to Groundstation"; const char* PX4FirmwarePlugin::_readyFlightMode = "Ready"; +const char* PX4FirmwarePlugin::_simpleFlightMode = "Simple"; /// Tranlates from PX4 custom modes to flight mode names @@ -61,6 +62,7 @@ static const struct Modes2Name rgModes2Name[] = { { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::_altCtlFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::_posCtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_SIMPLE, 0, PX4FirmwarePlugin::_simpleFlightMode, true, false, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::_missionFlightMode, true, true, true }, { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::_rtlFlightMode, true, true, true }, diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index 718f8b7f4..b30dfa192 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -81,6 +81,7 @@ public: static const char* _landingFlightMode; static const char* _rtgsFlightMode; static const char* _followMeFlightMode; + static const char* _simpleFlightMode; private: void _handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message); diff --git a/src/FirmwarePlugin/PX4/px4_custom_mode.h b/src/FirmwarePlugin/PX4/px4_custom_mode.h index b3e9de3ab..6569ce6e0 100644 --- a/src/FirmwarePlugin/PX4/px4_custom_mode.h +++ b/src/FirmwarePlugin/PX4/px4_custom_mode.h @@ -50,7 +50,8 @@ enum PX4_CUSTOM_MAIN_MODE { PX4_CUSTOM_MAIN_MODE_ACRO, PX4_CUSTOM_MAIN_MODE_OFFBOARD, PX4_CUSTOM_MAIN_MODE_STABILIZED, - PX4_CUSTOM_MAIN_MODE_RATTITUDE + PX4_CUSTOM_MAIN_MODE_RATTITUDE, + PX4_CUSTOM_MAIN_MODE_SIMPLE }; enum PX4_CUSTOM_SUB_MODE_AUTO { -- GitLab From 9fab90ee86fc58ce031311b27b52398940fb5e78 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Fri, 24 Feb 2017 12:56:40 -0800 Subject: [PATCH 355/398] Virtual should be override not final This way third parties can use PX4FirmwarePlugin as base class and override as needed --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index 718f8b7f4..b290d2b7f 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -28,40 +28,40 @@ public: // Overrides from FirmwarePlugin - QList componentsForVehicle(AutoPilotPlugin* vehicle) final; - QList supportedMissionCommands(void) final; + QList componentsForVehicle(AutoPilotPlugin* vehicle) override; + QList supportedMissionCommands(void) override; - AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) final; - bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities) final; - QStringList flightModes (Vehicle* vehicle) final; - QString flightMode (uint8_t base_mode, uint32_t custom_mode) const final; - bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) final; - void setGuidedMode (Vehicle* vehicle, bool guidedMode) final; - void pauseVehicle (Vehicle* vehicle) final; - void guidedModeRTL (Vehicle* vehicle) final; - void guidedModeLand (Vehicle* vehicle) final; - void guidedModeTakeoff (Vehicle* vehicle, double altitudeRel) final; - void guidedModeOrbit (Vehicle* vehicle, const QGeoCoordinate& centerCoord = QGeoCoordinate(), double radius = NAN, double velocity = NAN, double altitude = NAN) final; - void guidedModeGotoLocation (Vehicle* vehicle, const QGeoCoordinate& gotoCoord) final; - void guidedModeChangeAltitude (Vehicle* vehicle, double altitudeRel) final; - bool isGuidedMode (const Vehicle* vehicle) const; - int manualControlReservedButtonCount(void) final; - bool supportsManualControl (void) final; - void initializeVehicle (Vehicle* vehicle) final; - bool sendHomePositionToVehicle (void) final; - void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) final; - QString missionCommandOverrides (MAV_TYPE vehicleType) const final; - QString getVersionParam (void) final { return QString("SYS_PARAM_VER"); } - QString internalParameterMetaDataFile (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } - void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) final { PX4ParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } - QObject* loadParameterMetaData (const QString& metaDataFile); - bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message); - GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) { return new PX4GeoFenceManager(vehicle); } - QString offlineEditingParamFile(Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/PX4/PX4.OfflineEditing.params"); } - QString brandImage (const Vehicle* vehicle) const { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/PX4/BrandImage"); } - QString missionFlightMode (void) final; - QString rtlFlightMode (void) final; - QString takeControlFlightMode (void) final; + AutoPilotPlugin* autopilotPlugin (Vehicle* vehicle) override; + bool isCapable (const Vehicle *vehicle, FirmwareCapabilities capabilities) override; + QStringList flightModes (Vehicle* vehicle) override; + QString flightMode (uint8_t base_mode, uint32_t custom_mode) const override; + bool setFlightMode (const QString& flightMode, uint8_t* base_mode, uint32_t* custom_mode) override; + void setGuidedMode (Vehicle* vehicle, bool guidedMode) override; + void pauseVehicle (Vehicle* vehicle) override; + void guidedModeRTL (Vehicle* vehicle) override; + void guidedModeLand (Vehicle* vehicle) override; + void guidedModeTakeoff (Vehicle* vehicle, double altitudeRel) override; + void guidedModeOrbit (Vehicle* vehicle, const QGeoCoordinate& centerCoord = QGeoCoordinate(), double radius = NAN, double velocity = NAN, double altitude = NAN) override; + void guidedModeGotoLocation (Vehicle* vehicle, const QGeoCoordinate& gotoCoord) override; + void guidedModeChangeAltitude (Vehicle* vehicle, double altitudeRel) override; + bool isGuidedMode (const Vehicle* vehicle) const override; + int manualControlReservedButtonCount(void) override; + bool supportsManualControl (void) override; + void initializeVehicle (Vehicle* vehicle) override; + bool sendHomePositionToVehicle (void) override; + void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) override; + QString missionCommandOverrides (MAV_TYPE vehicleType) const override; + QString getVersionParam (void) override { return QString("SYS_PARAM_VER"); } + QString internalParameterMetaDataFile (Vehicle* vehicle) override { Q_UNUSED(vehicle); return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } + void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) override { PX4ParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } + QObject* loadParameterMetaData (const QString& metaDataFile) final; + bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) override; + GeoFenceManager* newGeoFenceManager (Vehicle* vehicle) override { return new PX4GeoFenceManager(vehicle); } + QString offlineEditingParamFile(Vehicle* vehicle) override { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/PX4/PX4.OfflineEditing.params"); } + QString brandImage (const Vehicle* vehicle) const override { Q_UNUSED(vehicle); return QStringLiteral("/qmlimages/PX4/BrandImage"); } + QString missionFlightMode (void) override; + QString rtlFlightMode (void) override; + QString takeControlFlightMode (void) override; // NOTE: For internal use only. Do not use in mainline QGC code. // Use these constants to set flight modes using setFlightMode method. Don't use hardcoded string names since the -- GitLab From 9a35dbf750b90b8faf0500ae93587b92fa2d43ed Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 24 Feb 2017 16:15:56 -0500 Subject: [PATCH 356/398] Add git hash property to vehicle --- src/Vehicle/Vehicle.cc | 7 +++++++ src/Vehicle/Vehicle.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 0794337dc..87ca27c37 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -290,6 +290,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _firmwareMajorVersion(versionNotSetValue) , _firmwareMinorVersion(versionNotSetValue) , _firmwarePatchVersion(versionNotSetValue) + , _gitHash(versionNotSetValue) , _rollFact (0, _rollFactName, FactMetaData::valueTypeDouble) , _pitchFact (0, _pitchFactName, FactMetaData::valueTypeDouble) , _headingFact (0, _headingFactName, FactMetaData::valueTypeDouble) @@ -666,6 +667,12 @@ void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& me versionType = (FIRMWARE_VERSION_TYPE)((autopilotVersion.flight_sw_version >> (8*0)) & 0xFF); setFirmwareVersion(majorVersion, minorVersion, patchVersion, versionType); } + + // Git hash + if (autopilotVersion.flight_custom_version[0] != 0) { + _gitHash = QString::fromUtf8((char*)autopilotVersion.flight_custom_version, 8); + emit gitHashChanged(_gitHash); + } } void Vehicle::_handleHilActuatorControls(mavlink_message_t &message) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 89516ba87..7ebf4e370 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -346,6 +346,7 @@ public: Q_PROPERTY(int firmwarePatchVersion READ firmwarePatchVersion NOTIFY firmwarePatchVersionChanged) Q_PROPERTY(int firmwareVersionType READ firmwareVersionType NOTIFY firmwareVersionTypeChanged) Q_PROPERTY(QString firmwareVersionTypeString READ firmwareVersionTypeString NOTIFY firmwareVersionTypeChanged) + Q_PROPERTY(QString gitHash READ gitHash NOTIFY gitHashChanged) /// Resets link status counters Q_INVOKABLE void resetCounters (); @@ -616,6 +617,8 @@ public: void setFirmwareVersion(int majorVersion, int minorVersion, int patchVersion, FIRMWARE_VERSION_TYPE versionType = FIRMWARE_VERSION_TYPE_OFFICIAL); static const int versionNotSetValue = -1; + QString gitHash(void) const { return _gitHash; } + bool soloFirmware(void) const { return _soloFirmware; } void setSoloFirmware(bool soloFirmware); @@ -706,6 +709,8 @@ signals: void firmwarePatchVersionChanged(int patch); void firmwareVersionTypeChanged(int type); + void gitHashChanged(QString hash); + /// New RC channel values /// @param channelCount Number of available channels, cMaxRcChannels max /// @param pwmValues -1 signals channel not available @@ -938,6 +943,8 @@ private: int _firmwarePatchVersion; FIRMWARE_VERSION_TYPE _firmwareVersionType; + QString _gitHash; + static const int _lowBatteryAnnounceRepeatMSecs; // Amount of time in between each low battery announcement QElapsedTimer _lowBatteryAnnounceTimer; -- GitLab From 70bd3c71977c0020bee3876caa7d0d05e606cbfd Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Fri, 24 Feb 2017 16:16:24 -0500 Subject: [PATCH 357/398] Display git hash in Sub frame summary --- src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml b/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml index 6dcf659fd..e2b33b2fa 100644 --- a/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponentSummary.qml @@ -53,5 +53,10 @@ FactPanel { labelText: qsTr("Firmware Version:") valueText: activeVehicle.firmwareMajorVersion == -1 ? qsTr("Unknown") : activeVehicle.firmwareMajorVersion + "." + activeVehicle.firmwareMinorVersion + "." + activeVehicle.firmwarePatchVersion + "-" + activeVehicle.firmwareVersionTypeString } + + VehicleSummaryRow { + labelText: qsTr("Git Revision:") + valueText: activeVehicle.gitHash == -1 ? qsTr("Unknown") : activeVehicle.gitHash + } } } -- GitLab From 9e29a3b943e994ab709d2630a33c9b0ee8a2be95 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 25 Feb 2017 11:38:39 -0800 Subject: [PATCH 358/398] All third-party flight mode overrides --- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc | 108 +++++++++++--------- src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h | 12 +++ 2 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 142f0f75a..f64b683f8 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -26,15 +26,6 @@ #include "px4_custom_mode.h" -struct Modes2Name { - uint8_t main_mode; - uint8_t sub_mode; - const char* name; ///< Name for flight mode - bool canBeSet; ///< true: Vehicle can be set to this flight mode - bool fixedWing; /// fixed wing compatible - bool multiRotor; /// multi rotor compatible -}; - const char* PX4FirmwarePlugin::_manualFlightMode = "Manual"; const char* PX4FirmwarePlugin::_altCtlFlightMode = "Altitude"; const char* PX4FirmwarePlugin::_posCtlFlightMode = "Position"; @@ -52,29 +43,6 @@ const char* PX4FirmwarePlugin::_rtgsFlightMode = "Return to Groundstation const char* PX4FirmwarePlugin::_readyFlightMode = "Ready"; const char* PX4FirmwarePlugin::_simpleFlightMode = "Simple"; -/// Tranlates from PX4 custom modes to flight mode names - -static const struct Modes2Name rgModes2Name[] = { - //main_mode sub_mode name canBeSet FW MC - { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::_manualFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::_stabilizedFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::_acroFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::_altCtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::_posCtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_SIMPLE, 0, PX4FirmwarePlugin::_simpleFlightMode, true, false, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::_missionFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::_rtlFlightMode, true, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::_followMeFlightMode, true, false, true }, - { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::_offboardFlightMode, true, false, true }, - // modes that can't be directly set by the user - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::_landingFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::_readyFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTGS, PX4FirmwarePlugin::_rtgsFlightMode, false, true, true }, - { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF, PX4FirmwarePlugin::_takeoffFlightMode, false, true, true }, -}; - PX4FirmwarePlugin::PX4FirmwarePlugin(void) : _versionNotified(false) { @@ -84,6 +52,52 @@ PX4FirmwarePlugin::PX4FirmwarePlugin(void) qmlRegisterType ("QGroundControl.Controllers", 1, 0, "SensorsComponentController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "PowerComponentController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "RadioComponentController"); + + struct Modes2Name { + uint8_t main_mode; + uint8_t sub_mode; + const char* name; ///< Name for flight mode + bool canBeSet; ///< true: Vehicle can be set to this flight mode + bool fixedWing; /// fixed wing compatible + bool multiRotor; /// multi rotor compatible + }; + + static const struct Modes2Name rgModes2Name[] = { + //main_mode sub_mode name canBeSet FW MC + { PX4_CUSTOM_MAIN_MODE_MANUAL, 0, PX4FirmwarePlugin::_manualFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_STABILIZED, 0, PX4FirmwarePlugin::_stabilizedFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_ACRO, 0, PX4FirmwarePlugin::_acroFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_RATTITUDE, 0, PX4FirmwarePlugin::_rattitudeFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_ALTCTL, 0, PX4FirmwarePlugin::_altCtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_POSCTL, 0, PX4FirmwarePlugin::_posCtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_SIMPLE, 0, PX4FirmwarePlugin::_simpleFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LOITER, PX4FirmwarePlugin::_holdFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION, PX4FirmwarePlugin::_missionFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTL, PX4FirmwarePlugin::_rtlFlightMode, true, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_FOLLOW_TARGET, PX4FirmwarePlugin::_followMeFlightMode, true, false, true }, + { PX4_CUSTOM_MAIN_MODE_OFFBOARD, 0, PX4FirmwarePlugin::_offboardFlightMode, true, false, true }, + // modes that can't be directly set by the user + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_LAND, PX4FirmwarePlugin::_landingFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_READY, PX4FirmwarePlugin::_readyFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_RTGS, PX4FirmwarePlugin::_rtgsFlightMode, false, true, true }, + { PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF, PX4FirmwarePlugin::_takeoffFlightMode, false, true, true }, + }; + + // Convert static information to dynamic list. This allows for plugin override class to manipulate list. + for (size_t i=0; imain_mode; + info.sub_mode = pModes2Name->sub_mode; + info.name = pModes2Name->name; + info.canBeSet = pModes2Name->canBeSet; + info.fixedWing = pModes2Name->fixedWing; + info.multiRotor = pModes2Name->multiRotor; + + _flightModeInfoList.append(info); + } } AutoPilotPlugin* PX4FirmwarePlugin::autopilotPlugin(Vehicle* vehicle) @@ -102,18 +116,16 @@ QStringList PX4FirmwarePlugin::flightModes(Vehicle* vehicle) { QStringList flightModes; - for (size_t i=0; icanBeSet) { - bool fw = (vehicle->fixedWing() && pModes2Name->fixedWing); - bool mc = (vehicle->multiRotor() && pModes2Name->multiRotor); + foreach (const FlightModeInfo_t& info, _flightModeInfoList) { + if (info.canBeSet) { + bool fw = (vehicle->fixedWing() && info.fixedWing); + bool mc = (vehicle->multiRotor() && info.multiRotor); // show all modes for generic, vtol, etc bool other = !vehicle->fixedWing() && !vehicle->multiRotor(); if (fw || mc || other) { - flightModes += pModes2Name->name; + flightModes += info.name; } } } @@ -130,11 +142,9 @@ QString PX4FirmwarePlugin::flightMode(uint8_t base_mode, uint32_t custom_mode) c px4_mode.data = custom_mode; bool found = false; - for (size_t i=0; imain_mode == px4_mode.main_mode && pModes2Name->sub_mode == px4_mode.sub_mode) { - flightMode = pModes2Name->name; + foreach (const FlightModeInfo_t& info, _flightModeInfoList) { + if (info.main_mode == px4_mode.main_mode && info.sub_mode == px4_mode.sub_mode) { + flightMode = info.name; found = true; break; } @@ -156,15 +166,13 @@ bool PX4FirmwarePlugin::setFlightMode(const QString& flightMode, uint8_t* base_m *custom_mode = 0; bool found = false; - for (size_t i=0; iname, Qt::CaseInsensitive) == 0) { + foreach (const FlightModeInfo_t& info, _flightModeInfoList) { + if (flightMode.compare(info.name, Qt::CaseInsensitive) == 0) { union px4_custom_mode px4_mode; px4_mode.data = 0; - px4_mode.main_mode = pModes2Name->main_mode; - px4_mode.sub_mode = pModes2Name->sub_mode; + px4_mode.main_mode = info.main_mode; + px4_mode.sub_mode = info.sub_mode; *base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED; *custom_mode = px4_mode.data; diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index 8b65d2dfb..cdcba598d 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -83,6 +83,18 @@ public: static const char* _followMeFlightMode; static const char* _simpleFlightMode; +protected: + typedef struct { + uint8_t main_mode; + uint8_t sub_mode; + QString name; ///< Name for flight mode + bool canBeSet; ///< true: Vehicle can be set to this flight mode + bool fixedWing; /// fixed wing compatible + bool multiRotor; /// multi rotor compatible + } FlightModeInfo_t; + + QList _flightModeInfoList; + private: void _handleAutopilotVersion(Vehicle* vehicle, mavlink_message_t* message); -- GitLab From c4da69536e067addfbf394609e9369c1c2d00129 Mon Sep 17 00:00:00 2001 From: Lorenz Meier Date: Sat, 25 Feb 2017 19:26:00 +0100 Subject: [PATCH 359/398] Update PX4 meta data --- .../PX4/AirframeFactMetaData.xml | 241 +- .../PX4/PX4ParameterFactMetaData.xml | 2204 +++++++++++------ 2 files changed, 1637 insertions(+), 808 deletions(-) diff --git a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml index a5e7bd16f..81d6019f7 100644 --- a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml +++ b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml @@ -14,18 +14,10 @@ - - Simon Wilks <simon@px4.io> - Flying Wing - - - Simon Wilks <simon@px4.io> - Flying Wing - - + Simon Wilks <simon@px4.io> Flying Wing - https://pixhawk.org/platforms/planes/z-84_wing_wing + https://pixhawk.org/platforms/planes/bormatec_camflyer_q feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -33,8 +25,8 @@ right aileron throttle - - Lorenz Meier <lorenz@px4.io> + + Simon Wilks <simon@px4.io> Flying Wing https://pixhawk.org/platforms/planes/z-84_wing_wing feed-through of RC AUX1 channel @@ -55,10 +47,10 @@ right aileron throttle - - Simon Wilks <simon@px4.io> + + Lorenz Meier <lorenz@px4.io> Flying Wing - https://pixhawk.org/platforms/planes/bormatec_camflyer_q + https://pixhawk.org/platforms/planes/z-84_wing_wing feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel @@ -66,6 +58,14 @@ right aileron throttle + + Simon Wilks <simon@px4.io> + Flying Wing + + + Simon Wilks <simon@px4.io> + Flying Wing + Simon Wilks <simon@px4.io> Flying Wing @@ -86,6 +86,11 @@ Bart Slinger <bartslinger@gmail.com> Helicopter + main motor + front swashplate servo + right swashplate servo + left swashplate servo + tail-rotor servo @@ -179,57 +184,69 @@ Anton Babushkin <anton@px4.io>, Simon Wilks <simon@px4.io> Quadrotor Wide - - Thomas Gubler <thomas@px4.io> - Quadrotor Wide - Anton Babushkin <anton@px4.io> Quadrotor Wide + + Thomas Gubler <thomas@px4.io> + Quadrotor Wide + Simon Wilks <simon@px4.io> Quadrotor Wide - - - Michael Schaeuble - Quadrotor x + + + Mark Whitehorn <kd0aij@gmail.com> + Quadrotor asymmetric + motor1 (front right: CCW) + motor2 (back left: CCW) + motor3 (front left: CW) + motor4 (back right: CW) + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + + Lorenz Meier <lorenz@px4.io> Quadrotor x - - Andreas Antener <andreas@uaventure.com> - Quadrotor x - - - Blankered - Quadrotor x - - + + Leon Mueller <thedevleon> Quadrotor x - - Mark Whitehorn <kd0aij@gmail.com> + + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Pavel Kirienko <pavel@px4.io> + + James Goppert <james.goppert@gmail.com> Quadrotor x + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel Leon Mueller <thedevleon> Quadrotor x - - Lorenz Meier <lorenz@px4.io> + + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x + feed-through of RC AUX1 channel + feed-through of RC AUX2 channel + feed-through of RC AUX3 channel + feed-through of RC FLAPS channel + motor1 (front right: CCW) + motor2 (back left: CCW) + motor3 (front left: CW) + motor4 (back right: CW) Lorenz Meier <lorenz@px4.io> @@ -238,51 +255,45 @@ feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - James Goppert <james.goppert@gmail.com> + + Lorenz Meier <lorenz@px4.io> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Thomas Gubler <thomas@px4.io> + + Pavel Kirienko <pavel@px4.io> Quadrotor x - - James Goppert <james.goppert@gmail.com> + + Blankered Quadrotor x - feed-through of RC AUX1 channel - feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - + Mark Whitehorn <kd0aij@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel + feed-through of RC FLAPS channel + motor1 (front right: CCW) + motor2 (back left: CCW) + motor3 (front left: CW) + motor4 (back right: CW) - - Leon Mueller <thedevleon> - Quadrotor x - - - Lorenz Meier <lorenz@px4.io> + + James Goppert <james.goppert@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel - - Lorenz Meier <lorenz@px4.io> + + Anton Matosov <anton.matosov@gmail.com> Quadrotor x feed-through of RC AUX1 channel feed-through of RC AUX2 channel - feed-through of RC AUX3 channel - - - Quadrotor x @@ -299,17 +310,21 @@ rudder throttle - + Anton Babushkin <anton@px4.io> Simulation - + Anton Babushkin <anton@px4.io> Simulation - + + Lorenz Meier <lorenz@px4.io> + Standard Plane + + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel @@ -317,25 +332,23 @@ feed-through of RC AUX3 channel aileron elevator - throttle - rudder + rudder + throttle flaps - - Andreas Antener <andreas@uaventure.com> + + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel aileron - aileron - elevator + elevator + throttle rudder - throttle - wheel - flaps + flaps - + Lorenz Meier <lorenz@px4.io> Standard Plane feed-through of RC AUX1 channel @@ -343,9 +356,7 @@ feed-through of RC AUX3 channel aileron elevator - rudder throttle - flaps Lorenz Meier <lorenz@px4.io> @@ -359,42 +370,58 @@ rudder flaps - - Lorenz Meier <lorenz@px4.io> + + Andreas Antener <andreas@uaventure.com> Standard Plane feed-through of RC AUX1 channel feed-through of RC AUX2 channel feed-through of RC AUX3 channel aileron - elevator - throttle - - - Lorenz Meier <lorenz@px4.io> - Standard Plane + aileron + elevator + rudder + throttle + wheel + flaps - - Andreas Antener <andreas@uaventure.com> - Standard VTOL - - - Sander Smeets <sander@droneslab.com> - Standard VTOL - - + Simon Wilks <simon@uaventure.com> Standard VTOL - - + Aileron 1 + Aileron 2 + Elevator + Rudder + Throttle + Front right motor: CCW + Back left motor: CCW + Front left motor: CW + Back right motor: CW + + Simon Wilks <simon@uaventure.com> Standard VTOL + Right elevon + Left elevon + Motor + Front right motor: CCW + Back left motor: CCW + Front left motor: CW + Back right motor: CW Sander Smeets <sander@droneslab.com> Standard VTOL + + Sander Smeets <sander@droneslab.com> + Standard VTOL + + + Andreas Antener <andreas@uaventure.com> + Standard VTOL + @@ -429,14 +456,36 @@ - - Roman Bapst <roman@px4.io> + + Roman Bapst <roman@uaventure.com> VTOL Tiltrotor + Tilt servo + Elevon 1 + Elevon 2 + Gear + Front right motor bottom + Front right motor top + Back motor bottom + Back motor top + Front left motor bottom + Front left motor top Samay Siga <samay_s@icloud.com> VTOL Tiltrotor + + Andreas Antener <andreas@uaventure.com> + VTOL Tiltrotor + Motor right + Motor left + Motor back + empty + Tilt servo right + Tilt servo left + Elevon right + Elevon left + diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index 83682333a..782fefd92 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -2,7 +2,7 @@ 3 1 - 9 + 14 Speed controller bandwidth @@ -137,57 +137,6 @@ 0 - - - Body angular rate process noise - examples/attitude_estimator_ekf - - - Body angular acceleration process noise - examples/attitude_estimator_ekf - - - Acceleration process noise - examples/attitude_estimator_ekf - - - Magnet field vector process noise - examples/attitude_estimator_ekf - - - Gyro measurement noise - examples/attitude_estimator_ekf - - - Accel measurement noise - examples/attitude_estimator_ekf - - - Mag measurement noise - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (1, 1) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (2, 2) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (3, 3) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia enabled in estimator - If set to != 0 the moment of inertia will be used in the estimator - - examples/attitude_estimator_ekf - - Complimentary filter accelerometer weight @@ -255,13 +204,6 @@ velocity 3 modules/attitude_estimator_q - - Threshold (of RMS) to warn about high vibration levels - 0.01 - 10 - 2 - modules/attitude_estimator_q - @@ -339,7 +281,7 @@ velocity Critical threshold - Sets the threshold when the battery will be reported as critically low. This has to be lower than the low threshold. This threshold commonly will trigger RTL or landing. + Sets the threshold when the battery will be reported as critically low. This has to be lower than the low threshold. This threshold commonly will trigger RTL. 0.05 0.1 norm @@ -347,9 +289,19 @@ velocity 0.01 modules/systemlib + + Emergency threshold + Sets the threshold when the battery will be reported as dangerously low. This has to be lower than the critical threshold. This threshold commonly will trigger landing. + 0.03 + 0.07 + norm + 2 + 0.01 + modules/systemlib + Voltage drop per cell on full throttle - This implicitely defines the internal resistance to maximum current ratio and assumes linearity. A good value to use is the difference between the 5C and 20-25C load. + This implicitely defines the internal resistance to maximum current ratio and assumes linearity. A good value to use is the difference between the 5C and 20-25C load. Not used if BAT_R_INTERNAL is set. 0.07 0.5 V @@ -357,6 +309,14 @@ velocity 0.01 modules/systemlib + + Explicitly defines the per cell internal resistance + If non-negative, then this will be used in place of BAT_V_LOAD_DROP for all calculations. + -1.0 + 0.2 + Ohms + modules/systemlib + Number of cells Defines the number of cells the attached battery consists of. @@ -400,6 +360,7 @@ velocity drivers/camera_trigger GPIO + MAVLink (forward via MAV_CMD_IMAGE_START_CAPTURE) Seagull MAP2 (PWM) @@ -666,6 +627,17 @@ velocity Don't allow arming without GPS + + Arm switch is only a button + The default uses the arm switch as real switch. If parameter set button gets handled like stick arming. + 0 + 1 + modules/commander + + Arm switch is a button that only triggers arming and disarming + Arm switch is a switch that stays on when armed + + Battery failsafe mode Action the system takes on low battery. Defaults to off @@ -673,8 +645,9 @@ velocity 1 modules/commander - Return to Land + Return to land Warning + Return to land at critically low level, land at current position if reaching dangerously low levels Land at current position @@ -687,33 +660,80 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action 1 modules/commander - - - - Airfield home Lat - Latitude of airfield home waypoint - -900000000 - 900000000 - deg * 1e7 - modules/navigator + + Maximum EKF position innovation test ratio that will allow arming + 0.1 + 1.0 + m + 2 + 0.05 + modules/commander - - Airfield home Lon - Longitude of airfield home waypoint - -1800000000 - 1800000000 - deg * 1e7 - modules/navigator + + Maximum EKF velocity innovation test ratio that will allow arming + 0.1 + 1.0 + m/s + 2 + 0.05 + modules/commander - - Airfield home alt - Altitude of airfield home waypoint - -50 + + Maximum EKF height innovation test ratio that will allow arming + 0.1 + 1.0 m - 1 - 0.5 - modules/navigator + 2 + 0.05 + modules/commander + + + Maximum EKF yaw innovation test ratio that will allow arming + 0.1 + 1.0 + rad + 2 + 0.05 + modules/commander + + + Maximum value of EKF accelerometer delta velocity bias estimate that will allow arming + 0.001 + 0.01 + m/s + 4 + 0.0005 + modules/commander + + + Maximum value of EKF gyro delta angle bias estimate that will allow arming + 0.0001 + 0.0017 + rad + 5 + 0.0001 + modules/commander + + + Maximum accelerometer inconsistency between IMU units that will allow arming + 0.1 + 1.0 + m/s/s + 2 + 0.05 + modules/commander + + + Maximum rate gyro inconsistency between IMU units that will allow arming + 0.02 + 0.2 + rad/s + 3 + 0.01 + modules/commander + + Comms hold wait time The amount of time in seconds the system should wait at the comms hold waypoint @@ -773,8 +793,41 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action modules/navigator + + Airfield home Lat + Latitude of airfield home waypoint + -900000000 + 900000000 + deg * 1e7 + modules/navigator + + + Airfield home Lon + Longitude of airfield home waypoint + -1800000000 + 1800000000 + deg * 1e7 + modules/navigator + + + Airfield home alt + Altitude of airfield home waypoint + -50 + m + 1 + 0.5 + modules/navigator + + + Minimum time of arrival delta between non-IMU observations before data is downsampled. +Baro and Magnetometer data will be averaged before downsampling, other data will be point sampled resulting in loss of information + 10 + 50 + ms + modules/ekf2 + Magnetometer measurement delay relative to IMU measurements 0 @@ -1015,6 +1068,14 @@ Assumes measurement is timestamped at trailing edge of integration period1 modules/ekf2 + + Noise for synthetic sideslip fusion + 0.1 + 1.0 + m/s + 2 + modules/ekf2 + Magnetic declination deg @@ -1313,6 +1374,12 @@ value will determine the minimum airspeed which will still be fused 1 modules/ekf2 + + Boolean determining if synthetic sideslip measurements should fused + A value of 1 indicates that fusion is active + + modules/ekf2 + Time constant of the velocity output prediction and smoothing filter 1.0 @@ -1352,6 +1419,14 @@ value will determine the minimum airspeed which will still be fused 3 modules/ekf2 + + Range sensor pitch offset + -0.75 + 0.75 + rad + 3 + modules/ekf2 + @@ -1501,6 +1576,14 @@ value will determine the minimum airspeed which will still be fused 0.5 modules/fw_att_control + + Roll control to yaw control feedforward gain + This gain can be used to counteract the "adverse yaw" effect for fixed wings. When the plane enters a roll it will tend to yaw the nose out of the turn. This gain enables the use of a yaw actuator (rudder, airbrakes, ...) to counteract this effect. + 0.0 + 1 + 0.01 + modules/fw_att_control + Wheel steering rate proportional gain This defines how much the wheel steering input will be commanded depending on the current body angular rate error. @@ -1699,6 +1782,45 @@ value will determine the minimum airspeed which will still be fused 0.01 modules/fw_att_control + + Whether to scale throttle by battery power level + This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The fixed wing should constantly behave as if it was fully charged with reduced max thrust at lower battery percentages. i.e. if cruise speed is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + modules/fw_att_control + + + Acro body x max rate + This is the rate the controller is trying to achieve if the user applies full roll stick input in acro mode. + 45 + 720 + degrees + modules/fw_att_control + + + Acro body y max rate + This is the body y rate the controller is trying to achieve if the user applies full pitch stick input in acro mode. + 45 + 720 + degrees + modules/fw_att_control + + + Acro body z max rate + This is the body z rate the controller is trying to achieve if the user applies full yaw stick input in acro mode. + 10 + 180 + degrees + modules/fw_att_control + + + Threshold for Rattitude mode + Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints + 0.0 + 1.0 + 2 + 0.01 + modules/fw_att_control + @@ -2181,6 +2303,7 @@ but also ignore less noise None Return to Land Loiter + Flight terminate @@ -2232,6 +2355,15 @@ but also ignore less noise modules/navigator + + + Satellite radio read interval + 0 + 300 + s + drivers/iridiumsbd + + Multicopter max climb rate @@ -2263,6 +2395,14 @@ but also ignore less noise 2 modules/land_detector + + Multicopter sub-hover throttle scaling + The range between throttle_min and throttle_hover is scaled by this parameter to define how close to minimum throttle the current throttle value needs to be in order to get accepted as landed. + 0.05 + 0.5 + 2 + modules/land_detector + Multicopter free-fall trigger time Seconds (decimal) that freefall conditions have to met before triggering a freefall. Minimal value is limited by LAND_DETECTOR_UPDATE_RATE=50Hz in landDetector.h @@ -2272,6 +2412,14 @@ but also ignore less noise 2 modules/land_detector + + Manual flight stick down threshold for landing + When controlling manually the throttle stick value (0 to 1) has to be bellow this threshold in order to pass the check for landing. So if set to 1 it's allowed to land with any stick position. + 0 + 1 + 2 + modules/land_detector + Fixedwing max horizontal velocity Maximum horizontal velocity allowed in the landed state (m/s) @@ -2308,6 +2456,18 @@ but also ignore less noise 1 modules/land_detector + + Total flight time in microseconds + Total flight time of this autopilot. Higher 32 bits of the value. Flight time in microseconds = (LND_FLIGHT_T_HI << 32) | LND_FLIGHT_T_LO. + 0 + modules/land_detector + + + Total flight time in microseconds + Total flight time of this autopilot. Lower 32 bits of the value. Flight time in microseconds = (LND_FLIGHT_T_HI << 32) | LND_FLIGHT_T_LO. + 0 + modules/land_detector + @@ -2356,11 +2516,6 @@ but also ignore less noise - - Publish AGL as Z - - modules/local_position_estimator - Optical flow z offset from center -1 @@ -2377,11 +2532,19 @@ but also ignore less noise 3 modules/local_position_estimator - - Optical flow gyro compensation - -1 - 1 - m + + Optical flow rotation (roll/pitch) noise gain + 0.1 + 10.0 + m/s / (rad) + 3 + modules/local_position_estimator + + + Optical flow angular velocity noise gain + 0.0 + 10.0 + m/s / (rad/s) 3 modules/local_position_estimator @@ -2445,16 +2608,11 @@ but also ignore less noise Barometric presssure altitude z standard deviation 0.01 - 3 + 100 m 2 modules/local_position_estimator - - Enables GPS data, also forces alt init with GPS - - modules/local_position_estimator - GPS delay compensaton 0 @@ -2514,6 +2672,7 @@ EPV used if greater than this value Vision delay compensaton + Set to zero to enable automatic compensation from measurement timestamps 0 0.1 sec @@ -2531,16 +2690,11 @@ EPV used if greater than this value Vision z standard deviation 0.01 - 2 + 100 m 3 modules/local_position_estimator - - Vision correction - - modules/local_position_estimator - Vicon position standard deviation 0.0001 @@ -2600,7 +2754,14 @@ Used to calculate increased terrain random walk nosie due to movement3 modules/local_position_estimator - + + Enable publishing of a fake global position (e.g for AUTO missions using Optical Flow) +by initializing the estimator to the LPE_LAT/LON parameters when global information is unavailable + 0 + 1 + modules/local_position_estimator + + Local origin latitude for nav w/o GPS -90 90 @@ -2608,7 +2769,7 @@ Used to calculate increased terrain random walk nosie due to movement8 modules/local_position_estimator - + Local origin longitude for nav w/o GPS -180 180 @@ -2648,6 +2809,31 @@ Used to calculate increased terrain random walk nosie due to movement3 modules/local_position_estimator + + Land detector xy velocity standard deviation + 0.01 + 10.0 + m/s + 3 + modules/local_position_estimator + + + Integer bitmask controlling data fusion + Set bits in the following positions to enable: 0 : Set to true to fuse GPS data if available, also requires GPS for altitude init 1 : Set to true to fuse optical flow data if available 2 : Set to true to fuse vision position 3 : Set to true to fuse vision yaw 4 : Set to true to fuse land detector 5 : Set to true to publish AGL as local position down component 6 : Set to true to enable flow gyro compensation 7 : Set to true to enable baro fusion default (247, no vision yaw) + 0 + 255 + modules/local_position_estimator + + fuse GPS, requires GPS for alt. init + fuse optical flow + fuse vision position + fuse vision yaw + fuse land detector + pub agl as lpos down + flow gyro compensation + fuse baro + + @@ -2719,48 +2905,30 @@ Used to calculate increased terrain random walk nosie due to movementdrivers/mkblctrl - - - Low pass filter frequency for Gyro - platforms/qurt/fc_addon/mpu_spi - - MPU9X50_GYRO_LPF_184HZ - MPU9X50_GYRO_LPF_250HZ - MPU9X50_GYRO_LPF_41HZ - MPU9X50_GYRO_LPF_92HZ - MPU9X50_GYRO_LPF_10HZ - MPU9X50_GYRO_LPF_20HZ - MPU9X50_GYRO_LPF_3600HZ_NOLPF - MPU9X50_GYRO_LPF_5HZ - - - - Low pass filter frequency for Accelerometer - platforms/qurt/fc_addon/mpu_spi + + + Set offboard loss failsafe mode + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander - MPU9X50_ACC_LPF_184HZ - MPU9X50_ACC_LPF_460HZ - MPU9X50_ACC_LPF_41HZ - MPU9X50_ACC_LPF_92HZ - MPU9X50_ACC_LPF_10HZ - MPU9X50_ACC_LPF_20HZ - MPU9X50_ACC_LPF_460HZ_NOLPF - MPU9X50_ACC_LPF_5HZ + Loiter + Land at current position + Return to Land - - Sample rate in Hz - platforms/qurt/fc_addon/mpu_spi + + Set offboard loss failsafe mode when RC is available + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander - MPU9x50_SAMPLE_RATE_200HZ - MPU9x50_SAMPLE_RATE_100HZ - MPU9x50_SAMPLE_RATE_1000HZ - MPU9x50_SAMPLE_RATE_500HZ + Altitude control + Position control + Return to Land + Manual + Land at current position - - - + Take-off altitude This is the minimum altitude the system will take off to. 0 @@ -2844,8 +3012,13 @@ Used to calculate increased terrain random walk nosie due to movement modules/navigator + + Enable weather-vane mode takeoff for missions + + modules/navigator + - Weather-vane mode for loiter mode + Weather-vane mode for loiter modules/navigator @@ -2898,6 +3071,9 @@ Used to calculate increased terrain random walk nosie due to movementDisabled Land at current position Return to Land + Terminate + Data Link Auto Recovery (CASA Outback Challenge rules) + Lockdown @@ -2909,29 +3085,15 @@ Used to calculate increased terrain random walk nosie due to movementDisabled Land at current position Return to Land + Terminate + RC Auto Recovery (CASA Outback Challenge rules) + Lockdown - - Set offboard loss failsafe mode - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander - - Loiter - Land at current position - Return to Land - - - - Set offboard loss failsafe mode when RC is available - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander - - Altitude control - Position control - Return to Land - Manual - Land at current position - + + Force VTOL mode takeoff and land + + modules/navigator @@ -3062,6 +3224,7 @@ if required for the gimbal (only in AUX output mode) Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 8 + 1/s 2 0.1 modules/mc_att_control @@ -3083,6 +3246,14 @@ if required for the gimbal (only in AUX output mode) 0.01 modules/mc_att_control + + Roll rate integrator limit + Roll rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large roll moment trim changes. + 0.0 + 2 + 0.01 + modules/mc_att_control + Roll rate D gain Roll rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. @@ -3106,7 +3277,7 @@ if required for the gimbal (only in AUX output mode) 10 1/s 2 - 0.0005 + 0.1 modules/mc_att_control @@ -3126,6 +3297,14 @@ if required for the gimbal (only in AUX output mode) 0.01 modules/mc_att_control + + Pitch rate integrator limit + Pitch rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large pitch moment trim changes. + 0.0 + 2 + 0.01 + modules/mc_att_control + Pitch rate D gain Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. @@ -3168,6 +3347,14 @@ if required for the gimbal (only in AUX output mode) 0.01 modules/mc_att_control + + Yaw rate integrator limit + Yaw rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large yaw moment trim changes. + 0.0 + 2 + 0.01 + modules/mc_att_control + Yaw rate D gain Yaw rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. @@ -3260,7 +3447,7 @@ if required for the gimbal (only in AUX output mode) 5 modules/mc_att_control - + Threshold for Rattitude mode Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints 0.0 @@ -3269,152 +3456,65 @@ if required for the gimbal (only in AUX output mode) 0.01 modules/mc_att_control - - Threshold for Throttle PID Attenuation (TPA) - Magnitude of throttle setpoint at which to begin attenuating roll/pitch P gain + + Battery power level scaler + This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The copter should constantly behave as if it was fully charged with reduced max acceleration at lower battery percentages. i.e. if hover is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + modules/mc_att_control + + + TPA P Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch P gain 0.0 1.0 2 0.1 modules/mc_att_control - - Slope for Throttle PID Attenuation (TPA) - Rate at which to attenuate roll/pitch P gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - slope*(abs(throttle)-breakpoint)) + + TPA I Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch I gain 0.0 - 2.0 + 1.0 2 0.1 modules/mc_att_control - - Max manual roll - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Max manual pitch - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Max manual yaw rate - 0.0 - deg/s - examples/mc_pos_control_multiplatform - - - Roll P gain - Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - examples/mc_att_control_multiplatform - - - Roll rate P gain - Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform - - - Roll rate I gain - Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform - - - Roll rate D gain - Roll rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform - - - Pitch P gain - Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - 1/s - examples/mc_att_control_multiplatform - - - Pitch rate P gain - Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform - - - Pitch rate I gain - Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform - - - Pitch rate D gain - Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw P gain - Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - 1/s - examples/mc_att_control_multiplatform - - - Yaw rate P gain - Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw rate I gain - Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw rate D gain - Yaw rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw feed forward - Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + TPA D Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch D gain 0.0 1.0 - examples/mc_att_control_multiplatform - - - Max yaw rate - Limit for yaw rate, has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. - 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform + 2 + 0.1 + modules/mc_att_control - - Max acro roll rate + + TPA Rate P + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch P gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform + 1.0 + 2 + 0.05 + modules/mc_att_control - - Max acro pitch rate + + TPA Rate I + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch I gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform + 1.0 + 2 + 0.05 + modules/mc_att_control - - Max acro yaw rate + + TPA Rate D + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch D gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 - deg/s - examples/mc_att_control_multiplatform + 1.0 + 2 + 0.05 + modules/mc_att_control @@ -3668,6 +3768,13 @@ if required for the gimbal (only in AUX output mode) 2 modules/mc_pos_control + + Deadzone of Z stick where altitude hold is enabled + 0.0 + 1.0 + 2 + modules/mc_pos_control + Maximum horizontal velocity for which position hold is enabled (use 0 to disable check) 0.0 @@ -3701,6 +3808,24 @@ if required for the gimbal (only in AUX output mode) 1 modules/mc_pos_control + + Maximum vertical acceleration in velocity controlled modes upward + 2.0 + 15.0 + m/s/s + 2 + 1 + modules/mc_pos_control + + + Maximum vertical acceleration in velocity controlled modes down + 2.0 + 15.0 + m/s/s + 2 + 1 + modules/mc_pos_control + Altitude control mode, note mode 1 only tested with LPE 0 @@ -3711,117 +3836,122 @@ if required for the gimbal (only in AUX output mode) Altitude following - - Minimum thrust - Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform - - - Maximum thrust - Limit max allowed thrust. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform + + Manual control stick exponential curve sensitivity attenuation with small velocity setpoints + The higher the value the less sensitivity the stick has around zero while still reaching the maximum value with full stick deflection. + 0 + 1 + 2 + modules/mc_pos_control + + Purely cubic input curve + Purely linear input curve (default) + - - Proportional gain for vertical position error - 0.0 - examples/mc_pos_control_multiplatform + + + + Invert direction of aux output channel 1 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Proportional gain for vertical velocity error - 0.0 - examples/mc_pos_control_multiplatform + + Invert direction of aux output channel 2 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Integral gain for vertical velocity error - Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. - 0.0 - examples/mc_pos_control_multiplatform + + Invert direction of aux output channel 3 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Differential gain for vertical velocity error - 0.0 - examples/mc_pos_control_multiplatform + + Invert direction of aux output channel 4 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Maximum vertical velocity - Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL). - 0.0 - m/s - examples/mc_pos_control_multiplatform + + Invert direction of aux output channel 5 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Vertical velocity feed forward - Feed forward weight for altitude control in stabilized modes (ALTCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform + + Invert direction of aux output channel 6 + Set to 1 to invert the channel, 0 for default direction. + + true + drivers/px4fmu - - Proportional gain for horizontal position error - 0.0 - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 1 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Proportional gain for horizontal velocity error - 0.0 - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 2 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Integral gain for horizontal velocity error - Non-zero value allows to resist wind. - 0.0 - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 3 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again - 0.0 - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 4 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Maximum horizontal velocity - Maximum horizontal velocity in AUTO mode and endpoint for position stabilized mode (POSCTRL). - 0.0 - m/s - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 5 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Horizontal velocity feed forward - Feed forward weight for position control in position control mode (POSCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform + + Trim value for FMU PWM output channel 6 + Set to normalized offset + -0.2 + 0.2 + 2 + drivers/px4fmu - - Maximum tilt angle in air - Limits maximum tilt in AUTO and POSCTRL modes during flight. - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Maximum tilt during landing - Limits maximum tilt angle on landing. - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Landing descend rate - 0.0 - m/s - examples/mc_pos_control_multiplatform + + Set the PWM output frequency for the main outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. Set to 400 for industry default or 1000 for high frequency ESCs. + -1 + 2000 + Hz + true + modules/sensors - - - Set the minimum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for industry default or 900 to increase servo travel. + Set the minimum PWM for the main outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. Set to 1000 for industry default or 900 to increase servo travel. 800 1400 us @@ -3829,17 +3959,17 @@ if required for the gimbal (only in AUX output mode) modules/sensors - Set the maximum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for industry default or 2100 to increase servo travel. + Set the maximum PWM for the main outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. Set to 2000 for industry default or 2100 to increase servo travel. 1600 2200 us true modules/sensors - - Set the disarmed PWM for MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. + + Set the disarmed PWM for the main outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. 0 2200 us @@ -3847,8 +3977,8 @@ if required for the gimbal (only in AUX output mode) modules/sensors - Set the minimum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 1000 for default or 900 to increase servo travel + Set the minimum PWM for the auxiliary outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. Set to 1000 for default or 900 to increase servo travel 800 1400 us @@ -3856,23 +3986,30 @@ if required for the gimbal (only in AUX output mode) modules/sensors - Set the maximum PWM for the MAIN outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. Set to 2000 for default or 2100 to increase servo travel + Set the maximum PWM for the auxiliary outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. Set to 2000 for default or 2100 to increase servo travel 1600 2200 us true modules/sensors - - Set the disarmed PWM for AUX outputs - IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. COMPLETELY POWER-CYCLE THE SYSTEM TO PUT CHANGES INTO EFFECT. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. + + Set the disarmed PWM for auxiliary outputs + IMPORTANT: CHANGING THIS PARAMETER REQUIRES A COMPLETE SYSTEM REBOOT IN ORDER TO APPLY THE CHANGES. This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. 0 2200 us true modules/sensors + + Thrust to PWM model parameter + Parameter used to model the relationship between static thrust and motor input PWM. Model is: thrust = (1-factor)*PWM + factor * PWM^2 + 0.0 + 1.0 + modules/sensors + Minimum motor rise time (slew rate limit) Minimum time allowed for the motor input signal to pass through a range of 1000 PWM units. A value x means that the motor signal can only go from 1000 to 2000 PWM in maximum x seconds. Zero means that slew rate limiting is disabled. @@ -3880,110 +4017,6 @@ if required for the gimbal (only in AUX output mode) s/(1000*PWM) modules/sensors - - Invert direction of aux output channel 1 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of aux output channel 2 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of aux output channel 3 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of aux output channel 4 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of aux output channel 5 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of aux output channel 6 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4fmu - - - Invert direction of main output channel 1 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 2 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 3 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 4 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 5 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 6 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 7 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - Invert direction of main output channel 8 - Set to 1 to invert the channel, 0 for default direction. - - true - drivers/px4io - - - S.BUS out - Set to 1 to enable S.BUS version 1 output instead of RSSI. - - drivers/px4io - @@ -4393,13 +4426,6 @@ if required for the gimbal (only in AUX output mode) modules/position_estimator_inav - - - RC receiver type - Acceptable values: - RC_RECEIVER_SPEKTRUM = 1, - RC_RECEIVER_LEMONRX = 2, - platforms/qurt/fc_addon/rc_receiver - - RC Channel 1 Minimum @@ -5507,6 +5533,20 @@ if required for the gimbal (only in AUX output mode) 2000 modules/sensors + + Sample rate of the remote control values for the low pass filter on roll,pitch, yaw and throttle + Has an influence on the cutoff frequency precision. + 1.0 + Hz + modules/sensors + + + Cutoff frequency for the low pass filter on roll,pitch, yaw and throttle + Does not get set unless below RC_FLT_SMP_RATE/2 because of filter instability characteristics. + 0.1 + Hz + modules/sensors + Roll trim The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. @@ -5792,6 +5832,33 @@ if required for the gimbal (only in AUX output mode) Channel 8 + + Arm switch channel + 0 + 18 + modules/sensors + + Channel 11 + Channel 10 + Channel 13 + Channel 12 + Channel 15 + Channel 14 + Channel 17 + Channel 16 + Channel 18 + Channel 1 + Unassigned + Channel 3 + Channel 2 + Channel 5 + Channel 4 + Channel 7 + Channel 6 + Channel 9 + Channel 8 + + Flaps channel 0 @@ -5873,6 +5940,60 @@ if required for the gimbal (only in AUX output mode) Channel 8 + + Stabilize switch channel mapping + 0 + 18 + modules/sensors + + Channel 11 + Channel 10 + Channel 13 + Channel 12 + Channel 15 + Channel 14 + Channel 17 + Channel 16 + Channel 18 + Channel 1 + Unassigned + Channel 3 + Channel 2 + Channel 5 + Channel 4 + Channel 7 + Channel 6 + Channel 9 + Channel 8 + + + + Manual switch channel mapping + 0 + 18 + modules/sensors + + Channel 11 + Channel 10 + Channel 13 + Channel 12 + Channel 15 + Channel 14 + Channel 17 + Channel 16 + Channel 18 + Channel 1 + Unassigned + Channel 3 + Channel 2 + Channel 5 + Channel 4 + Channel 7 + Channel 6 + Channel 9 + Channel 8 + + Threshold for selecting assist mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th @@ -5936,6 +6057,13 @@ if required for the gimbal (only in AUX output mode) 1 modules/sensors + + Threshold for the arm switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + Threshold for the VTOL transition switch 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th @@ -5950,6 +6078,20 @@ if required for the gimbal (only in AUX output mode) 1 modules/sensors + + Threshold for the stabilize switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for the manual switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + @@ -6127,6 +6269,26 @@ This is used for gathering replay logs for the ekf2 module min modules/logger + + Logging Mode + Determines when to start and stop logging. By default, logging is started when arming the system, and stopped when disarming. This parameter is only for the new logger (SYS_LOGGER=1). + 0 + 3 + true + modules/logger + + from boot until disarm + when armed until disarm (default) + from boot until shutdown - IMU and Baro data only (used for thermal calibration) + from boot until shutdown + + + + Log UUID + If set to 1, add an ID to the log, which uniquely identifies the vehicle + + modules/logger + @@ -6625,9 +6787,9 @@ This is used for gathering replay logs for the ekf2 module Roll 90°, Yaw 90° - + PX4Flow board rotation - This parameter defines the rotation of the PX4FLOW board relative to the platform. Zero rotation is defined as Y on flow board pointing towards front of vehicle + This parameter defines the yaw rotation of the PX4FLOW board relative to the vehicle body frame. Zero rotation is defined as X on flow board pointing towards front of vehicle. The recommneded installation default for the PX4FLOW board is with the Y axis forward (270 deg yaw). true modules/sensors @@ -6696,120 +6858,693 @@ This is used for gathering replay logs for the ekf2 module 0 2 modules/sensors - - External is primary Mag - Auto-select Mag - Internal is primary Mag - + + External is primary Mag + Auto-select Mag + Internal is primary Mag + + + + Threshold (of RMS) to warn about high vibration levels + 0.01 + 10 + 2 + modules/sensors + + + + + Lidar-Lite (LL40LS) PWM + + true + modules/sensors + + + Lightware laser rangefinder (serial) + 0 + 4 + true + modules/sensors + + SF02 + Disabled + SF10/b + SF10/a + SF11/c + SF10/c + + + + Maxbotix Soanr (mb12xx) + + true + modules/sensors + + + TeraRanger One (trone) + + true + modules/sensors + + + Lightware SF1xx laser rangefinder (i2c) + 0 + 4 + true + modules/sensors + + SF10/a + Disabled + SF10/c + SF10/b + SF11/c + + + + Thermal control of sensor temperature + modules/sensors + + Thermal control off + Thermal control unavailable + + + + + + Set to 1 to enable thermal compensation for accelerometer sensors. Set to 0 to disable + 0 + 1 + modules/sensors + + + ID of Accelerometer that the calibration is for + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer scale factor - X axis + modules/sensors + + + Accelerometer scale factor - Y axis + modules/sensors + + + Accelerometer scale factor - Z axis + modules/sensors + + + Accelerometer calibration reference temperature + modules/sensors + + + Accelerometer calibration minimum temperature + modules/sensors + + + Accelerometer calibration maximum temperature + modules/sensors + + + ID of Accelerometer that the calibration is for + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer scale factor - X axis + modules/sensors + + + Accelerometer scale factor - Y axis + modules/sensors + + + Accelerometer scale factor - Z axis + modules/sensors + + + Accelerometer calibration reference temperature + modules/sensors + + + Accelerometer calibration minimum temperature + modules/sensors + + + Accelerometer calibration maximum temperature + modules/sensors + + + ID of Accelerometer that the calibration is for + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - X axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis + modules/sensors + + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis + modules/sensors + + + Accelerometer scale factor - X axis + modules/sensors + + + Accelerometer scale factor - Y axis + modules/sensors + + + Accelerometer scale factor - Z axis + modules/sensors + + + Accelerometer calibration reference temperature + modules/sensors + + + Accelerometer calibration minimum temperature + modules/sensors + + + Accelerometer calibration maximum temperature + modules/sensors + + + Set to 1 to enable thermal compensation for barometric pressure sensors. Set to 0 to disable + 0 + 1 + modules/sensors + + + ID of Barometer that the calibration is for + modules/sensors + + + Barometer offset temperature ^5 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^4 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^3 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^2 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^1 polynomial coefficients + modules/sensors + + + Barometer offset temperature ^0 polynomial coefficient + modules/sensors + + + Barometer scale factor - X axis + modules/sensors + + + Barometer calibration reference temperature + modules/sensors + + + Barometer calibration minimum temperature + modules/sensors + + + Barometer calibration maximum temperature + modules/sensors + + + ID of Barometer that the calibration is for + modules/sensors + + + Barometer offset temperature ^5 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^4 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^3 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^2 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^1 polynomial coefficients + modules/sensors + + + Barometer offset temperature ^0 polynomial coefficient + modules/sensors + + + Barometer scale factor - X axis + modules/sensors + + + Barometer calibration reference temperature + modules/sensors + + + Barometer calibration minimum temperature + modules/sensors + + + Barometer calibration maximum temperature + modules/sensors + + + ID of Barometer that the calibration is for + modules/sensors + + + Barometer offset temperature ^5 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^4 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^3 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^2 polynomial coefficient + modules/sensors + + + Barometer offset temperature ^1 polynomial coefficients + modules/sensors + + + Barometer offset temperature ^0 polynomial coefficient + modules/sensors + + + Barometer scale factor - X axis + modules/sensors + + + Barometer calibration reference temperature + modules/sensors + + + Barometer calibration minimum temperature + modules/sensors + + + Barometer calibration maximum temperature + modules/sensors + + + Set to 1 to enable thermal compensation for rate gyro sensors. Set to 0 to disable + 0 + 1 + modules/sensors + + + ID of Gyro that the calibration is for + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis + modules/sensors + + + Gyro scale factor - X axis + modules/sensors + + + Gyro scale factor - Y axis + modules/sensors + + + Gyro scale factor - Z axis + modules/sensors + + + Gyro calibration reference temperature + modules/sensors + + + Gyro calibration minimum temperature + modules/sensors + + + Gyro calibration maximum temperature + modules/sensors + + + ID of Gyro that the calibration is for + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - X axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis + modules/sensors + + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis + modules/sensors + + + Gyro scale factor - X axis + modules/sensors + + + Gyro scale factor - Y axis + modules/sensors + + + Gyro scale factor - Z axis + modules/sensors + + + Gyro calibration reference temperature + modules/sensors + + + Gyro calibration minimum temperature + modules/sensors + + + Gyro calibration maximum temperature + modules/sensors + + + ID of Gyro that the calibration is for + modules/sensors + + + Gyro rate offset temperature ^3 polynomial coefficient - X axis + modules/sensors - - - - Lidar-Lite (LL40LS) PWM - - true + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis modules/sensors - - Lightware SF0x laser rangefinder - - true + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis modules/sensors - - Maxbotix Soanr (mb12xx) - - true + + Gyro rate offset temperature ^2 polynomial coefficient - X axis modules/sensors - - TeraRanger One (trone) - - true + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis modules/sensors - - Lightware SF1xx laser rangefinder - 0 - 4 - true + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis modules/sensors - - SF10/a - Disabled - SF10/c - SF10/b - SF11/c - - - - - ESC model - See esc_model_t enum definition in uart_esc_dev.h for all supported ESC model enum values. - platforms/qurt/fc_addon/uart_esc - - ESC_350QX - ESC_200QX - ESC_210QC - + + Gyro rate offset temperature ^1 polynomial coefficient - X axis + modules/sensors - - ESC UART baud rate - Default rate is 250Kbps, whic is used in off-the-shelf QRP ESC products. - platforms/qurt/fc_addon/uart_esc + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis + modules/sensors - - Motor 1 Mapping - platforms/qurt/fc_addon/uart_esc + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis + modules/sensors - - Motor 2 Mapping - platforms/qurt/fc_addon/uart_esc + + Gyro rate offset temperature ^0 polynomial coefficient - X axis + modules/sensors - - Motor 3 Mapping - platforms/qurt/fc_addon/uart_esc + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis + modules/sensors - - Motor 4 Mapping - platforms/qurt/fc_addon/uart_esc + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis + modules/sensors - - - - Interval of one subscriber in the example in ms - ms - examples/subscriber + + Gyro scale factor - X axis + modules/sensors - - Float Demonstration Parameter in the Example - examples/subscriber + + Gyro scale factor - Y axis + modules/sensors - - - - Operating channel of the NRF51 - 0 - 125 - modules/syslink + + Gyro scale factor - Z axis + modules/sensors - - Operating datarate of the NRF51 - 0 - 2 - modules/syslink + + Gyro calibration reference temperature + modules/sensors - - Operating address of the NRF51 (most significant byte) - modules/syslink + + Gyro calibration minimum temperature + modules/sensors - - Operating address of the NRF51 (least significant 4 bytes) - modules/syslink + + Gyro calibration maximum temperature + modules/sensors + + RGB Led brightness limit + Set to 0 to disable, 1 for minimum brightness up to 15 (max) + 0 + 15 + drivers/rgbled + Auto-start script index CHANGING THIS VALUE REQUIRES A RESTART. Defines the auto-start script used to bootstrap the system. @@ -6849,16 +7584,15 @@ This is used for gathering replay logs for the ekf2 module Data does not survive reset - + Set multicopter estimator group - Set the group of estimators used for multicopters and vtols - 0 + Set the group of estimators used for multicopters and VTOLs + 1 2 true modules/systemlib local_position_estimator, attitude_estimator_q - position_estimator_inav, attitude_estimator_q ekf2 @@ -6875,12 +7609,14 @@ This is used for gathering replay logs for the ekf2 module Normal Telemetry (57600 baud, 8N1) Command Receiver (57600 baud, 8N1) OSD (57600 baud, 8N1) + Iridium Telemetry (19200 baud, 8N1) Normal Telemetry (38400 baud, 8N1) Disabled + Normal Telemetry (19200 baud, 8N1) ESP8266 (921600 baud, 8N1) Companion Link (57600 baud, 8N1) Companion Link (921600 baud, 8N1) - Normal Telemetry (19200 baud, 8N1) + Normal Telemetry (115200 baud, 8N1) @@ -6900,12 +7636,50 @@ This is used for gathering replay logs for the ekf2 module sdlog2 (default) - - RGB Led brightness limit - Set to 0 to disable, 1 for minimum brightness up to 15 (max) + + Enable stack checking + + modules/systemlib + + + Enable auto start of rate gyro thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) 0 - 15 - drivers/rgbled + 1 + modules/systemlib + + + Enable auto start of accelerometer thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) + 0 + 1 + modules/systemlib + + + Enable auto start of barometer thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) + 0 + 1 + modules/systemlib + + + Required temperature rise during thermal calibration + A temperature increase greater than this value is required during calibration. Calibration will complete for each sensor when the temperature increase above the starting temeprature exceeds the value set by SYS_CAL_TDEL. If the temperature rise is insufficient, the calibration will continue indefinitely and the board will need to be repowered to exit. + 10 + deg C + modules/systemlib + + + Minimum starting temperature for thermal calibration + Temperature calibration for each sensor will ignore data if the temperature is lower than the value set by SYS_CAL_TMIN. + deg C + modules/systemlib + + + Maximum starting temperature for thermal calibration + Temperature calibration will not start if the temperature of any sensor is higher than the value set by SYS_CAL_TMAX. + deg C + modules/systemlib @@ -6965,15 +7739,14 @@ This is used for gathering replay logs for the ekf2 module UAVCAN mode - 0 - UAVCAN disabled. 1 - Enabled support for UAVCAN actuators and sensors. 2 - Enabled support for dynamic node ID allocation and firmware update. 3 - Sets the motor control outputs to UAVCAN and enables support for dynamic node ID allocation and firmware update. + 0 - UAVCAN disabled. 1 - Basic support for UAVCAN actuators and sensors. 2 - Full support for dynamic node ID allocation and firmware update. 3 - Sets the motor control outputs to UAVCAN and enables support for dynamic node ID allocation and firmware update. 0 3 modules/uavcan - Enabled Disabled - Motors/Update - Dynamic ID/Update + Sensors and Motors + Sensors Enabled @@ -6990,14 +7763,96 @@ This is used for gathering replay logs for the ekf2 module bit/s modules/uavcan - + UAVCAN ESC will spin at idle throttle when armed, even if the mixer outputs zero setpoints - 0 - 1 + + true modules/uavcan + + Target throttle value for pusher/puller motor during the transition to fw mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Maximum allowed down-pitch the controller is able to demand. This prevents large, negative +lift values being created when facing strong winds. The vehicle will use the pusher motor +to accelerate forward if necessary + 0.0 + 45.0 + modules/vtol_att_control + + + Fixed wing thrust scale for hover forward flight + Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. + 0.0 + 2.0 + modules/vtol_att_control + + + Position of tilt servo in mc mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Position of tilt servo in transition mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Position of tilt servo in fw mode + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + + + Duration of front transition phase 2 + Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. + 0.1 + 5.0 + s + 3 + 0.01 + modules/vtol_att_control + + + The channel number of motors that must be turned off in fixed wing mode + 0 + 12345678 + 0 + 1 + modules/vtol_att_control + + + Differential thrust in forwards flight + Set to 1 to enable differential thrust in fixed-wing flight. + 0 + 1 + 0 + modules/vtol_att_control + + + Differential thrust scaling factor + This factor specifies how the yaw input gets mapped to differential thrust in forwards flight. + 0.0 + 1.0 + 2 + 0.1 + modules/vtol_att_control + VTOL number of engines 0 @@ -7178,12 +8033,6 @@ This is used for gathering replay logs for the ekf2 module s modules/vtol_att_control - - Force VTOL mode takeoff and land - 0 - 1 - modules/vtol_att_control - QuadChute Minimum altitude for fixed wing flight, when in fixed wing the altitude drops below this altitude the vehicle will transition back to MC mode and enter failsafe RTL @@ -7191,69 +8040,12 @@ This is used for gathering replay logs for the ekf2 module 200.0 modules/vtol_att_control - - Position of tilt servo in mc mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in transition mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in fw mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Duration of front transition phase 2 - Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. - 0.1 - 5.0 - s - 3 - 0.01 - modules/vtol_att_control - - - The channel number of motors that must be turned off in fixed wing mode - 0 - 12345678 - 0 - 1 - modules/vtol_att_control - - - Target throttle value for pusher/puller motor during the transition to fw mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Maximum allowed down-pitch the controller is able to demand. This prevents large, negative -lift values being created when facing strong winds. The vehicle will use the pusher motor -to accelerate forward if necessary - 0.0 - 45.0 - modules/vtol_att_control - - - Fixed wing thrust scale for hover forward flight - Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. - 0.0 - 2.0 + + Airspeed less front transition time (open loop) + The duration of the front transition when there is no airspeed feedback available. + 1.0 + 30.0 + seconds modules/vtol_att_control @@ -7513,22 +8305,6 @@ Maps the change of airspeed error to the acceleration setpoint - - SEG_TH2V_P - modules/segway - - - SEG_TH2V_I - modules/segway - - - SEG_TH2V_I_MAX - modules/segway - - - SEG_Q2V - modules/segway - Failsafe channel mapping The RC mapping index indicates which channel is used for failsafe If 0, whichever channel is mapped to throttle is used otherwise the value indicates the specific rc channel to use @@ -7557,6 +8333,10 @@ Maps the change of airspeed error to the acceleration setpoint Channel 8 + + COM_RC_STICK_OV + modules/commander + First flightmode slot (1000-1160) If the main switch channel is in this range the selected flight mode will be applied. @@ -7683,6 +8463,10 @@ Maps the change of airspeed error to the acceleration setpoint Stabilized + + RV_YAW_P + examples/rover_steering_control + EXFW_HDNG_P examples/fixedwing_control @@ -7695,9 +8479,5 @@ Maps the change of airspeed error to the acceleration setpoint EXFW_PITCH_P examples/fixedwing_control - - RV_YAW_P - examples/rover_steering_control - -- GitLab From 083af0b305d3604d606bd5c307c80a25e2cf9219 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sat, 25 Feb 2017 16:39:50 -0500 Subject: [PATCH 360/398] Allow setting the video streaming aspect ratio. --- src/FlightDisplay/FlightDisplayViewVideo.qml | 26 +++++++++----------- src/Settings/Video.SettingsGroup.json | 8 ++++++ src/Settings/VideoSettings.cc | 19 +++++++++++--- src/Settings/VideoSettings.h | 6 ++++- src/ui/preferences/GeneralSettings.qml | 14 +++++++++++ 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 86d08aff7..65d9af211 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -23,35 +23,31 @@ import QGroundControl.Controllers 1.0 Item { id: root + property double _ar: QGroundControl.settingsManager.videoSettings.aspectRatio.rawValue Rectangle { id: noVideo anchors.fill: parent color: Qt.rgba(0,0,0,0.75) visible: !QGroundControl.videoManager.videoRunning QGCLabel { - text: qsTr("NO VIDEO") + text: qsTr("WAITING FOR VIDEO") font.family: ScreenTools.demiboldFontFamily color: "white" font.pointSize: _mainIsMap ? ScreenTools.smallFontPointSize : ScreenTools.largeFontPointSize anchors.centerIn: parent } } - QGCVideoBackground { + Rectangle { anchors.fill: parent - display: QGroundControl.videoManager.videoSurface - receiver: QGroundControl.videoManager.videoReceiver + color: "black" visible: QGroundControl.videoManager.videoRunning - /* TODO: Come up with a way to make this an option - QGCAttitudeHUD { - id: attitudeHUD - visible: !_mainIsMap - rollAngle: _activeVehicle ? _activeVehicle.roll.value : 0 - pitchAngle: _activeVehicle ? _activeVehicle.pitch.value : 0 - width: ScreenTools.defaultFontPixelHeight * (30) - height: ScreenTools.defaultFontPixelHeight * (30) - active: QGroundControl.multiVehicleManager.activeVehicleAvailable - z: QGroundControl.zOrderWidgets + QGCVideoBackground { + height: parent.height + width: _ar != 0.0 ? height * _ar : parent.width + anchors.centerIn: parent + display: QGroundControl.videoManager.videoSurface + receiver: QGroundControl.videoManager.videoReceiver + visible: QGroundControl.videoManager.videoRunning } - */ } } diff --git a/src/Settings/Video.SettingsGroup.json b/src/Settings/Video.SettingsGroup.json index c9835ee05..17e8254db 100644 --- a/src/Settings/Video.SettingsGroup.json +++ b/src/Settings/Video.SettingsGroup.json @@ -27,5 +27,13 @@ "longDescription": "Directory to save videos to.", "type": "string", "defaultValue": "" +}, +{ + "name": "VideoAspectRatio", + "shortDescription": "Video Aspect Ratio", + "longDescription": "Video Aspect Ratio (width / height). Use 0.0 to ignore it.", + "type": "float", + "decimalPlaces": 6, + "defaultValue": 1.777777 } ] diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index 190c87fff..f36751d24 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -19,10 +19,11 @@ const char* VideoSettings::videoSettingsGroupName = "Video"; -const char* VideoSettings::videoSourceName = "VideoSource"; -const char* VideoSettings::udpPortName = "VideoUDPPort"; -const char* VideoSettings::rtspUrlName = "VideoRTSPUrl"; -const char* VideoSettings::videoSavePathName = "VideoSavePath"; +const char* VideoSettings::videoSourceName = "VideoSource"; +const char* VideoSettings::udpPortName = "VideoUDPPort"; +const char* VideoSettings::rtspUrlName = "VideoRTSPUrl"; +const char* VideoSettings::videoSavePathName = "VideoSavePath"; +const char* VideoSettings::videoAspectRatioName = "VideoAspectRatio"; const char* VideoSettings::videoSourceNoVideo = "No Video Available"; const char* VideoSettings::videoSourceUDP = "UDP Video Stream"; @@ -34,6 +35,7 @@ VideoSettings::VideoSettings(QObject* parent) , _udpPortFact(NULL) , _rtspUrlFact(NULL) , _videoSavePathFact(NULL) + , _videoAspectRatioFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); @@ -104,3 +106,12 @@ Fact* VideoSettings::videoSavePath(void) return _videoSavePathFact; } + +Fact* VideoSettings::aspectRatio(void) +{ + if (!_videoAspectRatioFact) { + _videoAspectRatioFact = _createSettingsFact(videoAspectRatioName); + } + + return _videoAspectRatioFact; +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index df3821720..d914fc701 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -15,7 +15,7 @@ class VideoSettings : public SettingsGroup { Q_OBJECT - + public: VideoSettings(QObject* parent = NULL); @@ -23,11 +23,13 @@ public: Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) Q_PROPERTY(Fact* videoSavePath READ videoSavePath CONSTANT) + Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) Fact* videoSource (void); Fact* udpPort (void); Fact* rtspUrl (void); Fact* videoSavePath (void); + Fact* aspectRatio (void); static const char* videoSettingsGroupName; @@ -35,6 +37,7 @@ public: static const char* udpPortName; static const char* rtspUrlName; static const char* videoSavePathName; + static const char* videoAspectRatioName; static const char* videoSourceNoVideo; static const char* videoSourceUDP; @@ -45,6 +48,7 @@ private: SettingsFact* _udpPortFact; SettingsFact* _rtspUrlFact; SettingsFact* _videoSavePathFact; + SettingsFact* _videoAspectRatioFact; }; #endif diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index a83ceb588..319049398 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -503,6 +503,20 @@ QGCView { fact: QGroundControl.settingsManager.videoSettings.rtspUrl } } + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.isGStreamer && videoSource.currentIndex < 2 + QGCLabel { + anchors.baseline: aspectField.baseline + text: qsTr("Aspect Ratio:") + width: _labelWidth + } + FactTextField { + id: aspectField + width: _editFieldWidth + fact: QGroundControl.settingsManager.videoSettings.aspectRatio + } + } Row { spacing: ScreenTools.defaultFontPixelWidth visible: QGroundControl.settingsManager.videoSettings.videoSavePath.visible && QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.recordingEnabled -- GitLab From 141b42851d2d5107fd1b8f2b63fda49c14ae147f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 09:51:52 -0800 Subject: [PATCH 361/398] Handle videoSource changes correctly --- src/FlightDisplay/VideoManager.cc | 7 +++++++ src/FlightDisplay/VideoManager.h | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/FlightDisplay/VideoManager.cc b/src/FlightDisplay/VideoManager.cc index e2cf39875..c5c858b59 100644 --- a/src/FlightDisplay/VideoManager.cc +++ b/src/FlightDisplay/VideoManager.cc @@ -56,6 +56,7 @@ VideoManager::setToolbox(QGCToolbox *toolbox) _videoSettings = toolbox->settingsManager()->videoSettings(); QString videoSource = _videoSettings->videoSource()->rawValue().toString(); + connect(_videoSettings->videoSource(), &Fact::rawValueChanged, this, &VideoManager::_videoSourceChanged); #if defined(QGC_GST_STREAMING) #ifndef QGC_DISABLE_UVC @@ -91,6 +92,12 @@ VideoManager::setToolbox(QGCToolbox *toolbox) #endif } +void VideoManager::_videoSourceChanged(void) +{ + emit hasVideoChanged(); + emit isGStreamerChanged(); +} + //----------------------------------------------------------------------------- bool VideoManager::hasVideo() diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index f96f1acb0..aaffec22e 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -67,11 +67,13 @@ signals: void isGStreamerChanged (); void videoSourceIDChanged (); +private slots: + void _videoSourceChanged(void); + private: void _updateTimer (); void _updateVideo (); -private: VideoSurface* _videoSurface; VideoReceiver* _videoReceiver; bool _videoRunning; -- GitLab From 8f878ced68909707685d65be6f77520074a913ec Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 10:48:10 -0800 Subject: [PATCH 362/398] More work on visuals --- src/MissionEditor/FWLandingPatternEditor.qml | 18 +++- .../FWLandingPatternMapVisual.qml | 92 ++++++++++--------- .../FWLandingPattern.FactMetaData.json | 4 +- .../FixedWingLandingComplexItem.cc | 8 +- 4 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/MissionEditor/FWLandingPatternEditor.qml b/src/MissionEditor/FWLandingPatternEditor.qml index af6a9d5a2..8d26b50b1 100644 --- a/src/MissionEditor/FWLandingPatternEditor.qml +++ b/src/MissionEditor/FWLandingPatternEditor.qml @@ -46,7 +46,7 @@ Rectangle { anchors.right: parent.right wrapMode: Text.WordWrap font.pointSize: ScreenTools.smallFontPointSize - text: "WIP (NOT FOR REAL FLIGHT!)" + text: qsTr("WIP (NOT FOR REAL FLIGHT!)") } Item { width: 1; height: _margin } @@ -122,9 +122,21 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right visible: !missionItem.landingCoordSet + spacing: ScreenTools.defaultFontPixelHeight - QGCLabel { text: "WIP (NOT FOR REAL FLIGHT!)" } + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("WIP (NOT FOR REAL FLIGHT!)") + } - QGCLabel { text: qsTr("Click in map to set landing point.") } + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + wrapMode: Text.WordWrap + text: qsTr("Click in map to set landing point.") + } } } diff --git a/src/MissionEditor/FWLandingPatternMapVisual.qml b/src/MissionEditor/FWLandingPatternMapVisual.qml index d2c01674b..b393d755f 100644 --- a/src/MissionEditor/FWLandingPatternMapVisual.qml +++ b/src/MissionEditor/FWLandingPatternMapVisual.qml @@ -22,40 +22,36 @@ Item { property var map ///< Map control to place item in property var _missionItem: object + property var _itemVisuals: [ ] property var _mouseArea - property var _dragLoiter - property var _dragLand - property var _loiterPoint - property var _landPoint - property var _flightPath + property var _dragAreas: [ ] + + readonly property int _flightPathIndex: 0 + readonly property int _loiterPointIndex: 1 + readonly property int _loiterRadiusIndex: 2 + readonly property int _landPointIndex: 3 function hideItemVisuals() { - if (_flightPath) { - _flightPath.destroy() - _flightPath = undefined - } - if (_loiterPoint) { - _loiterPoint.destroy() - _loiterPoint = undefined - } - if (_landPoint) { - _landPoint.destroy() - _landPoint = undefined + for (var i=0; i<_itemVisuals.length; i++) { + _itemVisuals[i].destroy() } + _itemVisuals = [ ] } function showItemVisuals() { - if (!_flightPath) { - _flightPath = flightPathComponent.createObject(map) - map.addMapItem(_flightPath) - } - if (!_loiterPoint) { - _loiterPoint = loiterPointComponent.createObject(map) - map.addMapItem(_loiterPoint) - } - if (!_landPoint) { - _landPoint = landPointComponent.createObject(map) - map.addMapItem(_landPoint) + if (_itemVisuals.length === 0) { + var itemVisual = flightPathComponent.createObject(map) + map.addMapItem(itemVisual) + _itemVisuals[_flightPathIndex] =itemVisual + itemVisual = loiterPointComponent.createObject(map) + map.addMapItem(itemVisual) + _itemVisuals[_loiterPointIndex] = itemVisual + itemVisual = loiterRadiusComponent.createObject(map) + map.addMapItem(itemVisual) + _itemVisuals[_loiterRadiusIndex] = itemVisual + itemVisual = landPointComponent.createObject(map) + map.addMapItem(itemVisual) + _itemVisuals[_landPointIndex] = itemVisual } } @@ -74,24 +70,16 @@ Item { } function hideDragAreas() { - console.log("hideDragAreas") - if (_dragLoiter) { - _dragLoiter.destroy() - _dragLoiter = undefined - } - if (_dragLand) { - _dragLand.destroy() - _dragLand = undefined + for (var i=0; i<_dragAreas.length; i++) { + _dragAreas[i].destroy() } + _dragAreas = [ ] } function showDragAreas() { - console.log("showDragAreas") - if (!_dragLoiter) { - _dragLoiter = dragAreaComponent.createObject(map, { "dragLoiter": true }) - } - if (!_dragLand) { - _dragLand = dragAreaComponent.createObject(map, { "dragLoiter": false }) + if (_dragAreas.length === 0) { + _dragAreas.push(dragAreaComponent.createObject(map, { "dragLoiter": true })) + _dragAreas.push(dragAreaComponent.createObject(map, { "dragLoiter": false })) } } @@ -116,7 +104,6 @@ Item { target: _missionItem onIsCurrentItemChanged: { - console.log("onIsCurrentItemChanged", _missionItem.isCurrentItem) if (_missionItem.isCurrentItem) { if (_missionItem.landingCoordSet) { showDragAreas() @@ -172,7 +159,7 @@ Item { z: QGroundControl.zOrderMapItems + 1 // Above item icons property bool dragLoiter - property var mapQuickItem: dragLoiter ? _loiterPoint : _landPoint + property var mapQuickItem: dragLoiter ? _itemVisuals[_loiterPointIndex] : _itemVisuals[_landPointIndex] property bool _preventCoordinateBindingLoop: false onXChanged: liveDrag() @@ -216,7 +203,7 @@ Item { MapPolyline { z: QGroundControl.zOrderMapItems - 1 // Under item indicators - line.color: "white" + line.color: "#be781c" line.width: 2 path: _missionItem.landingCoordSet ? [ _missionItem.loiterCoordinate, _missionItem.landingCoordinate ] : undefined } @@ -234,11 +221,25 @@ Item { sourceItem: MissionItemIndexLabel { - label: "P" + label: "L" } } } + // Loiter radius visual + Component { + id: loiterRadiusComponent + + MapCircle { + z: QGroundControl.zOrderMapItems + center: _missionItem.loiterCoordinate + radius: _missionItem.loiterRadius.value + border.width: 2 + border.color: "green" + color: "transparent" + } + } + // Land point Component { id: landPointComponent @@ -252,6 +253,7 @@ Item { sourceItem: MissionItemIndexLabel { label: "L" + checked: _missionItem.isCurrentItem } } } diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json index 96e5657ed..0efffaee4 100644 --- a/src/MissionManager/FWLandingPattern.FactMetaData.json +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -12,8 +12,10 @@ "shortDescription": "Heading from land point to loiter point.", "type": "double", "units": "deg", + "min": 0.0, + "max": 360.0, "decimalPlaces": 0, - "defaultValue": 0.0 + "defaultValue": 270.0 }, { "name": "Loiter altitude", diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 3603eba38..dd1dc98fc 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -230,7 +230,7 @@ void FixedWingLandingComplexItem::_recalcLoiterCoordFromFacts(void) QPointF originPoint(east, north); north += _loiterToLandDistanceFact.rawValue().toDouble(); QPointF loiterPoint(east, north); - QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, _landingHeadingFact.rawValue().toDouble()); + QPointF rotatedLoiterPoint = _rotatePoint(loiterPoint, originPoint, -_landingHeadingFact.rawValue().toDouble()); convertNedToGeo(rotatedLoiterPoint.y(), rotatedLoiterPoint.x(), down, tangentOrigin, &_loiterCoordinate); @@ -276,7 +276,11 @@ void FixedWingLandingComplexItem::_recalcFactsFromCoords(void) QPointF vector(eastLoiter - eastLand, northLoiter - northLand); double radians = atan2(vector.y(), vector.x()); double degrees = qRadiansToDegrees(radians); - degrees -= 90; // north up + // Change angle to north up = 0 degrees + degrees -= 90; + // Reverse the angle direction to go from mathematic angle (counter-clockwise) to compass heading (clockwise) + degrees *= -1.0; + // Bring with 0-360 range if (degrees < 0.0) { degrees += 360.0; } else if (degrees > 360.0) { -- GitLab From 66e05199e4537789e7c0e81fdabb96e40950bff7 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 11:19:19 -0800 Subject: [PATCH 363/398] Add Mixers tuning page --- qgroundcontrol.pro | 2 + qgroundcontrol.qrc | 1 + .../APM/APMAutoPilotPlugin.cc | 6 ++ src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h | 3 + .../Common/MixersComponent.cc | 64 +++++++++++++++++++ src/AutoPilotPlugins/Common/MixersComponent.h | 42 ++++++++++++ .../Common/MixersComponent.qml | 38 +++++++++++ .../PX4/PX4AutoPilotPlugin.cc | 6 ++ src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h | 3 + 9 files changed, 165 insertions(+) create mode 100644 src/AutoPilotPlugins/Common/MixersComponent.cc create mode 100644 src/AutoPilotPlugins/Common/MixersComponent.h create mode 100644 src/AutoPilotPlugins/Common/MixersComponent.qml diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index f06292340..51e7f3146 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -750,6 +750,7 @@ HEADERS+= \ src/AutoPilotPlugins/AutoPilotPlugin.h \ src/AutoPilotPlugins/Common/ESP8266Component.h \ src/AutoPilotPlugins/Common/ESP8266ComponentController.h \ + src/AutoPilotPlugins/Common/MixersComponent.h \ src/AutoPilotPlugins/Common/MotorComponent.h \ src/AutoPilotPlugins/Common/RadioComponentController.h \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.h \ @@ -771,6 +772,7 @@ SOURCES += \ src/AutoPilotPlugins/AutoPilotPlugin.cc \ src/AutoPilotPlugins/Common/ESP8266Component.cc \ src/AutoPilotPlugins/Common/ESP8266ComponentController.cc \ + src/AutoPilotPlugins/Common/MixersComponent.cc \ src/AutoPilotPlugins/Common/MotorComponent.cc \ src/AutoPilotPlugins/Common/RadioComponentController.cc \ src/AutoPilotPlugins/Generic/GenericAutoPilotPlugin.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 870f12057..512e9dcd0 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -39,6 +39,7 @@ src/ui/preferences/MavlinkSettings.qml src/MissionEditor/MissionEditor.qml src/MissionEditor/MissionSettingsEditor.qml + src/AutoPilotPlugins/Common/MixersComponent.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml src/MultiVehicle/MultiVehicleView.qml diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc index 200b04d86..c8bf2d92f 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.cc @@ -28,6 +28,7 @@ #include "APMLightsComponent.h" #include "APMSubFrameComponent.h" #include "ESP8266Component.h" +#include "MixersComponent.h" /// This is the AutoPilotPlugin implementatin for the MAV_AUTOPILOT_ARDUPILOT type. APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent) @@ -49,6 +50,7 @@ APMAutoPilotPlugin::APMAutoPilotPlugin(Vehicle* vehicle, QObject* parent) , _tuningComponent(NULL) , _airframeFacts(new APMAirframeLoader(this, vehicle->uas(), this)) , _esp8266Component(NULL) + , _mixersComponent(NULL) { APMAirframeLoader::loadAirframeFactMetaData(); } @@ -102,6 +104,10 @@ const QVariantList& APMAutoPilotPlugin::vehicleComponents(void) _tuningComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_tuningComponent)); + _mixersComponent = new MixersComponent(_vehicle, this); + _mixersComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_mixersComponent)); + _cameraComponent = new APMCameraComponent(_vehicle, this); _cameraComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_cameraComponent)); diff --git a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h index 8ac267b42..838bbe393 100644 --- a/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h +++ b/src/AutoPilotPlugins/APM/APMAutoPilotPlugin.h @@ -27,6 +27,7 @@ class APMCameraComponent; class APMLightsComponent; class APMSubFrameComponent; class ESP8266Component; +class MixersComponent; /// This is the APM specific implementation of the AutoPilot class. class APMAutoPilotPlugin : public AutoPilotPlugin @@ -55,6 +56,7 @@ public: APMSensorsComponent* sensorsComponent (void) const { return _sensorsComponent; } APMTuningComponent* tuningComponent (void) const { return _tuningComponent; } ESP8266Component* esp8266Component (void) const { return _esp8266Component; } + MixersComponent* mixersComponent (void) { return _mixersComponent; } private: bool _incorrectParameterVersion; ///< true: parameter version incorrect, setup not allowed @@ -76,6 +78,7 @@ private: APMTuningComponent* _tuningComponent; APMAirframeLoader* _airframeFacts; ESP8266Component* _esp8266Component; + MixersComponent* _mixersComponent; }; #endif diff --git a/src/AutoPilotPlugins/Common/MixersComponent.cc b/src/AutoPilotPlugins/Common/MixersComponent.cc new file mode 100644 index 000000000..75579a5e1 --- /dev/null +++ b/src/AutoPilotPlugins/Common/MixersComponent.cc @@ -0,0 +1,64 @@ +/**************************************************************************** + * + * (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 "MixersComponent.h" +#include "APMAutoPilotPlugin.h" +#include "APMAirframeComponent.h" +#include "ParameterManager.h" + +MixersComponent::MixersComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) + : VehicleComponent(vehicle, autopilot, parent) + , _name(tr("Mixers")) +{ +} + +QString MixersComponent::name(void) const +{ + return _name; +} + +QString MixersComponent::description(void) const +{ + return tr("Mixers tuning is used to blah, blah, blah... [WIP]"); +} + +QString MixersComponent::iconResource(void) const +{ + return QStringLiteral("/qmlimages/TuningComponentIcon.png"); +} + +bool MixersComponent::requiresSetup(void) const +{ + return false; +} + +bool MixersComponent::setupComplete(void) const +{ + return true; +} + +QStringList MixersComponent::setupCompleteChangedTriggerList(void) const +{ + return QStringList(); +} + +QUrl MixersComponent::setupSource(void) const +{ + return QUrl::fromUserInput(QStringLiteral("qrc:/qml/MixersComponent.qml")); +} + +QUrl MixersComponent::summaryQmlSource(void) const +{ + return QUrl(); +} + +QString MixersComponent::prerequisiteSetup(void) const +{ + return QString(); +} diff --git a/src/AutoPilotPlugins/Common/MixersComponent.h b/src/AutoPilotPlugins/Common/MixersComponent.h new file mode 100644 index 000000000..5a624d6f7 --- /dev/null +++ b/src/AutoPilotPlugins/Common/MixersComponent.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * + * (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 MixersComponent_H +#define MixersComponent_H + +#include "VehicleComponent.h" + +// Mixers Tuning vehicle component +class MixersComponent : public VehicleComponent +{ + Q_OBJECT + +public: + MixersComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent = NULL); + + // Virtuals from VehicleComponent + QStringList setupCompleteChangedTriggerList(void) const final; + + // Virtuals from VehicleComponent + QString name(void) const final; + QString description(void) const final; + QString iconResource(void) const final; + bool requiresSetup(void) const final; + bool setupComplete(void) const final; + QUrl setupSource(void) const final; + QUrl summaryQmlSource(void) const final; + QString prerequisiteSetup(void) const final; + bool allowSetupWhileArmed(void) const final { return true; } + +private: + const QString _name; + QVariantList _summaryItems; +}; + +#endif diff --git a/src/AutoPilotPlugins/Common/MixersComponent.qml b/src/AutoPilotPlugins/Common/MixersComponent.qml new file mode 100644 index 000000000..84034427b --- /dev/null +++ b/src/AutoPilotPlugins/Common/MixersComponent.qml @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +// Mixer Tuning setup page +SetupPage { + id: tuningPage + pageComponent: tuningPageComponent + + Component { + id: tuningPageComponent + + Column { + width: availableWidth + spacing: _margins + + FactPanelController { id: controller; factPanel: tuningPage.viewPanel } + + QGCPalette { id: palette; colorGroupEnabled: true } + + QGCLabel { text: qsTr("Lot of Qml code goes here...") } + } // Column + } // Component +} // SetupView diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index 0b96501b3..3036a1576 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -31,6 +31,8 @@ PX4AutoPilotPlugin::PX4AutoPilotPlugin(Vehicle* vehicle, QObject* parent) , _safetyComponent(NULL) , _powerComponent(NULL) , _motorComponent(NULL) + , _tuningComponent(NULL) + , _mixersComponent(NULL) , _incorrectParameterVersion(false) { Q_ASSERT(vehicle); @@ -89,6 +91,10 @@ const QVariantList& PX4AutoPilotPlugin::vehicleComponents(void) _tuningComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_tuningComponent)); + _mixersComponent = new MixersComponent(_vehicle, this); + _mixersComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_mixersComponent)); + //-- Is there support for cameras? if(_vehicle->parameterManager()->parameterExists(_vehicle->id(), "TRIG_MODE")) { _cameraComponent = new CameraComponent(_vehicle, this); diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h index 32f63dd14..e5f0efa6c 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h @@ -23,6 +23,7 @@ #include "PowerComponent.h" #include "MotorComponent.h" #include "PX4TuningComponent.h" +#include "MixersComponent.h" #include "Vehicle.h" #include @@ -54,6 +55,7 @@ public: PowerComponent* powerComponent(void) { return _powerComponent; } MotorComponent* motorComponent(void) { return _motorComponent; } PX4TuningComponent* tuningComponent(void) { return _tuningComponent; } + MixersComponent* mixersComponent(void) { return _mixersComponent; } private: PX4AirframeLoader* _airframeFacts; @@ -68,6 +70,7 @@ private: PowerComponent* _powerComponent; MotorComponent* _motorComponent; PX4TuningComponent* _tuningComponent; + MixersComponent* _mixersComponent; bool _incorrectParameterVersion; ///< true: parameter version incorrect, setup not allowed }; -- GitLab From 9935949a6f704445a5b0d9d69c69c6a2c41791cc Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Sun, 26 Feb 2017 15:54:52 -0500 Subject: [PATCH 364/398] Check for visibility and remove duplicated test. --- src/ui/preferences/GeneralSettings.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 319049398..85186ba5c 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -505,7 +505,7 @@ QGCView { } Row { spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.isGStreamer && videoSource.currentIndex < 2 + visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex < 2 && QGroundControl.settingsManager.videoSettings.aspectRatio.visible QGCLabel { anchors.baseline: aspectField.baseline text: qsTr("Aspect Ratio:") -- GitLab From fff9c07716100a9d66a9f51883152a0f292a74f9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 13:17:39 -0800 Subject: [PATCH 365/398] Remove bad includes --- src/AutoPilotPlugins/Common/MixersComponent.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/AutoPilotPlugins/Common/MixersComponent.cc b/src/AutoPilotPlugins/Common/MixersComponent.cc index 75579a5e1..efe440b97 100644 --- a/src/AutoPilotPlugins/Common/MixersComponent.cc +++ b/src/AutoPilotPlugins/Common/MixersComponent.cc @@ -8,8 +8,6 @@ ****************************************************************************/ #include "MixersComponent.h" -#include "APMAutoPilotPlugin.h" -#include "APMAirframeComponent.h" #include "ParameterManager.h" MixersComponent::MixersComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) -- GitLab From 906e49838fa8c6840ec2e55755fcd64bdf41be8b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 13:18:00 -0800 Subject: [PATCH 366/398] Remove deprecated methods Replaced by new settings capability --- src/api/QGCOptions.h | 60 +++++++++++++------------------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index c465d5d2b..bf070a8df 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -26,60 +26,36 @@ public: Q_PROPERTY(bool combineSettingsAndSetup READ combineSettingsAndSetup CONSTANT) Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) - Q_PROPERTY(bool definesVideo READ definesVideo CONSTANT) - Q_PROPERTY(uint16_t videoUDPPort READ videoUDPPort CONSTANT) - Q_PROPERTY(QString videoRSTPUrl READ videoRSTPUrl CONSTANT) Q_PROPERTY(double toolbarHeightMultiplier READ toolbarHeightMultiplier CONSTANT) Q_PROPERTY(double defaultFontPointSize READ defaultFontPointSize CONSTANT) Q_PROPERTY(bool enablePlanViewSelector READ enablePlanViewSelector CONSTANT) Q_PROPERTY(CustomInstrumentWidget* instrumentWidget READ instrumentWidget CONSTANT) - //! Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? - /*! - @return true if QGC should consolidate both menus into one. - */ + /// Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? + /// @return true if QGC should consolidate both menus into one. virtual bool combineSettingsAndSetup () { return false; } - //! Should QGC use virtual Joysticks? - /*! - @return false to disable Virtual Joysticks. - */ + + /// Should QGC use virtual Joysticks? + /// @return false to disable Virtual Joysticks. virtual bool enableVirtualJoystick () { return true; } - //! Does your plugin defines its on video source? - /*! - @return true to define your own video source. - */ - virtual bool definesVideo () { return false; } - //! UDP port to use for (RTP) video source. - /*! - @return UDP Port to use. Return 0 to disable UDP RTP. - */ - virtual uint16_t videoUDPPort () { return 0; } - //! RTSP URL to use for video source. - /*! - @return RTSP url to use. Return "" to disable RTSP. - */ - virtual QString videoRSTPUrl () { return QString(); } - //! Main ToolBar Multiplier. - /*! - @return Factor to use when computing toolbar height - */ + + /// Main ToolBar Multiplier. + /// @return Factor to use when computing toolbar height virtual double toolbarHeightMultiplier () { return 1.0; } - //! Application wide default font point size - /*! - @return Font size or 0.0 to use computed size. - */ + + /// Application wide default font point size + /// @return Font size or 0.0 to use computed size. virtual double defaultFontPointSize () { return 0.0; } - //! Enable Plan View Selector (Mission, Fence or Rally) - /*! - @return True or false - */ + + /// Enable Plan View Selector (Mission, Fence or Rally) + /// @return True or false virtual bool enablePlanViewSelector () { return true; } - //! Provides an alternate instrument widget for the Fly View - /*! - @return An alternate widget (see QGCInstrumentWidget.qml, the default widget) - */ + + /// Provides an alternate instrument widget for the Fly View + /// @return An alternate widget (see QGCInstrumentWidget.qml, the default widget) virtual CustomInstrumentWidget* instrumentWidget(); + private: CustomInstrumentWidget* _defaultInstrumentWidget; }; -- GitLab From a0b67850e0f68ad55e56b4937d0e251d3d9c21d8 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 14:01:13 -0800 Subject: [PATCH 367/398] Showing up under toolbar on short screens --- src/QmlControls/DropPanel.qml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/QmlControls/DropPanel.qml b/src/QmlControls/DropPanel.qml index a0f2ab117..0bc38aedd 100644 --- a/src/QmlControls/DropPanel.qml +++ b/src/QmlControls/DropPanel.qml @@ -44,11 +44,9 @@ Item { property var _dropEdgeTopPoint property real _dropEdgeHeight - property alias _dropDownComponent: dropDownLoader.sourceComponent - property real _viewportMaxLeft: -x + viewportMargins - property real _viewportMaxRight: parent.width - (viewportMargins * 2) - x - property real _viewportMaxTop: -y + viewportMargins - property real _viewportMaxBottom: parent.height - (viewportMargins * 2) - y + property alias _dropDownComponent: dropDownLoader.sourceComponent + property real _viewportMaxTop: 0 + property real _viewportMaxBottom: parent.parent.height - parent.y function show(panelEdgeTopPoint, panelEdgeHeight, panelComponent) { _dropEdgeTopPoint = panelEdgeTopPoint @@ -88,8 +86,8 @@ Item { dropItemHolderRect.x = _arrowPointHeight // Validate that dropdown is within viewport - dropDownItem.y = Math.max(dropDownItem.y, _viewportMaxTop) dropDownItem.y = Math.min(dropDownItem.y + dropDownItem.height, _viewportMaxBottom) - dropDownItem.height + dropDownItem.y = Math.max(dropDownItem.y, _viewportMaxTop) // Arrow points arrowCanvas.arrowPoint.y = (_dropEdgeTopPoint.y + radius) - dropDownItem.y -- GitLab From 0532656204c06e1e0a907274ef3969455ea4d69f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 15:15:42 -0800 Subject: [PATCH 368/398] Regular text color on section labels --- src/MissionEditor/MissionSettingsEditor.qml | 6 +----- src/MissionEditor/SurveyItemEditor.qml | 12 ++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/MissionEditor/MissionSettingsEditor.qml b/src/MissionEditor/MissionSettingsEditor.qml index 729370c2a..b209b6e11 100644 --- a/src/MissionEditor/MissionSettingsEditor.qml +++ b/src/MissionEditor/MissionSettingsEditor.qml @@ -53,10 +53,7 @@ Rectangle { anchors.top: parent.top spacing: _margin - QGCLabel { - text: qsTr("Planned Home Position") - color: qgcPal.buttonHighlight - } + QGCLabel { text: qsTr("Planned Home Position") } Rectangle { anchors.left: parent.left @@ -96,7 +93,6 @@ Rectangle { QGCLabel { text: qsTr("Vehicle Info") - color: qgcPal.buttonHighlight visible: _multipleFirmware } diff --git a/src/MissionEditor/SurveyItemEditor.qml b/src/MissionEditor/SurveyItemEditor.qml index 7d442e427..75f952aca 100644 --- a/src/MissionEditor/SurveyItemEditor.qml +++ b/src/MissionEditor/SurveyItemEditor.qml @@ -286,7 +286,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Camera"); color: qgcPal.buttonHighlight; visible: gridTypeCombo.currentIndex !== _gridTypeManual} + QGCLabel { text: qsTr("Camera"); visible: gridTypeCombo.currentIndex !== _gridTypeManual} Rectangle { anchors.left: parent.left @@ -420,7 +420,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Grid"); color: qgcPal.buttonHighlight;} + QGCLabel { text: qsTr("Grid") } Rectangle { anchors.left: parent.left @@ -523,7 +523,7 @@ Rectangle { spacing: _margin visible: gridTypeCombo.currentIndex == _gridTypeManual - QGCLabel { text: qsTr("Grid"); color: qgcPal.buttonHighlight;} + QGCLabel { text: qsTr("Grid") } Rectangle { anchors.left: parent.left @@ -547,7 +547,7 @@ Rectangle { onClicked: missionItem.gridAltitudeRelative = checked } - QGCLabel { text: qsTr("Camera"); color: qgcPal.buttonHighlight;} + QGCLabel { text: qsTr("Camera") } Rectangle { anchors.left: parent.left @@ -578,7 +578,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Polygon"); color: qgcPal.buttonHighlight;} + QGCLabel { text: qsTr("Polygon") } Rectangle { anchors.left: parent.left @@ -621,7 +621,7 @@ Rectangle { } } - QGCLabel { text: qsTr("Statistics"); color: qgcPal.buttonHighlight;} + QGCLabel { text: qsTr("Statistics") } Rectangle { anchors.left: parent.left -- GitLab From 2d64f0cbcaa5f7e8d418c4c56bd22b8aad3b35c0 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 15:15:59 -0800 Subject: [PATCH 369/398] Increase size of target touch areas --- src/QmlControls/ToolStrip.qml | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml index 337b1b40f..89ae07a46 100644 --- a/src/QmlControls/ToolStrip.qml +++ b/src/QmlControls/ToolStrip.qml @@ -48,12 +48,13 @@ Rectangle { } MouseArea { - x: -_root.x - y: -_root.y - width: _root.parent.width - height: _root.parent.height - visible: dropPanel.visible - onClicked: dropPanel.hide() + x: -_root.x + y: -_root.y + width: _root.parent.width + height: _root.parent.height + visible: dropPanel.visible + onClicked: dropPanel.hide() + preventStealing: true } Column { @@ -144,11 +145,15 @@ Rectangle { } MouseArea { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - height: parent.height + (_showOptionalElements? buttonLabel.height + buttonColumn.spacing : 0) - visible: _root.buttonEnabled ? _root.buttonEnabled[index] : true + // Size of mouse area is expanded to make touch easier + anchors.leftMargin: buttonStripColumn.margins + anchors.rightMargin: buttonStripColumn.margins + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + height: parent.height + (_showOptionalElements? buttonLabel.height + buttonColumn.spacing : 0) + visible: _root.buttonEnabled ? _root.buttonEnabled[index] : true + preventStealing: true onClicked: { if (modelData.dropPanelComponent === undefined) { -- GitLab From 2dbd8519edec41903b5b74bb833b765f18b8948f Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 15:16:34 -0800 Subject: [PATCH 370/398] Consistent sizing between Combo and TextField Also correctly set implicitHeight on QGCTextField --- src/QmlControls/QGCComboBox.qml | 2 +- src/QmlControls/QGCTextField.qml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/QmlControls/QGCComboBox.qml b/src/QmlControls/QGCComboBox.qml index 8b00e725d..9950708b4 100644 --- a/src/QmlControls/QGCComboBox.qml +++ b/src/QmlControls/QGCComboBox.qml @@ -19,7 +19,7 @@ ComboBox { background: Item { implicitWidth: Math.round(ScreenTools.defaultFontPixelWidth * 4.5) - implicitHeight: ScreenTools.isMobile ? Math.max(25, Math.round(ScreenTools.defaultFontPixelHeight * 2)) : Math.max(25, Math.round(ScreenTools.defaultFontPixelHeight * 1.2)) + implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 2 : 1.2)) Rectangle { anchors.fill: parent diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 6542d290b..502e428be 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -26,7 +26,8 @@ TextField { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } textColor: qgcPal.textFieldText - height: Math.round(Math.max(25, ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 2.5 : 1.2))) + + implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 2 : 1.2)) QGCLabel { id: unitsLabelWidthGenerator -- GitLab From 88f084eb8af4154cdffd07f13b50188c108df01b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 18:38:42 -0800 Subject: [PATCH 371/398] More moves of settings to new support --- src/AnalyzeView/AnalyzeView.qml | 4 +- src/FlightDisplay/FlightDisplayView.qml | 12 +-- .../FlightDisplayViewWidgets.qml | 8 +- src/FlightDisplay/VirtualJoystick.qml | 2 +- src/QGCApplication.cc | 26 +------ src/QGCApplication.h | 17 +--- src/QmlControls/QGroundControlQmlGlobal.cc | 35 --------- src/QmlControls/QGroundControlQmlGlobal.h | 21 ----- src/QmlControls/ScreenTools.qml | 16 ++-- src/Settings/AppSettings.cc | 75 +++++++++++++++--- src/Settings/AppSettings.h | 23 +++++- src/Settings/SettingsGroup.app.json | 25 ++++++ src/VehicleSetup/SetupView.qml | 4 +- src/ui/linechart/ChartPlot.cc | 3 +- src/ui/linechart/LinechartWidget.cc | 5 +- src/ui/preferences/GeneralSettings.qml | 77 +++++++++---------- 16 files changed, 177 insertions(+), 176 deletions(-) diff --git a/src/AnalyzeView/AnalyzeView.qml b/src/AnalyzeView/AnalyzeView.qml index cb827796f..cd910754a 100644 --- a/src/AnalyzeView/AnalyzeView.qml +++ b/src/AnalyzeView/AnalyzeView.qml @@ -58,8 +58,8 @@ Rectangle { // I don't know why this does not work Connections { - target: QGroundControl - onBaseFontPointSizeChanged: buttonColumn.reflowWidths() + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: buttonColumn.reflowWidths() } function reflowWidths() { diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 368279771..07f4c6832 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -81,7 +81,7 @@ QGCView { } function px4JoystickCheck() { - if ( _activeVehicle && !_activeVehicle.supportsManualControl && (QGroundControl.virtualTabletJoystick || _activeVehicle.joystickEnabled)) { + if ( _activeVehicle && !_activeVehicle.supportsManualControl && (QGroundControl.settingsManager.appSettings.virtualJoystick.value || _activeVehicle.joystickEnabled)) { px4JoystickSupport.open() } } @@ -99,8 +99,8 @@ QGCView { } Connections { - target: QGroundControl - onVirtualTabletJoystickChanged: px4JoystickCheck() + target: QGroundControl.settingsManager.appSettings.virtualJoystick + onValueChanged: px4JoystickCheck() } onActiveVehicleJoystickEnabledChanged: px4JoystickCheck() @@ -302,14 +302,16 @@ QGCView { z: _panel.z + 5 width: parent.width - (_flightVideoPipControl.width / 2) height: Math.min(ScreenTools.availableHeight * 0.25, ScreenTools.defaultFontPixelWidth * 16) - visible: QGroundControl.virtualTabletJoystick + visible: _virtualJoystick.value anchors.bottom: _flightVideoPipControl.top anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 2 anchors.horizontalCenter: flightDisplayViewWidgets.horizontalCenter source: "qrc:/qml/VirtualJoystick.qml" - active: QGroundControl.virtualTabletJoystick + active: _virtualJoystick.value property bool useLightColors: root.isBackgroundDark + + property Fact _virtualJoystick: QGroundControl.settingsManager.appSettings.virtualJoystick } } } diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 6688eb6d6..222ac38fb 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -63,7 +63,7 @@ Item { break; } } else { - var useAlternateInstruments = QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen + var useAlternateInstruments = QGroundControl.settingsManager.appSettings.virtualJoystick.value || ScreenTools.isTinyScreen if(useAlternateInstruments) { instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" instrumentsLoader.state = "topMode" @@ -75,10 +75,8 @@ Item { } Connections { - target: QGroundControl - onVirtualTabletJoystickChanged: { - _setInstrumentWidget() - } + target: QGroundControl.settingsManager.appSettings.virtualJoystick + onValueChanged: _setInstrumentWidget() } Component.onCompleted: { diff --git a/src/FlightDisplay/VirtualJoystick.qml b/src/FlightDisplay/VirtualJoystick.qml index 07248d6df..f10d4e2d2 100644 --- a/src/FlightDisplay/VirtualJoystick.qml +++ b/src/FlightDisplay/VirtualJoystick.qml @@ -21,7 +21,7 @@ Item { Timer { interval: 40 // 25Hz, same as real joystick rate - running: QGroundControl.virtualTabletJoystick && _activeVehicle + running: QGroundControl.settingsManager.appSettings.virtualJoystick.value && _activeVehicle repeat: true onTriggered: { if (_activeVehicle) { diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index a92a4ed89..c6e66960c 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -79,6 +79,7 @@ #include "MissionCommandTree.h" #include "QGCMapPolygon.h" #include "ParameterManager.h" +#include "SettingsManager.h" #ifndef NO_SERIAL_LINK #include "SerialLink.h" @@ -115,7 +116,6 @@ const char* QGCApplication::telemetryFileExtension = "tlog"; const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot"; const char* QGCApplication::_settingsVersionKey = "SettingsVersion"; -const char* QGCApplication::_styleKey = "StyleIsDark"; const char* QGCApplication::_lastKnownHomePositionLatKey = "LastKnownHomePositionLat"; const char* QGCApplication::_lastKnownHomePositionLonKey = "LastKnownHomePositionLon"; const char* QGCApplication::_lastKnownHomePositionAltKey = "LastKnownHomePositionAlt"; @@ -166,11 +166,6 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) : QApplication(argc, argv) #endif , _runningUnitTests(unitTesting) -#if defined (__mobile__) - , _styleIsDark(false) -#else - , _styleIsDark(true) -#endif , _fakeMobile(false) , _settingsUpgraded(false) #ifdef QT_DEBUG @@ -400,8 +395,7 @@ bool QGCApplication::_initForNormalAppBoot(void) { QSettings settings; - _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool(); - _loadCurrentStyle(); + _loadCurrentStyleSheet(); // Exit main application when last window is closed connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit); @@ -527,17 +521,7 @@ void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile) } #endif -void QGCApplication::setStyle(bool styleIsDark) -{ - QSettings settings; - - settings.setValue(_styleKey, styleIsDark); - _styleIsDark = styleIsDark; - _loadCurrentStyle(); - emit styleChanged(_styleIsDark); -} - -void QGCApplication::_loadCurrentStyle() +void QGCApplication::_loadCurrentStyleSheet(void) { #ifndef __mobile__ bool success = true; @@ -553,7 +537,7 @@ void QGCApplication::_loadCurrentStyle() success = false; } - if (success && !_styleIsDark) { + if (success && !_toolbox->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()) { // Load the slave light stylesheet. QFile styleSheet(_lightStyleFile); if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -571,8 +555,6 @@ void QGCApplication::_loadCurrentStyle() setStyle("plastique"); } #endif - - QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light); } void QGCApplication::reportMissingParameter(int componentId, const QString& name) diff --git a/src/QGCApplication.h b/src/QGCApplication.h index c080b2490..d1e2cb974 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -78,12 +78,6 @@ public: /// @brief Returns truee if unit test are being run bool runningUnitTests(void) { return _runningUnitTests; } - /// @return true: dark ui style, false: light ui style - bool styleIsDark(void) { return _styleIsDark; } - - /// Set the current UI style - void setStyle(bool styleIsDark); - /// Used to report a missing Parameter. Warning will be displayed to user. Method may be called /// multiple times. void reportMissingParameter(int componentId, const QString& name); @@ -127,10 +121,6 @@ public slots: #endif signals: - /// Signals that the style has changed - /// @param darkStyle true: dark style, false: light style - void styleChanged(bool darkStyle); - /// This is connected to MAVLinkProtocol::checkForLostLogFiles. We signal this to ourselves to call the slot /// on the MAVLinkProtocol thread; void checkForLostLogFiles(void); @@ -150,14 +140,15 @@ public: /// unit tests. Although public should only be called by main. bool _initForUnitTests(void); + void _loadCurrentStyleSheet(void); + static QGCApplication* _app; ///< Our own singleton. Should be reference directly by qgcApp private slots: void _missingParamsDisplay(void); private: - void _loadCurrentStyle (); - QObject* _rootQmlObject (); + QObject* _rootQmlObject(void); #ifdef __mobile__ QQmlApplicationEngine* _qmlAppEngine; @@ -167,7 +158,6 @@ private: static const char* _darkStyleFile; static const char* _lightStyleFile; - bool _styleIsDark; ///< true: dark style, false: light style static const int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display QStringList _missingParams; ///< List of missing facts to be displayed @@ -186,7 +176,6 @@ private: static const char* _settingsVersionKey; ///< Settings key which hold settings version static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted - static const char* _styleKey; ///< Settings key for UI style static const char* _lastKnownHomePositionLatKey; static const char* _lastKnownHomePositionLonKey; static const char* _lastKnownHomePositionAltKey; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index a3fb27497..af893774e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -19,9 +19,6 @@ static const char* kQmlGlobalKeyName = "QGCQml"; -const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; -const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; - QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) : QGCTool(app) , _flightMapSettings(NULL) @@ -35,13 +32,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _corePlugin(NULL) , _firmwarePluginManager(NULL) , _settingsManager(NULL) - , _virtualTabletJoystick(false) - , _baseFontPointSize(0.0) { - QSettings settings; - _virtualTabletJoystick = settings.value(_virtualTabletJoystickKey, false).toBool(); - _baseFontPointSize = settings.value(_baseFontPointSizeKey, 0.0).toDouble(); - // We clear the parent on this object since we run into shutdown problems caused by hybrid qml app. Instead we let it leak on shutdown. setParent(NULL); } @@ -157,12 +148,6 @@ void QGroundControlQmlGlobal::stopAllMockLinks(void) #endif } -void QGroundControlQmlGlobal::setIsDarkStyle(bool dark) -{ - qgcApp()->setStyle(dark); - emit isDarkStyleChanged(dark); -} - void QGroundControlQmlGlobal::setIsVersionCheckEnabled(bool enable) { qgcApp()->toolbox()->mavlinkProtocol()->enableVersionCheck(enable); @@ -175,26 +160,6 @@ void QGroundControlQmlGlobal::setMavlinkSystemID(int id) emit mavlinkSystemIDChanged(id); } -void QGroundControlQmlGlobal::setVirtualTabletJoystick(bool enabled) -{ - if (_virtualTabletJoystick != enabled) { - QSettings settings; - settings.setValue(_virtualTabletJoystickKey, enabled); - _virtualTabletJoystick = enabled; - emit virtualTabletJoystickChanged(enabled); - } -} - -void QGroundControlQmlGlobal::setBaseFontPointSize(qreal size) -{ - if (size >= 6.0 && size <= 48.0) { - QSettings settings; - settings.setValue(_baseFontPointSizeKey, size); - _baseFontPointSize = size; - emit baseFontPointSizeChanged(size); - } -} - int QGroundControlQmlGlobal::supportedFirmwareCount() { return _firmwarePluginManager->supportedFirmwareTypes().count(); diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index dc28ca655..047e3f86e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -54,11 +54,6 @@ public: Q_PROPERTY(qreal zOrderWidgets READ zOrderWidgets CONSTANT) ///< z order value to widgets, for example: zoom controls, hud widgetss Q_PROPERTY(qreal zOrderMapItems READ zOrderMapItems CONSTANT) ///< z order value for map items, for example: mission item indicators - // Various QGC settings exposed to Qml - Q_PROPERTY(bool isDarkStyle READ isDarkStyle WRITE setIsDarkStyle NOTIFY isDarkStyleChanged) // TODO: Should be in ScreenTools? - Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) - Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) - //------------------------------------------------------------------------- // MavLink Protocol Q_PROPERTY(bool isVersionCheckEnabled READ isVersionCheckEnabled WRITE setIsVersionCheckEnabled NOTIFY isVersionCheckEnabledChanged) @@ -142,10 +137,6 @@ public: qreal zOrderWidgets () { return 100; } qreal zOrderMapItems () { return 50; } - bool isDarkStyle () { return _app->styleIsDark(); } - bool virtualTabletJoystick () { return _virtualTabletJoystick; } - qreal baseFontPointSize () { return _baseFontPointSize; } - bool isVersionCheckEnabled () { return _toolbox->mavlinkProtocol()->versionCheckEnabled(); } int mavlinkSystemID () { return _toolbox->mavlinkProtocol()->getSystemId(); } @@ -153,10 +144,6 @@ public: int supportedFirmwareCount (); - void setIsDarkStyle (bool dark); - void setVirtualTabletJoystick (bool enabled); - void setBaseFontPointSize (qreal size); - void setIsVersionCheckEnabled (bool enable); void setMavlinkSystemID (int id); @@ -170,9 +157,6 @@ public: virtual void setToolbox(QGCToolbox* toolbox); signals: - void isDarkStyleChanged (bool dark); - void virtualTabletJoystickChanged (bool enabled); - void baseFontPointSizeChanged (qreal size); void isMultiplexingEnabledChanged (bool enabled); void isVersionCheckEnabledChanged (bool enabled); void mavlinkSystemIDChanged (int id); @@ -192,13 +176,8 @@ private: FirmwarePluginManager* _firmwarePluginManager; SettingsManager* _settingsManager; - bool _virtualTabletJoystick; - qreal _baseFontPointSize; QGeoCoordinate _flightMapPosition; double _flightMapZoom; - - static const char* _virtualTabletJoystickKey; - static const char* _baseFontPointSizeKey; }; #endif diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 964559273..0eb0c5593 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -67,10 +67,10 @@ Item { I've disabled (in release builds) until I figure out why. Changes require a restart for now. */ Connections { - target: QGroundControl - onBaseFontPointSizeChanged: { + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: { if(ScreenToolsController.isDebug) - _setBasePointSize(QGroundControl.baseFontPointSize) + _setBasePointSize(QGroundControl.settingsManager.appSettings.appFontPointSize.value) } } @@ -108,12 +108,10 @@ Item { property real fontWidth: contentWidth property real fontHeight: contentHeight Component.onCompleted: { - var baseSize = QGroundControl.corePlugin.options.defaultFontPointSize - if(baseSize == 0.0) { - baseSize = QGroundControl.baseFontPointSize; - } + var _appFontPointSizeFact = QGroundControl.settingsManager.appSettings.appFontPointSize + var baseSize = _appFontPointSizeFact.value //-- If this is the first time (not saved in settings) - if(baseSize < 6 || baseSize > 48) { + if(baseSize < _appFontPointSizeFact.min || baseSize > _appFontPointSizeFact.max) { //-- Init base size base on the platform if(ScreenToolsController.isMobile) { //-- Check iOS really tiny screens (iPhone 4s/5/5s) @@ -142,7 +140,7 @@ Item { else baseSize = _defaultFont.font.pointSize; } - QGroundControl.baseFontPointSize = baseSize + _appFontPointSizeFact.value = baseSize //-- Release build doesn't get signal if(!ScreenToolsController.isDebug) _screenTools._setBasePointSize(baseSize); diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index c0fb64fe8..7e4646321 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -8,6 +8,8 @@ ****************************************************************************/ #include "AppSettings.h" +#include "QGCPalette.h" +#include "QGCApplication.h" #include #include @@ -23,6 +25,9 @@ const char* AppSettings::missionAutoLoadDirSettingsName = "Mission const char* AppSettings::promptFlightTelemetrySaveName = "PromptFLightDataSave"; const char* AppSettings::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; const char* AppSettings::audioMutedName = "AudioMuted"; +const char* AppSettings::virtualJoystickName = "VirtualTabletJoystick"; +const char* AppSettings::appFontPointSizeName = "BaseDeviceFontPointSize"; +const char* AppSettings::indoorPaletteName = "StyleIsDark"; AppSettings::AppSettings(QObject* parent) : SettingsGroup(appSettingsGroupName, QString() /* root settings group */, parent) @@ -33,12 +38,24 @@ AppSettings::AppSettings(QObject* parent) , _batteryPercentRemainingAnnounceFact(NULL) , _defaultMissionItemAltitudeFact(NULL) , _missionAutoLoadDirFact(NULL) - , _promptFlightTelemetrySave(NULL) - , _promptFlightTelemetrySaveNotArmed(NULL) - , _audioMuted(NULL) + , _promptFlightTelemetrySaveFact(NULL) + , _promptFlightTelemetrySaveNotArmedFact(NULL) + , _audioMutedFact(NULL) + , _virtualJoystickFact(NULL) + , _appFontPointSizeFact(NULL) + , _indoorPaletteFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); + + // Set up correct default for palette setting + QVariant outdoorPalette; +#if defined (__mobile__) + outdoorPalette = 0; +#else + outdoorPalette = 1; +#endif + _nameToMetaDataMap[indoorPaletteName]->setRawDefaultValue(outdoorPalette); } Fact* AppSettings::offlineEditingFirmwareType(void) @@ -104,27 +121,61 @@ Fact* AppSettings::missionAutoLoadDir(void) Fact* AppSettings::promptFlightTelemetrySave(void) { - if (!_promptFlightTelemetrySave) { - _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); + if (!_promptFlightTelemetrySaveFact) { + _promptFlightTelemetrySaveFact = _createSettingsFact(promptFlightTelemetrySaveName); } - return _promptFlightTelemetrySave; + return _promptFlightTelemetrySaveFact; } Fact* AppSettings::promptFlightTelemetrySaveNotArmed(void) { - if (!_promptFlightTelemetrySaveNotArmed) { - _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); + if (!_promptFlightTelemetrySaveNotArmedFact) { + _promptFlightTelemetrySaveNotArmedFact = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); } - return _promptFlightTelemetrySaveNotArmed; + return _promptFlightTelemetrySaveNotArmedFact; } Fact* AppSettings::audioMuted(void) { - if (!_audioMuted) { - _audioMuted = _createSettingsFact(audioMutedName); + if (!_audioMutedFact) { + _audioMutedFact = _createSettingsFact(audioMutedName); + } + + return _audioMutedFact; +} + +Fact* AppSettings::appFontPointSize(void) +{ + if (!_appFontPointSizeFact) { + _appFontPointSizeFact = _createSettingsFact(appFontPointSizeName); } - return _audioMuted; + return _appFontPointSizeFact; +} + +Fact* AppSettings::virtualJoystick(void) +{ + if (!_virtualJoystickFact) { + _virtualJoystickFact = _createSettingsFact(virtualJoystickName); + } + + return _virtualJoystickFact; +} + +Fact* AppSettings::indoorPalette(void) +{ + if (!_indoorPaletteFact) { + _indoorPaletteFact = _createSettingsFact(indoorPaletteName); + connect(_indoorPaletteFact, &Fact::rawValueChanged, this, &AppSettings::_indoorPaletteChanged); + } + + return _indoorPaletteFact; +} + +void AppSettings::_indoorPaletteChanged(void) +{ + qgcApp()->_loadCurrentStyleSheet(); + QGCPalette::setGlobalTheme(_indoorPaletteFact->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light); } diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index c9bbae84a..7e39514ca 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -29,6 +29,9 @@ public: Q_PROPERTY(Fact* promptFlightTelemetrySave READ promptFlightTelemetrySave CONSTANT) Q_PROPERTY(Fact* promptFlightTelemetrySaveNotArmed READ promptFlightTelemetrySaveNotArmed CONSTANT) Q_PROPERTY(Fact* audioMuted READ audioMuted CONSTANT) + Q_PROPERTY(Fact* virtualJoystick READ virtualJoystick CONSTANT) + Q_PROPERTY(Fact* appFontPointSize READ appFontPointSize CONSTANT) + Q_PROPERTY(Fact* indoorPalette READ indoorPalette CONSTANT) Fact* offlineEditingFirmwareType (void); Fact* offlineEditingVehicleType (void); @@ -39,7 +42,10 @@ public: Fact* missionAutoLoadDir (void); Fact* promptFlightTelemetrySave (void); Fact* promptFlightTelemetrySaveNotArmed (void); - Fact* audioMuted (void); + Fact* audioMuted (void); + Fact* virtualJoystick (void); + Fact* appFontPointSize (void); + Fact* indoorPalette (void); static const char* appSettingsGroupName; @@ -53,6 +59,12 @@ public: static const char* promptFlightTelemetrySaveName; static const char* promptFlightTelemetrySaveNotArmedName; static const char* audioMutedName; + static const char* virtualJoystickName; + static const char* appFontPointSizeName; + static const char* indoorPaletteName; + +private slots: + void _indoorPaletteChanged(void); private: SettingsFact* _offlineEditingFirmwareTypeFact; @@ -62,9 +74,12 @@ private: SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; SettingsFact* _missionAutoLoadDirFact; - SettingsFact* _promptFlightTelemetrySave; - SettingsFact* _promptFlightTelemetrySaveNotArmed; - SettingsFact* _audioMuted; + SettingsFact* _promptFlightTelemetrySaveFact; + SettingsFact* _promptFlightTelemetrySaveNotArmedFact; + SettingsFact* _audioMutedFact; + SettingsFact* _virtualJoystickFact; + SettingsFact* _appFontPointSizeFact; + SettingsFact* _indoorPaletteFact; }; #endif diff --git a/src/Settings/SettingsGroup.app.json b/src/Settings/SettingsGroup.app.json index 439727ccc..a3b074c84 100644 --- a/src/Settings/SettingsGroup.app.json +++ b/src/Settings/SettingsGroup.app.json @@ -82,5 +82,30 @@ "longDescription": "If this option is enabled all audio output will be muted.", "type": "bool", "defaultValue": false +}, +{ + "name": "VirtualTabletJoystick", + "shortDescription": "Show virtual joystick", + "longDescription": "If this option is enabled the virtual joystick will be shown on the Fly view.", + "type": "bool", + "defaultValue": false +}, +{ + "name": "BaseDeviceFontPointSize", + "shortDescription": "QGroundControl font size", + "longDescription": "The point size for the default font used in QGroundControl.", + "type": "uint32", + "units": "pt", + "min": 6, + "max": 48, + "defaultValue": 0 +}, +{ + "name": "StyleIsDark", + "shortDescription": "QGroundControl color scheme", + "longDescription": "The color scheme for the QGroundControl user interface.", + "type": "uint32", + "enumStrings": "Indoor,Outdoor", + "enumValues": "1,0" } ] diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index 54e0e0ffc..ac1156397 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -233,8 +233,8 @@ Rectangle { // I don't know why this does not work Connections { - target: QGroundControl - onBaseFontPointSizeChanged: buttonColumn.reflowWidths() + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: buttonColumn.reflowWidths() } function reflowWidths() { diff --git a/src/ui/linechart/ChartPlot.cc b/src/ui/linechart/ChartPlot.cc index 736120dcb..574be4c63 100644 --- a/src/ui/linechart/ChartPlot.cc +++ b/src/ui/linechart/ChartPlot.cc @@ -1,5 +1,6 @@ #include "ChartPlot.h" #include "QGCApplication.h" +#include "SettingsManager.h" const QColor ChartPlot::baseColors[numColors] = { QColor(242, 255, 128), @@ -44,7 +45,7 @@ ChartPlot::ChartPlot(QWidget* parent): _colors.append(baseColors[i]); } // Now that all objects have been initialized, color everything. - styleChanged(qgcApp()->styleIsDark()); + styleChanged(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()); } ChartPlot::~ChartPlot() diff --git a/src/ui/linechart/LinechartWidget.cc b/src/ui/linechart/LinechartWidget.cc index 339e1f84f..f0d1a3a3b 100644 --- a/src/ui/linechart/LinechartWidget.cc +++ b/src/ui/linechart/LinechartWidget.cc @@ -40,6 +40,7 @@ #include "QGCFileDialog.h" #include "QGCMessageBox.h" #include "QGCApplication.h" +#include "SettingsManager.h" LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent), sysid(systemid), @@ -116,7 +117,7 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent createLayout(); // And make sure we're listening for future style changes - connect(qgcApp(), &QGCApplication::styleChanged, this, &LinechartWidget::recolor); + connect(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette(), &Fact::rawValueChanged, this, &LinechartWidget::recolor); updateTimer->setInterval(updateInterval); connect(updateTimer, &QTimer::timeout, this, &LinechartWidget::refresh); @@ -637,7 +638,7 @@ void LinechartWidget::removeCurve(QString curve) void LinechartWidget::recolor() { - activePlot->styleChanged(qgcApp()->styleIsDark()); + activePlot->styleChanged(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()); foreach (const QString &key, colorIcons.keys()) { QWidget* colorIcon = colorIcons.value(key, 0); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 94f196821..682f41998 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -34,6 +34,7 @@ QGCView { property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.appSettings.batteryPercentRemainingAnnounce property Fact _autoLoadDir: QGroundControl.settingsManager.appSettings.missionAutoLoadDir + property Fact _appFontPointSize: QGroundControl.settingsManager.appSettings.appFontPointSize property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -135,11 +136,11 @@ QGCView { //----------------------------------------------------------------- //-- Base UI Font Point Size Row { - visible: QGroundControl.corePlugin.options.defaultFontPointSize < 1.0 + visible: _appFontPointSize.visible spacing: ScreenTools.defaultFontPixelWidth QGCLabel { id: baseFontLabel - text: qsTr("Base UI font size:") + text: qsTr("Font size:") anchors.verticalCenter: parent.verticalCenter } Row { @@ -152,32 +153,23 @@ QGCView { height: baseFontEdit.height text: "-" onClicked: { - if(ScreenTools.defaultFontPointSize > 6) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize - 1 + if (_appFontPointSize.value > _appFontPointSize.min) { + _appFontPointSize.value = _appFontPointSize.value - 1 } } } - QGCTextField { - id: baseFontEdit - width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) - text: QGroundControl.baseFontPointSize - showUnits: true - unitsLabel: "pt" - maximumLength: 6 - validator: DoubleValidator {bottom: 6.0; top: 48.0; decimals: 2;} - onEditingFinished: { - var point = parseFloat(text) - if(point >= 6.0 && point <= 48.0) - QGroundControl.baseFontPointSize = point; - } + FactTextField { + id: baseFontEdit + width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) + fact: QGroundControl.settingsManager.appSettings.appFontPointSize } QGCButton { width: height height: baseFontEdit.height text: "+" onClicked: { - if(ScreenTools.defaultFontPointSize < 49) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize + 1 + if (_appFontPointSize.value < _appFontPointSize.max) { + _appFontPointSize.value = _appFontPointSize.value + 1 } } } @@ -191,45 +183,47 @@ QGCView { //-- Palette Styles Row { spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.settingsManager.appSettings.indoorPalette.visible QGCLabel { anchors.baseline: paletteCombo.baseline - text: qsTr("UI Style:") + text: qsTr("Color scheme:") width: _labelWidth } - QGCComboBox { - id: paletteCombo - width: _editFieldWidth - model: [ qsTr("Indoor"), qsTr("Outdoor") ] - currentIndex: QGroundControl.isDarkStyle ? 0 : 1 - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.isDarkStyle = index === 0 ? true : false - } - } + FactComboBox { + id: paletteCombo + width: _editFieldWidth + fact: QGroundControl.settingsManager.appSettings.indoorPalette + indexModel: false } } //----------------------------------------------------------------- //-- Audio preferences FactCheckBox { - text: qsTr("Mute all audio output") - fact: QGroundControl.settingsManager.appSettings.audioMuted + text: qsTr("Mute all audio output") + fact: _audioMuted + visible: _audioMuted.visible + + property Fact _audioMuted: QGroundControl.settingsManager.appSettings.audioMuted } //----------------------------------------------------------------- //-- Prompt Save Log FactCheckBox { id: promptSaveLog text: qsTr("Prompt to save Flight Data Log after each flight") - fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave - visible: !ScreenTools.isMobile + fact: _promptFlightTelemetrySave + visible: !ScreenTools.isMobile && _promptFlightTelemetrySave.visible + + property Fact _promptFlightTelemetrySave: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave } //----------------------------------------------------------------- //-- Prompt Save even if not armed FactCheckBox { text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed - visible: !ScreenTools.isMobile + fact: _promptFlightTelemetrySaveNotArmed + visible: !ScreenTools.isMobile && _promptFlightTelemetrySaveNotArmed.visible enabled: promptSaveLog.checked + + property Fact _promptFlightTelemetrySaveNotArmed: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed } //----------------------------------------------------------------- //-- Clear settings @@ -283,11 +277,12 @@ QGCView { } //----------------------------------------------------------------- //-- Virtual joystick settings - QGCCheckBox { + FactCheckBox { text: qsTr("Virtual Joystick") - checked: QGroundControl.virtualTabletJoystick - onClicked: QGroundControl.virtualTabletJoystick = checked - visible: QGroundControl.corePlugin.options.enableVirtualJoystick + visible: _virtualJoystick.visible + fact: _virtualJoystick + + property Fact _virtualJoystick: QGroundControl.settingsManager.appSettings.virtualJoystick } //----------------------------------------------------------------- //-- Default mission item altitude -- GitLab From 147ebb387d489b95e8cef76e3531d35c529bb362 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 18:38:42 -0800 Subject: [PATCH 372/398] More moves of settings to new support --- src/AnalyzeView/AnalyzeView.qml | 4 +- src/FlightDisplay/FlightDisplayView.qml | 12 +-- .../FlightDisplayViewWidgets.qml | 8 +- src/FlightDisplay/VirtualJoystick.qml | 2 +- src/QGCApplication.cc | 26 +------ src/QGCApplication.h | 17 +--- src/QmlControls/QGroundControlQmlGlobal.cc | 35 --------- src/QmlControls/QGroundControlQmlGlobal.h | 21 ----- src/QmlControls/ScreenTools.qml | 16 ++-- src/Settings/App.SettingsGroup.json | 25 ++++++ src/Settings/AppSettings.cc | 75 +++++++++++++++--- src/Settings/AppSettings.h | 23 +++++- src/VehicleSetup/SetupView.qml | 4 +- src/ui/linechart/ChartPlot.cc | 3 +- src/ui/linechart/LinechartWidget.cc | 5 +- src/ui/preferences/GeneralSettings.qml | 77 +++++++++---------- 16 files changed, 177 insertions(+), 176 deletions(-) diff --git a/src/AnalyzeView/AnalyzeView.qml b/src/AnalyzeView/AnalyzeView.qml index cb827796f..cd910754a 100644 --- a/src/AnalyzeView/AnalyzeView.qml +++ b/src/AnalyzeView/AnalyzeView.qml @@ -58,8 +58,8 @@ Rectangle { // I don't know why this does not work Connections { - target: QGroundControl - onBaseFontPointSizeChanged: buttonColumn.reflowWidths() + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: buttonColumn.reflowWidths() } function reflowWidths() { diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 368279771..07f4c6832 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -81,7 +81,7 @@ QGCView { } function px4JoystickCheck() { - if ( _activeVehicle && !_activeVehicle.supportsManualControl && (QGroundControl.virtualTabletJoystick || _activeVehicle.joystickEnabled)) { + if ( _activeVehicle && !_activeVehicle.supportsManualControl && (QGroundControl.settingsManager.appSettings.virtualJoystick.value || _activeVehicle.joystickEnabled)) { px4JoystickSupport.open() } } @@ -99,8 +99,8 @@ QGCView { } Connections { - target: QGroundControl - onVirtualTabletJoystickChanged: px4JoystickCheck() + target: QGroundControl.settingsManager.appSettings.virtualJoystick + onValueChanged: px4JoystickCheck() } onActiveVehicleJoystickEnabledChanged: px4JoystickCheck() @@ -302,14 +302,16 @@ QGCView { z: _panel.z + 5 width: parent.width - (_flightVideoPipControl.width / 2) height: Math.min(ScreenTools.availableHeight * 0.25, ScreenTools.defaultFontPixelWidth * 16) - visible: QGroundControl.virtualTabletJoystick + visible: _virtualJoystick.value anchors.bottom: _flightVideoPipControl.top anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 2 anchors.horizontalCenter: flightDisplayViewWidgets.horizontalCenter source: "qrc:/qml/VirtualJoystick.qml" - active: QGroundControl.virtualTabletJoystick + active: _virtualJoystick.value property bool useLightColors: root.isBackgroundDark + + property Fact _virtualJoystick: QGroundControl.settingsManager.appSettings.virtualJoystick } } } diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 6688eb6d6..222ac38fb 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -63,7 +63,7 @@ Item { break; } } else { - var useAlternateInstruments = QGroundControl.virtualTabletJoystick || ScreenTools.isTinyScreen + var useAlternateInstruments = QGroundControl.settingsManager.appSettings.virtualJoystick.value || ScreenTools.isTinyScreen if(useAlternateInstruments) { instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" instrumentsLoader.state = "topMode" @@ -75,10 +75,8 @@ Item { } Connections { - target: QGroundControl - onVirtualTabletJoystickChanged: { - _setInstrumentWidget() - } + target: QGroundControl.settingsManager.appSettings.virtualJoystick + onValueChanged: _setInstrumentWidget() } Component.onCompleted: { diff --git a/src/FlightDisplay/VirtualJoystick.qml b/src/FlightDisplay/VirtualJoystick.qml index 07248d6df..f10d4e2d2 100644 --- a/src/FlightDisplay/VirtualJoystick.qml +++ b/src/FlightDisplay/VirtualJoystick.qml @@ -21,7 +21,7 @@ Item { Timer { interval: 40 // 25Hz, same as real joystick rate - running: QGroundControl.virtualTabletJoystick && _activeVehicle + running: QGroundControl.settingsManager.appSettings.virtualJoystick.value && _activeVehicle repeat: true onTriggered: { if (_activeVehicle) { diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 2e351302f..7933933f9 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -79,6 +79,7 @@ #include "MissionCommandTree.h" #include "QGCMapPolygon.h" #include "ParameterManager.h" +#include "SettingsManager.h" #ifndef NO_SERIAL_LINK #include "SerialLink.h" @@ -115,7 +116,6 @@ const char* QGCApplication::telemetryFileExtension = "tlog"; const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot"; const char* QGCApplication::_settingsVersionKey = "SettingsVersion"; -const char* QGCApplication::_styleKey = "StyleIsDark"; const char* QGCApplication::_lastKnownHomePositionLatKey = "LastKnownHomePositionLat"; const char* QGCApplication::_lastKnownHomePositionLonKey = "LastKnownHomePositionLon"; const char* QGCApplication::_lastKnownHomePositionAltKey = "LastKnownHomePositionAlt"; @@ -166,11 +166,6 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) : QApplication(argc, argv) #endif , _runningUnitTests(unitTesting) -#if defined (__mobile__) - , _styleIsDark(false) -#else - , _styleIsDark(true) -#endif , _fakeMobile(false) , _settingsUpgraded(false) #ifdef QT_DEBUG @@ -409,8 +404,7 @@ bool QGCApplication::_initForNormalAppBoot(void) { QSettings settings; - _styleIsDark = settings.value(_styleKey, _styleIsDark).toBool(); - _loadCurrentStyle(); + _loadCurrentStyleSheet(); // Exit main application when last window is closed connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit); @@ -536,17 +530,7 @@ void QGCApplication::saveTempFlightDataLogOnMainThread(QString tempLogfile) } #endif -void QGCApplication::setStyle(bool styleIsDark) -{ - QSettings settings; - - settings.setValue(_styleKey, styleIsDark); - _styleIsDark = styleIsDark; - _loadCurrentStyle(); - emit styleChanged(_styleIsDark); -} - -void QGCApplication::_loadCurrentStyle() +void QGCApplication::_loadCurrentStyleSheet(void) { #ifndef __mobile__ bool success = true; @@ -562,7 +546,7 @@ void QGCApplication::_loadCurrentStyle() success = false; } - if (success && !_styleIsDark) { + if (success && !_toolbox->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()) { // Load the slave light stylesheet. QFile styleSheet(_lightStyleFile); if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) { @@ -580,8 +564,6 @@ void QGCApplication::_loadCurrentStyle() setStyle("plastique"); } #endif - - QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light); } void QGCApplication::reportMissingParameter(int componentId, const QString& name) diff --git a/src/QGCApplication.h b/src/QGCApplication.h index f5234c533..91bb3a287 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -78,12 +78,6 @@ public: /// @brief Returns truee if unit test are being run bool runningUnitTests(void) { return _runningUnitTests; } - /// @return true: dark ui style, false: light ui style - bool styleIsDark(void) { return _styleIsDark; } - - /// Set the current UI style - void setStyle(bool styleIsDark); - /// Used to report a missing Parameter. Warning will be displayed to user. Method may be called /// multiple times. void reportMissingParameter(int componentId, const QString& name); @@ -127,10 +121,6 @@ public slots: #endif signals: - /// Signals that the style has changed - /// @param darkStyle true: dark style, false: light style - void styleChanged(bool darkStyle); - /// This is connected to MAVLinkProtocol::checkForLostLogFiles. We signal this to ourselves to call the slot /// on the MAVLinkProtocol thread; void checkForLostLogFiles(void); @@ -150,6 +140,8 @@ public: /// unit tests. Although public should only be called by main. bool _initForUnitTests(void); + void _loadCurrentStyleSheet(void); + static QGCApplication* _app; ///< Our own singleton. Should be reference directly by qgcApp public: @@ -162,8 +154,7 @@ private slots: void _missingParamsDisplay(void); private: - void _loadCurrentStyle (); - QObject* _rootQmlObject (); + QObject* _rootQmlObject(void); #ifdef __mobile__ QQmlApplicationEngine* _qmlAppEngine; @@ -173,7 +164,6 @@ private: static const char* _darkStyleFile; static const char* _lightStyleFile; - bool _styleIsDark; ///< true: dark style, false: light style static const int _missingParamsDelayedDisplayTimerTimeout = 1000; ///< Timeout to wait for next missing fact to come in before display QTimer _missingParamsDelayedDisplayTimer; ///< Timer use to delay missing fact display QStringList _missingParams; ///< List of missing facts to be displayed @@ -192,7 +182,6 @@ private: static const char* _settingsVersionKey; ///< Settings key which hold settings version static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted - static const char* _styleKey; ///< Settings key for UI style static const char* _lastKnownHomePositionLatKey; static const char* _lastKnownHomePositionLonKey; static const char* _lastKnownHomePositionAltKey; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index a3fb27497..af893774e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -19,9 +19,6 @@ static const char* kQmlGlobalKeyName = "QGCQml"; -const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick"; -const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize"; - QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) : QGCTool(app) , _flightMapSettings(NULL) @@ -35,13 +32,7 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app) , _corePlugin(NULL) , _firmwarePluginManager(NULL) , _settingsManager(NULL) - , _virtualTabletJoystick(false) - , _baseFontPointSize(0.0) { - QSettings settings; - _virtualTabletJoystick = settings.value(_virtualTabletJoystickKey, false).toBool(); - _baseFontPointSize = settings.value(_baseFontPointSizeKey, 0.0).toDouble(); - // We clear the parent on this object since we run into shutdown problems caused by hybrid qml app. Instead we let it leak on shutdown. setParent(NULL); } @@ -157,12 +148,6 @@ void QGroundControlQmlGlobal::stopAllMockLinks(void) #endif } -void QGroundControlQmlGlobal::setIsDarkStyle(bool dark) -{ - qgcApp()->setStyle(dark); - emit isDarkStyleChanged(dark); -} - void QGroundControlQmlGlobal::setIsVersionCheckEnabled(bool enable) { qgcApp()->toolbox()->mavlinkProtocol()->enableVersionCheck(enable); @@ -175,26 +160,6 @@ void QGroundControlQmlGlobal::setMavlinkSystemID(int id) emit mavlinkSystemIDChanged(id); } -void QGroundControlQmlGlobal::setVirtualTabletJoystick(bool enabled) -{ - if (_virtualTabletJoystick != enabled) { - QSettings settings; - settings.setValue(_virtualTabletJoystickKey, enabled); - _virtualTabletJoystick = enabled; - emit virtualTabletJoystickChanged(enabled); - } -} - -void QGroundControlQmlGlobal::setBaseFontPointSize(qreal size) -{ - if (size >= 6.0 && size <= 48.0) { - QSettings settings; - settings.setValue(_baseFontPointSizeKey, size); - _baseFontPointSize = size; - emit baseFontPointSizeChanged(size); - } -} - int QGroundControlQmlGlobal::supportedFirmwareCount() { return _firmwarePluginManager->supportedFirmwareTypes().count(); diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index dc28ca655..047e3f86e 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -54,11 +54,6 @@ public: Q_PROPERTY(qreal zOrderWidgets READ zOrderWidgets CONSTANT) ///< z order value to widgets, for example: zoom controls, hud widgetss Q_PROPERTY(qreal zOrderMapItems READ zOrderMapItems CONSTANT) ///< z order value for map items, for example: mission item indicators - // Various QGC settings exposed to Qml - Q_PROPERTY(bool isDarkStyle READ isDarkStyle WRITE setIsDarkStyle NOTIFY isDarkStyleChanged) // TODO: Should be in ScreenTools? - Q_PROPERTY(bool virtualTabletJoystick READ virtualTabletJoystick WRITE setVirtualTabletJoystick NOTIFY virtualTabletJoystickChanged) - Q_PROPERTY(qreal baseFontPointSize READ baseFontPointSize WRITE setBaseFontPointSize NOTIFY baseFontPointSizeChanged) - //------------------------------------------------------------------------- // MavLink Protocol Q_PROPERTY(bool isVersionCheckEnabled READ isVersionCheckEnabled WRITE setIsVersionCheckEnabled NOTIFY isVersionCheckEnabledChanged) @@ -142,10 +137,6 @@ public: qreal zOrderWidgets () { return 100; } qreal zOrderMapItems () { return 50; } - bool isDarkStyle () { return _app->styleIsDark(); } - bool virtualTabletJoystick () { return _virtualTabletJoystick; } - qreal baseFontPointSize () { return _baseFontPointSize; } - bool isVersionCheckEnabled () { return _toolbox->mavlinkProtocol()->versionCheckEnabled(); } int mavlinkSystemID () { return _toolbox->mavlinkProtocol()->getSystemId(); } @@ -153,10 +144,6 @@ public: int supportedFirmwareCount (); - void setIsDarkStyle (bool dark); - void setVirtualTabletJoystick (bool enabled); - void setBaseFontPointSize (qreal size); - void setIsVersionCheckEnabled (bool enable); void setMavlinkSystemID (int id); @@ -170,9 +157,6 @@ public: virtual void setToolbox(QGCToolbox* toolbox); signals: - void isDarkStyleChanged (bool dark); - void virtualTabletJoystickChanged (bool enabled); - void baseFontPointSizeChanged (qreal size); void isMultiplexingEnabledChanged (bool enabled); void isVersionCheckEnabledChanged (bool enabled); void mavlinkSystemIDChanged (int id); @@ -192,13 +176,8 @@ private: FirmwarePluginManager* _firmwarePluginManager; SettingsManager* _settingsManager; - bool _virtualTabletJoystick; - qreal _baseFontPointSize; QGeoCoordinate _flightMapPosition; double _flightMapZoom; - - static const char* _virtualTabletJoystickKey; - static const char* _baseFontPointSizeKey; }; #endif diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 964559273..0eb0c5593 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -67,10 +67,10 @@ Item { I've disabled (in release builds) until I figure out why. Changes require a restart for now. */ Connections { - target: QGroundControl - onBaseFontPointSizeChanged: { + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: { if(ScreenToolsController.isDebug) - _setBasePointSize(QGroundControl.baseFontPointSize) + _setBasePointSize(QGroundControl.settingsManager.appSettings.appFontPointSize.value) } } @@ -108,12 +108,10 @@ Item { property real fontWidth: contentWidth property real fontHeight: contentHeight Component.onCompleted: { - var baseSize = QGroundControl.corePlugin.options.defaultFontPointSize - if(baseSize == 0.0) { - baseSize = QGroundControl.baseFontPointSize; - } + var _appFontPointSizeFact = QGroundControl.settingsManager.appSettings.appFontPointSize + var baseSize = _appFontPointSizeFact.value //-- If this is the first time (not saved in settings) - if(baseSize < 6 || baseSize > 48) { + if(baseSize < _appFontPointSizeFact.min || baseSize > _appFontPointSizeFact.max) { //-- Init base size base on the platform if(ScreenToolsController.isMobile) { //-- Check iOS really tiny screens (iPhone 4s/5/5s) @@ -142,7 +140,7 @@ Item { else baseSize = _defaultFont.font.pointSize; } - QGroundControl.baseFontPointSize = baseSize + _appFontPointSizeFact.value = baseSize //-- Release build doesn't get signal if(!ScreenToolsController.isDebug) _screenTools._setBasePointSize(baseSize); diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 439727ccc..a3b074c84 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -82,5 +82,30 @@ "longDescription": "If this option is enabled all audio output will be muted.", "type": "bool", "defaultValue": false +}, +{ + "name": "VirtualTabletJoystick", + "shortDescription": "Show virtual joystick", + "longDescription": "If this option is enabled the virtual joystick will be shown on the Fly view.", + "type": "bool", + "defaultValue": false +}, +{ + "name": "BaseDeviceFontPointSize", + "shortDescription": "QGroundControl font size", + "longDescription": "The point size for the default font used in QGroundControl.", + "type": "uint32", + "units": "pt", + "min": 6, + "max": 48, + "defaultValue": 0 +}, +{ + "name": "StyleIsDark", + "shortDescription": "QGroundControl color scheme", + "longDescription": "The color scheme for the QGroundControl user interface.", + "type": "uint32", + "enumStrings": "Indoor,Outdoor", + "enumValues": "1,0" } ] diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index d2284a7b3..74feaa270 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -8,6 +8,8 @@ ****************************************************************************/ #include "AppSettings.h" +#include "QGCPalette.h" +#include "QGCApplication.h" #include #include @@ -23,6 +25,9 @@ const char* AppSettings::missionAutoLoadDirSettingsName = "Mission const char* AppSettings::promptFlightTelemetrySaveName = "PromptFLightDataSave"; const char* AppSettings::promptFlightTelemetrySaveNotArmedName = "PromptFLightDataSaveNotArmed"; const char* AppSettings::audioMutedName = "AudioMuted"; +const char* AppSettings::virtualJoystickName = "VirtualTabletJoystick"; +const char* AppSettings::appFontPointSizeName = "BaseDeviceFontPointSize"; +const char* AppSettings::indoorPaletteName = "StyleIsDark"; AppSettings::AppSettings(QObject* parent) : SettingsGroup(appSettingsGroupName, QString() /* root settings group */, parent) @@ -33,12 +38,24 @@ AppSettings::AppSettings(QObject* parent) , _batteryPercentRemainingAnnounceFact(NULL) , _defaultMissionItemAltitudeFact(NULL) , _missionAutoLoadDirFact(NULL) - , _promptFlightTelemetrySave(NULL) - , _promptFlightTelemetrySaveNotArmed(NULL) - , _audioMuted(NULL) + , _promptFlightTelemetrySaveFact(NULL) + , _promptFlightTelemetrySaveNotArmedFact(NULL) + , _audioMutedFact(NULL) + , _virtualJoystickFact(NULL) + , _appFontPointSizeFact(NULL) + , _indoorPaletteFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); + + // Set up correct default for palette setting + QVariant outdoorPalette; +#if defined (__mobile__) + outdoorPalette = 0; +#else + outdoorPalette = 1; +#endif + _nameToMetaDataMap[indoorPaletteName]->setRawDefaultValue(outdoorPalette); } Fact* AppSettings::offlineEditingFirmwareType(void) @@ -104,27 +121,61 @@ Fact* AppSettings::missionAutoLoadDir(void) Fact* AppSettings::promptFlightTelemetrySave(void) { - if (!_promptFlightTelemetrySave) { - _promptFlightTelemetrySave = _createSettingsFact(promptFlightTelemetrySaveName); + if (!_promptFlightTelemetrySaveFact) { + _promptFlightTelemetrySaveFact = _createSettingsFact(promptFlightTelemetrySaveName); } - return _promptFlightTelemetrySave; + return _promptFlightTelemetrySaveFact; } Fact* AppSettings::promptFlightTelemetrySaveNotArmed(void) { - if (!_promptFlightTelemetrySaveNotArmed) { - _promptFlightTelemetrySaveNotArmed = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); + if (!_promptFlightTelemetrySaveNotArmedFact) { + _promptFlightTelemetrySaveNotArmedFact = _createSettingsFact(promptFlightTelemetrySaveNotArmedName); } - return _promptFlightTelemetrySaveNotArmed; + return _promptFlightTelemetrySaveNotArmedFact; } Fact* AppSettings::audioMuted(void) { - if (!_audioMuted) { - _audioMuted = _createSettingsFact(audioMutedName); + if (!_audioMutedFact) { + _audioMutedFact = _createSettingsFact(audioMutedName); + } + + return _audioMutedFact; +} + +Fact* AppSettings::appFontPointSize(void) +{ + if (!_appFontPointSizeFact) { + _appFontPointSizeFact = _createSettingsFact(appFontPointSizeName); } - return _audioMuted; + return _appFontPointSizeFact; +} + +Fact* AppSettings::virtualJoystick(void) +{ + if (!_virtualJoystickFact) { + _virtualJoystickFact = _createSettingsFact(virtualJoystickName); + } + + return _virtualJoystickFact; +} + +Fact* AppSettings::indoorPalette(void) +{ + if (!_indoorPaletteFact) { + _indoorPaletteFact = _createSettingsFact(indoorPaletteName); + connect(_indoorPaletteFact, &Fact::rawValueChanged, this, &AppSettings::_indoorPaletteChanged); + } + + return _indoorPaletteFact; +} + +void AppSettings::_indoorPaletteChanged(void) +{ + qgcApp()->_loadCurrentStyleSheet(); + QGCPalette::setGlobalTheme(_indoorPaletteFact->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light); } diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index c9bbae84a..7e39514ca 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -29,6 +29,9 @@ public: Q_PROPERTY(Fact* promptFlightTelemetrySave READ promptFlightTelemetrySave CONSTANT) Q_PROPERTY(Fact* promptFlightTelemetrySaveNotArmed READ promptFlightTelemetrySaveNotArmed CONSTANT) Q_PROPERTY(Fact* audioMuted READ audioMuted CONSTANT) + Q_PROPERTY(Fact* virtualJoystick READ virtualJoystick CONSTANT) + Q_PROPERTY(Fact* appFontPointSize READ appFontPointSize CONSTANT) + Q_PROPERTY(Fact* indoorPalette READ indoorPalette CONSTANT) Fact* offlineEditingFirmwareType (void); Fact* offlineEditingVehicleType (void); @@ -39,7 +42,10 @@ public: Fact* missionAutoLoadDir (void); Fact* promptFlightTelemetrySave (void); Fact* promptFlightTelemetrySaveNotArmed (void); - Fact* audioMuted (void); + Fact* audioMuted (void); + Fact* virtualJoystick (void); + Fact* appFontPointSize (void); + Fact* indoorPalette (void); static const char* appSettingsGroupName; @@ -53,6 +59,12 @@ public: static const char* promptFlightTelemetrySaveName; static const char* promptFlightTelemetrySaveNotArmedName; static const char* audioMutedName; + static const char* virtualJoystickName; + static const char* appFontPointSizeName; + static const char* indoorPaletteName; + +private slots: + void _indoorPaletteChanged(void); private: SettingsFact* _offlineEditingFirmwareTypeFact; @@ -62,9 +74,12 @@ private: SettingsFact* _batteryPercentRemainingAnnounceFact; SettingsFact* _defaultMissionItemAltitudeFact; SettingsFact* _missionAutoLoadDirFact; - SettingsFact* _promptFlightTelemetrySave; - SettingsFact* _promptFlightTelemetrySaveNotArmed; - SettingsFact* _audioMuted; + SettingsFact* _promptFlightTelemetrySaveFact; + SettingsFact* _promptFlightTelemetrySaveNotArmedFact; + SettingsFact* _audioMutedFact; + SettingsFact* _virtualJoystickFact; + SettingsFact* _appFontPointSizeFact; + SettingsFact* _indoorPaletteFact; }; #endif diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index 54e0e0ffc..ac1156397 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -233,8 +233,8 @@ Rectangle { // I don't know why this does not work Connections { - target: QGroundControl - onBaseFontPointSizeChanged: buttonColumn.reflowWidths() + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: buttonColumn.reflowWidths() } function reflowWidths() { diff --git a/src/ui/linechart/ChartPlot.cc b/src/ui/linechart/ChartPlot.cc index 736120dcb..574be4c63 100644 --- a/src/ui/linechart/ChartPlot.cc +++ b/src/ui/linechart/ChartPlot.cc @@ -1,5 +1,6 @@ #include "ChartPlot.h" #include "QGCApplication.h" +#include "SettingsManager.h" const QColor ChartPlot::baseColors[numColors] = { QColor(242, 255, 128), @@ -44,7 +45,7 @@ ChartPlot::ChartPlot(QWidget* parent): _colors.append(baseColors[i]); } // Now that all objects have been initialized, color everything. - styleChanged(qgcApp()->styleIsDark()); + styleChanged(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()); } ChartPlot::~ChartPlot() diff --git a/src/ui/linechart/LinechartWidget.cc b/src/ui/linechart/LinechartWidget.cc index 339e1f84f..f0d1a3a3b 100644 --- a/src/ui/linechart/LinechartWidget.cc +++ b/src/ui/linechart/LinechartWidget.cc @@ -40,6 +40,7 @@ #include "QGCFileDialog.h" #include "QGCMessageBox.h" #include "QGCApplication.h" +#include "SettingsManager.h" LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent), sysid(systemid), @@ -116,7 +117,7 @@ LinechartWidget::LinechartWidget(int systemid, QWidget *parent) : QWidget(parent createLayout(); // And make sure we're listening for future style changes - connect(qgcApp(), &QGCApplication::styleChanged, this, &LinechartWidget::recolor); + connect(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette(), &Fact::rawValueChanged, this, &LinechartWidget::recolor); updateTimer->setInterval(updateInterval); connect(updateTimer, &QTimer::timeout, this, &LinechartWidget::refresh); @@ -637,7 +638,7 @@ void LinechartWidget::removeCurve(QString curve) void LinechartWidget::recolor() { - activePlot->styleChanged(qgcApp()->styleIsDark()); + activePlot->styleChanged(qgcApp()->toolbox()->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()); foreach (const QString &key, colorIcons.keys()) { QWidget* colorIcon = colorIcons.value(key, 0); diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index a83ceb588..486ab00cf 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -34,6 +34,7 @@ QGCView { property Fact _percentRemainingAnnounce: QGroundControl.settingsManager.appSettings.batteryPercentRemainingAnnounce property Fact _autoLoadDir: QGroundControl.settingsManager.appSettings.missionAutoLoadDir + property Fact _appFontPointSize: QGroundControl.settingsManager.appSettings.appFontPointSize property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 @@ -135,11 +136,11 @@ QGCView { //----------------------------------------------------------------- //-- Base UI Font Point Size Row { - visible: QGroundControl.corePlugin.options.defaultFontPointSize < 1.0 + visible: _appFontPointSize.visible spacing: ScreenTools.defaultFontPixelWidth QGCLabel { id: baseFontLabel - text: qsTr("Base UI font size:") + text: qsTr("Font size:") anchors.verticalCenter: parent.verticalCenter } Row { @@ -152,32 +153,23 @@ QGCView { height: baseFontEdit.height text: "-" onClicked: { - if(ScreenTools.defaultFontPointSize > 6) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize - 1 + if (_appFontPointSize.value > _appFontPointSize.min) { + _appFontPointSize.value = _appFontPointSize.value - 1 } } } - QGCTextField { - id: baseFontEdit - width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) - text: QGroundControl.baseFontPointSize - showUnits: true - unitsLabel: "pt" - maximumLength: 6 - validator: DoubleValidator {bottom: 6.0; top: 48.0; decimals: 2;} - onEditingFinished: { - var point = parseFloat(text) - if(point >= 6.0 && point <= 48.0) - QGroundControl.baseFontPointSize = point; - } + FactTextField { + id: baseFontEdit + width: _editFieldWidth - (decrementButton.width * 2) - (baseFontRow.spacing * 2) + fact: QGroundControl.settingsManager.appSettings.appFontPointSize } QGCButton { width: height height: baseFontEdit.height text: "+" onClicked: { - if(ScreenTools.defaultFontPointSize < 49) { - QGroundControl.baseFontPointSize = QGroundControl.baseFontPointSize + 1 + if (_appFontPointSize.value < _appFontPointSize.max) { + _appFontPointSize.value = _appFontPointSize.value + 1 } } } @@ -191,45 +183,47 @@ QGCView { //-- Palette Styles Row { spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.settingsManager.appSettings.indoorPalette.visible QGCLabel { anchors.baseline: paletteCombo.baseline - text: qsTr("UI Style:") + text: qsTr("Color scheme:") width: _labelWidth } - QGCComboBox { - id: paletteCombo - width: _editFieldWidth - model: [ qsTr("Indoor"), qsTr("Outdoor") ] - currentIndex: QGroundControl.isDarkStyle ? 0 : 1 - onActivated: { - if (index != -1) { - currentIndex = index - QGroundControl.isDarkStyle = index === 0 ? true : false - } - } + FactComboBox { + id: paletteCombo + width: _editFieldWidth + fact: QGroundControl.settingsManager.appSettings.indoorPalette + indexModel: false } } //----------------------------------------------------------------- //-- Audio preferences FactCheckBox { - text: qsTr("Mute all audio output") - fact: QGroundControl.settingsManager.appSettings.audioMuted + text: qsTr("Mute all audio output") + fact: _audioMuted + visible: _audioMuted.visible + + property Fact _audioMuted: QGroundControl.settingsManager.appSettings.audioMuted } //----------------------------------------------------------------- //-- Prompt Save Log FactCheckBox { id: promptSaveLog text: qsTr("Prompt to save Flight Data Log after each flight") - fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave - visible: !ScreenTools.isMobile + fact: _promptFlightTelemetrySave + visible: !ScreenTools.isMobile && _promptFlightTelemetrySave.visible + + property Fact _promptFlightTelemetrySave: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySave } //----------------------------------------------------------------- //-- Prompt Save even if not armed FactCheckBox { text: qsTr("Prompt to save Flight Data Log even if vehicle was not armed") - fact: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed - visible: !ScreenTools.isMobile + fact: _promptFlightTelemetrySaveNotArmed + visible: !ScreenTools.isMobile && _promptFlightTelemetrySaveNotArmed.visible enabled: promptSaveLog.checked + + property Fact _promptFlightTelemetrySaveNotArmed: QGroundControl.settingsManager.appSettings.promptFlightTelemetrySaveNotArmed } //----------------------------------------------------------------- //-- Clear settings @@ -283,11 +277,12 @@ QGCView { } //----------------------------------------------------------------- //-- Virtual joystick settings - QGCCheckBox { + FactCheckBox { text: qsTr("Virtual Joystick") - checked: QGroundControl.virtualTabletJoystick - onClicked: QGroundControl.virtualTabletJoystick = checked - visible: QGroundControl.corePlugin.options.enableVirtualJoystick + visible: _virtualJoystick.visible + fact: _virtualJoystick + + property Fact _virtualJoystick: QGroundControl.settingsManager.appSettings.virtualJoystick } //----------------------------------------------------------------- //-- Default mission item altitude -- GitLab From 7a89279c68be16b8f27e7b7f7641bb6202caf49c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sun, 26 Feb 2017 19:35:04 -0800 Subject: [PATCH 373/398] Fix indoorPalette overriding --- src/Settings/AppSettings.cc | 14 +++----------- src/api/QGCCorePlugin.cc | 13 ++++++++++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 74feaa270..e30ca2c05 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -46,16 +46,8 @@ AppSettings::AppSettings(QObject* parent) , _indoorPaletteFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); - - // Set up correct default for palette setting - QVariant outdoorPalette; -#if defined (__mobile__) - outdoorPalette = 0; -#else - outdoorPalette = 1; -#endif - _nameToMetaDataMap[indoorPaletteName]->setRawDefaultValue(outdoorPalette); + qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); + QGCPalette::setGlobalTheme(indoorPalette()->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light); } Fact* AppSettings::offlineEditingFirmwareType(void) @@ -177,5 +169,5 @@ Fact* AppSettings::indoorPalette(void) void AppSettings::_indoorPaletteChanged(void) { qgcApp()->_loadCurrentStyleSheet(); - QGCPalette::setGlobalTheme(_indoorPaletteFact->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light); + QGCPalette::setGlobalTheme(indoorPalette()->rawValue().toBool() ? QGCPalette::Dark : QGCPalette::Light); } diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 78fa0150d..3d7ff1098 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -11,6 +11,7 @@ #include "QGCOptions.h" #include "QGCSettings.h" #include "FactMetaData.h" +#include "SettingsManager.h" #include #include @@ -164,6 +165,16 @@ bool QGCCorePlugin::overrideSettingsGroupVisibility(QString name) bool QGCCorePlugin::adjustSettingMetaData(FactMetaData& metaData) { - Q_UNUSED(metaData); // No mods to standard meta data + if (metaData.name() == AppSettings::indoorPaletteName) { + // Set up correct default for palette setting + QVariant outdoorPalette; +#if defined (__mobile__) + outdoorPalette = 0; +#else + outdoorPalette = 1; +#endif + metaData.setRawDefaultValue(outdoorPalette); + } + return true; // Show setting in ui } -- GitLab From 83553a248707684d45bfcb01722abe5ebf1f90a7 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Mon, 27 Feb 2017 12:03:46 -0500 Subject: [PATCH 374/398] Add Sub master to firmware options --- src/VehicleSetup/FirmwareUpgradeController.cc | 5 ++++- src/VehicleSetup/FirmwareUpgradeController.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index 838da4133..bf83ef8cc 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -226,7 +226,8 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v2.px4"}, { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v2.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v2.px4"} + { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v2.px4"}, + { AutoPilotStackAPM, DeveloperFirmware, SubFirmware, "http://firmware.ardupilot.org/Sub/latest/PX4/ArduSub-v2.px4"}, }; //////////////////////////////////// PX4FMU aerocore firmwares ////////////////////////////////////////////////// @@ -701,6 +702,8 @@ QStringList FirmwareUpgradeController::apmAvailableVersions(void) case CopterFirmware: version = "MultiRotor - "; break; + case SubFirmware: + version = "Sub - "; break; case PlaneFirmware: case RoverFirmware: diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index 683301931..1209135df 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -63,6 +63,7 @@ public: CopterFirmware, PlaneFirmware, RoverFirmware, + SubFirmware, DefaultVehicleFirmware } FirmwareVehicleType_t; -- GitLab From 149c1ca8b57b773e78f1a73efc336474ee790168 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 10:05:13 -0800 Subject: [PATCH 375/398] Remove deprecated methods --- src/api/QGCOptions.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index bf070a8df..cfccc4dbc 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -25,9 +25,7 @@ public: QGCOptions(QObject* parent = NULL); Q_PROPERTY(bool combineSettingsAndSetup READ combineSettingsAndSetup CONSTANT) - Q_PROPERTY(bool enableVirtualJoystick READ enableVirtualJoystick CONSTANT) Q_PROPERTY(double toolbarHeightMultiplier READ toolbarHeightMultiplier CONSTANT) - Q_PROPERTY(double defaultFontPointSize READ defaultFontPointSize CONSTANT) Q_PROPERTY(bool enablePlanViewSelector READ enablePlanViewSelector CONSTANT) Q_PROPERTY(CustomInstrumentWidget* instrumentWidget READ instrumentWidget CONSTANT) @@ -36,18 +34,10 @@ public: /// @return true if QGC should consolidate both menus into one. virtual bool combineSettingsAndSetup () { return false; } - /// Should QGC use virtual Joysticks? - /// @return false to disable Virtual Joysticks. - virtual bool enableVirtualJoystick () { return true; } - /// Main ToolBar Multiplier. /// @return Factor to use when computing toolbar height virtual double toolbarHeightMultiplier () { return 1.0; } - /// Application wide default font point size - /// @return Font size or 0.0 to use computed size. - virtual double defaultFontPointSize () { return 0.0; } - /// Enable Plan View Selector (Mission, Fence or Rally) /// @return True or false virtual bool enablePlanViewSelector () { return true; } -- GitLab From 5c3b9b0d3ccfb3038ac966280df6f9a16aeee057 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 10:27:12 -0800 Subject: [PATCH 376/398] Switch to Qt 5.8 --- .appveyor.yml | 14 +++----------- .travis.yml | 27 +++++++++++++-------------- QGCCommon.pri | 8 +++++++- QGCSetup.pri | 10 +++++++--- libs/qwt/qwt_plot_canvas.cpp | 7 +++++++ src/GPS/definitions.h | 4 ++++ tools/StripAndTarQtForAndroidMin.sh | 19 +++++++++++++++++++ tools/StripAndTarQtForLinuxMin.sh | 19 +++++++++++++++++++ tools/StripAndTarQtForOSXMin.sh | 21 +++++++++++++++++++++ tools/StripAndTarQtForiOSMin.sh | 23 +++++++++++++++++++++++ 10 files changed, 123 insertions(+), 29 deletions(-) create mode 100755 tools/StripAndTarQtForAndroidMin.sh create mode 100755 tools/StripAndTarQtForLinuxMin.sh create mode 100755 tools/StripAndTarQtForOSXMin.sh create mode 100755 tools/StripAndTarQtForiOSMin.sh diff --git a/.appveyor.yml b/.appveyor.yml index a096bf9a6..a506d8645 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,17 +14,9 @@ environment: install: - git submodule update --init --recursive - - call "%ProgramFiles(x86)%\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86 - - set PATH=C:\Qt\Tools\QtCreator\bin;C:\Qt\5.5\msvc2013\bin;%PATH% + - call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 + - set PATH=C:\Qt\Tools\QtCreator\bin;C:\Qt\5.8\msvc2015\bin;%PATH% - mkdir %LOCALAPPDATA%\QtProject && copy test\qtlogging.ini %LOCALAPPDATA%\QtProject\ - - ps: | - Write-Host "Installing NSIS..." -ForegroundColor Cyan - $exePath = "$($env:USERPROFILE)\nsis-3.0rc1-setup.exe" - Write-Host "Downloading..." - (New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/appveyor-download-cache/nsis/nsis-3.0rc1-setup.exe', $exePath) - Write-Host "Installing..." - cmd /c start /wait $exePath /S - Write-Host "Installed" -ForegroundColor Green - ps: | Write-Host "Installing GStreamer..." -ForegroundColor Cyan $msiPath = "$($env:USERPROFILE)\gstreamer-1.0-x86-1.5.2.msi" @@ -43,7 +35,7 @@ install: Write-Host "Installed" -ForegroundColor Green build_script: - - mkdir %SHADOW_BUILD_DIR% && cd %SHADOW_BUILD_DIR% && C:\Qt\5.5\msvc2013\bin\qmake -r CONFIG-=debug_and_release CONFIG+=%CONFIG% CONFIG+=WarningsAsErrorsOn %APPVEYOR_BUILD_FOLDER%\qgroundcontrol.pro + - mkdir %SHADOW_BUILD_DIR% && cd %SHADOW_BUILD_DIR% && C:\Qt\5.8\msvc2015\bin\qmake -r CONFIG-=debug_and_release CONFIG+=%CONFIG% CONFIG+=WarningsAsErrorsOn %APPVEYOR_BUILD_FOLDER%\qgroundcontrol.pro - cd %SHADOW_BUILD_DIR% && jom - if "%CONFIG%" EQU "installer" ( copy %SHADOW_BUILD_DIR%\release\QGroundControl-installer.exe %APPVEYOR_BUILD_FOLDER%\QGroundControl-installer.exe ) # Generate the source server information to embed in the PDB diff --git a/.travis.yml b/.travis.yml index 8533f3c78..bbb2485ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,9 +89,9 @@ before_install: install: # linux dependencies: qt - if [ "${SPEC}" = "linux-g++-64" ]; then - wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.5.1-linux-min.tar.bz2 && - tar jxf Qt5.5.1-linux-min.tar.bz2 -C /tmp && - export PATH=/tmp/Qt/5.5/gcc_64/bin:$PATH && + wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.8.0-linux-min.tar.bz2 && + tar jxf Qt5.8.0-linux-min.tar.bz2 -C /tmp && + export PATH=/tmp/Qt5.8-linux/5.8/gcc_64/bin:$PATH && export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start ; @@ -99,8 +99,8 @@ install: # android dependencies: qt, gstreamer, android-ndk - if [ "${SPEC}" = "android-g++" ]; then - wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.5.1-linux-min.tar.bz2 && - tar jxf Qt5.5.1-linux-min.tar.bz2 -C /tmp && + wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.8.0-android-min.tar.bz2 && + tar jxf Qt5.8.0-android-min.tar.bz2 -C /tmp && wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-android-armv7-1.5.2.tar.bz2 && mkdir -p ${TRAVIS_BUILD_DIR}/gstreamer-1.0-android-armv7-1.5.2 && tar jxf gstreamer-1.0-android-armv7-1.5.2.tar.bz2 -C ${TRAVIS_BUILD_DIR}/gstreamer-1.0-android-armv7-1.5.2 && @@ -109,35 +109,34 @@ install: ./android-ndk-r10e-linux-x86_64.bin > /dev/null && export ANDROID_NDK_ROOT=`pwd`/android-ndk-r10e && export ANDROID_SDK_ROOT=/usr/local/android-sdk && - export PATH=/tmp/Qt/5.5/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH && echo $PATH + export PATH=/tmp/Qt5.8-android/5.8/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH && echo $PATH ; fi # osx dependencies: qt, gstreamer, gstreamer-devel - if [ "${SPEC}" = "macx-clang" ]; then - wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.5.1-mac-clang-min.tar.bz2 && - tar jxf Qt5.5.1-mac-clang-min.tar.bz2 -C /tmp && + wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.8.0-mac-clang-min.tar.bz2 && + tar jxf Qt5.8.0-mac-clang-min.tar.bz2 -C /tmp && wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-1.5.2-x86_64.pkg && sudo installer -verboseR -pkg gstreamer-1.0-1.5.2-x86_64.pkg -target / && wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-devel-1.5.2-x86_64.pkg && sudo installer -verboseR -pkg gstreamer-1.0-devel-1.5.2-x86_64.pkg -target / && wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/osx-gstreamer.tar.bz2 && sudo tar jxf osx-gstreamer.tar.bz2 -C /Library/Frameworks && - export QT_DIR=Qt5.5-mac-clang/5.5/clang_64 && + export QT_DIR=Qt5.8-mac-clang/5.8/clang_64 && export QT_QPA_PLATFORM_PLUGIN_PATH=/tmp/$QT_DIR/plugins && export QML2_IMPORT_PATH=/tmp/$QT_DIR/qml && - export PATH=/tmp/$QT_DIR/bin:$PATH && - tools/patch_qt_for_xcode8.sh + export PATH=/tmp/$QT_DIR/bin:$PATH ; fi # ios dependencies: qt, TODO: add gstreamer - if [ "${SPEC}" = "macx-ios-clang" ]; then - wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.5.1-ios-min.tar.bz2 && - tar jxf Qt5.5.1-ios-min.tar.bz2 -C /tmp && + wget https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.8.0-ios-min.tar.bz2 && + tar jxf Qt5.8.0-ios-min.tar.bz2 -C /tmp && export IOS_CCACHE_CC=`/usr/bin/xcrun -sdk iphoneos -find clang` && export IOS_CCACHE_CXX=`/usr/bin/xcrun -sdk iphoneos -find clang++` && - export PATH=/tmp/ios/bin:$PATH && + export PATH=/tmp/Qt5.8-ios/5.8/ios/bin:$PATH && tools/patch_qt_for_xcode8.sh ; fi diff --git a/QGCCommon.pri b/QGCCommon.pri index de0c51665..732b99a00 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -46,7 +46,7 @@ linux { error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported") } } else : win32 { - win32-msvc2010 | win32-msvc2012 | win32-msvc2013 { + win32-msvc2010 | win32-msvc2012 | win32-msvc2013 | win32-msvc2015 { message("Windows build") CONFIG += WindowsBuild DEFINES += __STDC_LIMIT_MACROS @@ -77,6 +77,7 @@ linux { } message("iOS build") CONFIG += iOSBuild MobileBuild app_bundle NoSerialBuild + CONFIG -= bitcode DEFINES += __ios__ DEFINES += QGC_NO_GOOGLE_MAPS DEFINES += NO_SERIAL_LINK @@ -202,8 +203,13 @@ MacBuild | LinuxBuild { } WindowsBuild { + win32-msvc2015 { + QMAKE_CFLAGS -= -Zc:strictStrings + QMAKE_CXXFLAGS -= -Zc:strictStrings + } QMAKE_CFLAGS_RELEASE -= -Zc:strictStrings QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings + QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings QMAKE_CXXFLAGS_WARN_ON += /W3 \ diff --git a/QGCSetup.pri b/QGCSetup.pri index eabeca15f..fa0b3e5f6 100644 --- a/QGCSetup.pri +++ b/QGCSetup.pri @@ -84,6 +84,10 @@ WindowsBuild { QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"C:\\Windows\\System32\\msvcp120.dll\" \"$$DESTDIR_WIN\" QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"C:\\Windows\\System32\\msvcr120.dll\" \"$$DESTDIR_WIN\" } + else:win32-msvc2015 { + QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"C:\\Windows\\System32\\msvcp140.dll\" \"$$DESTDIR_WIN\" + QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"C:\\Windows\\System32\\vcruntime140.dll\" \"$$DESTDIR_WIN\" + } else { error("Visual studio version not supported, installation cannot be completed.") } @@ -122,9 +126,9 @@ LinuxBuild { !contains(DEFINES, __rasp_pi2__) { QT_LIB_LIST += \ - libicudata.so.54 \ - libicui18n.so.54 \ - libicuuc.so.54 + libicudata.so.56 \ + libicui18n.so.56 \ + libicuuc.so.56 } for(QT_LIB, QT_LIB_LIST) { diff --git a/libs/qwt/qwt_plot_canvas.cpp b/libs/qwt/qwt_plot_canvas.cpp index 0271713a8..e558a44ee 100644 --- a/libs/qwt/qwt_plot_canvas.cpp +++ b/libs/qwt/qwt_plot_canvas.cpp @@ -937,7 +937,14 @@ void QwtPlotCanvas::drawBorder( QPainter *painter ) else { #if QT_VERSION >= 0x040500 +#if !defined(_MSC_VER) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif QStyleOptionFrameV3 opt; +#if !defined(_MSC_VER) +#pragma GCC diagnostic pop +#endif opt.init(this); int frameShape = frameStyle() & QFrame::Shape_Mask; diff --git a/src/GPS/definitions.h b/src/GPS/definitions.h index ca655d0b7..63fd2c2b1 100644 --- a/src/GPS/definitions.h +++ b/src/GPS/definitions.h @@ -80,10 +80,14 @@ static inline gps_abstime gps_absolute_time() { //timespec is UNIX-specific #ifdef _WIN32 +#if _MSC_VER < 1900 struct timespec { time_t tv_sec; long tv_nsec; }; +#else +#include +#endif #endif diff --git a/tools/StripAndTarQtForAndroidMin.sh b/tools/StripAndTarQtForAndroidMin.sh new file mode 100755 index 000000000..548c74dfc --- /dev/null +++ b/tools/StripAndTarQtForAndroidMin.sh @@ -0,0 +1,19 @@ +#!/bin/bash +if [ $# -eq 0 ]; then + echo 'Qt directory must be specified as argument' + exit 1 +fi +if [ ! -d $1 ] ; then + echo 'Qt directory not found' + exit 1 +fi +cd $1 +rm * +find . ! -name '5.8' -maxdepth 1 -type d -exec rm -rf {} + +cd 5.8 +find . ! -name 'android_armv7' -maxdepth 1 -type d -exec rm -rf {} + +cd clang_64 +rm -rf doc +cd $1 +cd .. +tar -jcvf Qt5.8.0-android-min.tar.bz2 $1 diff --git a/tools/StripAndTarQtForLinuxMin.sh b/tools/StripAndTarQtForLinuxMin.sh new file mode 100755 index 000000000..b813c5b77 --- /dev/null +++ b/tools/StripAndTarQtForLinuxMin.sh @@ -0,0 +1,19 @@ +#!/bin/bash +if [ $# -eq 0 ]; then + echo 'Qt directory must be specified as argument' + exit 1 +fi +if [ ! -d $1 ] ; then + echo 'Qt directory not found' + exit 1 +fi +cd $1 +rm * +find . ! -name '5.8' -maxdepth 1 -type d -exec rm -rf {} + +cd 5.8 +find . ! -name 'gcc_64' -maxdepth 1 -type d -exec rm -rf {} + +cd clang_64 +rm -rf doc +cd $1 +cd .. +tar -jcvf Qt5.8.0-linux-min.tar.bz2 $1 diff --git a/tools/StripAndTarQtForOSXMin.sh b/tools/StripAndTarQtForOSXMin.sh new file mode 100755 index 000000000..1e16db34a --- /dev/null +++ b/tools/StripAndTarQtForOSXMin.sh @@ -0,0 +1,21 @@ +#!/bin/bash +if [ $# -eq 0 ]; then + echo 'Qt directory must be specified as argument' + exit 1 +fi +if [ ! -d $1 ] ; then + echo 'Qt directory not found' + exit 1 +fi +cd $1 +rm * +find . ! -name '5.8' -maxdepth 1 -type d -exec rm -rf {} + +cd 5.8 +find . ! -name 'clang_64' -maxdepth 1 -type d -exec rm -rf {} + +cd clang_64 +rm -rf doc +cd lib +rm -rf *.dSYM +cd $1 +cd .. +tar -jcvf Qt5.8.0-mac-clang-min.tar.bz2 $1 diff --git a/tools/StripAndTarQtForiOSMin.sh b/tools/StripAndTarQtForiOSMin.sh new file mode 100755 index 000000000..d50e537a2 --- /dev/null +++ b/tools/StripAndTarQtForiOSMin.sh @@ -0,0 +1,23 @@ +#!/bin/bash +if [ $# -eq 0 ]; then + echo 'Qt directory must be specified as argument' + exit 1 +fi +if [ ! -d $1 ] ; then + echo 'Qt directory not found' + exit 1 +fi +cd $1 +rm * +find . ! -name '5.8' -maxdepth 1 -type d -exec rm -rf {} + +cd 5.8 +find . ! -name 'ios' -maxdepth 1 -type d -exec rm -rf {} + +cd ios +rm -rf doc +find . -type f -name 'lib*_debug.a' -delete +find . -type f -name 'lib*_debug.la' -delete +find . -type f -name 'lib*_debug.prl' -delete +cd .. +cd .. +cd .. +tar -jcvf Qt5.8.0-mac-ios-min.tar.bz2 $1 -- GitLab From 02980062bf8380cc3c671977979164915aa30474 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 16:02:09 -0800 Subject: [PATCH 377/398] Modified DropPanel cancel MouseArea creation Previous mechanism was exposing possible bug in Qt which would prevent clicks from work on ToolStrip buttons after cancel MouseArea was hidden. --- src/QmlControls/DropPanel.qml | 16 ++++++++++++++++ src/QmlControls/ToolStrip.qml | 10 ---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/QmlControls/DropPanel.qml b/src/QmlControls/DropPanel.qml index 0bc38aedd..68303bb3b 100644 --- a/src/QmlControls/DropPanel.qml +++ b/src/QmlControls/DropPanel.qml @@ -47,6 +47,7 @@ Item { property alias _dropDownComponent: dropDownLoader.sourceComponent property real _viewportMaxTop: 0 property real _viewportMaxBottom: parent.parent.height - parent.y + property var _dropPanelCancel function show(panelEdgeTopPoint, panelEdgeHeight, panelComponent) { _dropEdgeTopPoint = panelEdgeTopPoint @@ -54,9 +55,13 @@ Item { _dropDownComponent = panelComponent _calcPositions() visible = true + _dropPanelCancel = dropPanelCancelComponent.createObject(toolStrip.parent) } function hide() { + if (_dropPanelCancel) { + _dropPanelCancel.destroy() + } if (visible) { visible = false _dropDownComponent = undefined @@ -101,6 +106,17 @@ Item { QGCPalette { id: qgcPal } + Component { + // Overlay which is used to cancel the panel when the user clicks away + id: dropPanelCancelComponent + + MouseArea { + anchors.fill: parent + z: toolStrip.z - 1 + onClicked: dropPanel.hide() + } + } + Item { id: dropDownItem diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml index 89ae07a46..76331e1f5 100644 --- a/src/QmlControls/ToolStrip.qml +++ b/src/QmlControls/ToolStrip.qml @@ -47,16 +47,6 @@ Rectangle { } } - MouseArea { - x: -_root.x - y: -_root.y - width: _root.parent.width - height: _root.parent.height - visible: dropPanel.visible - onClicked: dropPanel.hide() - preventStealing: true - } - Column { id: buttonStripColumn anchors.margins: ScreenTools.defaultFontPixelWidth / 2 -- GitLab From 1048dacc277d8aef0ad253572629a8fb6d3e30de Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 18:15:59 -0800 Subject: [PATCH 378/398] New QGCGroupBox control --- qgroundcontrol.qrc | 1 + src/AutoPilotPlugins/PX4/PowerComponent.qml | 865 +++++++++--------- src/QmlControls/QGCGroupBox.qml | 32 + .../QGroundControl.Controls.qmldir | 1 + 4 files changed, 443 insertions(+), 456 deletions(-) create mode 100644 src/QmlControls/QGCGroupBox.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 512e9dcd0..aa49c237b 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -79,6 +79,7 @@ src/QmlControls/QGCFlickable.qml src/QmlControls/QGCFlickableHorizontalIndicator.qml src/QmlControls/QGCFlickableVerticalIndicator.qml + src/QmlControls/QGCGroupBox.qml src/QmlControls/QGCLabel.qml src/QmlControls/QGCListView.qml src/QmlControls/QGCMapLabel.qml diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.qml b/src/AutoPilotPlugins/PX4/PowerComponent.qml index 5b9108341..7d992a40f 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -7,11 +7,6 @@ * ****************************************************************************/ - -/// @file -/// @brief Battery, propeller and magnetometer settings -/// @author Gus Grubba - import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 @@ -30,527 +25,485 @@ SetupPage { Component { id: pageComponent - Column { - id: innerColumn - width: availableWidth - spacing: ScreenTools.defaultFontPixelHeight * 0.5 - - property int textEditWidth: ScreenTools.defaultFontPixelWidth * 8 - - property Fact battNumCells: controller.getParameterFact(-1, "BAT_N_CELLS") - property Fact battHighVolt: controller.getParameterFact(-1, "BAT_V_CHARGED") - property Fact battLowVolt: controller.getParameterFact(-1, "BAT_V_EMPTY") - property Fact battVoltLoadDrop: controller.getParameterFact(-1, "BAT_V_LOAD_DROP") - property Fact battVoltageDivider: controller.getParameterFact(-1, "BAT_V_DIV") - property Fact battAmpsPerVolt: controller.getParameterFact(-1, "BAT_A_PER_V") - property Fact uavcanEnable: controller.getParameterFact(-1, "UAVCAN_ENABLE", false) - - readonly property string highlightPrefix: "" - readonly property string highlightSuffix: "" - - - function getBatteryImage() - { - switch(battNumCells.value) { - case 1: return "/qmlimages/PowerComponentBattery_01cell.svg"; - case 2: return "/qmlimages/PowerComponentBattery_02cell.svg" - case 3: return "/qmlimages/PowerComponentBattery_03cell.svg" - case 4: return "/qmlimages/PowerComponentBattery_04cell.svg" - case 5: return "/qmlimages/PowerComponentBattery_05cell.svg" - case 6: return "/qmlimages/PowerComponentBattery_06cell.svg" - default: return "/qmlimages/PowerComponentBattery_01cell.svg"; + Item { + width: Math.max(availableWidth, innerColumn.width) + height: innerColumn.height + + ColumnLayout { + id: innerColumn + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelHeight + + property int textEditWidth: ScreenTools.defaultFontPixelWidth * 8 + + property Fact battNumCells: controller.getParameterFact(-1, "BAT_N_CELLS") + property Fact battHighVolt: controller.getParameterFact(-1, "BAT_V_CHARGED") + property Fact battLowVolt: controller.getParameterFact(-1, "BAT_V_EMPTY") + property Fact battVoltLoadDrop: controller.getParameterFact(-1, "BAT_V_LOAD_DROP") + property Fact battVoltageDivider: controller.getParameterFact(-1, "BAT_V_DIV") + property Fact battAmpsPerVolt: controller.getParameterFact(-1, "BAT_A_PER_V") + property Fact uavcanEnable: controller.getParameterFact(-1, "UAVCAN_ENABLE", false) + + readonly property string highlightPrefix: "" + readonly property string highlightSuffix: "" + + + function getBatteryImage() + { + switch(battNumCells.value) { + case 1: return "/qmlimages/PowerComponentBattery_01cell.svg"; + case 2: return "/qmlimages/PowerComponentBattery_02cell.svg" + case 3: return "/qmlimages/PowerComponentBattery_03cell.svg" + case 4: return "/qmlimages/PowerComponentBattery_04cell.svg" + case 5: return "/qmlimages/PowerComponentBattery_05cell.svg" + case 6: return "/qmlimages/PowerComponentBattery_06cell.svg" + default: return "/qmlimages/PowerComponentBattery_01cell.svg"; + } } - } - - function drawArrowhead(ctx, x, y, radians) - { - ctx.save(); - ctx.beginPath(); - ctx.translate(x,y); - ctx.rotate(radians); - ctx.moveTo(0,0); - ctx.lineTo(5,10); - ctx.lineTo(-5,10); - ctx.closePath(); - ctx.restore(); - ctx.fill(); - } - - function drawLineWithArrow(ctx, x1, y1, x2, y2) - { - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); - var rd = Math.atan((y2 - y1) / (x2 - x1)); - rd += ((x2 > x1) ? 90 : -90) * Math.PI/180; - drawArrowhead(ctx, x2, y2, rd); - } - - PowerComponentController { - id: controller - factPanel: powerPage.viewPanel - - onOldFirmware: showMessage(qsTr("ESC Calibration"), qsTr("QGroundControl cannot perform ESC Calibration with this version of firmware. You will need to upgrade to a newer firmware."), StandardButton.Ok) - onNewerFirmware: showMessage(qsTr("ESC Calibration"), qsTr("QGroundControl cannot perform ESC Calibration with this version of firmware. You will need to upgrade QGroundControl."), StandardButton.Ok) - onBatteryConnected: showMessage(qsTr("ESC Calibration"), qsTr("Performing calibration. This will take a few seconds.."), 0) - onCalibrationFailed: showMessage(qsTr("ESC Calibration failed"), errorMessage, StandardButton.Ok) - onCalibrationSuccess: showMessage(qsTr("ESC Calibration"), qsTr("Calibration complete. You can disconnect your battery now if you like."), StandardButton.Ok) - onConnectBattery: showMessage(qsTr("ESC Calibration"), highlightPrefix + qsTr("WARNING: Props must be removed from vehicle prior to performing ESC calibration.") + highlightSuffix + qsTr(" Connect the battery now and calibration will begin."), 0) - onDisconnectBattery: showMessage(qsTr("ESC Calibration failed"), qsTr("You must disconnect the battery prior to performing ESC Calibration. Disconnect your battery and try again."), StandardButton.Ok) - } - - Component { - id: calcVoltageDividerDlgComponent - - QGCViewDialog { - id: calcVoltageDividerDlg - - QGCFlickable { - anchors.fill: parent - contentHeight: column.height - contentWidth: column.width - - Column { - id: column - width: calcVoltageDividerDlg.width - spacing: ScreenTools.defaultFontPixelHeight - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: "Measure battery voltage using an external voltmeter and enter the value below. Click Calculate to set the new voltage multiplier." - } + function drawArrowhead(ctx, x, y, radians) + { + ctx.save(); + ctx.beginPath(); + ctx.translate(x,y); + ctx.rotate(radians); + ctx.moveTo(0,0); + ctx.lineTo(5,10); + ctx.lineTo(-5,10); + ctx.closePath(); + ctx.restore(); + ctx.fill(); + } + + function drawLineWithArrow(ctx, x1, y1, x2, y2) + { + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + var rd = Math.atan((y2 - y1) / (x2 - x1)); + rd += ((x2 > x1) ? 90 : -90) * Math.PI/180; + drawArrowhead(ctx, x2, y2, rd); + } + + PowerComponentController { + id: controller + factPanel: powerPage.viewPanel + + onOldFirmware: showMessage(qsTr("ESC Calibration"), qsTr("QGroundControl cannot perform ESC Calibration with this version of firmware. You will need to upgrade to a newer firmware."), StandardButton.Ok) + onNewerFirmware: showMessage(qsTr("ESC Calibration"), qsTr("QGroundControl cannot perform ESC Calibration with this version of firmware. You will need to upgrade QGroundControl."), StandardButton.Ok) + onBatteryConnected: showMessage(qsTr("ESC Calibration"), qsTr("Performing calibration. This will take a few seconds.."), 0) + onCalibrationFailed: showMessage(qsTr("ESC Calibration failed"), errorMessage, StandardButton.Ok) + onCalibrationSuccess: showMessage(qsTr("ESC Calibration"), qsTr("Calibration complete. You can disconnect your battery now if you like."), StandardButton.Ok) + onConnectBattery: showMessage(qsTr("ESC Calibration"), highlightPrefix + qsTr("WARNING: Props must be removed from vehicle prior to performing ESC calibration.") + highlightSuffix + qsTr(" Connect the battery now and calibration will begin."), 0) + onDisconnectBattery: showMessage(qsTr("ESC Calibration failed"), qsTr("You must disconnect the battery prior to performing ESC Calibration. Disconnect your battery and try again."), StandardButton.Ok) + } + + Component { + id: calcVoltageDividerDlgComponent + + QGCViewDialog { + id: calcVoltageDividerDlg - Grid { - columns: 2 - spacing: ScreenTools.defaultFontPixelHeight / 2 - verticalItemAlignment: Grid.AlignVCenter + QGCFlickable { + anchors.fill: parent + contentHeight: column.height + contentWidth: column.width + + Column { + id: column + width: calcVoltageDividerDlg.width + spacing: ScreenTools.defaultFontPixelHeight QGCLabel { - text: "Measured voltage:" + width: parent.width + wrapMode: Text.WordWrap + text: "Measure battery voltage using an external voltmeter and enter the value below. Click Calculate to set the new voltage multiplier." } - QGCTextField { id: measuredVoltage } - QGCLabel { text: "Vehicle voltage:" } - QGCLabel { text: controller.vehicle.battery.voltage.valueString } + Grid { + columns: 2 + spacing: ScreenTools.defaultFontPixelHeight / 2 + verticalItemAlignment: Grid.AlignVCenter - QGCLabel { text: "Voltage divider:" } - FactLabel { fact: battVoltageDivider } - } + QGCLabel { + text: "Measured voltage:" + } + QGCTextField { id: measuredVoltage } - QGCButton { - text: "Calculate" + QGCLabel { text: "Vehicle voltage:" } + QGCLabel { text: controller.vehicle.battery.voltage.valueString } - onClicked: { - var measuredVoltageValue = parseFloat(measuredVoltage.text) - if (measuredVoltageValue == 0 || isNaN(measuredVoltageValue)) { - return - } - var newVoltageDivider = (measuredVoltageValue * battVoltageDivider.value) / controller.vehicle.battery.voltage.value - if (newVoltageDivider > 0) { - battVoltageDivider.value = newVoltageDivider - } + QGCLabel { text: "Voltage divider:" } + FactLabel { fact: battVoltageDivider } } - } - } // Column - } // QGCFlickable - } // QGCViewDialog - } // Component - calcVoltageDividerDlgComponent - - Component { - id: calcAmpsPerVoltDlgComponent - QGCViewDialog { - id: calcAmpsPerVoltDlg + QGCButton { + text: "Calculate" + + onClicked: { + var measuredVoltageValue = parseFloat(measuredVoltage.text) + if (measuredVoltageValue == 0 || isNaN(measuredVoltageValue)) { + return + } + var newVoltageDivider = (measuredVoltageValue * battVoltageDivider.value) / controller.vehicle.battery.voltage.value + if (newVoltageDivider > 0) { + battVoltageDivider.value = newVoltageDivider + } + } + } + } // Column + } // QGCFlickable + } // QGCViewDialog + } // Component - calcVoltageDividerDlgComponent - QGCFlickable { - anchors.fill: parent - contentHeight: column.height - contentWidth: column.width + Component { + id: calcAmpsPerVoltDlgComponent - Column { - id: column - width: calcAmpsPerVoltDlg.width - spacing: ScreenTools.defaultFontPixelHeight + QGCViewDialog { + id: calcAmpsPerVoltDlg - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: "Measure current draw using an external current meter and enter the value below. Click Calculate to set the new amps per volt value." - } + QGCFlickable { + anchors.fill: parent + contentHeight: column.height + contentWidth: column.width - Grid { - columns: 2 - spacing: ScreenTools.defaultFontPixelHeight / 2 - verticalItemAlignment: Grid.AlignVCenter + Column { + id: column + width: calcAmpsPerVoltDlg.width + spacing: ScreenTools.defaultFontPixelHeight QGCLabel { - text: "Measured current:" + width: parent.width + wrapMode: Text.WordWrap + text: "Measure current draw using an external current meter and enter the value below. Click Calculate to set the new amps per volt value." } - QGCTextField { id: measuredCurrent } - QGCLabel { text: "Vehicle current:" } - QGCLabel { text: controller.vehicle.battery.current.valueString } + Grid { + columns: 2 + spacing: ScreenTools.defaultFontPixelHeight / 2 + verticalItemAlignment: Grid.AlignVCenter - QGCLabel { text: "Amps per volt:" } - FactLabel { fact: battAmpsPerVolt } - } + QGCLabel { + text: "Measured current:" + } + QGCTextField { id: measuredCurrent } - QGCButton { - text: "Calculate" + QGCLabel { text: "Vehicle current:" } + QGCLabel { text: controller.vehicle.battery.current.valueString } - onClicked: { - var measuredCurrentValue = parseFloat(measuredCurrent.text) - if (measuredCurrentValue == 0) { - return - } - var newAmpsPerVolt = (measuredCurrentValue * battAmpsPerVolt.value) / controller.vehicle.battery.current.value - if (newAmpsPerVolt != 0) { - battAmpsPerVolt.value = newAmpsPerVolt + QGCLabel { text: "Amps per volt:" } + FactLabel { fact: battAmpsPerVolt } + } + + QGCButton { + text: "Calculate" + + onClicked: { + var measuredCurrentValue = parseFloat(measuredCurrent.text) + if (measuredCurrentValue == 0) { + return + } + var newAmpsPerVolt = (measuredCurrentValue * battAmpsPerVolt.value) / controller.vehicle.battery.current.value + if (newAmpsPerVolt != 0) { + battAmpsPerVolt.value = newAmpsPerVolt + } } } - } - } // Column - } // QGCFlickable - } // QGCViewDialog - } // Component - calcAmpsPerVoltDlgComponent - - - QGCLabel { - text: qsTr("Battery") - font.family: ScreenTools.demiboldFontFamily - } - - Rectangle { - width: parent.width - height: batteryGrid.height + ScreenTools.defaultFontPixelHeight - color: qgcPal.windowShade - - GridLayout { - id: batteryGrid - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.left: parent.left - anchors.top: parent.top - columns: 5 - columnSpacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - text: qsTr("Number of Cells (in Series)") - } + } // Column + } // QGCFlickable + } // QGCViewDialog + } // Component - calcAmpsPerVoltDlgComponent - FactTextField { - id: cellsField - width: textEditWidth - fact: battNumCells - showUnits: true - } + QGCGroupBox { + id: batteryGroup + title: qsTr("Battery") - QGCColoredImage { - id: batteryImage - Layout.rowSpan: 3 - width: height * 0.75 - height: 100 - sourceSize.height: height - fillMode: Image.PreserveAspectFit - smooth: true - color: qgcPal.text - cache: false - source: getBatteryImage(); - } + GridLayout { + id: batteryGrid + columns: 5 + columnSpacing: ScreenTools.defaultFontPixelWidth - Item { width: 1; height: 1; Layout.columnSpan: 2 } + QGCLabel { + text: qsTr("Number of Cells (in Series)") + } - QGCLabel { - id: battHighLabel - text: qsTr("Full Voltage (per cell)") - } + FactTextField { + id: cellsField + width: textEditWidth + fact: battNumCells + showUnits: true + } - FactTextField { - id: battHighField - width: textEditWidth - fact: battHighVolt - showUnits: true - } + QGCColoredImage { + id: batteryImage + Layout.rowSpan: 3 + width: height * 0.75 + height: 100 + sourceSize.height: height + fillMode: Image.PreserveAspectFit + smooth: true + color: qgcPal.text + cache: false + source: getBatteryImage(); + } - QGCLabel { - text: qsTr("Battery Max:") - } + Item { width: 1; height: 1; Layout.columnSpan: 2 } - QGCLabel { - text: (battNumCells.value * battHighVolt.value).toFixed(1) + ' V' - } + QGCLabel { + id: battHighLabel + text: qsTr("Full Voltage (per cell)") + } - QGCLabel { - id: battLowLabel - text: qsTr("Empty Voltage (per cell)") - } + FactTextField { + id: battHighField + width: textEditWidth + fact: battHighVolt + showUnits: true + } - FactTextField { - id: battLowField - width: textEditWidth - fact: battLowVolt - showUnits: true - } + QGCLabel { + text: qsTr("Battery Max:") + } - QGCLabel { - text: qsTr("Battery Min:") - } + QGCLabel { + text: (battNumCells.value * battHighVolt.value).toFixed(1) + ' V' + } - QGCLabel { - text: (battNumCells.value * battLowVolt.value).toFixed(1) + ' V' - } + QGCLabel { + id: battLowLabel + text: qsTr("Empty Voltage (per cell)") + } - QGCLabel { - text: qsTr("Voltage divider") - } + FactTextField { + id: battLowField + width: textEditWidth + fact: battLowVolt + showUnits: true + } - FactTextField { - id: voltMultField - fact: battVoltageDivider - } + QGCLabel { + text: qsTr("Battery Min:") + } - QGCButton { - id: voltMultCalculateButton - text: "Calculate" - onClicked: showDialog(calcVoltageDividerDlgComponent, qsTr("Calculate Voltage Divider"), powerPage.showDialogDefaultWidth, StandardButton.Close) - } + QGCLabel { + text: (battNumCells.value * battLowVolt.value).toFixed(1) + ' V' + } - Item { width: 1; height: 1; Layout.columnSpan: 2 } + QGCLabel { + text: qsTr("Voltage divider") + } - QGCLabel { - id: voltMultHelp - Layout.columnSpan: batteryGrid.columns - Layout.fillWidth: true - font.pointSize: ScreenTools.smallFontPointSize - wrapMode: Text.WordWrap - text: "If the battery voltage reported by the vehicle is largely different than the voltage read externally using a voltmeter you can adjust the voltage multiplier value to correct this. " + - "Click the Calculate button for help with calculating a new value." - } + FactTextField { + id: voltMultField + fact: battVoltageDivider + } - QGCLabel { - id: ampPerVoltLabel - text: qsTr("Amps per volt") - } + QGCButton { + id: voltMultCalculateButton + text: "Calculate" + onClicked: showDialog(calcVoltageDividerDlgComponent, qsTr("Calculate Voltage Divider"), powerPage.showDialogDefaultWidth, StandardButton.Close) + } - FactTextField { - id: ampPerVoltField - fact: battAmpsPerVolt - } + Item { width: 1; height: 1; Layout.columnSpan: 2 } - QGCButton { - id: ampPerVoltCalculateButton - text: "Calculate" - onClicked: showDialog(calcAmpsPerVoltDlgComponent, qsTr("Calculate Amps per Volt"), powerPage.showDialogDefaultWidth, StandardButton.Close) - } + QGCLabel { + id: voltMultHelp + Layout.columnSpan: batteryGrid.columns + Layout.fillWidth: true + font.pointSize: ScreenTools.smallFontPointSize + wrapMode: Text.WordWrap + text: "If the battery voltage reported by the vehicle is largely different than the voltage read externally using a voltmeter you can adjust the voltage multiplier value to correct this. " + + "Click the Calculate button for help with calculating a new value." + } - Item { width: 1; height: 1; Layout.columnSpan: 2 } + QGCLabel { + id: ampPerVoltLabel + text: qsTr("Amps per volt") + } - QGCLabel { - id: ampPerVoltHelp - Layout.columnSpan: batteryGrid.columns - Layout.fillWidth: true - font.pointSize: ScreenTools.smallFontPointSize - wrapMode: Text.WordWrap - text: "If the current draw reported by the vehicle is largely different than the current read externally using a current meter you can adjust the amps per volt value to correct this. " + - "Click the Calculate button for help with calculating a new value." - } - } // Grid - } // Rectangle - Battery settings - - QGCLabel { - text: qsTr("ESC PWM Minimum and Maximum Calibration") - font.family: ScreenTools.demiboldFontFamily - } - - Rectangle { - width: parent.width - height: escCalColumn.height + ScreenTools.defaultFontPixelHeight - color: qgcPal.windowShade - - Column { - id : escCalColumn - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - width: parent.width - color: qgcPal.warningText - wrapMode: Text.WordWrap - text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing ESC calibration.") - } + FactTextField { + id: ampPerVoltField + fact: battAmpsPerVolt + } - QGCLabel { - text: qsTr("You must use USB connection for this operation.") - } + QGCButton { + id: ampPerVoltCalculateButton + text: "Calculate" + onClicked: showDialog(calcAmpsPerVoltDlgComponent, qsTr("Calculate Amps per Volt"), powerPage.showDialogDefaultWidth, StandardButton.Close) + } - QGCButton { - text: qsTr("Calibrate") - width: ScreenTools.defaultFontPixelWidth * 20 - onClicked: controller.calibrateEsc() - } - } - } - - QGCCheckBox { - id: showUAVCAN - text: qsTr("Show UAVCAN Settings") - checked: uavcanEnable.rawValue != 0 - } - - QGCLabel { - text: qsTr("UAVCAN Bus Configuration") - font.family: ScreenTools.demiboldFontFamily - visible: showUAVCAN.checked - } - - Rectangle { - width: parent.width - height: uavCanConfigRow.height + ScreenTools.defaultFontPixelHeight - color: qgcPal.windowShade - visible: showUAVCAN.checked - - Row { - id: uavCanConfigRow - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.left: parent.left - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelWidth - - FactComboBox { - id: uavcanEnabledCheckBox - width: ScreenTools.defaultFontPixelWidth * 20 - fact: uavcanEnable - indexModel: false - } + Item { width: 1; height: 1; Layout.columnSpan: 2 } - QGCLabel { - anchors.verticalCenter: parent.verticalCenter - text: qsTr("Change required restart") - } - } - } - - QGCLabel { - text: qsTr("UAVCAN Motor Index and Direction Assignment") - font.family: ScreenTools.demiboldFontFamily - visible: showUAVCAN.checked - } - - Rectangle { - width: parent.width - height: uavCanEscCalColumn.height + ScreenTools.defaultFontPixelHeight - color: qgcPal.windowShade - visible: showUAVCAN.checked - enabled: uavcanEnabledCheckBox.checked - - Column { - id: uavCanEscCalColumn - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelWidth - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - color: qgcPal.warningText - text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing UAVCAN ESC configuration.") - } + QGCLabel { + id: ampPerVoltHelp + Layout.columnSpan: batteryGrid.columns + Layout.fillWidth: true + font.pointSize: ScreenTools.smallFontPointSize + wrapMode: Text.WordWrap + text: "If the current draw reported by the vehicle is largely different than the current read externally using a current meter you can adjust the amps per volt value to correct this. " + + "Click the Calculate button for help with calculating a new value." + } + } // Grid + } // QGCGroupBox - Battery settings - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("ESC parameters will only be accessible in the editor after assignment.") - } + QGCGroupBox { + anchors.left: batteryGroup.left + anchors.right: batteryGroup.right + title: qsTr("ESC PWM Minimum and Maximum Calibration") - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("Start the process, then turn each motor into its turn direction, in the order of their motor indices.") - } + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelWidth - QGCButton { - text: qsTr("Start Assignment") - width: ScreenTools.defaultFontPixelWidth * 20 - onClicked: controller.busConfigureActuators() - } + QGCLabel { + color: qgcPal.warningText + wrapMode: Text.WordWrap + text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing ESC calibration.") + Layout.fillWidth: true + } + + QGCLabel { + text: qsTr("You must use USB connection for this operation.") + } - QGCButton { - text: qsTr("Stop Assignment") - width: ScreenTools.defaultFontPixelWidth * 20 - onClicked: controller.stopBusConfigureActuators() + QGCButton { + text: qsTr("Calibrate") + width: ScreenTools.defaultFontPixelWidth * 20 + onClicked: controller.calibrateEsc() + } } } - } - - QGCCheckBox { - id: showAdvanced - text: qsTr("Show Advanced Settings") - } - - QGCLabel { - text: qsTr("Advanced Power Settings") - font.family: ScreenTools.demiboldFontFamily - visible: showAdvanced.checked - } - - Rectangle { - id: batteryRectangle - width: parent.width - height: advBatteryColumn.height + ScreenTools.defaultFontPixelHeight - color: qgcPal.windowShade - visible: showAdvanced.checked - - Column { - id: advBatteryColumn - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: ScreenTools.defaultFontPixelWidth - Row { - spacing: ScreenTools.defaultFontPixelWidth + QGCCheckBox { + id: showUAVCAN + text: qsTr("Show UAVCAN Settings") + checked: uavcanEnable.rawValue != 0 + } - QGCLabel { - text: qsTr("Voltage Drop on Full Load (per cell)") - anchors.baseline: battDropField.baseline + QGCGroupBox { + anchors.left: batteryGroup.left + anchors.right: batteryGroup.right + title: qsTr("UAVCAN Bus Configuration") + visible: showUAVCAN.checked + + Row { + id: uavCanConfigRow + spacing: ScreenTools.defaultFontPixelWidth + + FactComboBox { + id: uavcanEnabledCheckBox + width: ScreenTools.defaultFontPixelWidth * 20 + fact: uavcanEnable + indexModel: false } - FactTextField { - id: battDropField - width: textEditWidth - fact: battVoltLoadDrop - showUnits: true + QGCLabel { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Change required restart") } } + } - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: qsTr("Batteries show less voltage at high throttle. Enter the difference in Volts between idle throttle and full ") + - qsTr("throttle, divided by the number of battery cells. Leave at the default if unsure. ") + - highlightPrefix + qsTr("If this value is set too high, the battery might be deep discharged and damaged.") + highlightSuffix - } + QGCGroupBox { + anchors.left: batteryGroup.left + anchors.right: batteryGroup.right + title: qsTr("UAVCAN Motor Index and Direction Assignment") + visible: showUAVCAN.checked - Row { - spacing: ScreenTools.defaultFontPixelWidth + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelWidth QGCLabel { - text: qsTr("Compensated Minimum Voltage:") + wrapMode: Text.WordWrap + color: qgcPal.warningText + text: qsTr("WARNING: Propellers must be removed from vehicle prior to performing UAVCAN ESC configuration.") + Layout.fillWidth: true } QGCLabel { - text: ((battNumCells.value * battLowVolt.value) - (battNumCells.value * battVoltLoadDrop.value)).toFixed(1) + qsTr(" V") + wrapMode: Text.WordWrap + text: qsTr("ESC parameters will only be accessible in the editor after assignment.") + Layout.fillWidth: true + } + + QGCLabel { + wrapMode: Text.WordWrap + text: qsTr("Start the process, then turn each motor into its turn direction, in the order of their motor indices.") + Layout.fillWidth: true + } + + QGCButton { + text: qsTr("Start Assignment") + width: ScreenTools.defaultFontPixelWidth * 20 + onClicked: controller.busConfigureActuators() + } + + QGCButton { + text: qsTr("Stop Assignment") + width: ScreenTools.defaultFontPixelWidth * 20 + onClicked: controller.stopBusConfigureActuators() } } } - } // Rectangle - Advanced power settings - } // Column + + QGCCheckBox { + id: showAdvanced + text: qsTr("Show Advanced Settings") + } + + QGCGroupBox { + anchors.left: batteryGroup.left + anchors.right: batteryGroup.right + title: qsTr("Advanced Power Settings") + visible: showAdvanced.checked + + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelWidth + + Row { + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { + text: qsTr("Voltage Drop on Full Load (per cell)") + anchors.baseline: battDropField.baseline + } + + FactTextField { + id: battDropField + width: textEditWidth + fact: battVoltLoadDrop + showUnits: true + } + } + + QGCLabel { + wrapMode: Text.WordWrap + text: qsTr("Batteries show less voltage at high throttle. Enter the difference in Volts between idle throttle and full ") + + qsTr("throttle, divided by the number of battery cells. Leave at the default if unsure. ") + + highlightPrefix + qsTr("If this value is set too high, the battery might be deep discharged and damaged.") + highlightSuffix + Layout.fillWidth: true + } + + Row { + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { + text: qsTr("Compensated Minimum Voltage:") + } + + QGCLabel { + text: ((battNumCells.value * battLowVolt.value) - (battNumCells.value * battVoltLoadDrop.value)).toFixed(1) + qsTr(" V") + } + } + } // Column + } // QGCGroupBox - Advanced power settings + } // Column + } // Item } // Component } // SetupPage diff --git a/src/QmlControls/QGCGroupBox.qml b/src/QmlControls/QGCGroupBox.qml new file mode 100644 index 000000000..60217970c --- /dev/null +++ b/src/QmlControls/QGCGroupBox.qml @@ -0,0 +1,32 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Controls 2.0 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +GroupBox { + id: control + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + background: Rectangle { + y: control.topPadding - control.padding + width: parent.width + height: parent.height - control.topPadding + control.padding + color: qgcPal.windowShade + } + + label: QGCLabel { + width: control.availableWidth + text: control.title + } +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 2040fedc4..97d45d626 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -31,6 +31,7 @@ QGCCheckBox 1.0 QGCCheckBox.qml QGCColoredImage 1.0 QGCColoredImage.qml QGCComboBox 1.0 QGCComboBox.qml QGCFlickable 1.0 QGCFlickable.qml +QGCGroupBox 1.0 QGCGroupBox.qml QGCLabel 1.0 QGCLabel.qml QGCListView 1.0 QGCListView.qml QGCMapLabel 1.0 QGCMapLabel.qml -- GitLab From e5bc99f55ac11c2ec81c88d8715101f4e5e457f3 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 19:27:53 -0800 Subject: [PATCH 379/398] Add triggerCamera method --- src/Vehicle/Vehicle.cc | 9 +++++++++ src/Vehicle/Vehicle.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index cec74fee1..c2d265162 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -2241,6 +2241,15 @@ void Vehicle::setOfflineEditingDefaultComponentId(int defaultComponentId) } } +void Vehicle::triggerCamera(void) +{ + sendMavCommand(FactSystem::defaultComponentId, + MAV_CMD_DO_DIGICAM_CONTROL, + true, // show errors + 0.0, 0.0, 0.0, 0.0, // param 1-4 unused + 1.0); // trigger camera +} + const char* VehicleGPSFactGroup::_hdopFactName = "hdop"; const char* VehicleGPSFactGroup::_vdopFactName = "vdop"; const char* VehicleGPSFactGroup::_courseOverGroundFactName = "courseOverGround"; diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index d8f774d6a..4c6c16dcb 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -409,6 +409,8 @@ public: /// Clear Messages Q_INVOKABLE void clearMessages(); + Q_INVOKABLE void triggerCamera(void); + #if 0 // Temporarily removed, waiting for new command implementation /// Test motor -- GitLab From f5c3f3cf6860ccbc6495d4b52a318e01d68845a1 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 19:28:14 -0800 Subject: [PATCH 380/398] New CameraWidget for instrument panel page --- qgroundcontrol.qrc | 1 + src/FlightMap/Widgets/CameraWidget.qml | 54 ++++++++++++++++++++++++++ src/FlightMap/qmldir | 29 +++++++------- 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 src/FlightMap/Widgets/CameraWidget.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 512e9dcd0..53dad4533 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -143,6 +143,7 @@ src/FlightMap/Widgets/QGCPitchIndicator.qml src/FlightMap/QGCVideoBackground.qml src/FlightMap/qmldir + src/FlightMap/Widgets/CameraWidget.qml src/FlightMap/Widgets/ValuesWidget.qml src/FlightMap/Widgets/VehicleHealthWidget.qml src/FlightMap/MapItems/VehicleMapItem.qml diff --git a/src/FlightMap/Widgets/CameraWidget.qml b/src/FlightMap/Widgets/CameraWidget.qml new file mode 100644 index 000000000..8e0ec95ce --- /dev/null +++ b/src/FlightMap/Widgets/CameraWidget.qml @@ -0,0 +1,54 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 + +/// Camera controls used in InstrumentSwipeView +QGCFlickable { + id: _root + height: Math.min(maxHeight, column.height) + contentHeight: column.height + flickableDirection: Flickable.VerticalFlick + clip: true + + property var qgcView + property color textColor + property var maxHeight + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + MouseArea { + anchors.fill: parent + onClicked: showNextPage() + } + + Column { + id: column + width: parent.width + spacing: ScreenTools.defaultFontPixelHeight + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + color: textColor + text: qsTr("Camera Controls") + } + + QGCButton { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Trigger Camera") + onClicked: _activeVehicle.triggerCamera() + enabled: _activeVehicle + } + } +} diff --git a/src/FlightMap/qmldir b/src/FlightMap/qmldir index 84cdd15c3..8d6f8eded 100644 --- a/src/FlightMap/qmldir +++ b/src/FlightMap/qmldir @@ -5,20 +5,21 @@ FlightMap 1.0 FlightMap.qml QGCVideoBackground 1.0 QGCVideoBackground.qml # Widgets -CenterMapDropButton 1.0 CenterMapDropButton.qml -CenterMapDropPanel 1.0 CenterMapDropPanel.qml -InstrumentSwipeView 1.0 InstrumentSwipeView.qml -MapFitFunctions 1.0 MapFitFunctions.qml -MapScale 1.0 MapScale.qml -QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml -QGCAttitudeHUD 1.0 QGCAttitudeHUD.qml -QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml -QGCCompassWidget 1.0 QGCCompassWidget.qml -QGCPitchIndicator 1.0 QGCPitchIndicator.qml -QGCSlider 1.0 QGCSlider.qml -ValuesWidget 1.0 ValuesWidget.qml -VehicleHealthWidget 1.0 VehicleHealthWidget.qml -VibrationWidget 1.0 VibrationWidget.qml +CameraWidget 1.0 CameraWidget.qml +CenterMapDropButton 1.0 CenterMapDropButton.qml +CenterMapDropPanel 1.0 CenterMapDropPanel.qml +InstrumentSwipeView 1.0 InstrumentSwipeView.qml +MapFitFunctions 1.0 MapFitFunctions.qml +MapScale 1.0 MapScale.qml +QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml +QGCAttitudeHUD 1.0 QGCAttitudeHUD.qml +QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml +QGCCompassWidget 1.0 QGCCompassWidget.qml +QGCPitchIndicator 1.0 QGCPitchIndicator.qml +QGCSlider 1.0 QGCSlider.qml +ValuesWidget 1.0 ValuesWidget.qml +VehicleHealthWidget 1.0 VehicleHealthWidget.qml +VibrationWidget 1.0 VibrationWidget.qml # Map items MissionItemIndicator 1.0 MissionItemIndicator.qml -- GitLab From 8339e840b5fee01de877c9d3ad883b67f534632c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 27 Feb 2017 19:28:33 -0800 Subject: [PATCH 381/398] Rework InstrumentSwipeView --- src/FlightMap/Widgets/InstrumentSwipeView.qml | 156 ++++++++---------- src/FlightMap/Widgets/ValuesWidget.qml | 2 +- src/FlightMap/Widgets/VehicleHealthWidget.qml | 5 + src/FlightMap/Widgets/VibrationWidget.qml | 5 + 4 files changed, 78 insertions(+), 90 deletions(-) diff --git a/src/FlightMap/Widgets/InstrumentSwipeView.qml b/src/FlightMap/Widgets/InstrumentSwipeView.qml index 8a52ea4f8..22ec90583 100644 --- a/src/FlightMap/Widgets/InstrumentSwipeView.qml +++ b/src/FlightMap/Widgets/InstrumentSwipeView.qml @@ -1,123 +1,101 @@ import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.FlightMap 1.0 Item { - id: _root - clip: true - height: valuesPage.height + pageIndicatorRow.anchors.topMargin + pageIndicatorRow.height + id: _root + clip: true + height: column.height - property var qgcView ///< QGCView to use for showing dialogs + property var qgcView ///< QGCView to use for showing dialogs property color textColor property color backgroundColor - property var maxHeight ///< Maximum height that should be taken, smaller than this is ok + property var maxHeight ///< Maximum height that should be taken, smaller than this is ok - property real _margins: ScreenTools.defaultFontPixelWidth / 2 - property real _pageWidth: _root.width - property int _currentPage: 0 - property int _maxPage: 2 + property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property real _pageWidth: _root.width + property int _currentPage: 0 + property int _maxPage: 3 + + onWidthChanged: showPage(_currentPage) function showPicker() { valuesPage.showPicker() } function showPage(pageIndex) { - _root.height = Qt.binding(function() { return _root.children[pageIndex].height + pageIndicatorRow.anchors.topMargin + pageIndicatorRow.height } ) - _root.children[0].x = -(pageIndex * _pageWidth) - } - - ValuesWidget { - id: valuesPage - width: _pageWidth - qgcView: _root.qgcView - textColor: _root.textColor - maxHeight: _root.maxHeight - } - - VehicleHealthWidget { - id: healthPage - anchors.left: valuesPage.right - width: _pageWidth - qgcView: _root.qgcView - textColor: _root.textColor - maxHeight: _root.maxHeight + pageRow.x = -(pageIndex * _pageWidth) } - VibrationWidget { - id: vibrationPage - anchors.left: healthPage.right - width: _pageWidth - textColor: _root.textColor - backgroundColor: _root.backgroundColor - maxHeight: _root.maxHeight - } - - Row { - id: pageIndicatorRow - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - spacing: _margins - - Repeater { - model: _maxPage + 1 - - Rectangle { - height: radius * 2 - width: radius * 2 - radius: ScreenTools.defaultFontPixelWidth / 3 - border.color: textColor - border.width: 1 - color: _currentPage == index ? textColor : "transparent" - } + function showNextPage() { + if (_currentPage == _maxPage) { + _currentPage = 0 + } else { + _currentPage++ } + showPage(_currentPage) } MouseArea { - anchors.fill: parent - - onClicked: { - if (_currentPage == _maxPage) { - _currentPage = 0 - } else { - _currentPage++ - } - showPage(_currentPage) - } + anchors.fill: parent + onClicked: showNextPage() } - /* - Switching from swipe to click to change pages. Keeping swipe code for now in case of change back. - MouseArea { - anchors.fill: parent + Column { + id: column + anchors.left: parent.left + anchors.right: parent.right - property real xDragStart - property real xFirstPageSave + Row { + id: pageRow - onPressed: { - if (mouse.button == Qt.LeftButton) { - mouse.accepted = true - xDragStart = mouse.x - xFirstPageSave = _root.children[0].x + ValuesWidget { + id: valuesPage + width: _pageWidth + qgcView: _root.qgcView + textColor: _root.textColor + maxHeight: _root.maxHeight + } + CameraWidget { + width: _pageWidth + qgcView: _root.qgcView + textColor: _root.textColor + maxHeight: _root.maxHeight + } + VehicleHealthWidget { + width: _pageWidth + qgcView: _root.qgcView + textColor: _root.textColor + maxHeight: _root.maxHeight + } + VibrationWidget { + width: _pageWidth + textColor: _root.textColor + backgroundColor: _root.backgroundColor + maxHeight: _root.maxHeight } } - onPositionChanged: { - _root.children[0].x = xFirstPageSave + mouse.x - xDragStart - } - - onReleased: { - if (mouse.x < xDragStart) { - // Swipe left - _currentPage = Math.min(_currentPage + 1, _maxPage) - } else { - // Swipe right - _currentPage = Math.max(_currentPage - 1, 0) + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: _margins + + Repeater { + model: _maxPage + 1 + + Rectangle { + height: radius * 2 + width: radius * 2 + radius: ScreenTools.defaultFontPixelWidth / 3 + border.color: textColor + border.width: 1 + color: _currentPage == index ? textColor : "transparent" + } } - showPage(_currentPage) } } - */ } diff --git a/src/FlightMap/Widgets/ValuesWidget.qml b/src/FlightMap/Widgets/ValuesWidget.qml index e4ba42bfe..622921949 100644 --- a/src/FlightMap/Widgets/ValuesWidget.qml +++ b/src/FlightMap/Widgets/ValuesWidget.qml @@ -54,7 +54,7 @@ QGCFlickable { MouseArea { anchors.fill: parent - onClicked: showPicker() + onClicked: showNextPage() } Column { diff --git a/src/FlightMap/Widgets/VehicleHealthWidget.qml b/src/FlightMap/Widgets/VehicleHealthWidget.qml index 4b9b11663..b1c73ae9d 100644 --- a/src/FlightMap/Widgets/VehicleHealthWidget.qml +++ b/src/FlightMap/Widgets/VehicleHealthWidget.qml @@ -34,6 +34,11 @@ QGCFlickable { } } + MouseArea { + anchors.fill: parent + onClicked: showNextPage() + } + Column { id: healthColumn width: parent.width diff --git a/src/FlightMap/Widgets/VibrationWidget.qml b/src/FlightMap/Widgets/VibrationWidget.qml index e4ebade92..0a7cb0087 100644 --- a/src/FlightMap/Widgets/VibrationWidget.qml +++ b/src/FlightMap/Widgets/VibrationWidget.qml @@ -39,6 +39,11 @@ QGCFlickable { QGCPalette { id:qgcPal; colorGroupEnabled: true } + MouseArea { + anchors.fill: parent + onClicked: showNextPage() + } + Item { id: innerItem width: parent.width -- GitLab From 45cc573d95321354998dcfc29b41c15d60923a5a Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 28 Feb 2017 03:13:00 -0500 Subject: [PATCH 382/398] Add grid lines to video view --- src/FlightDisplay/FlightDisplayViewVideo.qml | 32 +++++++++++++++++++- src/Settings/Video.SettingsGroup.json | 9 ++++++ src/Settings/VideoSettings.cc | 11 +++++++ src/Settings/VideoSettings.h | 4 +++ src/ui/preferences/GeneralSettings.qml | 14 +++++++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 65d9af211..275bfd6ab 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -23,7 +23,9 @@ import QGroundControl.Controllers 1.0 Item { id: root - property double _ar: QGroundControl.settingsManager.videoSettings.aspectRatio.rawValue + property double _ar: QGroundControl.settingsManager.videoSettings.aspectRatio.rawValue + property bool _showGrid: QGroundControl.settingsManager.videoSettings.gridLines.rawValue > 0 + Rectangle { id: noVideo anchors.fill: parent @@ -50,4 +52,32 @@ Item { visible: QGroundControl.videoManager.videoRunning } } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + height: parent.height + width: 1 + x: parent.width * 0.33 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + height: parent.height + width: 1 + x: parent.width * 0.66 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + width: parent.width + height: 1 + y: parent.height * 0.33 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + width: parent.width + height: 1 + y: parent.height * 0.66 + visible: _showGrid + } } diff --git a/src/Settings/Video.SettingsGroup.json b/src/Settings/Video.SettingsGroup.json index 17e8254db..457a519c2 100644 --- a/src/Settings/Video.SettingsGroup.json +++ b/src/Settings/Video.SettingsGroup.json @@ -35,5 +35,14 @@ "type": "float", "decimalPlaces": 6, "defaultValue": 1.777777 +}, +{ + "name": "VideoGridLines", + "shortDescription": "Video Grid Lines", + "longDescription": "Displays a grid overlayed over the video view.", + "type": "uint32", + "enumStrings": "Hide,Show", + "enumValues": "1,0", + "defaultValue": 0 } ] diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index f36751d24..52cf59615 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -24,6 +24,7 @@ const char* VideoSettings::udpPortName = "VideoUDPPort"; const char* VideoSettings::rtspUrlName = "VideoRTSPUrl"; const char* VideoSettings::videoSavePathName = "VideoSavePath"; const char* VideoSettings::videoAspectRatioName = "VideoAspectRatio"; +const char* VideoSettings::videoGridLinesName = "VideoGridLines"; const char* VideoSettings::videoSourceNoVideo = "No Video Available"; const char* VideoSettings::videoSourceUDP = "UDP Video Stream"; @@ -36,6 +37,7 @@ VideoSettings::VideoSettings(QObject* parent) , _rtspUrlFact(NULL) , _videoSavePathFact(NULL) , _videoAspectRatioFact(NULL) + , _gridLinesFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); @@ -115,3 +117,12 @@ Fact* VideoSettings::aspectRatio(void) return _videoAspectRatioFact; } + +Fact* VideoSettings::gridLines(void) +{ + if (!_gridLinesFact) { + _gridLinesFact = _createSettingsFact(videoGridLinesName); + } + + return _gridLinesFact; +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index d914fc701..48eb53dfc 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -24,12 +24,14 @@ public: Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) Q_PROPERTY(Fact* videoSavePath READ videoSavePath CONSTANT) Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) + Q_PROPERTY(Fact* gridLines READ gridLines CONSTANT) Fact* videoSource (void); Fact* udpPort (void); Fact* rtspUrl (void); Fact* videoSavePath (void); Fact* aspectRatio (void); + Fact* gridLines (void); static const char* videoSettingsGroupName; @@ -38,6 +40,7 @@ public: static const char* rtspUrlName; static const char* videoSavePathName; static const char* videoAspectRatioName; + static const char* videoGridLinesName; static const char* videoSourceNoVideo; static const char* videoSourceUDP; @@ -49,6 +52,7 @@ private: SettingsFact* _rtspUrlFact; SettingsFact* _videoSavePathFact; SettingsFact* _videoAspectRatioFact; + SettingsFact* _gridLinesFact; }; #endif diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 4993e8c7e..4506b9174 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -512,6 +512,20 @@ QGCView { fact: QGroundControl.settingsManager.videoSettings.aspectRatio } } + Row { + spacing: ScreenTools.defaultFontPixelWidth + visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex < 2 && QGroundControl.settingsManager.videoSettings.gridLines.visible + QGCLabel { + anchors.baseline: gridField.baseline + text: qsTr("Grid Lines:") + width: _labelWidth + } + FactComboBox { + id: gridField + width: _editFieldWidth + fact: QGroundControl.settingsManager.videoSettings.gridLines + } + } Row { spacing: ScreenTools.defaultFontPixelWidth visible: QGroundControl.settingsManager.videoSettings.videoSavePath.visible && QGroundControl.videoManager.isGStreamer && QGroundControl.videoManager.recordingEnabled -- GitLab From 94cac7e5fe1618c2d845e4c7a9c00dad8e51aba9 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 28 Feb 2017 12:36:15 -0500 Subject: [PATCH 383/398] Grids should be within actual video frame and not the entire window. --- src/FlightDisplay/FlightDisplayViewVideo.qml | 56 ++++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index 275bfd6ab..b3ea10af4 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -50,34 +50,34 @@ Item { display: QGroundControl.videoManager.videoSurface receiver: QGroundControl.videoManager.videoReceiver visible: QGroundControl.videoManager.videoRunning + Rectangle { + color: Qt.rgba(1,1,1,0.5) + height: parent.height + width: 1 + x: parent.width * 0.33 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + height: parent.height + width: 1 + x: parent.width * 0.66 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + width: parent.width + height: 1 + y: parent.height * 0.33 + visible: _showGrid + } + Rectangle { + color: Qt.rgba(1,1,1,0.5) + width: parent.width + height: 1 + y: parent.height * 0.66 + visible: _showGrid + } } } - Rectangle { - color: Qt.rgba(1,1,1,0.5) - height: parent.height - width: 1 - x: parent.width * 0.33 - visible: _showGrid - } - Rectangle { - color: Qt.rgba(1,1,1,0.5) - height: parent.height - width: 1 - x: parent.width * 0.66 - visible: _showGrid - } - Rectangle { - color: Qt.rgba(1,1,1,0.5) - width: parent.width - height: 1 - y: parent.height * 0.33 - visible: _showGrid - } - Rectangle { - color: Qt.rgba(1,1,1,0.5) - width: parent.width - height: 1 - y: parent.height * 0.66 - visible: _showGrid - } } -- GitLab From d930895b1fa921cb7182e8cdf39b8c131bb779f6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 28 Feb 2017 09:45:32 -0800 Subject: [PATCH 384/398] Tweaking control sizing for better touch usability --- qgcresources.qrc | 2 + src/QmlControls/QGCButton.qml | 63 +++++++++--------------- src/QmlControls/QGCCheckBox.qml | 82 +++++++++++++++++++++++++------ src/QmlControls/QGCComboBox.qml | 5 +- src/QmlControls/QGCTextField.qml | 2 +- src/QmlControls/ScreenTools.qml | 8 +++ src/QmlControls/check.png | Bin 0 -> 176 bytes 7 files changed, 103 insertions(+), 59 deletions(-) create mode 100644 src/QmlControls/check.png diff --git a/qgcresources.qrc b/qgcresources.qrc index bc66e1716..cb4c509db 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -46,6 +46,7 @@ src/AutoPilotPlugins/Common/Images/HelicopterCoaxial.svg src/AutoPilotPlugins/Common/Images/wifi.svg src/QmlControls/arrow-down.png + src/QmlControls/check.png src/VehicleSetup/FirmwareUpgradeIcon.png src/AutoPilotPlugins/PX4/Images/FlightModesComponentIcon.png src/AutoPilotPlugins/PX4/Images/CameraTrigger.svg @@ -160,6 +161,7 @@ src/FirmwarePlugin/APM/APMBrandImage.png src/FirmwarePlugin/APM/APMBrandImageSub.png src/FirmwarePlugin/PX4/PX4BrandImage.png + src/FirmwarePlugin/PX4/PX4BrandImage.png resources/Antenna_RC.svg diff --git a/src/QmlControls/QGCButton.qml b/src/QmlControls/QGCButton.qml index 5238f025a..55fe10af6 100644 --- a/src/QmlControls/QGCButton.qml +++ b/src/QmlControls/QGCButton.qml @@ -22,7 +22,8 @@ Button { property int __lastGlobalMouseX: 0 property int __lastGlobalMouseY: 0 - property int __padding: Math.round(ScreenTools.defaultFontPixelHeight * 0.5) + property int _horizontalPadding: ScreenTools.defaultFontPixelWidth + property int _verticalPadding: Math.round(ScreenTools.defaultFontPixelHeight / 2) Connections { target: __behavior @@ -50,42 +51,26 @@ Button { style: ButtonStyle { /*! The padding between the background and the label components. */ padding { - top: __padding * 0.5 - left: __padding - right: control.menu !== null ? Math.round(ScreenTools.defaultFontPixelHeight) : __padding - bottom: __padding * 0.5 + top: _verticalPadding + bottom: _verticalPadding + left: _horizontalPadding + right: _horizontalPadding } /*! This defines the background of the button. */ - background: Item { - property bool down: control.pressed || (control.checkable && control.checked) - implicitWidth: Math.round(ScreenTools.defaultFontPixelWidth * 4.5) - implicitHeight: ScreenTools.isMobile ? Math.max(25, Math.round(ScreenTools.defaultFontPixelHeight * 2)) : Math.max(25, Math.round(ScreenTools.defaultFontPixelHeight * 1.2)) - - Rectangle { - anchors.fill: parent - border.width: _showBorder ? 1: 0 - border.color: _qgcPal.buttonText - //radius: 3 - color: _showHighlight ? - control._qgcPal.buttonHighlight : - (primary ? control._qgcPal.primaryButton : control._qgcPal.button) - } - - Image { - id: imageItem - visible: control.menu !== null - source: "/qmlimages/arrow-down.png" - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: __padding - opacity: control.enabled ? 0.6 : 0.5 - } + background: Rectangle { + implicitWidth: ScreenTools.implicitButtonWidth + implicitHeight: ScreenTools.implicitButtonHeight + border.width: _showBorder ? 1: 0 + border.color: _qgcPal.buttonText + color: _showHighlight ? + control._qgcPal.buttonHighlight : + (primary ? control._qgcPal.primaryButton : control._qgcPal.button) } /*! This defines the label of the button. */ label: Item { - implicitWidth: control.menu === null ? row.implicitWidth : row.implicitWidth + ScreenTools.defaultFontPixelWidth + implicitWidth: row.implicitWidth implicitHeight: row.implicitHeight baselineOffset: row.y + text.y + text.baselineOffset @@ -95,20 +80,20 @@ Button { spacing: ScreenTools.defaultFontPixelWidth * 0.25 Image { - source: control.iconSource + source: control.iconSource anchors.verticalCenter: parent.verticalCenter } Text { - id: text - antialiasing: true - text: control.text - font.pointSize: pointSize - font.family: ScreenTools.normalFontFamily + id: text anchors.verticalCenter: parent.verticalCenter - color: _showHighlight ? - control._qgcPal.buttonHighlightText : - (primary ? control._qgcPal.primaryButtonText : control._qgcPal.buttonText) + antialiasing: true + text: control.text + font.pointSize: pointSize + font.family: ScreenTools.normalFontFamily + color: _showHighlight ? + control._qgcPal.buttonHighlightText : + (primary ? control._qgcPal.primaryButtonText : control._qgcPal.buttonText) } } } diff --git a/src/QmlControls/QGCCheckBox.qml b/src/QmlControls/QGCCheckBox.qml index f57d52143..7cf0c4bc6 100644 --- a/src/QmlControls/QGCCheckBox.qml +++ b/src/QmlControls/QGCCheckBox.qml @@ -1,30 +1,32 @@ -import QtQuick 2.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 -import QGroundControl.Palette 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 CheckBox { property var __qgcPal: QGCPalette { colorGroupEnabled: enabled } style: CheckBoxStyle { label: Item { - implicitWidth: text.implicitWidth + 2 + implicitWidth: text.implicitWidth + 2 implicitHeight: text.implicitHeight baselineOffset: text.baselineOffset + Rectangle { - anchors.fill: text - anchors.margins: -1 + anchors.margins: -1 anchors.leftMargin: -3 anchors.rightMargin: -3 - visible: control.activeFocus - height: 6 - radius: 3 - color: "#224f9fef" - border.color: "#47b" - opacity: 0.6 + anchors.fill: text + visible: control.activeFocus + height: 6 + radius: 3 + color: "#224f9fef" + border.color: "#47b" + opacity: 0.6 } + Text { id: text text: control.text @@ -34,6 +36,54 @@ CheckBox { color: control.__qgcPal.text anchors.verticalCenter: parent.verticalCenter } - } - } + } // label + + indicator: Item { + implicitWidth: ScreenTools.implicitCheckBoxWidth + implicitHeight: implicitWidth + + Rectangle { + anchors.fill: parent + anchors.bottomMargin: -1 + color: "#44ffffff" + radius: baserect.radius + } + + Rectangle { + id: baserect + gradient: Gradient { + GradientStop {color: "#eee" ; position: 0} + GradientStop {color: control.pressed ? "#eee" : "#fff" ; position: 0.1} + GradientStop {color: "#fff" ; position: 1} + } + radius: ScreenTools.defaultFontPixelHeight * 0.16 + anchors.fill: parent + border.color: control.activeFocus ? "#47b" : "#999" + } + + Image { + source: "/qmlimages/check.png" + opacity: control.checkedState === Qt.Checked ? control.enabled ? 1 : 0.5 : 0 + anchors.centerIn: parent + anchors.verticalCenterOffset: 1 + Behavior on opacity {NumberAnimation {duration: 80}} + } + + Rectangle { + anchors.fill: parent + anchors.margins: Math.round(baserect.radius) + antialiasing: true + gradient: Gradient { + GradientStop {color: control.pressed ? "#555" : "#999" ; position: 0} + GradientStop {color: "#555" ; position: 1} + } + radius: baserect.radius - 1 + anchors.centerIn: parent + anchors.alignWhenCentered: true + border.color: "#222" + Behavior on opacity {NumberAnimation {duration: 80}} + opacity: control.checkedState === Qt.PartiallyChecked ? control.enabled ? 1 : 0.5 : 0 + } + } // indicator + } // style } diff --git a/src/QmlControls/QGCComboBox.qml b/src/QmlControls/QGCComboBox.qml index 9950708b4..1a7402167 100644 --- a/src/QmlControls/QGCComboBox.qml +++ b/src/QmlControls/QGCComboBox.qml @@ -18,13 +18,12 @@ ComboBox { control._qgcPal.buttonText background: Item { - implicitWidth: Math.round(ScreenTools.defaultFontPixelWidth * 4.5) - implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 2 : 1.2)) + implicitWidth: ScreenTools.implicitComboBoxWidth + implicitHeight: ScreenTools.implicitComboBoxHeight Rectangle { anchors.fill: parent color: _showHighlight ? control._qgcPal.buttonHighlight : control._qgcPal.button - //radius: 3 border.width: _showBorder ? 1: 0 border.color: control._qgcPal.buttonText } diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 502e428be..699a82c64 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -27,7 +27,7 @@ TextField { textColor: qgcPal.textFieldText - implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 2 : 1.2)) + implicitHeight: ScreenTools.implicitTextFieldHeight QGCLabel { id: unitsLabelWidthGenerator diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 0eb0c5593..0d2d2a27f 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -60,6 +60,14 @@ Item { property bool isTinyScreen: (Screen.width / Screen.pixelDensity) < 120 // 120mm property bool isShortScreen: ScreenToolsController.isMobile && ((Screen.height / Screen.width) < 0.6) // Nexus 7 for example + // The implicit heights/widths for our custom control set + property real implicitButtonWidth: Math.round(defaultFontPixelWidth * (isMobile ? 7.0 : 5.0)) + property real implicitButtonHeight: Math.round(defaultFontPixelHeight * (isMobile ? 2.0 : 1.6)) + property real implicitCheckBoxWidth: Math.round(defaultFontPixelHeight * (isMobile ? 1.5 : 1.0)) + property real implicitTextFieldHeight: Math.round(defaultFontPixelHeight * (isMobile ? 2.0 : 1.6)) + property real implicitComboBoxHeight: Math.round(defaultFontPixelHeight * (isMobile ? 2.0 : 1.6)) + property real implicitComboBoxWidth: Math.round(defaultFontPixelWidth * (isMobile ? 7.0 : 5.0)) + readonly property string normalFontFamily: "opensans" readonly property string demiboldFontFamily: "opensans-demibold" diff --git a/src/QmlControls/check.png b/src/QmlControls/check.png new file mode 100644 index 0000000000000000000000000000000000000000..ad1df9572e99cebd676eaa8db86a66593b2f5345 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^AT~D#3y^%1eq}O{D))484B?PvJ*dcah(Uzm!t&xn z4r)>1EX}#%1?CYOX6)@`k1&kLVSB8XC~#1E#+h5{YdG$IXK1)|`Mey%GC8MY9tP`$ zri=}D&fXUC(0Q_=^%%pA7qOyxR@x0`0xf=LEM+)zr*#7>!?k^J&lnDzICxTkVL<@n a2X^;^l3rbszj=X9VDNPHb6Mw<&;$UUI6bHU literal 0 HcmV?d00001 -- GitLab From 65a68c62525fe9f9cf31250adc20f2a57f786374 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 28 Feb 2017 12:54:24 -0500 Subject: [PATCH 385/398] Test message info before using it. --- src/ui/MAVLinkDecoder.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ui/MAVLinkDecoder.cc b/src/ui/MAVLinkDecoder.cc index 04603ef0f..e7c814e59 100644 --- a/src/ui/MAVLinkDecoder.cc +++ b/src/ui/MAVLinkDecoder.cc @@ -60,14 +60,18 @@ void MAVLinkDecoder::receiveMessage(LinkInterface* link,mavlink_message_t messag { Q_UNUSED(link); - msgDict[message.msgid] = message; - uint32_t msgid = message.msgid; const mavlink_message_info_t* msgInfo = mavlink_get_message_info(&message); + if(!msgInfo) { + qWarning() << "Invalid MAVLink message received. ID:" << msgid; + return; + } + + msgDict[message.msgid] = message; // Store an arrival time for this message. This value ends up being calculated later. quint64 time = 0; - + // The SYSTEM_TIME message is special, in that it's handled here for synchronizing the QGC time with the remote time. if (message.msgid == MAVLINK_MSG_ID_SYSTEM_TIME) { -- GitLab From 857776ed20099350c649f94b000c50884679ca54 Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 28 Feb 2017 14:26:06 -0500 Subject: [PATCH 386/398] Updating MAVLink V2 --- libs/mavlink/include/mavlink/v2.0 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/mavlink/include/mavlink/v2.0 b/libs/mavlink/include/mavlink/v2.0 index f29883acd..b5aaac46c 160000 --- a/libs/mavlink/include/mavlink/v2.0 +++ b/libs/mavlink/include/mavlink/v2.0 @@ -1 +1 @@ -Subproject commit f29883acd90be17987d0cba79a1c569a5427d01d +Subproject commit b5aaac46c4819bab0f80d4d071ae9a8fec439676 -- GitLab From 0b676964508fcaa35d454bf2c2f77ee1aa46539c Mon Sep 17 00:00:00 2001 From: Gus Grubba Date: Tue, 28 Feb 2017 14:36:35 -0500 Subject: [PATCH 387/398] Removing duplicated resource. --- qgcresources.qrc | 1 - 1 file changed, 1 deletion(-) diff --git a/qgcresources.qrc b/qgcresources.qrc index cb4c509db..ae67a275d 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -161,7 +161,6 @@ src/FirmwarePlugin/APM/APMBrandImage.png src/FirmwarePlugin/APM/APMBrandImageSub.png src/FirmwarePlugin/PX4/PX4BrandImage.png - src/FirmwarePlugin/PX4/PX4BrandImage.png resources/Antenna_RC.svg -- GitLab From 2b9c7c086a05b96676bb62be3191d048400f6826 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 28 Feb 2017 11:42:29 -0800 Subject: [PATCH 388/398] Workaround strange lost clicks possible Qt bug --- src/MissionEditor/MissionEditor.qml | 23 +++++++++++++---------- src/MissionEditor/MissionItemEditor.qml | 1 - 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index c3fe618e2..d8038782a 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -253,16 +253,19 @@ QGCView { } function setCurrentItem(sequenceNumber) { - editorMap.polygonDraw.cancelPolygonEdit() - _currentMissionItem = undefined - for (var i=0; i<_visualItems.count; i++) { - var visualItem = _visualItems.get(i) - if (visualItem.sequenceNumber == sequenceNumber) { - _currentMissionItem = visualItem - _currentMissionItem.isCurrentItem = true - _currentMissionIndex = i - } else { - visualItem.isCurrentItem = false + if (sequenceNumber !== _currentMissionIndex) { + editorMap.polygonDraw.cancelPolygonEdit() + _currentMissionItem = undefined + _currentMissionIndex = -1 + for (var i=0; i<_visualItems.count; i++) { + var visualItem = _visualItems.get(i) + if (visualItem.sequenceNumber == sequenceNumber) { + _currentMissionItem = visualItem + _currentMissionItem.isCurrentItem = true + _currentMissionIndex = i + } else { + visualItem.isCurrentItem = false + } } } } diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index 25b68ffe3..0ee2e90ac 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -42,7 +42,6 @@ Rectangle { MouseArea { anchors.fill: parent - visible: !missionItem.isCurrentItem onClicked: _root.clicked() } -- GitLab From 9359f581b851c3e3e948ea7b56da15969e33a30c Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 28 Feb 2017 17:49:27 -0800 Subject: [PATCH 389/398] Show Follow Vehicle checkbox on Fly view --- src/FlightDisplay/FlightDisplayViewMap.qml | 8 ++++++-- src/FlightMap/Widgets/CenterMapDropPanel.qml | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index 27bdeddd4..ed30b1510 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -139,8 +139,12 @@ FlightMap { id: centerMapDropPanel CenterMapDropPanel { - map: _flightMap - fitFunctions: mapFitFunctions + map: _flightMap + fitFunctions: mapFitFunctions + showFollowVehicle: true + followVehicle: _followVehicle + + onFollowVehicleChanged: _followVehicle = followVehicle } } diff --git a/src/FlightMap/Widgets/CenterMapDropPanel.qml b/src/FlightMap/Widgets/CenterMapDropPanel.qml index fe3a43b6d..a8adffc0c 100644 --- a/src/FlightMap/Widgets/CenterMapDropPanel.qml +++ b/src/FlightMap/Widgets/CenterMapDropPanel.qml @@ -97,7 +97,7 @@ ColumnLayout { onClicked: { dropPanel.hide() - _root.followVehicle = checked + root.followVehicle = checked } } } // Column -- GitLab From 559818424953a3325af70a11ae7bcc92c0af2f3d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 28 Feb 2017 18:34:53 -0800 Subject: [PATCH 390/398] Use MAV_FRAME_MISSION for DO_LAND_START --- src/MissionManager/FixedWingLandingComplexItem.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index dd1dc98fc..3ec84230b 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -141,7 +141,7 @@ QmlObjectListModel* FixedWingLandingComplexItem::getMissionItems(void) const MissionItem* item = new MissionItem(seqNum++, // sequence number MAV_CMD_DO_LAND_START, // MAV_CMD - MAV_FRAME_GLOBAL_RELATIVE_ALT, // MAV_FRAME + MAV_FRAME_MISSION, // MAV_FRAME 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // param 1-7 true, // autoContinue false, // isCurrentItem -- GitLab From 139157ffd95ee9399bddd4c85249749fbf34495d Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 10:30:25 -0800 Subject: [PATCH 391/398] New waypoint coordinate indicators --- src/FlightDisplay/FlightDisplayViewMap.qml | 10 ++- src/FlightMap/FlightMap.qml | 4 +- .../MapItems/MissionItemIndicator.qml | 4 +- .../FWLandingPatternMapVisual.qml | 17 ++-- src/MissionEditor/MissionEditor.qml | 17 ++-- src/MissionEditor/SurveyComplexItem.qml | 18 +++-- src/QmlControls/MissionItemIndexLabel.qml | 77 ++++++++++++++----- 7 files changed, 100 insertions(+), 47 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index ed30b1510..607f1e02c 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -233,7 +233,8 @@ FlightMap { // GeoFence breach return point MapQuickItem { - anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY coordinate: geoFenceController.breachReturnPoint visible: geoFenceController.breachReturnEnabled sourceItem: MissionItemIndexLabel { label: "F" } @@ -246,7 +247,8 @@ FlightMap { delegate: MapQuickItem { id: itemIndicator - anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY coordinate: object.coordinate z: QGroundControl.zOrderMapItems @@ -262,8 +264,8 @@ FlightMap { coordinate: _gotoHereCoordinate visible: _activeVehicle && _activeVehicle.guidedMode && _gotoHereCoordinate.isValid z: QGroundControl.zOrderMapItems - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY sourceItem: MissionItemIndexLabel { checked: true diff --git a/src/FlightMap/FlightMap.qml b/src/FlightMap/FlightMap.qml index ed8042802..076641a91 100644 --- a/src/FlightMap/FlightMap.qml +++ b/src/FlightMap/FlightMap.qml @@ -129,8 +129,8 @@ Map { /// Ground Station location MapQuickItem { - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY visible: mainWindow.gcsPosition.isValid coordinate: mainWindow.gcsPosition sourceItem: MissionItemIndexLabel { diff --git a/src/FlightMap/MapItems/MissionItemIndicator.qml b/src/FlightMap/MapItems/MissionItemIndicator.qml index ec325c1ec..58ad84543 100644 --- a/src/FlightMap/MapItems/MissionItemIndicator.qml +++ b/src/FlightMap/MapItems/MissionItemIndicator.qml @@ -24,8 +24,8 @@ MapQuickItem { signal clicked - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY sourceItem: MissionItemIndexLabel { diff --git a/src/MissionEditor/FWLandingPatternMapVisual.qml b/src/MissionEditor/FWLandingPatternMapVisual.qml index b393d755f..ec4af3236 100644 --- a/src/MissionEditor/FWLandingPatternMapVisual.qml +++ b/src/MissionEditor/FWLandingPatternMapVisual.qml @@ -214,14 +214,17 @@ Item { id: loiterPointComponent MapQuickItem { - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY z: QGroundControl.zOrderMapItems coordinate: _missionItem.loiterCoordinate sourceItem: MissionItemIndexLabel { - label: "L" + label: "Loiter" + checked: _missionItem.isCurrentItem + + onClicked: setCurrentItem(_missionItem.sequenceNumber) } } } @@ -245,15 +248,17 @@ Item { id: landPointComponent MapQuickItem { - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY z: QGroundControl.zOrderMapItems coordinate: _missionItem.landingCoordinate sourceItem: MissionItemIndexLabel { - label: "L" + label: "Land" checked: _missionItem.isCurrentItem + + onClicked: setCurrentItem(_missionItem.sequenceNumber) } } } diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index c3fe618e2..d3f1cd656 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -400,8 +400,8 @@ QGCView { id: itemDragger x: mapCoordinateIndicator ? (mapCoordinateIndicator.x + mapCoordinateIndicator.anchorPoint.x - (itemDragger.width / 2)) : 100 y: mapCoordinateIndicator ? (mapCoordinateIndicator.y + mapCoordinateIndicator.anchorPoint.y - (itemDragger.height / 2)) : 100 - width: ScreenTools.defaultFontPixelHeight * 2 - height: ScreenTools.defaultFontPixelHeight * 2 + width: ScreenTools.defaultFontPixelHeight * 3 + height: ScreenTools.defaultFontPixelHeight * 3 color: "transparent" visible: false z: QGroundControl.zOrderMapItems + 1 // Above item icons @@ -501,9 +501,10 @@ QGCView { model: object.isSimpleItem ? object.childItems : 0 delegate: MissionItemIndexLabel { - label: object.abbreviation - checked: object.isCurrentItem - z: 2 + label: object.abbreviation + checked: object.isCurrentItem + z: 2 + specifiesCoordinate: false onClicked: setCurrentItem(object.sequenceNumber) } @@ -686,7 +687,8 @@ QGCView { // GeoFence breach return point MapQuickItem { - anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY coordinate: geoFenceController.breachReturnPoint visible: geoFenceController.breachReturnEnabled sourceItem: MissionItemIndexLabel { label: "F" } @@ -727,7 +729,8 @@ QGCView { delegate: MapQuickItem { id: itemIndicator - anchorPoint: Qt.point(sourceItem.width / 2, sourceItem.height / 2) + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY coordinate: object.coordinate z: QGroundControl.zOrderMapItems diff --git a/src/MissionEditor/SurveyComplexItem.qml b/src/MissionEditor/SurveyComplexItem.qml index 64d261ec5..a438fb707 100644 --- a/src/MissionEditor/SurveyComplexItem.qml +++ b/src/MissionEditor/SurveyComplexItem.qml @@ -72,14 +72,17 @@ Item { id: entryPointComponent MapQuickItem { - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY z: QGroundControl.zOrderMapItems coordinate: _missionItem.coordinate sourceItem: MissionItemIndexLabel { - label: "S" + label: "Entry" + checked: _missionItem.isCurrentItem + + onClicked: setCurrentItem(_missionItem.sequenceNumber) } } } @@ -89,14 +92,17 @@ Item { id: exitPointComponent MapQuickItem { - anchorPoint.x: sourceItem.width / 2 - anchorPoint.y: sourceItem.height / 2 + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY z: QGroundControl.zOrderMapItems coordinate: _missionItem.exitCoordinate sourceItem: MissionItemIndexLabel { - label: "S" + label: "Exit" + checked: _missionItem.isCurrentItem + + onClicked: setCurrentItem(_missionItem.sequenceNumber) } } } diff --git a/src/QmlControls/MissionItemIndexLabel.qml b/src/QmlControls/MissionItemIndexLabel.qml index 9561f61c0..7e6bb8e74 100644 --- a/src/QmlControls/MissionItemIndexLabel.qml +++ b/src/QmlControls/MissionItemIndexLabel.qml @@ -5,38 +5,75 @@ import QtQuick.Controls.Styles 1.2 import QGroundControl.ScreenTools 1.0 import QGroundControl.Palette 1.0 -Rectangle { - id: root - - property alias label: _label.text - property bool checked: false - property bool small: false +Canvas { + id: root + width: _width + (_singleChar ? 0 : _label.width) + height: specifiesCoordinate ? (_width * 1.5) : _width signal clicked - width: _width - height: _width - radius: _width / 2 - border.width: small ? 1 : 2 - border.color: "white" - color: checked ? "green" : qgcPal.mapButtonHighlight + property alias label: _label.text + property bool checked: false + property bool small: false + property var color: checked ? "green" : qgcPal.mapButtonHighlight + property real anchorPointX: _width / 2 + property real anchorPointY: _width * 1.5 + property bool specifiesCoordinate: true + + property real _width: small ? ScreenTools.defaultFontPixelHeight * ScreenTools.smallFontPointRatio * 1.25 : ScreenTools.defaultFontPixelHeight * 1.25 + property bool _singleChar: _label.text.length <= 1 - property real _width: small ? ScreenTools.defaultFontPixelHeight * ScreenTools.smallFontPointRatio * 1.75 : ScreenTools.defaultFontPixelHeight * 1.75 + onColorChanged: requestPaint() QGCPalette { id: qgcPal } + function paintSingleCoordinate(context) { + context.arc(_width / 2, _width / 2, _width / 2, (Math.PI / 8) * 7, Math.PI / 8); + context.lineTo(_width / 2, _width * 1.5) + } + + function paintSingleNoCoordinate(context) { + context.arc(_width / 2, _width / 2, _width / 2, Math.PI * 2, 0); + } + + function paintMultipleCoordinate(context) { + context.arc(_width / 2, _width / 2, _width / 2, (Math.PI / 8) * 7, (Math.PI / 2) * 3); + context.lineTo(_label.width, 0) + context.arc(_label.width, _width / 2, _width / 2, (Math.PI / 2) * 3, Math.PI / 2); + context.lineTo((_width / 4) * 3, _width) + context.lineTo(_width / 2, _width * 1.5) + } + + onPaint: { + var context = getContext("2d") + context.reset() + context.beginPath() + if (_singleChar) { + if (specifiesCoordinate) { + paintSingleCoordinate(context) + } else { + paintSingleNoCoordinate(context) + } + } else { + paintMultipleCoordinate(context) + } + context.closePath() + context.fillStyle = color + context.fill() + } + QGCLabel { - id: _label - anchors.fill: parent - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: "white" - font.pointSize: small ? ScreenTools.smallFontPointSize : ScreenTools.defaultFontPointSize + id: _label + x: Math.round((_width / 2) - (_singleChar ? (width / 2) : (ScreenTools.defaultFontPixelWidth / 2))) + y: Math.round((_width / 2) - (height / 2)) + color: "white" + font.pointSize: small ? ScreenTools.smallFontPointSize : ScreenTools.defaultFontPointSize + + onWidthChanged: requestPaint() } MouseArea { anchors.fill: parent onClicked: parent.clicked() } - } -- GitLab From 9bcd1a41fba21549cf053cdb58f15c38d5d98d4e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 11:02:16 -0800 Subject: [PATCH 392/398] Consider any message from vehicle comm lost restart --- src/Vehicle/Vehicle.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index c2d265162..2ba2fc255 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -493,6 +493,10 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes } } + + // Mark this vehicle as active + _connectionActive(); + // Give the plugin a change to adjust the message contents if (!_firmwarePlugin->adjustIncomingMavlinkMessage(this, &message)) { return; @@ -900,8 +904,6 @@ void Vehicle::_handleHeartbeat(mavlink_message_t& message) return; } - _connectionActive(); - mavlink_heartbeat_t heartbeat; mavlink_msg_heartbeat_decode(&message, &heartbeat); -- GitLab From 5ee3a9f567e90bca5f8f7bc56f64dcbd397e5731 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 13:14:51 -0800 Subject: [PATCH 393/398] Drop focus after virtual keyboard is done Some android versions leave a focus arrow indicator turd in the control. This gets rid of it. --- src/FactSystem/FactControls/FactTextField.qml | 4 ++++ src/QmlControls/QGCTextField.qml | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index 3dd02aa68..58a709360 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -26,6 +26,10 @@ QGCTextField { Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard onEditingFinished: { + if (ScreenTools.isMobile) { + // Toss focus on mobile after Done on virtual keyboard. Prevent strange interactions. + focus = false + } if (typeof qgcView !== 'undefined' && qgcView) { var errorString = fact.validate(text, false /* convertOnly */) if (errorString == "") { diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 699a82c64..8b5497e90 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -29,6 +29,13 @@ TextField { implicitHeight: ScreenTools.implicitTextFieldHeight + onEditingFinished: { + if (ScreenTools.isMobile) { + // Toss focus on mobile after Done on virtual keyboard. Prevent strange interactions. + focus = false + } + } + QGCLabel { id: unitsLabelWidthGenerator text: unitsLabel -- GitLab From fc3f135027f4dcb3b63fc72203a2adc57fa3ea70 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 13:15:10 -0800 Subject: [PATCH 394/398] Increase hamburger touch size --- src/MissionEditor/MissionItemEditor.qml | 77 ++++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/src/MissionEditor/MissionItemEditor.qml b/src/MissionEditor/MissionItemEditor.qml index 0ee2e90ac..156345685 100644 --- a/src/MissionEditor/MissionItemEditor.qml +++ b/src/MissionEditor/MissionItemEditor.qml @@ -66,50 +66,57 @@ Rectangle { visible: missionItem.isCurrentItem && missionItem.sequenceNumber != 0 color: qgcPal.windowShade - MouseArea { - anchors.fill: parent - onClicked: hamburgerMenu.popup() + } - Menu { - id: hamburgerMenu + MouseArea { + // The MouseArea for the hamburger is larger than the hamburger image itself in order to provide a larger + // touch area on mobile + anchors.top: parent.top + anchors.bottom: editorLoader.top + anchors.leftMargin: -hamburger.anchors.rightMargin + anchors.left: hamburger.left + anchors.right: parent.right + onClicked: hamburgerMenu.popup() + + Menu { + id: hamburgerMenu + + MenuItem { + text: qsTr("Insert") + onTriggered: insert() + } - MenuItem { - text: qsTr("Insert") - onTriggered: insert() - } + MenuItem { + text: qsTr("Delete") + onTriggered: remove() + } - MenuItem { - text: qsTr("Delete") - onTriggered: remove() - } + MenuItem { + text: "Change command..." + onTriggered: commandPicker.clicked() + } - MenuItem { - text: "Change command..." - onTriggered: commandPicker.clicked() - } + MenuSeparator { + visible: missionItem.isSimpleItem + } - MenuSeparator { - visible: missionItem.isSimpleItem - } + MenuItem { + text: qsTr("Show all values") + checkable: true + checked: missionItem.isSimpleItem ? missionItem.rawEdit : false + visible: missionItem.isSimpleItem - MenuItem { - text: qsTr("Show all values") - checkable: true - checked: missionItem.isSimpleItem ? missionItem.rawEdit : false - visible: missionItem.isSimpleItem - - onTriggered: { - if (missionItem.rawEdit) { - if (missionItem.friendlyEditAllowed) { - missionItem.rawEdit = false - } else { - qgcView.showMessage(qsTr("Mission Edit"), qsTr("You have made changes to the mission item which cannot be shown in Simple Mode"), StandardButton.Ok) - } + onTriggered: { + if (missionItem.rawEdit) { + if (missionItem.friendlyEditAllowed) { + missionItem.rawEdit = false } else { - missionItem.rawEdit = true + qgcView.showMessage(qsTr("Mission Edit"), qsTr("You have made changes to the mission item which cannot be shown in Simple Mode"), StandardButton.Ok) } - checked = missionItem.rawEdit + } else { + missionItem.rawEdit = true } + checked = missionItem.rawEdit } } } -- GitLab From 0ad9f79d8f54064235d4842ec4d20006fc6ce5f6 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 13:15:35 -0800 Subject: [PATCH 395/398] ToolStrip auto-determine show/hide elements for fit --- src/FlightDisplay/FlightDisplayViewMap.qml | 1 + src/MissionEditor/MissionEditor.qml | 2 + src/QmlControls/ToolStrip.qml | 48 +++++++++++++++++++--- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index ed30b1510..f10ead043 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -85,6 +85,7 @@ FlightMap { title: qsTr("Fly") z: QGroundControl.zOrderWidgets buttonVisible: [ true, true, _showZoom, _showZoom ] + maxHeight: (_flightVideo.visible ? _flightVideo.y : parent.height) - toolStrip.y // Massive reach across hack property bool _showZoom: !ScreenTools.isShortScreen diff --git a/src/MissionEditor/MissionEditor.qml b/src/MissionEditor/MissionEditor.qml index d8038782a..87d7e1268 100644 --- a/src/MissionEditor/MissionEditor.qml +++ b/src/MissionEditor/MissionEditor.qml @@ -777,6 +777,7 @@ QGCView { rotateImage: [ false, false, _syncDropDownController.syncInProgress, false, false, false, false ] buttonEnabled: [ true, true, !_syncDropDownController.syncInProgress, true, true, true, true ] buttonVisible: [ true, true, true, true, true, _showZoom, _showZoom ] + maxHeight: mapScale.y - toolStrip.y property bool _showZoom: !ScreenTools.isShortScreen @@ -839,6 +840,7 @@ QGCView { } MapScale { + id: mapScale anchors.margins: ScreenTools.defaultFontPixelHeight * (0.66) anchors.bottom: waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom anchors.left: parent.left diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml index 76331e1f5..0df07900f 100644 --- a/src/QmlControls/ToolStrip.qml +++ b/src/QmlControls/ToolStrip.qml @@ -22,17 +22,55 @@ Rectangle { property string title: "Title" property alias model: repeater.model - property var showAlternateIcon - property var rotateImage - property var buttonEnabled - property var buttonVisible + property var showAlternateIcon ///< List of bool values, one for each button in strip - true: show alternate icon, false: show normal icon + property var rotateImage ///< List of bool values, one for each button in strip - true: animation rotation, false: static image + property var buttonEnabled ///< List of bool values, one for each button in strip - true: button enabled, false: button disabled + property var buttonVisible ///< List of bool values, one for each button in strip - true: button visible, false: button invisible + property real maxHeight ///< Maximum height for control, determines whether text is hidden to make control shorter signal clicked(int index, bool checked) readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2 readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 readonly property real _buttonSpacing: ScreenTools.defaultFontPixelWidth - readonly property bool _showOptionalElements: !ScreenTools.isShortScreen + + // All of the following values, connections and function are to support the ability to determine + // whether to show or hide the optional elements on the fly. + + property bool _showOptionalElements: true + property bool _needRecalc: true + + Component.onCompleted: recalcShowOptionalElements() + + onMaxHeightChanged: recalcShowOptionalElements() + + Connections { + target: ScreenTools + + onDefaultFontPixelWidthChanged: recalcShowOptionalElements() + onDefaultFontPixelHeightChanged: recalcShowOptionalElements() + } + + onHeightChanged: { + if (_needRecalc) { + _needRecalc = false + if (maxHeight && height > maxHeight) { + _showOptionalElements = false + } + } + } + + function recalcShowOptionalElements() { + if (_showOptionalElements) { + if (maxHeight && height > maxHeight) { + _showOptionalElements = false + } + } else { + _needRecalc = true + _showOptionalElements = true + } + + } QGCPalette { id: qgcPal } ExclusiveGroup { id: dropButtonsExclusiveGroup } -- GitLab From 7ad8220822c5047d75d182ba181482e97e76046e Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 1 Mar 2017 13:42:41 -0800 Subject: [PATCH 396/398] Change trigger for AutoHide --- src/FlightDisplay/FlightDisplayViewWidgets.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 222ac38fb..fd553fde2 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -200,7 +200,7 @@ Item { interval: 7000 running: true onTriggered: { - if (ScreenTools.isShortScreen) { + if (ScreenTools.isTinyScreen) { _guidedModeBar.state = "Hidden" } } -- GitLab From dc2a7235ed99aa10cbfa5279fb5ce0022b1fa12c Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 1 Mar 2017 15:29:54 -0500 Subject: [PATCH 397/398] Add autopilot version info to parameter file --- src/FactSystem/ParameterManager.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 0f1c5d83e..4d3e73cd6 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -842,6 +842,17 @@ QString ParameterManager::readParametersFromStream(QTextStream& stream) void ParameterManager::writeParametersToStream(QTextStream &stream) { stream << "# Onboard parameters for Vehicle " << _vehicle->id() << "\n"; + stream << "#\n"; + + stream << "# Stack: " << _vehicle->firmwareTypeString() << "\n"; + stream << "# Vehicle: " << _vehicle->vehicleTypeString() << "\n"; + stream << "# Version: " + << _vehicle->firmwareMajorVersion() << "." + << _vehicle->firmwareMinorVersion() << "." + << _vehicle->firmwarePatchVersion() << " " + << _vehicle->firmwareVersionTypeString() << "\n"; + stream << "# Git Revision: " << _vehicle->gitHash() << "\n"; + stream << "#\n"; stream << "# Vehicle-Id Component-Id Name Value Type\n"; -- GitLab From 5fb93f4878ad7d2d363edc4fcb2b86379975ad68 Mon Sep 17 00:00:00 2001 From: Jacob Walser Date: Wed, 1 Mar 2017 16:34:24 -0500 Subject: [PATCH 398/398] Add support for PX4 Firmware git hash --- src/Vehicle/Vehicle.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index c2d265162..9c267d65a 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -675,7 +675,17 @@ void Vehicle::_handleAutopilotVersion(LinkInterface *link, mavlink_message_t& me // Git hash if (autopilotVersion.flight_custom_version[0] != 0) { - _gitHash = QString::fromUtf8((char*)autopilotVersion.flight_custom_version, 8); + // PX4 Firmware stores the first 16 characters of the git hash as binary, with the individual bytes in reverse order + if (px4Firmware()) { + _gitHash = ""; + QByteArray array((char*)autopilotVersion.flight_custom_version, 8); + for (int i = 7; i >= 0; i--) { + _gitHash.append(QString("%1").arg(autopilotVersion.flight_custom_version[i], 2, 16, QChar('0'))); + } + } else { + // APM Firmware stores the first 8 characters of the git hash as an ASCII character string + _gitHash = QString::fromUtf8((char*)autopilotVersion.flight_custom_version, 8); + } emit gitHashChanged(_gitHash); } } -- GitLab