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 6918021849..0065962f19 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 4435feddbe..6cd378e1ff 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 d3d85307e7..324c5af6a4 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 9dfad863f0..c8adcb54b0 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 5c63154bed..fd569b7b5a 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 540c917f3b..1b0080de5f 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 abd618bc38..c249191bdb 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 6918021849..b23823f448 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 0000000000..2b45f8a231 --- /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 0000000000..de41bc1ef6 --- /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 37ac332847..9f0cf1e400 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 8c935cf039..6866b517e7 --- 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 67f947e675..ce0cb02a96 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 b68ea44a40..83682333a9 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 e93ac62981..13a478092f 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 36f37bde1d..094e765e63 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 75f8b0e181..37f8ca580b 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 09480d7bdb..ff154aa869 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 c2b3bfbed2..808d65092f 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 2b45f8a231..9563dd1a4e 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 de41bc1ef6..3ef3dc5850 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 cf022f5932..10ad42fe84 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 9f0cf1e400..b73fc56209 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 ff154aa869..6a5044b6bd 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 808d65092f..372a848e53 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 3e23813897..bf1ce9852a 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 9563dd1a4e..17b1e8ecaf 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 3ef3dc5850..7f5cf85944 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 b73fc56209..c5b34c9762 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 17b1e8ecaf..655dd37ad9 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 7f5cf85944..aabe10413b 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 c5b34c9762..f7ea0101aa 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 c8adcb54b0..a070e4a2d0 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 655dd37ad9..56270fb3ea 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 aabe10413b..701c69e2d2 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 f7ea0101aa..d0b2426c75 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 56270fb3ea..bc9e14daa6 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 701c69e2d2..0ea7c8b991 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 d0b2426c75..836e902fd7 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 bc9e14daa6..7da59ae9a7 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 836e902fd7..3beaa60679 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 7da59ae9a7..5d6a95863c 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 0ea7c8b991..b0c69fbb5d 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 6a5044b6bd..f4ac52a02d 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 5d6a95863c..ccb067f69b 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 b0c69fbb5d..b39d2528c8 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 f4ac52a02d..c9ed3a839b 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 372a848e53..0f62435199 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 3beaa60679..b98e2b5c39 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 b98e2b5c39..35afc392d4 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 703a8361fd..0ac994c112 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 33545d0f97..cb827796f4 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 zcmeAS@N?(olHy`uVBq!ia0y~yU{GaXU{K~@V_;wqK7Lq^fq{Xuz$3Dlfq`2Xgc%uT z&5>YWV2~_vjVKAuPb(=;EJ|f?Ovz75Rq)JBOiv9;O-!jQJeg|4z`)q->EaktaqGOg`*W5;&(Ma4u;IV3Lh!QjM`PSb0E5Pj&_$N5yMicY(XX5&zGet32>- z4`W&5B8$!~*QOdK_RM87A?h z<4nHFbR~9*fz&3Ss$6E#p4@DPQ{l6v)7VxWbP}>mT()$ht6;;lV;f@(g5Nf73{81s zDt>0lr@r$7lI15Elb6ceuS;EXFoi>Ps?ctUy1vb z`=C5|zQ_0VQ}l)RKRekQxgyB1@W!9D^BEXc%rT#>>fZaNpiwSa+vfx*+&&t;uc GLK6TE0Lsn) diff --git a/src/AnalyzeView/GeoTagIcon.svg b/src/AnalyzeView/GeoTagIcon.svg new file mode 100644 index 0000000000..ba15cb0ad2 --- /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 zcmeAS@N?(olHy`uVBq!ia0y~yU{GaXU{K~@V_;wqK7Lq^fq{Xuz$3Dlfq`2Xgc%uT z&5>YWV2~_vjVKAuPb(=;EJ|f?Ovz75Rq)JBOiv9;O-!jQJeg|4z`)q->EaktaqGOg`*W5;&(Ma4u;IV3Lh!QjM`PSb0E5Pj&_$N5yMicY(XX5&zGet32>- z4`W&5B8$!~*QOdK_RM87A?h z<4nHFbR~9*fz&3Ss$6E#p4@DPQ{l6v)7VxWbP}>mT()$ht6;;lV;f@(g5Nf73{81s zDt>0lr@r$7lI15Elb6ceuS;EXFoi>Ps?ctUy1vb z`=C5|zQ_0VQ}l)RKRekQxgyB1@W!9D^BEXc%rT#>>fZaNpiwSa+vfx*+&&t;uc GLK6TE0Lsn) diff --git a/src/AnalyzeView/LogDownloadIcon.svg b/src/AnalyzeView/LogDownloadIcon.svg new file mode 100644 index 0000000000..a8d9b0b7a1 --- /dev/null +++ b/src/AnalyzeView/LogDownloadIcon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/src/QmlControls/SubMenuButton.qml b/src/QmlControls/SubMenuButton.qml index 640458cebe..6698d27f1f 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 3088987155..d96303a5ef 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 ccb067f69b..85074c9b65 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 f6ce5a4505..e807c815d7 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 35afc392d4..466704b22a 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 a070e4a2d0..bf438801aa 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 fd569b7b5a..1d80469cdf 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 1b0080de5f..cb36a06e6d 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 b23823f448..473f7a72ff 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 85074c9b65..ea153d396c 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 b39d2528c8..f0319502b9 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 466704b22a..8cec2eb598 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 bf1ce9852a..4d9be09c4c 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 bf438801aa..c919ffda60 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 0000000000..083aff242c --- /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 0000000000..9a29da3806 --- /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 f9314c31eb..820be6b3d4 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 a2820fa35f..ef870b1741 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 c919ffda60..8d2ea11d05 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 083aff242c..4ef943e3ba 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 820be6b3d4..a367e654cd 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 ef870b1741..a9c67f68cc 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 a7d8407108..a4881a7f8b 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 f83818fb81..b2220c18a7 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 9dad9bbccc..13dfcb1996 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 34dea1a2fb..95c1a56000 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 dd054d3260..c370749550 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 7b019f73be..0590c11efe 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 fe98aaeb93..33fe15912e 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 8061f8e574..76eb2a8769 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 dbf9a54440..64d78bc135 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 d5f14cf3f9..654b933c64 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 24d8d55347..331cfe5eb2 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 e924c2531f..0b3bfeb019 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 7ac1af8be5..4585d93e93 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 2698fc5df5..ba92da6a14 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 95c1a56000..550cf2461a 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 69dd1d0bf7..96cdbe10d6 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 7e5128d4e7..0a31ffb250 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 98548effde..a5352549c5 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 0000000000..4b9b116634 --- /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 8bc71ce14b..7617530325 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 c9ed3a839b..a3eaa1de60 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 0f62435199..6dbabf76e0 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 37f8ca580b..3877ade682 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 f58cd8766f..41756cd151 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 75e038a5ee..2d4bd7fc6d 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 17eb6fefb7..6704f871f5 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 db0c98db95..51941c9a65 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 ed8cefe4bb..3b039e22e3 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 934774eeff..6510bfd773 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 fcedf7fd41..9777157df1 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 a367e654cd..5527888a58 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 affae197ec..12f8103225 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 decd6d79cd..2d631ecec5 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 550cf2461a..007234becd 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 f0da718fa2..480d3213d3 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 0590c11efe..a2562dd271 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 33fe15912e..8002de668c 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 64d78bc135..d331c40e81 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 654b933c64..ae5b8c6dc0 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 20294ed5d8..eb2e8f6b67 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 fa0ce387b4..472dd453fc 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 6a4602abeb..7e3ce2171f 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 35a91d71e1..f6e94581c4 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 03cfd07ab6..e8bd7a5970 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 146e5d7f31..e9a0bff65f 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 77c9ea238f..765058fd62 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 e5933b5541..0efdf4e6ac 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 a6f69d44fe..0f2e4c6261 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 bd3c056cf3..2b6062b7c6 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 0d8f21c674..63c8ba31c9 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 d66031e582..7791d097cf 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 2d3b7f2df9..96018f1ad3 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 4ef943e3ba..4e4087ffa0 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 5527888a58..9c49c4f596 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 d331c40e81..fc20278c68 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 b6178a6e16..1e19e2e890 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 16eafae338..b9b1de3db4 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 139955c62d..79d3353094 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 f286e54eb4..e27729f3fd 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 2f46da4b4d..dc91aad2df 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 76eb2a8769..f25aaf3cc0 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 80dc758c48..4a5ac20672 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 79d3353094..53ea7a1ae9 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 ea153d396c..72e2d6e6e1 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 a3eaa1de60..11572f8c10 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 007234becd..9641df47a7 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 f7eae40a5a..956115e605 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 96cba71db2..e4ebade92f 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 0a31ffb250..8a52ea4f80 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 0ac994c112..b8ce22efee 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 0000000000..07622b617c --- /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 048b045dfa..13a3f3b8f8 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 cce214a04f..e47938565d 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 e27729f3fd..6c1197cac1 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 dc91aad2df..18ea33e4e4 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 c860370677..84dd088118 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 50a6d298ce..5001be8e38 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 99b8f232f6..19fb78e0be 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 e10d5ea855..4a80743186 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 8d2ea11d05..d9902929f7 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 3f488f2171..cc298091a8 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 9b8b702455..618e2a7ab7 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 970d9fced4..58410af772 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 b45391758e..0000000000 --- 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 4927cfbc21..0000000000 --- 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 bfe76f3849..206b54bcd5 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 fe7b611d20..972d243fff 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 0f2e4c6261..46a27dbef6 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 2048101a02..d656a998c5 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 e8044064e7..57818a6de2 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 af9d98d3c5..0b96501b34 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 8f0bebfd52..65ae2e7f12 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 6c1197cac1..1adfafe65d 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 18ea33e4e4..1064262772 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 c47d8bed32..ae362f2780 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 374eebba5a..7d732169f2 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 526df49bc4..7b3d2ced12 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 f9ca107ca9..7306599c4e 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 0000000000..e82523e8d9 --- /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 0000000000..7ca1e0f031 --- /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 bbc3309695..5a19fc7771 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 6038748b66..640b491c83 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 b7ade237bb..b2ec571cdb 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 efdbb07c54..0e5f1a4f04 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 6510bfd773..9644e8a830 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 47f963e6d5..c4a340f2d9 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 0000000000..4b742f90bf --- /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 0000000000..254cf7b644 --- /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 196e3223e7..1bb1e18807 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 137c5806bf..c9608f75f5 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 a5c9e76435..ddb14cca0b 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 9e82eb1669..59ff5db458 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 8ecc447432..7cc2d81115 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 a3628bc6d5..2c77673c50 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 1d80469cdf..3e625d66a1 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 cb36a06e6d..8948fda3a0 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 b9b1de3db4..6fe546c04c 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 53ea7a1ae9..9bac096f60 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 5c6a8e3ee5..8dc19950f2 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 a3eaa1de60..7b2f2815eb 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 6dbabf76e0..1ee3d62f69 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 5dbec5851f..4509928d71 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 5a582ce66f..eb8319cdf0 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 4d9be09c4c..49c1a1a38e 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 4a78d7f990..346de17fef 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 0000000000..eef0ae3f9c --- /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 0000000000..79edce901b --- /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 0000000000..6bd0fa859b --- /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 0000000000..f285446176 --- /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 0000000000..5e59c21692 --- /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 0000000000..8c0ffc0f65 --- /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 0000000000..a25e792214 --- /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 d9902929f7..3652d75294 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 96cdbe10d6..c1938e2971 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 cd421ad5ef..5dc7788a42 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 c9608f75f5..278c0a06c6 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 ddb14cca0b..6a9dbf9664 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 0000000000..668d46204d --- /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 668d46204d..0000000000 --- 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 4a80743186..2fb1d80163 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 b8ce22efee..51a35405d4 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 3652d75294..28a09d033a 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 0000000000..fbca7c7819 --- /dev/null +++ b/resources/waves.svg @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 85faa00b0e..c739d322c1 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 c370749550..094a3fa25e 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 02c325fabf..73a200d778 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 278c0a06c6..9847f4b8e5 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 c249191bdb..90a67453b3 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 8f3ef8777c..d566425879 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 4537f0f357..90ad8003d1 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 77b6781790..89389ee405 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 367051b73b..915c20cda6 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 0158cc4a5d..59fa8f36ff 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 7aef4cad9f..34a0320fd1 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 330572033e..23cddcbdf4 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 eb8319cdf0..80000cd33c 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 840bf7c3aa..e313e87aa0 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 6cd378e1ff..cd40b19277 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 3f7c31d381..a8851c7e28 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 e3242e1c3f..22267f376b 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 324c5af6a4..ff6fbd4436 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 84dd088118..41c6cdb40c 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 73a200d778..86582d05d4 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 9641df47a7..6b3faeaafb 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 6bd0fa859b..0000000000 --- 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 f285446176..0000000000 --- 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 5e59c21692..0000000000 --- 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 28a09d033a..f056999455 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 c739d322c1..3eb92fd5e0 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 86582d05d4..a68e880aa9 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 9847f4b8e5..578634b6d3 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 6a9dbf9664..002c88e39d 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 3e625d66a1..213c319969 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 8948fda3a0..607ab0a665 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 90a67453b3..2a8168db32 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 d566425879..b0b01d0921 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 9bac096f60..5baa4fab65 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 89389ee405..54e0e0ffca 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 0000000000..605b7ef1e6 --- /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 0000000000..9d1a1afa03 --- /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 79edce901b..3b598fe3df 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 0000000000..b329472ed0 --- /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 0000000000..6fbe9dbe33 --- /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 0000000000..682364cb20 --- /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 e807c815d7..337aac725d 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 ff6fbd4436..1d81a84a18 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 41c6cdb40c..2d5670039a 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 3eb92fd5e0..85faa00b0e 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 213c319969..24408eafd2 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 605b7ef1e6..a530547ddc 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 b329472ed0..e1e9135120 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 24408eafd2..28e1afbe22 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 a04b37ba7e..5334f916da 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 2d5670039a..574b323209 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 ce0cb02a96..a5e7bd16fe 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 9777157df1..a12ed9840c 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 f056999455..85adc4fabd 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 0000000000..5e8a492533 --- /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 0000000000..0e82f5b623 --- /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 80000cd33c..4dec260fa0 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 c1938e2971..4801189b02 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 6b3faeaafb..3c82b84184 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 9777157df1..b90c796fba 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 f851faaf9a..de919c5511 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 48f5454bab..1a8503ecff 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 b1e72301d5..050aa21a54 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 52b1b0c44e..80a7e03467 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 0000000000..3c5205a9ea --- /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 91928a84fe..99b48a41df 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 5334f916da..8392f8abe5 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 8cec2eb598..b33f1e7187 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 c887663043..988ee88630 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 bb87f6985f..a0dcf688a5 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 c887663043..823fc0cb6c 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 efd4d03382..ca7e7a350b 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 a68e880aa9..d920acc4d6 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 520aa48b27..594a028f00 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 0faa58864b..6069a99a15 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 ae362f2780..9ed18f0cde 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 7d732169f2..374eebba5a 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 1adfafe65d..2c8bf1bbe6 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 1943f30e99..4224c88d6f 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 1e19e2e890..03bf70e4e9 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 2d631ecec5..69cc038a59 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 3c82b84184..fadd6595fc 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 30032c2f26..929fe9e5e3 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 9ed804ed2b..93bc9b8de2 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 472dd453fc..c41aed3455 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 34a0320fd1..fb9056cbc1 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 42939faee2..88dd8a67be 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 206b54bcd5..c454a61209 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 d0b82db400..64c831fb15 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 d656a998c5..bbcb97b471 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 d9bc3b4e8b..dd9350fa6e 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 9644e8a830..16d04d301d 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 72e2d6e6e1..5014150e07 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 f0319502b9..ae86041215 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 90ad8003d1..76ec74bd4e 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 1ee3d62f69..2501a7acd6 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 cfefb1ae9a..b8791a5dc8 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 81f018e803..514e7a6922 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 e313e87aa0..6d1a40f1a0 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 1e93a06702..ad9b946464 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 10ad42fe84..8ae866f9b4 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 c454a61209..4ba9430c75 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 64c831fb15..afba9939c4 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 5014150e07..567b7d935d 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 ae86041215..dd1051542a 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 2501a7acd6..fd19666a2c 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 76ec74bd4e..79b61d2ad0 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 85adc4fabd..c0cd800d16 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 0000000000..850bcc9800 --- /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 0000000000..28624573ba --- /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 4509928d71..411287c4d1 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 d0c70f8a76..924ae4024c 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 2c8bf1bbe6..2cec90baa0 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 a2562dd271..f65fb476ce 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 8002de668c..0540c3e68e 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 c7b2251eaf..063b428baa 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 fbeed77746..b29cf1038f 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 69cc038a59..cce975b99c 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 fadd6595fc..c7b8bfd148 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 ae5b8c6dc0..34c87bdad6 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 eb2e8f6b67..c88de86dd6 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 b915bc4b27..6a5eecb369 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 90ad8003d1..be65e93039 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 68ea784d7e..b1bf782aff 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 4ba9430c75..1e7c937d14 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 afba9939c4..1093eb2676 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 2cec90baa0..925621b655 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 82afb84899..2c7cb24d60 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 fd19666a2c..1f8eb3cb39 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 6d1a40f1a0..b7ada3e587 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 fb9056cbc1..530b9b05ca 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 106267f332..f7d6c8ac9d 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 4224c88d6f..9f54ee8125 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 574b323209..5054eb8e79 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 7a815a3035..73c339325b 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 82afb84899..6f7bf35193 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 6c0740bc7c..df7b8afa37 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 1f8eb3cb39..2d6bdcd4e6 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 938e3dd37e..db76da950c 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 b7ada3e587..a7c74fab34 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 ad9b946464..4dad4d5fda 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 8ae866f9b4..8ce2460b97 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 dd9350fa6e..dee9bc7892 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 094a3fa25e..7492de0d02 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 6c0740bc7c..d06a10603b 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 a39be4d577..3f546a30ed 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 6a5eecb369..f6ca0c9b05 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 844ce29d7c..3ffb4672f0 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 a7c74fab34..55a2c2ee7d 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 567b7d935d..11effa34c5 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 dd1051542a..a64b8998f7 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 b33f1e7187..4de149c26c 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 11effa34c5..94e9b3467b 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 51a35405d4..96751a0132 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 0000000000..7081555714 --- /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 a16b2782ea..2a3aff1e5d 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 a46a506911..d56d7c2cfe 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 61f1de095b..736120dcb9 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 acbb85a46b..1a6a5b52a3 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 ff594e27cd..11f336e6a1 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 dc6b84f96f..61d14b870f 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 ba6ca0414c..339e1f84f7 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 cac806a149..372acfa436 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 2e1ec5bdcb..066b12356d 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 7081555714..433119885f 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 96751a0132..c8597ebdbf 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 0000000000..3f71ec00d6 --- /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 0000000000..991d6185e2 --- /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 4bc9a3a498..8940e563d6 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 3ffb4672f0..8936de6093 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 3ffb4672f0..a91eb23ed6 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 202d5edcd7..4479824747 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 1d81a84a18..d1975d1895 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 530b9b05ca..8a400a9552 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 8936de6093..a632ceb9cd 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 2d6bdcd4e6..df0d0036c9 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 850bcc9800..de4a8c7ad7 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 c0cd800d16..ce9cd426e2 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 2a8168db32..27966dd365 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 5baa4fab65..eae33e12bf 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 52a7183f9d..4b8387c038 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 2d6bdcd4e6..aadbf51b9e 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 59fa8f36ff..af6b79b4b3 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 0000000000..a65d3fb315 --- /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 ecb8947406..63ce682571 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 8a400a9552..c4482009d7 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 23cddcbdf4..f75fc50f14 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 4a15dd8417..d6b818467c 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 923dfb6373..2b78756266 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 49c1a1a38e..4fee754823 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 411287c4d1..f8dcb64c1a 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 cdb25373f8..15e8a5eebd 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 3e345f682a..9e600db18d 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 82b0edee71..be46a06e93 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 a57cb61d87..d540f64449 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 c67606f583..9eb2827feb 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 627c6d282a..51faba4ad3 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 1c1c977a90..df0c58e736 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 bc5af5bb3a..77f4e8b3c5 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 f1a1835cef..996951eaf4 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 196da6ef02..e34d993da7 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 22267f376b..55f26ec585 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 f8dcb64c1a..6b946f30f1 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 0f31a6e155..52e5de7fb6 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 94a9ed024d..0000000000 --- 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 a5dbbfe0f3..0000000000 --- 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 ef5923b568..0000000000 --- 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 692bd7b165..0000000000 --- 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 57b7f6a0c5..0000000000 --- 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 9990d7f2e3..0000000000 --- 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 7eaa6c2701..0000000000 --- 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 d7250a3cc4..0000000000 --- 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 1d046d7921..0000000000 --- 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 1883c3262e..0000000000 --- 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 2df9136221..0000000000 --- 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 45063f7736..0000000000 --- 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 e2c495c41a..0000000000 --- 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 9774f40817..0000000000 --- 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 7eaa6c2701..0000000000 --- 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 32f085b268..0000000000 --- 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 3da3c6f217..0000000000 --- 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 1859405aa7..0000000000 --- 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 31baf1c73b..0000000000 --- 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 5b69945a51..0000000000 --- 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 158f1857c9..0000000000 --- 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 16accc5713..0000000000 --- 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 801cbfaf90..0000000000 --- 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 5a9b7d948c..0000000000 --- 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 zcmeZ`n!v!!z`(%5z`*eTKLf)K1_*F~P^1JvLws4+R+`;H`RxubKuV$87Qq2oiXI7mA7N1+)4W`#rbA#!%PP4xHLn;L7z7v?7&b711O&irCI$vp z1~4B^Gb~_&1R4Va0~3P-BUlqiA4mh328%FUc*w}Gl$C+O<|ZRU0La7wCWZu1V1wAm zxPgg*Lkw8}JKN$4BLhm@Y+z*QfH)1S2ECHhiV_9}28&q`CxZQoX1@XhLxNsWN@7VO z14F4f)bADy3=9iU4R&B)aL|J&V{j>AU|{&b$iNW7z`$?=L)`(rqT-@t1_p><3z!%f z*cccXbeK@w2sW=MH77rrfk8}$fq|iciGe|mfq}sYL!A?f#!(K{LLjvJP2(FLMh1qk z@ZP;&{{8>I)0u&RVaJ&>XU=?Hy--1)Nx=AMr|X{=Q}`Je0$#|0$PAkgi~_x_KmPsy zAMnC@2?Im(5rOV6SqvEgAn{(1cwiR8f69q{6$FayKuSJrP{I4xmdh&>E)Is${#*YYrwvcEX=|NsBj zvdy(Tj3sQ%wLDCvg3Y!qAcIOdx?MRIgP1!&>Dci*K}`Xiv*mnX10lq2ZBD9Z+LfXrUZz|eZ21Z13V z_m^&0nSgFziNNkqk)SMwZdZYTZr>k)|3w)fit`YP6}rE4hyDoqFUkXv3PwmloCOKv z4}uH=FQOKM!Z@z^2#@uf7g~!Up(?@9{U7ARAA$@5KmPy!4=M&&gct-evOzutxftx< z#y2udko556@BjZh+p(pG?k^cB9~cEPdO%U$`UR8}{)<*@WE23|_~pfpMGOqxUovKZ zlxzVh>3#9{|NlT_B~!sldZ&UUg8qxDK>Sp{h=C!Xw-qE9kj0qA(Ax_#J1_%OIJ}tf z@BjbqFBv;Pral0fIu#@tglwuk*whRbMg|6`Dc!uFm<{|dssOP;7OVo@UUq!;NJ#!0sstkj-C^6W4!H4oGzUoe%ae%O^&G42e%zBl{LuNiQU4RV0a0V{!~AynOsV5y84knR?cZip4gy5qn~u)5qCER`_@qUvB#8Bf7RKkRI zzX|Jh(d+`Y&n#iH^K^pu)0Ls{6}- zQ5}d-^gIRz+o#M74B#R(u=`6EW49|u^8pT!-sXb>pwhORDd4}T21LC!#0VuMBP8ZA zFf{*AE9L6;jU4&~@};)t_;^WytlXbphHy((DUYXY%s-&_p$uAYnC zy(|#-s-U~K8e#;pdo$+Zb}tLaz3xcvZLZ~DC}n!h)9uO;@M0}U=Mfg`H!oDcN>~s@ zJt#WB_30ZKaDCtD`lQqKMW^eVPS+2eu3tJ`e{{P3K{NqC#+In`y1oc_!89LK>9chE zzUU6+=;Y$x?)o9~zzb#uhHhVuPDd7a{+9sd|2d#!1gYyM%>n6?v3~Pn`W$cyI>f=y z>j|pf8G1l!|Ns9FYC6x5U=S$D%uP*#X} z-#^WjAq*vj-JyS)t3nt`B)Y$3{qJ`D)9n!u_+Qip65Z!#Gcdex0A&YI>J4Zd?C*Pa-RasL@`k94>ivMVqX1h28M3eFVKp+5X5^L zdq@NkX`$V3vKV0P1W3c;#f^Xe|69L#;XE6fX2B5)B|^L3fN~Z8HeZgw7y5c2`#G%N zybuSg}j3j0_CTwmne0p^8&sj)2%5`ls6|AON0z`e!mQWNZOh_5?(H z0NKs)jZq-u3&?Iznt9C%u^XBSL3YE^Ak=Peklm9Q7#Lu_OocfVVkOL}&?GAZva$eV zM+b=b0lL!N3sL3@&&=q|*A$i>4Wv2`;qz4VL%? zS;$br-OUSPSo`vnRKxt9iP!H6Acwf}1Z0SOXB5bA0TH0mC!+wwiUIi_6yy~wpdgol z842<~T9DtE4hn6aPEnBk&5-0)!nF_-0M@P?CDpJv&xDx)i3yk~(3qG6HiaW7;|9oj zmLH4);D&340f;5@0~#Bzm?5sg9UERCb9^~E!4Uurax;(^B*>v90ffR9p2WHK$uy9k zIf61~fSh*(MDQ{)Fl5{Su^fIvU28+owR1q`fT~@I15ykEQ0KOT1fQZ84v_vAM*I`! zUK5a!p&UUOAwL-fG8#aHD9F7lK&&Y+_x3Om3f(tT!S&j4M+s1619k6pkRUGiLW?8* zeXI=5OaiS3O0BwiEj};`G#}&+%nC00C;;VLt{^|A=xcsOy^iQ`d&%p;Q-M$?B+glhHFfcGEyZ-rpL;Aq? z8=y`(tUnxxYCf#`0QGTS)c*hfzx6;V#9&vj!4En?G|$0@EZw0XgSi$V8w~MU#}r6& zfQJFq{h;P0-2JRna)0w14zzwcxYG@-Oskw?^KXP zP`9f@K)0_*Aad(FFeK=OAE<$RL;^CR;G@JK@PcD9s2vv%X~>-;|_b3lOf|F9LfkcizuVI89Y7)@+jW;=yer9b1x5) zdlUK410UjEaE{4_yLk)9%?l?YxjAVfQZT1Xgak9h&3lmCd;-bMkoHtSH!o;xEbs*{ z0|SHgn->}rK{YF=&^b6E$#)UIJ93b3hu#&=$hB2@DLNj>)hhu-)E7D6P$HJm@t0BHMFxZ$@SQ6PikA0w!38uY^9&;S3;MrJF`X_)% zAPC;*lmdyna#*v1(nzThB&~E$1Qk@Bq98Ho2y3Zm_m_nrtpWckg!5as9t>x3>hh)z?lIe;z41x1jJec3M)_tW!W!CSov~* zBN`M|u<(Z3%+|-i0PRn{T+RSuUtwinV8~+3^6UnMwu>StIq`J5e(4VVa@+~r7U*{U z(hcd@eCgx@^>R8u&JN5t0J1jpOAxpNo^1%yC~(~M2`Irf*FIq=RqFP=(j9uG`4wYl z=#|!!B|_bo1-U!YL;PyYq@unV-H6YFxP%n|A+oK}zzo-Kw_7ryF! zdv{<>5M2Go9;kX(j&6?{M6dTINE{S^383%*wdE2(wt~9SB~sm?U%H)4I$huN`hE#~ z0h(It4t>)Z`sMWl@VHM7NF`{HCorQk^a)r9Gzi83_OWcY@00F{ApdlR-syFH67V91 z5#-4`oxV?A-vy0-b96h!Ad+JM$RS_ArlkbI6R8zM6y)fVK)9$1NHmnA`H)Z03pS9c zt{lx43?&vI-Ju-ajMh#erNSVNV9<+ovp|u?(&>8PHMDHwKp1qp8x%j=M)m~A_ivSnb(rt5Eq9aT-*UR@(fZj3e9i%-3$!9 zpl&&6nyuUQ52SXxqRAleqP!biQ^YkNF|mI0!mS%zI~-zR=>88H^SGnQAn*k`#{WZ; zLEs6L{Y8^Opdh=1Avr$}TqZ;6i)IT@8$ulF`^s*JM_f5TV;JBHG7l6he zKxqp+Ru}k!ixI5iC|CuepoerfK;sK&>a`&1r{YzQ(x_YksVS1dI>4<^sNsEzT|xbJ zhKwbkobLK1;6)2$G^^Y9%W-g-3(jRL%)FQQ0o_(pd`r<@ZtivjldF+aRyW-e*qDHKm=&kI78q+Xq^2& zs73K5@WsC`pmJIvL+3xEzzZ&jK5%&(0rJmli40JOAd3Z*`o467^4tkIQWg zbg<6%3rd4Vse^$bBLie%2#9zJswXypSUW)WgF>+mY`+A|GpZS9K++FD#50iUFCf+* zkm}GcK`#QJsv)6KqW$9k$N&EWG6Wcy1Tqvr#B-1y6A;UWfk~hnR36HJ^~k^+to_3J z6G%@0NKpcacmdK=0%FyG^nhIT;xi~zWMHmx$(R6=z5ya$f>gf&vA%#*gIu)>tXcvV z5}>*c77oz5PO2T$3i)!}0bFW>l8hs`2nL5Vq9t0SYrvu;9z7D$S?pA79ipq z$TvP9RtO_Byxx2Q`CTC+gON$##Sd5s3r%963fT8ccR+}BAgHJXCDo9?7aym9i?2@C z2jHR>R2_vNstj4MgCBq^r~kh|lT|Mlaf7Ql^t|%9738%$V6WZjbV_Niy~DuYdIY=I zY&sxbdkk@6K*j`+H+F!C10Vv_70pxO%Up}4Gu879eSlRu%x;63Il)ZEs%$y zN!$gN2CZLsgA$QI(2Gwqz@Z(GA;H8Xkl_F#JU|3969Yp=42YGTS};k;1z2#L!I zCMJOwIuLzu1Hr-k1yOB&=>}Dss*}N{gDPThwfP0q>ws73fi0jIx&W#+zjS-VAgXAK z7LW%)MmnV+qF$+mfdL%v>_5TrzKRnvqV?iCcqE3U`2b@8*rC&TK<1tS2gI4qK%eH? zGYtH#put&Ku0je3r#1$LZh@c|FQTm;7j2h5?Ru(X;33lwPM9JSD0+XOCbx;=aXGX$8K1Tu6$gcvAL z96&4&W+qVa9rS|fJt$CQV1{8Sx01j{ftnp(0$&(&fV}*tx%LkOe~SR9JqN9LQeh@w zb&gyUC|QCmo&X}GK<-!qVyywW1C&Rq-htc!uLD5t02Lviz8SRqywwN_7)a@64GJAs zjC7kj0UBLLutnF*Mv&W&U`AI1$Yf9tqZ>KhW>iVVmfKR{+vv2?ZgHyKd zH*nNlU<0M>FUOle9THG;u-o-Zm#;_%Xq=4!>|%*d&}chDx9=DJeG{iJFfg=U;%~_X z1*7Yi<{dLYML&N_EQk-PwT^LtW;OX+d_V$_aOQ7WiBvG8!cqk!&|v`v?K-b)U|`5N z01D+NAVM1y=s!R#1{P>B&-xY;=n%8PU1v~f4w`9$26}1(*h`>t5#F?hlo5>m(6By$ zEv$7LKw*6VGpt2G24aNuzj|*opZfCuKe%N00`D_?=@txnQPl@^#vVwY z;Y&ACpJ91D$Q64)eYY2#U%+vs!wSwA#~VQ1o&WzqD?Y$2S>G>#84B490wB(RQ6EUV zEgh_X2RQ0>bUIZu*Y058Z#4l$2WG=-11M!M1iT0bXIPei3=tMk(F`IyK!gJ*{9{0@ z6j=DTz6OQALPiA(lfa9Kkg^7rzj4$>UA<8MZUF~7D9Vwl*==*FiVfhpPv|o^*tdcvi$M)zP}&d7C|HRW(tm3~4qpQ@-E~c;Q%!U28r&i621-K= z0WaQ81cyaH#u8AdUH}m{Km@4Ap7936`T`4S=~s}D=3r$Kc%cr_Mq3Xm^I3K~HQphW=wVm!Qtfg!_$l}R8Y0YoH% zQgjK3Rl^ExY;1l33TJ%9xNr>v!^<2{#shVlKyCuJaf!13dNl(>#srXsYd}Og$o>N$ z)(M#X#sux3Tn(Bo2K53NN<*+Tt&6J}7#4$u-@t>jydV~02qvr=TpU8i>kz|q(8AK9 znt|bUEH-^|AbsFLzRu7$-L7w%cYsFd7)toMecwQb07Kure%9^!rrY;RX9q~K`%5=$ z01;~Z%_?xw1?mHq;4k^tS1~YT+yMp52M|#K3MUpeCV>ndHfYJu{Tvcc&|n8ux1h2C zspQXw*ajK;xM0N~@S>&)HuMo={pN)?NFKaO0=kyuh81G$va{)yJs5j)0#SAKeLCgc-5gwnw|Dq|7gnY9S6!;vSOrT*&i~szs zpfv-q;aq3fz!0P=1dT$0JOix?=T$N=WSjsk(R%QSQ6S48gW)r1&A}(=5c1!rkkD|2 zj1fC#WPqfXd}b8LGR)WjVoQJ*lW+vSxCB*-XZ#oB%rD>(D5Nnn8Hgxo3>9h2j13|R zaxK!B*@p_SN5Nxemq1nYiT^JZL6y#zUe_C-AxrR5tprG@90n_?2)9w49`6Oef z?}P5p2hETEc7{Iac74$N;CH9%1CZ3i?=Y$JFsc0zsm6!(|NsAo$TmLu|L^~QhyvCB zfB*l7Df#~!Ny&GFlJiJP_JfribA82e%=ImE>}=eX+u2nFirF&uXVjU|Eg?ttr!fatwa4ldppLE#HpZ59F%-dhfuSAZne z7Fz~^7w5|%i4`())?W@P%YCfhyqE&g51ye1HPCu&83b^K2>v~@7gta=rE#cBQSSoacr9z7r3qeuK5b(kSQho8%N%K|cHfE=_2L~H@M&_3e;h|tbJ$YMsQ3hbQkP-72G{MXf@S+GTRVtOmoRRRCQ2?s{#a?iGho|*G3DhkhJ6@Y)F=wQJ zR6*UL+71qs-WJxcptadA+R=5L0!?l(Lk-Qy09gteX8sH63BZkB%D}*Ito0|zD^Op& zFa{e0S|iZe2qT?z3x+z0toq)m3IG592Ng^KFM_XvR$i1ay?y~J0y05ygS2uHIynffh(wD)MY}*y zh7D*=Hv&X}M*A~zK&%qb6fbDvEcZSrr^&#K1l7) zpclCi{onv}{SfdX7QzQRgRk3{C-WqzK=1VZ@|yj)>z4@^m>GIQKS0b=f?M^!G!f=u z7g!itzlZ`QABKPzjBOBqW^{l;Y6*x~10o)QCddzfSSLWC2TJ|=_dubika6cXqreO6 zRY?)v3}Clf=j?+5T&jTcuLVetkEOlWJy1?E2M z7rda{dxgG##E z7YzKZ>ihuvJ>1=o&ehO$>7Ey@Ztae{~Qbq z3>t0>0-(YKv~EYujX?mE&p>PyH_*PT5{8m|NDCS~AEg6oKVR9&jlHA;&rE^#K0OG? z0#7M}mhX$(qK36w}NUnq&)#FdC+D0{H<*apbW~xP|A7S^$MtQ_*$;pm8Y8{^CYOe?hfVY zc4C>i55)fdkNHHW?-kJK(iKpq=??Tjth3mZ3(5pH8f$MbF)}ce8bWs}fE%FD)?$Ay zs1XTj6gYVx#yu)?L2>$8^)+kriT|(VKwWzdh{@1t;fP!YhLQTz`PJsda}!mw-c7gap3GWdM2k&vDlupjI?!EiQPi+lm}; z)qD61sJ_Hvo=7LmJOR*>a?lVjN8k$u?1n|d4TJUPJV5Dj$4=0Sq8*^A;m=4r7eR}i z(3T9jfI8mbB|{2qOad=VA>4o$k>IvD2Pk1>cz`;o0c=djONJh0gQDf7GZ!dz2rz)Q z5ek6Q2x$13;Wck_tpF2f(}4hJuWNIy09%PzbFBbJi9mC$09OfDbFBb3f9o_*G3qPQ ze2}HnSEM^sq}%Baq+@gNr$pmJP`TFu+E(%4;4gv3M__K~%Wl^P2Y+y=f_bj5x_uwO z6u#~b6=^=i(itk!?JCmk@dwg#I`~Thru7Y2s{mB%+iu?n2Y+(FbieBkeE?JV{&hxk ztpIBY4``t%*k_!jZr!dT%?DUIT}8TmMY;q3K>A_`m%Ko7$;)G|uNYu`tz)il8IHNW zV>ssep5e6sxUL9&5%l5$xX=b=-2dPnHE6;H+8Sxk0+ogw%~c5u{4G|X8Vb}CP3ZIm z?c`wKZvk~Wn`;G_OZB=#e{?&sSi1_8Dns)HXrc=`FyR0)#`VX`1sn_vFRy@eu>eb{ zZMW->ZVwh~Ux891{%xT=-L5}CD`2fJf{h4;E-8T;@i!ARw(;X-EXat75F;2%t-5`G zbO*9nhYFPH^KWzI>Gu7>zb%l%`cSC?DDQJXW}l!&?1CC0#lgVvIuPVwR{?7$mQpwV zZN5C+p+ESyIdNE@C^gUG==S~5e2_D+7Zf^^!R-p3)&r%W3=i@?D14wsrbCT9&JGHv zAI()JpamOzV2_z}`u=!%6|_e&^hamt7fknPWil{iS%6m?#(n}-=|4dFd<8l^Siq@N z2%JhmG0z2#dG1nPkfWKJPcU}-{^(?ca31_c=KMzHe1~zmTzNX2K)&i0Y<_gV+x17M zKo&=@?+Z{MG#}y&dg0pyDw9A-0DZQ#H3L*~+yJ}mMyH1YC>Lfif`(w=#d{K1$rX@u zT(5LGsbEoJ4_0yk6kDMeIs-MpN*GW~kpU|?1GeZ)r-u%xs;CNK;BOHIO`5n0bh@7C z_C3-3lCjhGM04#4F8=l&&`5pgiT^57j|4Laz*+%;GNSNJ_p(W4|4rmVwG!Hqa zgHvudNAp9*?>D7SHa|1~?S{+P0_1)6Q}1gxPfup^$w`Oe$CnK zdZ+mSBX|tw2PhI5zu%I+)clC8`4E$3=$%><&?I;X=W*8~9PCU2phol&0d~-c>mB}W zKCGa4J_2e@gV$zo6@laW^_mO`kP=Wn3ki7fr4S^_0c)ZeXUHH)UPqP0(vFQwV_)G2wG;-{J^03fIvWo0y~pHh6{+01cgZmh!q2J z2&ma!aS1IpkZ>L0%TO^9sX@DqAvoveGh;d z#o+nuZv`OV^SoY@(Ew5ciouYe7Z1Rqpl#^jAywmy79`2TsFI*C0cit8JT#qnr7|$Q z3Kt2}WU=qmC0woFs5KDstl)5fuV%E8?jUNZ^Z_XT4;Z5PcVZ(iDODi5kvQXBVPuAEkO(dED;GN0^pjqA~kT`6)^4lw*xlizNWzec@@Yba-uocM=$Fx>~rVL#< zx_$q2dgMT(7qrR<8ohhs8K9d`Dndc~pW{Js8`pe9#QM#P1@Yj1&>;@cpe;i~D54+M z6Urb^0NL*h8aD^|t{Xf}7xY3Bveq;u9u!CD1McAdI7b-jh9)dA6#yzhz%f+;iYZV} zx%q%Yz>Cx@L`*FJ#ncMan0gn-!0^%@RF1-83e?<&#?-|)XiOz^`u^z-{nHux0~8Y_ z(xBe5?;mT950P-!U z6n(7<8Z-#~V;z`K$_MJKfHu*ZbOt1Vw$=aWhVo1>+uHkLK@E;S%~cW%{4FyfDVzc9 zeFeni?aWwkvaN*`uJ{Yi>{ted3r7T0Z=LW2UH~mz2N)nuACU|KDWKE72>I`06n6iCR-J>+i;4kl zP}IaW3W!kxxxP4;$-vMIE`g44F$uib58(#9c$^CIHl(h214`CkxKQefoG1o{<`e&4 zW`hn-03GGT0pfwW=deLSuz(L(TBN=X%(DZnU4-O*{+4G*O=CA$;{j{Q!Vty4kRiYg zT9gbT{(`ca1Bm6p4J}z>jzO{-#OxB)j2Mt~0f_hqQr!Szb%0cZGNv9>wJ%S1AWP@e z6wnCuUJ%K{-);wLthItT|5aL3k{JXt=76+;D#MVV7iUwzq4H7!G$yTU>pb}?65*I)cP@^)T5?eqLPe2k-qXM83 zvq2JHKoU@+%%KuZAPE*8aMKvv*OP-tyw1rG0Et4);ed*UWypX;p@w`)27B$bLxv7W z6zZ5eP*JT62aqV#gkw-qkqjS@DAa@v5K&ldA(9b;Bsm!-$Xu-^QG1v)MzQ;Io=A&A9Hw^1YUDN*h@eL`tmUQe&OF1$QjfPRsin@-AV$53(xC? z2!E{sYk+LE+zpn7cMvo|+jcWHfZVwUL`Z_1eFnt30@4KP+iy4wa<&Y@*`O7SjNmO> zU#xva_EzxWb5Qu6&~^FfXd@K)pQP@XO)j*iyC|Ns9Z0uHn?j{2+uKaqxJu>R?zPDf3LN{L$XsrN`hWkL6l@M!L@=KxPdPgLBzpH z{NfAv_{qQK156#QH6#QN=uCjukl^7(37&)?%;3@SWng#-ItTJ)8K}|e3JM-}SnzQ0 zw}OsvXs#7Wfkb6ENKGgx&e=<~kAbU9#^bFS|Ns97Z{9?B3FOtl7w5qmrh<|Ks8P#t@Ff#G(SU>I;0tDW*syS) z=$;C)y`x7oA0>oPb6zcIlO!`(B_t(vadh;Eg5ve{)$Y(Q5SgG}uzx_sL*NV3KuA!9 zg7&NZfQT{rfQEe-85mxAgQ^m6FmRP{f}*j4g|oBq$9|&S#cnR4}2AaR)=5O5qZs?16K;jFuvlbHe0>}v%G!V`M z9tek~Ni}f)5iQvs1E)HMW38YB#em41XkjY?DS1FE-nlw@SnD8}GxSUIAwHBKedLcB zr0cvG7+$mP_y=B90XneKCz(Ou1#b|zl?9zqJsSvWW`PPA>o+gX2Z9@mhXi12N7a4iDnSk8_giFB8EX=QVfH@wP*%`_t6Xj95D<6iZKiVRxu0$ z0Wk~$X)z1}H8BhVptdmxWACp=1cDl7@cue_{}0+#KJNMhw1f;a81bX|1!E`pSS*GT zZP>XzFXACBU{IH=gz2?#_m?cA|FCr^UjrBzULWgr{R3^eKMjER6s-e_-F-U(P~3+( zUyRW{ba}BZ9@ajr;AIkcF(1MWcyTxyR4qcYF1qrGjpfT-$7ay3w-8K%;Y=`fc*Am^J z`IbMRL1D;{17xzQ^fajY=Wo3UYF|Klm`I}o^ZY@Bui(xtf6IE15@?Nve|$3CAJpXq z`ThxrcnZp#UqGxsu*`XQH#l=Zvwh~Uj$|d@PS&kAmTa5 z00$7ugAY0y)&w>{0oM2c)v%yC8)=O3ksmmur+{myKMW%j(xFqGhL>51!m}tpcgkb{0B`~f`?UgyM6Bjc8A^w%2)x?aRfv#gABL=V%-550BSLsf(=lB z6~W$MuM`Aid;qCp;b#)aU;$|o0kLHGp?wPmuqGK;aSe(AP{KtT)lTq*bjmp(`-7mP z7G5Ax*DsI(#SiHW0xzO{AthGx5zzF%7DygE{SVn2{sW}sKV;p9a0X=EDQFFdKn8)8DyyksQ7`**Nb2q4n$fq4Vn)X=$-&tB557SQlbW`g1~!-nD|>6LHW{Gpt%y< z$_W){u3};1Z+#7_v0Ql|JB^rO%POyfM16UhcY;h}0WG}bX$CL6WGn?On&aQ*!2&u- z2QqyMUH-TjQ~}OG*2q+9(hatUwNx9_su5^DzzI5qxA`Df;0s+)xAO>SmX#l}`Xm{o zAAHI{DAY2Lepi9!3Kq6fZBWf50IHe5%>$mG7jH8_r4f>TWsrUuko}-0W%EPE5|E}) z0Z?NC6b=V?0$%Ju*Yu8of#KzIQ0|8|-LSZ{8C}y(kfu!_O`!sywul#tbq6S*_*>^f z1e>c^KusmE+d24KK?nDOn}!Epu{A$pEae4h;yCz{9puQ|W3C_mA9MZq|CsBi|HoWE z|3BvX<^M6)um6v^e*1sS_51(sPyz6)S&1ZQ1{buf3+$yAZ_=RwP@>%JDgd5rELF&2 zf-Iv02lb1SFeRnX)t&Ganp3hs=RbRa5+`JVW`h^VG|x z1zLjyPL81T7=}@K*uN-@gjF6-gqQ?gBtf_VFWN#u=0hqE9$_Yd3=v_J%7Y(N;q!uu zA@C+=P=NwEwdRK>QvC%#e+*Q*fQzU9C9E&6fx_IE19JKXsKLs19^q=ob%2 zgn(KZkgx}l;PI_3ERge|eSZJ{zf+eH%X#6)Td({ET_*q@A8y_O_9lPJXMF~S=DlE< z5}w{(5F_wK)n0H_74)Leo`Ipm^*8@E*Wb-Q>iAn4At4MtR-hObI9YfD$H9Yv0i-`5 zV+trZ_JD|kpwKx1VqJiR&h#yWLg%|XX!;P^;e^B>crBW-KEy{jqi3HxLO}(r_*cz% z08n6n48e_r665Zvdwzo!`9kC} zbU<1?K*VW~Ng*Is49FxrIb0s3;~^|0**5M zZm{v7MWq>AK>F{1h}$6JUVvC1K*oXc11H!xnMSZlpo=KVY+w%2gGIY)28$?@K!yc~ zcmOiO1;p|Zg^ouY+XV8r#LIrrdDhTTaL|Gm@JST<-C+MWzhDf=NC0We0TItYrc{7f z4IooMq1OO51(a!mUQDq8CB|R;+gyKjwm$j&|NrsU7tj>_TCTen7)(((=K=HI;BPdi9KpL8VDAcL& zZ}a^P8k>>k-xm5iDB}!B9jJj368Iu608~zbhGnjRM4_W^9$--p{%yX$8h>^$Fff#I zHiP}cQj*TUE%aC8ujxpFd?lc@d%tW!T_2c2t`a+lZct{139^)ELUe+`CX8ai67Wcu;>-ZNI{Z34v~D>4NBLL#PtfA%0Sa1 zFWy;#0^$U7FDQj}g2`q`>MM(endl44nJyU_VoU-V9UwxKje#L!3WzlaWF08=H?0Sy z7JTcG9yu{Eywv&i|9|&XkbRJ)gN?sHBhe)#Fk8~P!OnoJF@!h?<`VC2aN-Wg*Z{Kl z42X~ex#I?i^#JCMU+X~b0Odk(?#0pyjsm%3$ltW1 z?S}9hK)c&P6+0v*yFh8Dl?#*=VLgX%aO(}S2tYOC3&=GR;-HfXK#o%Zv&5nGYAo1s z@IX?{Z~#erfCycX>JShs2BaF4&-Edy`M33gvUg{12I$DPy&#f}za4a{QTJ33=fBF- zH***SG72F2z{&Hh4>(!81f}qd29N|aVQzs)yw1s(0TP8KvUyO^u#6=jQE1xgfQmX~ zYypWvO(=qjYGs@Oi9$_?fr^S`JOPP9O>l#V@^708@4n~eS`NrS68k8^~H3)x8A1Gl!Qb~zw29E@jK!yp3@CHS- z1Bm4T@p8Zmmo=bN3m+TPg@;%SNLdbu@B!(n0I?ciy7<7l;LH0>`L|8|2CD4&x3zu+ zwFEMzfRwKQ5xyYZTR^NmAl;xGdto)mPEc(R&MB%H7eLZ?K!hJi^$QT|14uO}F)jkD zmf+vk`~CO-|E(uW#WGkV!7UK*%Cs18OPu3%3RFS!PsUQQ3=Sj}R!|jAa1~6YVi^KR zDkPvP6yPeDOGPpikW_s01Se~#d|-wNlH4t@9H`?DZa;`*I3P*x1xs>tgJbV_BdFg4 zj!kg-0(DgWL8LkvArTJ}`im*_8&l{zrqFv#q5G&pU9F(}(BZ@a5$>7_imr}8R!Cy* z>IKDOhX)(PEnTgk5btneht%55FPS0Yon4@!={0{hxM=|zp@T5MRV;Iv9K>v9a4P}Q z#^`Dh1>5T=@cPut7->+-1y#+UTA07J6R}d1zeN^2xd1EETqKzUGIBsfE+}DCfLINn z&;=!<&Xu5K1TWKc`L}^R7mzUpq-+U@$OGxx0AlTc=?Vtxf)~@O8D~J!4?sjdNc9^K z>kCLVDDkO+RZARiy#p8__p!CJ_dJ(kkR3efg0n!95`pUu5@WrR9 z88T9!5it-^0&=tsh~)y&5cHy81vp?(91ZpWXoW0<0d^}BilY;L|NkEWbu@^b05T5j zMgDD5c>jX3cXumD0yK&bI?m|DLpPYCp)1iuGBQA#pe6Gmurz$)&y;^#FT#BpeAE2 zShWQIwpPYJpzU>~k{NeEa!{w3L!9ywv_S@{ruhe`oOpnwNC=_`RFZ&AY5oB!Cte__ z_}~iiFvn{rxEai)Vi`Y>RGfpVP=KpoDHX}!kp@@8;6PggQ2~_?$dEvin*f#rpA!q} zsEK5#AW0U3B{?8D^>_oQBmr%Tfn+w&Jla2qRHpz!=r5+wZ%m=@7(%B6FoYi6M-_tA zL@W^Du3k{G?(pF02A6DIt>BWxi5KFQuBqUXB#;lL_{Z=6|2sVRVTyl(xK2=qcTN2T z;sy%B6#oWsJ%nJ2|A4qo!Z4FTDYGLGS{--w{sYN+aKjXXQd)--D@-vc0d@qk!Au4v zmJSbgh~i^T9E=bN{%s!YttX-5Q(*t_Z*yX8{^7|F+E&g0F^qp(AY1dV;L>VX3sN;h zLz+n-!vjPt1|`uD5Gw`}g@G?_EdeEHi7bXJ!7Tn>a6KLbu@01>0$9Qw3BO zLz)r%Edii51vHFD4 za!0^&NUee;NRrFJk|?bL(4}UOSOhfzh-wv_fY##3Z3k+$3fLjZ6Ve6+^+do2vw(_J zq_vc-rig(f)r=LONO}Mw&Vu6X4T$vxf#-PZ22i_zfq?uJV_+*$B zxbKKz!~~F@B_QGj$cPOf)()5vMqndk@U$KvEgdS0gsqKdy~H3 zYJS9c@Da2s0`)BSbDx5wPEZ#S;!n7hpak&!X7fYFgAbv#RX39==*$`JgOJ(?sSG&5 zgdy~R^8N!PVS^GQv|facQh?eaph-B0ouGxf&`K264uLfkcp#Y?)=+>}S+IrzA4ET_ zp}-GQ3~49`Kmw+#71B@;geitJ6og=kAq@rL*Nzb7knTG>XgD71SWqCNb>F+dBODzb zTo4^y;O=XO6F0;HNYa6KUO3T2L9yB4$PS6#=7WrozK-@qh;uq8Kzm8xeh_%j6*O!L zF|+v)qa}D;tV|1%yqXU%K`L|Y6A-6BI#*pSte}+M;ld^IdfQ7SK2U)I>N$aiS73z= zXpE1)Rge`V2OV4FZ`}s+HBzUO3&aOEWxB!pBf(OzL<8#;@<2QY>lN}sJOb+#@lO0D6oaEE5ZYqw>IFxUhagNbIEtKvV2Z&}6exVWbq%yEgxLXUw{g6Vf@p-q9Vf`u zq{bb@Y;f5M_GQ;phzp@jXV4bf=AVppIuMIMn_8P+Fdlrs!U`Id3xGHjR6qs2ST4rE z02!Tww(>q0kK5np+#FC*aXxv8eGnT2lGK4&2Df% z&l()eC}niS@BjZbICelagY`5YV(px|0yJL)PA^D}7H}c}b&$yI4Ly1f zNu;n64OGY}pb3H9fLT(2eA5N#@^?70A8&ntG>iaZgNG473`AnI1S{fi23=Cs-3#)O zHMj)f2Vao@YgFrWgOvqjECB_{4G<9n3ZW+;)*Dy|rOf~bZ}VP|6^vzk-BUq~&Z!~4 z|NjRyLO?WlII0!I!kPyb3&ZmOIAFVaK}K|VaJPkqB~&4v1x<08O*E zri0xAaY9gcE68U7ki^n_kU0=bkEjD|HAvmdbKq^_kaa<-86F^mGeATd$et1qs|I8b zC`lE8?U8u-l><~Df(pxSaBl{*m=@|{{+2vY*9MxLwLp!ez>EnXQx1TL9FU=BK&&eu zLqSaeS+Jop&|;uO7dkW^nDGLn><5U*1?gf@1f3D62p*mcdhvW3#LG~NO!>FfI8qlbSWX2aHMYo`ezJh{z8&r{KsbmI&5;*F?g$Srw-F$@OCFr_(ewZS~QppSv zBt;XTCV}qkfh$rfmCVpUQj`r<1RcwPxJkKGEW-gwksCx2cq|8^LZwtJBLGQ-3RDGX zEC-@OwNxx41xW=HR0U`(2cn{+R3xJWNyP(WaPUB#8j#V0BzFWXhcuQm14(i@SQ1`l zs%FelViL&M03vEZk+cWIIs(xU_+r5nP%@I>-!>Igx3*p?70b8*l7r55L_rJ#`4z07 z`4?zp>jRPs3#bYws1eP-Kz&^nWpD=xJZ&QeRiOY^0qX0DAgTCb1obP}j-U(!B)J=4 zIiwjC8zjlyU`gog2c%jD9ohmZKe05)Kth-^DkoVWB_ybA44Rq&srrs7M0iF8*5ZSj z*9A_m;4yBHvBw)hjW$qU_vKb_?+r9r4BDE+-|EN0z;L{E$?yOF!EGr>p5|{^h?saO z(E>G+gEAbHnFKO2K*StSWR`$fHINVqcyW0WC^C`q=LC@S5)d&DqZ+@mGmR#vPC*XgToR z037+S;!ic>0mzkKK*SP|qZw2{yBJl#**);ZhlwCBqBP*`N9b-&Cxv-6lbtLWAg*X z5~Tj00m$J2AYv8BA2A?S3djsl;A=qq(G8AtkRw11n&yX~4kXk<)r<;|js+lMHOPQ9 zAl4R;0iY86`2=u4boWAB09h^3{79ih4-t|lKsug)h&3PsK7d$1Kn8$XILn|0pn1Uo z*$X_XpebJvu@+>21&HOK3ax=sp$34fZqTJqOqh8FbFT6sSvdI4lu(LK+|;pqMKG5$8Y=*#Kg7z#?)^A1Eoo z8z7+71)#i%w95I9A_K!qx10a}Lp%jJpB+?^VH>qOuLvs4jzC+Z5FOnd2Vb%^KV&Qw zfdpDN)4><)%?}v)TTB^2`*&vi{{R2~vDTRoY8Hf=4WZ^hsJReo9)y|?qQGmQ6FR{| zc+Go319=S4HP4ViHb_zlV6mPE8cE=9-3m%SV5#N`7WNVW$Pis~B?||CD|lZr*d)*x zDPxIz7DsO{$XS7qfk4pO)fdd*F(c3!ojTBQJkZf6r3qO~y;DJIgCL@y#kDW4+k(n9 z(5hI+hn_2qLyxt1jx7|RItPX;(CS}X&WgQx(PYMo4w zkZ-P(0S7e51cp*rV1e!Hbdq_^-;HE;XCO=W7f3pQxD`62yF(6n4#ox)Oc5YLfCF?> zABdF$3N=u?b9X~R4Ut$t4hza?0I8Y*A_PI2mVj7mV4ALVfi%G@e|=cENCadY04cly zB7{Kto`6_yK>9%Sz&xlvXte`P*Z~;~>Y#CN5FrfGqyl2;s6*p56IGKpB$o$dIDk|| zfCv$grW6n>2d2pcRTGxPFhv%$mOl#~rwWMs_$p-?7+MeTw?2Wii8^}=K>cxWGU=RJ z0%Aj37x)i(F_H!CWCb~E0f>+TxpEDNwFTzNzD`g$;!CpcWFW50V(6U;az#)^!xFR` z6Hd#3dbSbI{XgD2ok`gqTUzBEGcqx4uytWp+W+eyOF^1kh z2Fw1?rQ3_7K{*%{KtUN5Agd;T2z8Lnb3m*mFq^-(L);0?6Ig5x2N?w_Cww`&CxQl( z5vNYtfTUbGx;sD%nmT(Mz~w{@yqo~>LFL3$5TkP{NTRzH#0Kw{1~I_p1gPC+umn_2 zfP|X&g4BUZ16Pi2u#uf$<6rZ4Bbm?%S`GtJ-aQrM$xcCt!N(myb8-xjLKfy&XbAR8 zF)+M_$F57p4p2D!01=j;U}4b&4FhOGqwhf*C|K~NRtG6ibqwl&fGX^l4HrQtTR`u- z1Z~2CMuemkbQ4wv$V;Kn0nJXQjF+G>VDJ=54nv6ucu`3Xq~XyC-s1H-4Y3RuESu2@ zUT)ak3tDv848GWcp+vcRDu~++Iavm(05oO|9;E^?UY~<1ft0*pg`f%^Je1uTSOCfn z;0hgd!VDo8p_oD7SuumapJE09p%Rq+mWcD~1wh+|0$=z-mbF1|W-pb^ z`0$HS;Ke^R@RpN+7fQ;Y#t%mp*pZZ5#(5a)9@?itul9V&p#2`0zjITB6WDX`tI@1(;nW zkkm-P)qn=tL2A61x?TV9ZwHbyaa!3hv~m!ul> z_CN;^kpo)Y9{^%SfOZRl^75rdP+pb+Yvcg+rm+~t1d3+R=>`UY895-`H6S7nWLOV~ zH3eo^E2?2EC0Gn&K{9LwNcRp9Q2;XR1c-G3W|#%4VI0^D<3KX(2}t)B5K#m&j70!c z=nFu5-me=_yeWXqFaadP6hOKSKtu`1Fb5FJ17_GfRKpao8K!__SPV#a4u~iN8CC;g zwZIGuL^aF-n_&(}hRp!!UI8L1K!)uAu@1ltV?j2o@gb;6FL8yX4jXVJdoV%7k&L_o zGUEw|r~(=J1;qLTGjd}+Qphzv1RXhs%}9_ql92*}pt%PSQ3Ep40K~EoghoajnvqNl z3?=?BSGqx5$`0G72=m;kYZK0xn;iSOPM9K&GXDh&qtrB_LJ}$Z$~O zoDpm|L@|>psL(I*2iL{m4ZWZu%89Mh^~-J7FDPdF{^8#q$QG0_0c6+;5YYfKdk2Vh z0Ax0(4qI9W3JaBPkYd5cM+~4c#~(Is;06{B0;|j>MHz1-3 zWcVKti$w?;Djrb7L5evVA2NVSCG=3?>hyhc+w~2K;b1Ru1!PDFffi4Ih!&9H4j`5X z$Z$}oysm|~9Hf{@l>t<2p@#}jr|XN`t}jr`26;4)CnzHZWLOD^Xakwu0%G-m%m#%@ zKh$iHVo<1n3O4jm;p+^2a@+L@is2xSdhi8iEC89d14MLy3_k&4T>u#lDxZv?hPz5M zACa+s^J0krq&}2D+FcAi58MEBe)ol)py6zA&G>mWr~$+QK2(ka)PHsT0vUYy(jBOR zXz2!lOa$FbD#O6vk`1bye7|%D%771Q{nG7w2Xy9AfDGu2wl65XQz?+X&^z5sovtss zegA-4?rz%_H8{D(e0UaJ0C=rxl z15y-_90MoG=Rfk7+ z=!>9?DI z$Z?=>zEcGX0C+fq90xj#4r%<(24tJ-7x*X~C-`)2k>>yZUuuEcLau+n;hhQ#Lr8>& zLVXC0@bA2!tD-?RuK*E^Acq_Pu}**-;`=A?MKIJM&}0L02rSV+`!<_-!CmM7;4}1) zdLOev;-F-g!BEN#k_6qvo$)#e?4TS5@R^C-PC4DaFQAIg^+G&E9S`2Z5Quo!{{?kpY#hKxI) z(EI@+7J&j+KnFAeqXSKX-zp%n3<{+X2JqR~un2-WFbrhd7jT|I8cc8ii9+I@1=POc z&te2!x!DcMI+xafI)k928z+JW8W1B8+&r)YJ9Snv2)vNzfpk!sj~H0LdGQW(H}uok zLo5v4pyn@w$x6hTorxgj|Ns97^+{q^G6>v(vLjY92$ZDe7BCcKmoOyf=OGt(;PVNw zpRbC(fxH-GHRz^F)Kk!7xEUBaT|a)b zACTpsTUkN3okAmcCl_S=3i*h&Y*=jzO%l+1RG>QAL6(9dE-*s}loEVE#9>gvNC2@i zKnVj>soX6CB@BGS_qJe@K(`cIgNG>K$HIEBbasKf^jfI7_6Gy#aETuvXM3=8dVwyy z4|>rAy4T?d%WJl7*Dsx}KVEV{R;z-70u(}s1ak{?Z!{?IK%*?6kVOpA?uSUFz<1TJ z<78lX*#ug30bchC3v1{hn-jpwLA`KTnuJP}b22bM&y39hsR5@z=>B^||CA%(g$)B} zpYnhHZNC4Tf7X@?fp+`pQE*LYu1UlP-qq%~IfxiW`NClMAE5XOeLdOpwX%1~T4m{NR z0^}|RT_%ByKOkocfLIc`pemXp=tX`B$eDQW|6ylf$k5Sc5_s`m3_2VjEDQ<;&~U&J z&?q?a{iX}qK?msYw=Vt%-t7Gy94(NsktJ&x1YUsVfFq*fnvV!rzj;vty6OQO@1R+W zHES6JzW@LKAH?3VmO&sHTBzVDPoSqjfm=?l0^J@Oh-4}OG8>eFL1RlLh`i4L68GhS z4k<#;t>XxM;hq65I$?^TMet?NeF@Oy0104_0#GvnnyEqC4WLoM-|`mJI)FxpYDNSo zoKrxA947-qMhS>j0}6Ifv-C$1IM|_=$UskGI_?U(f1TmA5ctjv#!lZKpj&faF@kQq z0G&Vc!x~%^7H5M_Ung4wL*1btm_xsGhJI+S{Q}xq zv;awE5LhMXKsaX5T^&pT8B0L2plQO8pcf`!S)P}rpwa<4jwX_^21$hwSOt7h3aDiO z(u>qYnF*TZ0Ue%l@Fi>WLq`4`7`ZeM|J@RELLd_Dq6xeD-a z^DyZ2ebZd~hMB+pGN>X6ee+*s>X}W%-i$;e$r)k7z9P{BJlpHRU5Wda|u4-I+zp?VQ? zo$m{f1z$jfCCEM&eNbOQAJmZJ2za3jwGVfw_CgXl|F%Gb)=MSq$6Y~(U4SMFU_}-* z^i!D_7IPSX-JySu zG1VP+1+|1=hY%ij{QxSLkGnnrQLlx%T|a=<6}f)s_I(0wkU!}T{bC&|z~2ro1zo>@ zt}l}A_I(36C1Dq6H;y)FfE84khdzF7-CX;Gu~eYjRRD3>(&N{v&9zUMO8L5d1t8iw zd>_9S1c{Y#cZc#Yhkof|>TrF0+!b^`AOrtC-^a~A1xuv)_k})g{v})@#J|t=ar2Mb z5>ALzU8z!c=nv33=g=SBu78+aKOAGI?e_fucJGJdt}j4d0pC){2{NKH^v-K8un`{= zz_-KvVfOvd6?&%w;sRmt>8GHxWkIGh`@ZS)ee)7@^(d&%;`*Z#-0lH&3XZ5)zj*;( ze*ld%a0Dp?ha2C{VmCez*<1UhGxovf)t0e5rJ^sMfez$j0NuX%Vlrsk=86B{$s+5+ zB~QY@W9JWgT?GPy|CjRgx;_Xv?)v2a|Nr&JUEeq{FfhE(1}~@<`0x9q`$GkLx9fur ziPvCt!7rAYGB89&b{~t2k3M_+QHM z;)w@HeSBPWR7CgF*u&lbL%ZJu1_p<9e=@$^eX%q2M`!FGOSwAx@cp1RTDKcZH;;s6 zjo_bB?rs+e7UqMNHUcFg-EJ)ZOC&fz=A8$ftrYqrGV;IcA5f?q=CNk0IT0TheG+0e z_=GFn|D_yR4F63P+?fRaixzBP6lgtADiQVqe1YSK|Drk@7zF}eG%_+UM0T>pL0#4O z2GmeOoXr;2UCI$|{J(Rr&;S4bt-qJbznBENPERbZ)8K`J3j;&vZFJ!RgDuki`H#tSczsMYRuz*Lawr;hV;m44HDBlcfs?2qHF7eGU7ov~L+I6Gr+ z9CQ5-I+@z_1}LX@#QyJfy%X@_Ru(kM@3Jv4>|khMU}$v}C>8qdD!|~t&QQu~>3XMx zrNQ@qL$|9y^8;q>!#f!m7!K@WU|?t{arP+D@F-Du;S6$%L^I$27i$0i|IY%awJVOG zwDuBImw|3ymHy@`z)`{pGM0gzq4W);k>~K58Qh!`$r6avK3HO|eXvAJ`(TNz_Q4W? z<`)j7OD#jc@J~6=>H6li+;P`8paVr-iyn7<1KOkVn&-Id8<0<4Gk32?*^1!~MH zAKD2TNDki*8s)Y=SZ`dSVSTtn#rk-OxGi|PE5w$8nSr5H_qgi|uqh$OU0;Cu^RK;+ zyMm5$V|eXk%fQOOP-0@sz{bE(qGElhMDs-==w@=T4_*GjeE7)$CKJNMgbp9~t zdMt1;+U+WE-1P&f6WMyaM68>4fisi9_lwHD5-ley*rAoj3WpM*Mqf}Hm!;A73j+fq zL$mK2mX+YaWM!v?mXYwSS<+lS+zU}5-Q`l8qMLcsshH~&k& z{4eGCf4$T7f%V~1&_SC!K+&sw!umw1_BU4n#uC-W)Gwey7OcJ3^}_$s7nmB&yUn_f z9sI%C&C;q6$RyDGO#a|Awq6$lmIj4?uyV`r@BjaiFId4Y1NF93Sr{1pmp=F}S_3KQ zTnL)c{UPkd>p%bhM}oD1I?k=%O4q*F^atc`UeNZ?1)$=Dt-<%d!)wmwAIznogv$X^ zZT7;?kAb0^t@}dr1Nno$Si420f*cxo@Bv$7m+$|ElZ+icOe`4k2OqHYx-h9UC^U5V z{%uJ8>(Sx+zqIb#!3O@8-O1p zuKyTcOLY4R>|kVOU;rlqu4AtMm|n9SbN$Bv&Aa?9YZw_AjQ@Xg{qN9O`-H!BE~u_> z{Q@o*ln>f6Ffcgqw}7^UftU>?h1Li8TPwk8URWRJZv%})HUD5L<7@uGQl{PfgO$HE z8njDdpX)#4{~r7;a*PZN9{jEDjG&`=pZx#Fz>vlCQV3LsF@=Z!H+!KP0SX)e?n5PK z!odO}5ey8-q!~j`HA>gn6tF^S6|Pj;{N`RFc*FgPFf|4Fdy1 zW9^goyZ1AcMu2?%@&>54>I!xs=-PzlA58phnV^M;KUnzN!$GAQ#P;RkP}})iy#9l0 zhyP|TCP4(14_P1PZ|wt}+Q|W`BV3=Tf{sMA{r8`Np;YR>*$cM>28PHNOuwKdNN4Gf zm+_1Y47MOomP&_bG5t4t(F8vH9_;4BfB*jn2frQIpBWID*Rzhhf?b})9Nm3b`*=XF zi!Io<7GWT#@ihPV&)?GV_y2$9AJ+Hl?>3+Q-_6#2;^0r#Zk}FHng?Y7P_kxJ>EbZ* z{nzlY-hcy_if&(lE`ftD1r9u9Z+KMCb*iEEFGB}MsUo;Amj>wqnFco+oLv}M8aNcX zeZL?Ik~(lfQUS_`qM%$0D@wAMK_yAI>lal}0TTc!h(tjpmG$9L6IiM7`^SH<`?1=i z@bACbi*H^G43RGvffmV`b)Q4>C%6#mWZ~;}{qZvF*Z=>)An@Xq6$8VWERl=_R~Q9e z@PqEF@O|>%_(l+Uxq{Ma>UT4@;!e_0DnEKcI6#Ux2Qq?Jm7z{k_EIh3Jp}|2qv{?>GK$S^Jo6YYTyIgtxep=UjoY8{4gvI#4Yo_kwf8tMqbne)x z&nU1sz?4bgIBSS0lfZg4MuFq35vEK6$zWEDDU-k)Fe|~7Nq|+2QDAWjSaF6alYpT< zNO6uSlYk;vNr5SoK(i`Hti+T_fM1nS0MbHvF%6VeYB?B6SYOP_XJEMfDE4p`PsRaI z7_UxZU@*Sj8T-WeKzA*N^>0uJA1;-6@%tM*gjZOCL-^%U2GDSsWh@U^P05|kT8{7+ zaV((rZtS0L?mP~q`Qa~&3K$q7L2XCr<{$q`#JVqlit`hYpk(F&7wfNCcXF6A34C)@ zsV~(8DU}2%6#?f0P(pgZ#>@iF3$IysfE7BZG?a+E$V9kE4CEr;C;u&DpOh+xzj&9& zz|j2x*7XQ~aZnpHB>yBl>_q|S2DBqGu&4*`yLEl?;&&ng!{Ps>94|nZi$p>KDHzl= z?{?$qKGE=y+4y$DPo`3bl^l!=3?-J`ZXDs=cG^E$|JUh+2ZX)w%V%JS40w?i1kTac z$4mLU-FR9KRPr}J_}l5m^P0Wck4K^T^#Ay7H;(2s7KTo?xbDNz&>jKEn$~ZnoMA6a z85kHMU+agx0NvgY*?OP^e7&^%cQ+nTrBNah_Tp(C14F=zb0F)!--whx@S4f^KwRwM zERl={ptyUV04nJ_YoB!1-pOKo@v{&V6}5kizjc;Aur_r_ViMr*1yynjErOT?dRaDD zT9m|fGkkO4X)Xy12zvoq&KenMe95UZ^bc4`H_L`@51wxan@jX9Lpe$X173g%QE+U^ z{daxxTGFzXqg3SEL8cP^fEW6pmTvQp|E0{Fu6H_X|GWT=~wtsGa44Tav&2y3d}%OgUmhyQVEG=Q0Rv@ zpa0+btyJWN_!oE@KVrtfz{4~in53=NP+d_c$MgNm>N z-K89@-}t89xXoT>;N10^_nRA!LkTa~2-hbsBH|es4u`z}4MjvYr~Y9G zdjaZOA^R8Hq`lHz$`SU$2h{02ykiBZU$PJs?_pUYajgeR*>`{%)vwvCkJYkwA6pEP z$zln3Q3(p`$i<*k0lMBeizTcRoLXKaph&Q0NkzmRHomkIWbSKLaGZAXFf|`kXgq2GyLPTXkRj$zxz>^y2~bkRo4mH6K$5`!CwCfl&a~1p`fY z{Sc`Y16?=!;ykFpaOG+AfT)i>3_ckgwBj421)5R2T{*x$w?0@TZwt0iqA`KzfBpae z+6NE*U>_`>3#le?JK7T9pWQF*YA$Y&lS33lS)N-~GRvao7KU|LT}8G=KPSe5m;# z%Sr}D#>SEt-Hv}+4wNXjJSmY{$-wZRp@eTG0|UeV5~kN@dn^CH=IyQg_nM=(^6zWr z-pW7SJ}NvABTHnv1VMIODoN`QZ$8cl5iNc7nzd8B`8W&Mu5L!CZQX)!`yRGjDluvK zR-*p>5ZE>$5Dm8Oc&B)`iwZ~l;WP;2m^%{#oD+Yz11byiKghojOG{K>-t~hBl)eY0 zcm`tGH)?rOq5|>1Fv9o1%I3JwMtp`eZ{W?TWQWdy5L zfH|BE)P=T$xcbQp(0NzTG9ozaMT`xoC18Bp`g1A!jt3hV1zszK?+5Luwv1CLmCj-e ze^Hwa3ffLL73;<wo>fanZ4$#z1$O%8P$cr@jVXcmo>XW9dHj`Yxzx8vMe~05-~$ zVX%o&;J;`OxNBS@nxU|XQ6S*IXa|JP`oEOp{{?7C0IG;ifGRMMpy-4Ri~?t}I9~XH zhII~SalFXM1h*yshrP(M0y#bG#m9V5jk8J*L|y~44}!^Uxge4G;CqlH;t=sI z>w!{%Zobx&B~1S>HXmcLF6AkD11tENZ7*zO6ksTk%mC3ZUV|D5tp`fD!0rJFMZQQ* zWnjoyvXN0BW5Py8ffoxw&W&t6P#O{zp3!H`DDYx?0RzL1j*W~0`+7Dq3Pg7Qus&AI z-_727poHoFh34Zd*8hq=Hrtv6F$pll$CXHDfLJdYp*HbkF=R1iG5i+=i5ee>J)EVG z(E&<21`!MlSsX8%KtTXX{l*6zELkr&GYRmwfckrj&p0y)bh!#F0@V!7wLA>`tqVY9 zn5j+>lR&rWlnsmm|F3o5=#0J4db=d!zo|wL6G-4kXYG~F*gKuIH=5tGbk<(6{$1q# zg4dFP!IHh?t@ZCByZ`1OZ7)E_e{{0{zx*F;9!Lmggk|iVQdUdX8zp=ob7C)ay58sp znc5wD;q~g{u1{8MWE5zueX?aEqd>7>x9gK`253OIK4E$N?6~WL9bg3y_COUpKqz75A^%>pl0dC>{sL$toAC}Lm$sX7R%?lVA5j2HX< z|NjpeK@IMXpgcb;yxA5s*3D2V@xrZ$fgwxo#e@O|28enEP+<|-db@h@tTrpz-G)t^Z5avUuVwS!eh&36$^#zfcENeys;eSH5O{G4~ay-}Pb|m;$w! zUW)|3U;`<5(ew&bu7Ucf5!UPa2iyxS<#_SF5R_S(kMOj9E0wTx{Zk_R z!s``i1wske3&&TW4M2Z7Il7O*bvE|u6= z1^z8H?+WXCYa9M;4uAQ#dH#KE#J|nq5C1mLKd&_)wsn{OdCl7;&~m9n&`PkB*TK!v z`g75H;{%{+3I1&i{M$IS55E2ct2rTbcZdoPs3vQ@RFY|Ypvy&tqvb%USmQ6yU}q6u zvn@*?xRv#?t(bu!y!&VKFP0)h&;eY`B|O>}JJ_3#Gd90uEdADf;@|@g=7S(gfcaqe zspdz_2VXLkuzg`1bO+o_QwH)0CyDu^yv;J4c zVf}-@X#pbxL*q}dOJra!5e^S)e$LFdFg+ZsRkDq1iFv!0!cQ%WGUile!$Fp;=f9(%XtQY|0=x>7Z?O4{8#Bca-Kn8 zLdbarfrj%80!z*_2q<6dV(VbQ!=0)57<2P+u;FLmhWC0hz}13P zF*YA#YCg^ca>6pWstFJ^&Bqu)4m{2XlI?)Y_Ifg?UtkarxxgU6bAdsCy$K(|ZZlTMfZC*3Z6FFIZNUv#_lz3Fu6f79*K_o36J|3kM+-HpKq(hF)7GJjzH0Sk@)AWwT-U=Rqoz#xzT3LjUFxcHNY zCxCq1jl_od9xBr7$pDgvh;_Mvf&(fup|`~FPj8LoA7~t?fV>Ea1Kz)%m;M2*E4auY z(Eb1W&HpOBH5VBKdOaCnaq<%qC)%Jmxo`m-Cuc4)2uyeY3Wtjf0y-BN1iIXql`nR< zGdI5jhu(W|=xstW8ss*p8bn;ZV`_d6j)e{+l@nlQL445V1~Re3ow4~HbMt#p%8(d)_3aFIcv1LUZS3<5JQG6*cV$RM!dB7?w&iwpvXK|XWolj(Hnm+5xtljwBm zm*{rs6X|s67wLBC6XpeXoy4n+u_a(@dZ>A!nB_;gZ4EVQ6YJV;fHoo2Z zjeq{(-V*8mt>5@twZW%@6fp8{i)6$i%>$8U;@=j@ghl!-Gw3{>0%rbgk<8kkOZbrt zf6Zk44P?P7(9)U$7VAQmk~02n1uXpAB3ZDQv>0L%EC04gRxHvj5NS64ZINtPq*Ec% z?EKpz*>SkT3G5CI>p~8&J2?2aMRH&CQcYyA=>n`B2F605bgNJ`xBo7voCPPf(<=+;`i$%HwBF)FYEs_t5bSOlcpMP5< zKMr>offjBQ2v`>ifZP!&z`w0P0E zfiVBJNMRiASOuDjC=jtO6al%TK!kr=qzD$1njt2M@^6b2#Uh;okrw0M7Ab~B+7%)# z&c7{E9EUrU!0wQ+E|dVfLxO)>qy!d|euI~G7D)1Mi^P_l?m(oa__sw$VbKk`nv;K9 zfi(ZNNNF7I0JTKA3uLSdWlFO6w-w0nZ;O;cQ`-8iw48rifh_;FNLfs&82)Vqa{SvO z|9|G=%;%f`%k#G{VP;_Xud?^+ zGX{a~V=rfbGE%@3>%b=^nf%*6@ox)!qW!6a-}n-!TshQRA_N+1yUxF@;3@yMz^9l} zd-%5%JmcRM_zY8OA^*05=lt6OpQA}Z-PhC|@WMLqMM){d+85fNN`z6Yr zTftxcZGnF=rKI_{75wAh7Wfa@da$k%mhMBZrD5F(?Sm!!#wS}3@XtTk>%#dTG)vHW zpp*%;MrJl>2S&hS>%hmbka&!27&v$Gx0?O|N5~Us=0(p%Vi0Lq21b|u4!$c3YbJ)| zRDSTb`tE?|)`8Dq&H&|Ubc5DG40^%8Ef8B~?uSUf#F3dnTg{O&Gja(8bB6El|NpxK zURwvghB*V2uhE^M4zcGA|F%GEnVAD3jVm+1`~?n6SZ2oNjANiXn*!cj2fl|n1C+1P zov{F7&zbz13W-fwAUHbJ8aB#pfG`jQ|h%_uyV{^tzuoBPz zpv`mf^8Eqxw52@w~DQdgCy7l%m0GCVeCeE9})#yji4cQ9vQtH{rN1BV8zB1d<|dWbZx z3_k%P4a@M@oRJH5#wY8*PcUa-tH|9U2Ei(FbZ2Nlq;X|Yf%jjJ434v~hH1K6C=@(C0gAFTsFf)mF_{%r-=TJ*6HlVFViba&W6q;VAl z@(^iQL4eI2e?EfT@zpx;D=av$HR!K>1P2GK5rFQD?GR~LgC1Ra21FWG@ndsFDcBi5 ztpk6;oB=8l&<*m37zAqrpi3J-q;Zu4f)Hs~Ie^U>?>>P239bD3TSPIkb_)}z_CEA7 z8I-AfIl4=E__s0gZxho#1n#;X;Gch}*G1?*XpR3v@X&!HBmcHAMl8~YAks|y+rpTz zNH2y+GxKlrV+OS%CWF>)A-V{#gSC(y0P+#S0i`MY+u)X?NqO>bb7bP*=EwAU323h@ zk|)p5F_lQ9NJf;fT<59}}zm(_2*C+q~N5&ooO=GlvE9C{vlzlt<|9_~z=*4G{mulJo!nA7_BfZh<`i zV)FU_|GVM4N1V@t)- zK-;A(7#SE|w8%sFFNDMy7+$}8!5hrL@LzPv21bF!3PDT)FIwXm7#81fW)gUzW)4zW z$^ODXhJoSr`4<~vLHr7Z7lzSbhIkgki!BfT{|C(nbN^?(;>;xQ@)BqU-pG=H;q^)5 z|JKJ#qb+Sg;&qMxc|mk%?H@~9(By3?>x-Jl|NmQpmOsB>vSeU*aax*z;YFPoXzeg$ z5)^cc3HVY~& zSPtU@4xO$~TECU@Sc-x?$=?E64hfoPnGXB6K*9P$3C{~*Fx$cU11NLyyqF8}5NNeq*b7e(^Sc`lXx(Nwe+y`? zvD=NK+pgDxv6HXcPeS{LXQv;F_3t{_aL`opi^Uq?3E@uHKd*h8PbhS{@id?K|GKi< zm#6vX{}SoupP(hI2b&+rcV9gCi}fIAWh!(D>ki1uR2GF2ku0X*;J_CfHKFU=LF?UA zyWKdtd^tKeUW*z3cMtD%9&_Vi zXgyGx{Nk@914E}9N4G~#CttTmMyDT3H=i}{4m&1+5}|J10vjfQR*!;G_7^?rp#4$Z zZY@y(%tsZ^xdqk!p{8xP}au5OM_zHSGWPB)hB zV{x&ESQx%J@;H>RHCJ*ll&JjoWU1ip7VKd9&&a>+z-u1(^#F*Rdjmwa?f{b?z~mV)`2tK{0F!&b}g8TKfgxDNxb{FDKv4 z!@$t6cf;TR{~1cSx?9(P7GWe5{B1t*|K(FI1_sa~&C zWzO9n4*p|3_>hg6XDetq|7)i1Q{aWpvq9=$3!Oo$oPR=+6J((?Q}>~74k``&t%aa! z@tgHO{uWRL*(Gj#vUBSlkc+oI0Fzt({{P=G6%_I`~SZsSQ&qh zIu`>&uhRpYh;n}ZeTVq>HGm4G<_G+*nT!wc?`t`jv58S2i~ZmamdMW5DS!X}w?4?< zeufh?px-*}2ZKO!FDR`3cW#{kwhkOvTfu?26&!q9!NIx}6nrnIaxyUR?>p4|LZQT~ z`2}-{S@R2y5>*e664B-t%>1oooD2-jy&&}*FSFommX~pGHsi}6P6mcXuo_TSed!FB zVtQ%L$-vO?ORH3;`7lc-IA*(}|8&j;xyAY(f3E~wDa&`T;7c~pNgAyn`RoH+{QFMz zx^P_R1oL0M=3rpx+zRs9%Lg0~W7SZN1<8SoJ;(uaG>G^ARm3bgGvVkD}%UNJk zJWE0wer1%nH2lgbk!bi;&fnU^0rL;&NVAv4AZ7MBrDE<7Pl9%jL89wr1lS@_=m@;@ zfwL7}I&d&Bbg|o*mC7~LGn$l2G#_Vz>TO_Pfa;auU|_H@2CFcFs8|Xz2(&a6s^S+r z1A|Qpf6FHjoyp(w3Pk7gw>$#TrTi_oKy(Fv%OwyU!QXO<9TY&I^w7K%6t@h$t)P4r zz`yNKzzd61a7(7E6_o5EOO(2MLAk9LoZwzu{r&%c7dtc&wjAJZSr2OABCFW^`~Ux! zne33X@*{=6AG8OLzon9ifuZ?FFn|9iP*W9LR5!nHED`YVD2ed!DBc?QN-Wt0ottG3SzT?_WUen zVqo~*3JNC!&<#4RpkvvgAUCw_xzzUCp)qw;d8X>NFsSjEWzzoj@*2hY$yFFN}&y|=!ya!6b{QDU?dqI9E z5t*rd=;99~PrPCVB^i*PUfySBU|47X_iw-pvn!wm)(K`%3>WbCe*sOmw%lO^aZ~ww zwt>ad`TI=3EwIdz!sZ`&B^u2?^7&h5F*7iH1IJPW#Mz)-1tp2l*Z`e{5ZY}Fck>I; zL{KA@zs2g;|Njte3MD*g-L0S^Gp&>DH3x**$%Y7+mxn+B3@&pVAXb3yJpl`RKL}bs z&fl^KH2KTi9n8{vnB(O+Py5BBRg9`#dqt05@^Ec+zLvs-9KJ)K@0|I?`DQ9ZEQZw(b)=` z>gsm>)7jed_y2!rmi!Mcn)&w~`VY1Sk^lq#gN^D1>j)3{ZwlHT^#9ub3*B=;DZcrL zOygrv30orD40b3(sR-1#@c*Wu%|alxk&z91L0LS#RHF4ZvI5Y)BB%m;-BLklWH5s3 z9#G;4f64Xl|9^-dK=IYt3#x)&t2DnT=5N{i2b`{3CV(OvR4;4?Z5;$x(x7vZAh8CG zYyoglQ1I{n|1PismcV1JSN{J0Z_v?t6|{Y}6;ua-_65K6`S<_-_k*DFl)p6yWb$E_ zZwD1hBp|`@?Vv*m8~=XiKd)Ipxn|~T;cn+YAVKDtubGT5ArwisUaA+bVQD>3EcpFK z^CgDww-BC#=7I?f4BfZ7f0QyA--d==Ef2)2;B+9>3@&@WgNq^5G|_#W`*10nBG}Qr zrJuXO3FSYyG-*9h&jyk%(d`Cj&Y3|0!Qn6D6Brnp4;pmNg(QpaTg^W$>)4pVq1nOb z_j+%y>u2p_Zcv9Yfj0AlN`TI-phzf5YCTZG-hHmY(x4>#KRDBNgUMrz1`e<7J<}`= zO4yqZFj=1~{j3cxkuLsttpP5o5Lp*eRJ~^RY_MP`VRf)RSNavAwiE2k=Dnceg@M1d z4wON_*>r_NiP1{Xw&fB|NL=Z@mRP|E<7mI;Ucpo%2$4}Z4h{-Xuyup0Z0)%q=WEXe z#VW+pD|kxPyMMRdu2V#=WHu=HzsUUxx~2h?um6{@wEnNZ{~z2S>D~*ji@+Jaw-=OF zUX*=>l?5D;B^r?Q#ZoH&S`t!Yb)Vm0;Qc}l#kkw`kDK>`iZX_7u#-x{TW{9|cZ0)~ ze>+=17F)0Dmw?{Z1)$~W-p$9DUa-eO%iL0BOR#W>^#83O>0XFae80SS_XX5zzFoTS zwM=s_*x|2*Au$pRQS{;rNJaBrkQ_se9@M2Js@>rDM{=)%_lvwQ$leFL7gUxolnDRd z3Sztn+57*0^HLC#A+j6XBJ5)C0N2d zBPimG&qlu}-1GlGXbElO2L^$LcMJj>-Z2R9y=M@Jde0z``kp~x^?L?^J0G9}DBZ3c zi(fc{&QpLk-8Def0_ZFcXQDEtVKmY$TWcl>AKKb+i ze-=Z)3%jeJVh$wS*?Qv-h;0CBe;?uLY`yd6|9?m#>;wm9=TvZGe=aD>JHbhzb1$gv zm)0}&$KU_|(|UT}fT_8lXie*B1+|gWdX|DxOIpubP%@s=4^F_{t}MO2ESP_%FHwycM8Sp!I(#57g7wVi*`AK|u=&S@GSFkPXXX+$YY$a3Cy;>BUh{Id+66 z?7!$9&`~1)MR$PqFF|@$|4l(b`(Jbl*xVB4*#8gymrDFE75QH(0NL>RVm)XHxAB1h z5O{GM)R#IU0@2*~1{4*Gf4DFSbn<=yk)j_!r0g3IY5D?0+CFh%5=iS26>tSnyewc! z_5oOs^$wU)y#c0luedM?%;{(4a0MMI#IX2}3y5Y}%;3r-(Cf;vm_tR60) zvqBgd7J?)f|8WLwbN%4VB*4kSaNs!W7iW+xw0{SQ@r5e-pk@;&wjp7=*ujNKAdB(E zjA#aiNYE|oFbXg*boyTC^?lyyd!^I$Pp|Kpz$~_aj1J*khyS7nHZTgjW;Z_IfHY3Rzs>de ziv#MQ{fa6X9M>2HUgRN}#{@C2+n1x$_X;Sz_}&1UeFv0ed>;^RLmbEkXcltegN6<` z3w68l1c!Bh4DbGBd^@aj>zR+BEO7)x?mYk^=brfZ|No2Mply$xtrtH2|8IS`loz_s zv$GW>862GTKfK%a#0Exz;C(OuGBAYwzYaclqgXDWxAn@$|NjHB{{PR&kk#S;!S48P(%Z3>QQ*ZRNE!wWU>h(pFc=+p z&9s5h66~;29?Q8PNAb6aGBPk&9WLR6#4t!LGb01T4i-iRhJaphMhxuj1^F&8pttqS z@BjY;x?4fq<^#V2UMPdMV;zxzm{p<|{(mdT8IYE3M#MJIMjWt0^l9GJb~T5904y5 zg9l}x+d&%NfHs05hEJMp9gLX-{(}dXN_jfLcLegcYzLhdW5*E8-vYW3u=`wZAY&J! zW6OckpW3II59&7l1RdE@Ufw$wblI5k$*#r)TNwp9T6jTRkg|8($GWd|aX7S`Ed3eY zyB8#^eaOGdpV7VLWa*FY69->1eO}sqqWK~7jy+o$1wJq7KG7xM&~mBtCsgsl?vGfN ze_j&*nsqnJc1D5jUeI)C7Sn%IkO#UizW@~wk&)5ezJEYxC7s*~3ay0_?o0xQ7s`Oh zEQSnF;}BF}T-d-U0A4^b_26RAewOZohhc{}T>+hQqzV#*?yBi_( zfG$&y1RZ>_pMik^e58;tXnPk(1awwsuj`F~7kV&}*`R^_h-lEb?wL;6EAC7J&FBAj z+I|7K&h~{d=t907i~=v#M}cax51pk)tUs5OSjd$WEWF{)Bw&5GL@^-j1t&-rbWBx? zBO?O?Oolz+#l|iF|3`+skUaMPzeB)_^&r zsFejiZQ~CE0|R6m-tiy*|HDk>4R{d^GPP3{q&`CfJOcnay~_GHe+y`e%=n~ZCojk& z3qf+(2W&cBpQKsJ=D9Nol<;+n*0?hXWM8sjJ_Hdy1QM27cmd)W!A{>h+N@wDAbmGp zvq3@|bU06NmVLMDgZ~EM89MGv0>S@F&vb)}7I3-2Zy9@`l&jnIK{rE3g#ByKS!dl0 z9TG_T3RtrKXEZ={g7pZ1^l*dpIIw^;aX^_8$6c?0Tg3Cx0yn zk%t@XtV%81!d@%@Y05re!|3|tHLHzfJ-b^Mn~g#3SCG7fTiA;RkUYAiW$cL(=I^mj z`1i4Oy56xqTp|f}=l^n+7gxZywSopRxL$%*n|8aibVziUK6&vO)c7;#F1_>OE|~Sg zN&!?0b7XjcvcwB{1_tAAmZo>ynFLC;ve;gH0J*ZW^bb^s`^A1xQ;iiQ4+|*JkkE@S z@*q>#UOd+a`RWV^1b4frh;Sb$mFV_S5$JYN;W57S{Uc}t^Y@S4hm0@14(LA79it)u z-e%>Z!g2BC#oHHOUA)14vh>UM&)p}!r>KZb{GOsBzN+06FFR z$L`Co9l;I|=?;N756uA|zNe^2be{lu;`=kG8wAQZirB4PRCvl5{fsYlABzK72`U)3 zY+w{P{9p9O21Wt!dD>zBO<6#bZ=!cV)0rPyzm-b=H+=%mQlMsM_XoIw2OtH<;tnqa zO}v2yw#5Seo4$do{C}aD7bNq4A;>(CJYT?n(I=q7mKP-Q|D~KBXzOQ21}OS8|`g~YtFD29qU1} z&ELZRi@w>wC=lO$47A-h3|y3h?cvB0cwux5bkN+fxWic-852Ok_y~0C8u*NfmLH&& zj++W-JRsnOHOR1THysF17s4}v@Z=#p8wgJj!gGM|m?1nD2=D7Ukacbz5Z-eLFUA%W zO(pCBFRpuHa;iGSduN9#8?Ass*KNP+I5AVhH#z3d$m&R#-T4(gtmc<$Q4rv@GO6{Nb=} z(~b>{0vS0w7zM)rU&}}U(Tg8|veC7S5HJUv`>tj9fH~m#?Q6}pAQcSVrXU*@gEC!- zShFoiAwvmovn@yw1Nf{Bm4zVhEC$Ie1m(TstRRhrEQ|u3U=M)xLbCs2sN4Bl4uW@B zgY*P|0s?YU*o&Bh|Ns9t1!?a#1(h2AuR#h1sAK;_%me8H$$;Fn803!s*T8nXc%}&& zr7cPBwgr_V8Afc30ua;wU;lq$F(^nu;pPI%J-4*sxyJxC_gw!En%I{H<(z%L|NoCf z$vMUc9A2}($ovgjR}vQw>G^_My5X${Dk7~}c>=0nZW+PaLjj=3)D9eBia{3!k-c zSA?wn|G!fL>tD@I3OWZB=tfBs^;e!xRJ}= z|NsB`DkSWXk16}+!ok8=a<-d=BNlXA#tu+96a~69u9lxH`}sBDh?1L8|&EZ}Eg=yl@>_%HecQf4rB#yCnd6A zvtj4UfKBdou>k1ZmWamz)Cql6Tyj~- zSNUIB>Hbm&be^6=mcolbkh>x~T_3a_C`s()0sEtqu^F6|6uJc)4}tx3s95}%0}Del z$NysvtX$0r91P8%wK5N0-#z%8wX^mKIJUaaLyiE0o~CvTlyJgdfX3!P3yWAlK@E#$ zP*ws(Vwf1Hb;kX|Qw<(}JDx}}FuaHeKpPYRtq1@es@rPv^#A|Py%u27;3;I~5ok@+ zIyMFd>*J;JFDzF5|KDlxLT(i(X@lqcPrv#99~7tEpwsighh+W>?v(*2s%~aT8td}0 zP+>ZG;b-Sum8bvzcl6pn{r|t=CsPS)!%xOi^=@B*?-#!vWGt0^acdm2Q7GXDorD1@zW%X+dNu-KFCJKf6Bd8VOwa~Q=5H?SEDiiEJxmM? zFE~I;>>YB9Pg;72^tLKI{r{hT%E5qc=Km(_-5>vpGHe9(YpoCRw|APZwl=^}E4mk;N5(@)^^Z!f8$QUuQ4KW~joT|6s8fk3g-SG;453 z^0#_|>;W6f)C_TFiDnjWL_jB4{>4@l#WG-bfc*C#?3!*@0ZXub{7paq{{R1y1GJBm zuS*!>%8n3zlWx9F7k-PE@0l1Fj8AqmgHmtQEn<8RvFgwWgS@$~@%LDR#13nN3j}Ld z0sf|(|NsBLRE0a8iN6&Tq1^~agH{VPF9oGThVIi4muE3Stc?RX-kE>9F!Md;a}f78 z|Kci@V%!Rf)hyoVv`&d`u(z0xF>VDJ0U^^m1(=UD?ga__1Nm$RsXogDRVZja1Jym< z$Ubui`3%%4>IVB3n#V!$PzuuC3rdDik0E*M=l}m;Z&`Ow1u5X)F2o2<^~~o$UgLv! z3a58gurV+c9WeeD7M2mp%fQgv`{DKf|AD=|9zQ{cqk&fn1auz^2+IHsF++3a4eNXS zEub-2c&v6G);<={+hPjRj4f(Qc$l|>qTl*n{pr^{kZu<)nii_7#Lb_S8!O%f*PAeQZHPg%Cz9hUR;0r|NrYt-G>kU zVC}VmRCS*r4Yh7|{(UY6EX@xXOO6G<(ERrQKlt#$ZgxoP6gt}!{2~v0swz*X>yy{w zFD@*ChsTXY5dZi-`46kkL2I2s)p*J+@FN)!YcOUP*{@Uw*sYtKypTPg6KLY-XvTS4&=yd&IeY{i{ zk~~@ulyC;TNL>VqhV!`2y#Cwm`lCDaPkh`-&`FvKpnzS<3punq_D*N)188em<^>nT zH!t2Vgn6+0aQEex3qe7Q++u#T5bWz0$KmQ;lsx(W|Mf+1iy79@0ChSJK4a_V=ildJ z2)+*OSip;8;3;6xk!jE%mO+`*yZ#S!daVHHs*KPFuO(lkB5cToSPCAJe1IC*ce+C# zyb$Mwp6kZ+6@H3h^BalaFwoTwjKRVGOF24YKXk@^c~JyjnEV2KNONcC8&F>?611ij z)Uyo(Z7e^be5(0~0BA+5Bj{+KS^@13(!cmSjxjJW)N@#W=5O1>z`(E@G~OPZ#qnR1 zXCou%G&ImLP@S%SS`Uvu!hC@ zb8&VssKhAc0T~$m_y2!zn1GLL23-*V5?%A@|NqE<|Dqfl83n%IkUsGJhR4iK*AL2v zIzzvJmcnYjI5;2VSCH{9PR>Ux?(bylF8!cf`lb2bfBu$l|Nj3E1|RVL0%YiiKgd?y z{0Hj7z64R=K4kM7neI>lP%g>(7XiATI`oP4hf?Vm+4CXEE&yyHtN^HGWnc&foq$)v zVQmU71LR*g&Ife~U#Pkrylw&9CNxyalr5 zbp~kf@&EdAzs}e%)|dHP*jO1Dz8z%ZZv|aeWEsm-V%p_mpu%+E!jBHufBgFncx16e zmPmS(@^v5jegU-jyZO<-&QO8wSf0+<53j2rqNS2AX3YaTvjJ=qxOJ}k@c;kUX5EJ_ zzUcH7us&GC-03Rtzf|Bwku(EC_l?NT*bl9@OXXhpfpvhBR~VQHT7?8E4M9!EEbtL% zpkVFC&wTEiBa27(;gYH@Pu7N?TqQ-#wE_$!IsbhH8fyPB zbZ~d`_c}8kcLgnBV>sr*$I4X_(9PfF!>0;T>lbDQ29SwrTv-ggzAr#kRX{iUe-pO4t z_}v}*q1*Kf^Wn}|flk*i);IW@FM^tDz5-p0Hd6d8>lqjrY{V|7&F)F(oR=_*SY^)1+o}=U0(!%+QI)(U0uorD~U^8 zLD$uk*n=t$HWrT(k%rp;TqQe?G4pn_clrMB@L^+pt!f?np_C0Qu^@{v>wngNP&_^S z2`=@(!Ys#_d7ZmmzgU7=ac-c>U9_7W;&%s-Cz)Qeb}@D^J9i(}cKy&$`wx8QO)Kbr zPS8dR(3r7z7kA6a3N~kOF<^Yafq&b{*Zj>tSn7qXV?Pw}dGl}Mc4*kiVFEfB?M0U` z|F%>7`#M3~){~{YFFwx(C3`K<)`coZ>l39c%r{;;bn}06F=X*5QFZPJF=TE2!BoT9 zCEU&55yGeH-28*N6vk6?*1qvt==+VCS&TseAfK^AeAeyyL_7AwMbK3ZJi-4j28`sF3) zM4WEdFWutCC#?_jPd&(d0CahvkC?&hW34AkxYL^PCkjTeF#`PCIhub;@weOrjWGP= zEfGxX%o-PY{^y13%;63AdBlI=x}{dA_85~ z)a)w21WhGOy{*dW2NFR-p&N20#M=i;_FP%(Se(j z>BY^dZx~+2fQxO!77kE>9Uk_g`VA=le*7;9SO^*!0`2wT{MG;pA;wZ}(7qv=Zw@Yu zC1P1jF9P2D|DVO!{o{o@NKd5o@e-M^7uFzlz<==3{H+II*{l1rvXnu0}#m4XMt4A8|?pdBKhV;@0-d!UA5_m5^!=>myZ&~^yW z3Xgyn7BfH&JMsUuG-fzL?nwCyI-n9XmLJ;vrumIeSg-4rfd8Tr8yN+%9I_PR!d_^K zf|?%2C#?@c#s~R9ccV=J4UR1aMR_N<8n!-EY}R~$wb|qM|Fq){{~5SCJ($yuJIFI| z9dl6R>hv(~@X+rz1*va7;LscJyYU#v0PAZ-XM@AjEPbExxA1|p=nK$@4`{tiw*)xf z{b4@552O;*4!&2z)P2tq-14u@-menK1a{$zP7#p%S`U;2LdWiUJHR^Ftq&F(LfzDy zz|7E)Am7lOV9Wq80PHH665DRjurPR7@Zd8x=-4n= zb0=H3>z^)$4mTEz5m5dXP$LsG0P6aK#r?nQAM4*Wea5%DKZA?_2U|BA$Zl?s-E0kh zEDf&V6Yzo-CcknRAu zPypq@Z=eap{h&w&pPp+5i3sb1#frTJJk51~7)sc~{)@_hT*uyg%z+gYO}&0!tWOj@ z`fvWCVDkU}&FT!1&Bs|9|AY2%*6fT2O$~;2zX45JfHDL)4fl#bI(1x-P95{PE=Lun z1L6GJDp~mV9cX+GS_yvfN8@ksH3I>Szd_CL5~s%B;G1aFKr|CWi5!S#VJNk2@MLM| z=IZq^ROu3Ic+}W%im^lRwK}S3Q^P5y*Do3pS?XW&B4ipA8(ROz$3=tAp>JSdh&>F7 z09A$q3=FIwtD#w{+ZD3rrgI9YPysjl%&!0cpCtxfb8{hpNuU$F<_0`Jp3$JpD3J0W zbaes)Xm-3aM@67FM@2yUL2y_&c%hS_>mTEz0iCHY{+EI-(u4MlI$eMCx_(Fjt*~b} z-0k}zr4!UZXNZiCkBjbp8XE{bz7%o>z_DhbN9z5>0WFFJ$&bVu`m z?#c6g)BO8iNqny-LoZ8ow@;f$uS=T%=n`oLP#@@IiAeYL7gMYl7>>FAXKX!C;ZW}m z${shGfBY*w*HHVPp+p6g*#DIvoN#;s#0g^P!f6zU35MxijcxcJM@KW;s|Nqebk3bmcN9Qi@P3ll zJZ}8k*aBXNT7f1RBTGMmCA~ny>dikYN|~FFF?IWX=!rtxrqdB4AFKD?lxSWUEnFF%Zm7|NJyYxfL$r91-X<(y-x`eDw zm9RT>akQK){R&?GR$}&=yWwYQ2~WdMSD4KE?h76b76v81y8FP|ID@++tR$mL89lnV z9Gp5pn!h#F{$Yqicfrkf-L4#WBcEquY)%buz>|yxIv%o;m#5H83phObXx_Z$H ziU;2xpj8eW-M)W1Jy>1~g0`K$5V2-p=my=$%hAcve1r$;KTyR8TR6NDyjB=|bwOD7 zht~g~n@36(fEH?nP|5BkP-`N-vg70Of4ZZHGaZ|NjShyaX2Vpp_{n4_o?jl-;!S<*8%s zcI5%p(#;1rEnPXvIbURgv?{$m9B$z&Q6kqJDq?)V(wB#S{sH5Y%~rbnQx10jU_RY_ z@P!#D-PDRS+v|3JE@eB$tlL}62s%EzPPEI9vD@`S2VZvsI4T&r7~DG8j1O42O7KrP z!0gJy{Gqv)<9{h@^XdPuS+(ym$8uy;>|hiK4tsH%4?JbjdZ4tV*N@54RiK3V7@KRi zI}a#o^}F3!JSy3~-#F&RsN4J_rG|TER!aQ${|s(S-G`cgaDn(-U_NvAAqF>=Z>(1W zm;@N(OGFwh4NCd5n0p-=0|H+tT7m6A2@~T3FQ0>(m7qo40WZ#6gq_me{6+^{+CkR^ zbAWEI5ee_E<;fC@4(|1R64>i|CGdX%%YTCx6YN0dinM~7NUHtd9`?11|NnO%ex2Lx z3R-OS5;ShX!hPVii1Eqh+AE--)IMl@5_Ep!LG6PegL?fK0|Sf?KqL=9rI?R1p9p+$ z#s*|M$QagR>(Y+!e+MC)z1ao=xC-#*4>(Ba0|?a$Ku_(9`C28Ivy#s``Ys~cb9K3J-9 z%#F#Q+n=SIz477y|NIQi$C$Yfma=w+zIiQh-1W^rW(J1W?8n@g44aQJcOP@>bmg%O z6)9!u5O~es?Ry7argC<0AQ{r>`=gx0`VfEH7SIHE=oj$4;rtBEwIU34KHcFg%Ar5H z+4=YT{!w=Q()b8u?Ta*928Oie0|xxt4>bRb-AdVxF&KEfeh2DF zi-3eJOW7L^TXjgh=KOcCgX1;xKZpNdmkRzb70EIPd$Afc27H8vxm2Xvm8Zdip_Hln z11L5&n!rN2po6WOy*r%c#R419(p*(gqEWuA?fND-Qu<;Ae?5!!;UdmsZj9Q^$CzI8 zg3E|wOy6$^U;cip``iy@0EDBy)AimJ}o z4D?+3pZsr&!`Z@pcr{NiC3 zERXe{1?4eNqZL|tb{~5QTJ->`oXV~@fCkb;txI`I1&j|g+v|eL*kftOStJY?P|D_R zcb3)z^)g|-af~ni&IDIG!7oHi89*7M`G`RG=hsZ2f|-Y*E)cBdCQQu-6Hv|e0a2QF zuyxn7G#?RY{a>l}{XWS4QdY}ao*L%PTAuD&p6jVpoOzBic9Ar-XD#{*vEn4^@hBL7PzdO_uDSaA3G z7oY=On~w;n#lqWn(EIu671I6G=nMGQ^l?uP$06Em+#m>|J|92mM zITcjQ`3kh2EO{2(9ecz0fH$=G@3k=iZH8vyKKYtS`$Vtnm4N@HB7xyA4uLMRIs&N) z*ce`ice~#3u)e`R=V0@XKQ+46t|GM>EhkI3y4c-Y4wU}vaQ_D?*IloGF02sgKKH*= z;6**Sq7Zm31Ct5(Un$0Z%vbzjzAfgHB6HFh1Y_xhUwn8xId?TeDwqc&|!z2h=o)Pa54Qj2yZ=I>- zU?@=sB|6Y`b^;6xC6Zw;rcC(%zcG#H|2xoiX%Y<0Y7C$yzL79Ju#R9u8@wZ^dlEcN z5qr4X0o*%X)yxP)Lpj7mQ7ed$f6JTAhE4qEblZ&ksvVIZ`r`}x95Ui zWZnP`Z9&J}*jN}#41-_1xel4XQrHMOtEiM4G8_gPgkxz=eZl}bvZzGzg+MFl4wWaJ z94|h%z+Hu9Qg_pb|NjqngC=$7fb2R98s0Gew*LnxLD_t*F=Y~9ES2qL`C4JhB+zLA z9zgkT(0p71w#2W5$F2FFO3h{C0}kH84Fo02VT6`0E&>g@BjY?Ejh1!Vtm_D7PK*_RN{pvH~8|K$(*pu zZvm{=C#?3+ZBmFMrf3c$(6m+05OydKu1G2bYM1U@Q z>;zp+U#blbi*Q&tl!}MHnA8Zmfw>v3sWbM;Ywk|F*X-dh%0W`lk&G9h6apF)HGBX6 z|KTjI7n-2mJ8+MM^+J5sY{yV49PmOJBnevdo+bF=|MdU=BmbL0mJ7l%9jFlLcH>#m zTq3uEqeSX|8OICGMsPL-9qVj-AhMGUl>OaU;2Oe>zk!Oi^V9$T@BR_l`oC1&lGVeL zNuW$V?7yf7XcKH3s5N5^5oB&Y@xS{6sE2V1X&5yu-1r-)umn%EDse&YW+vXO!)Rvx zhfLWTZvwjsLv=Kmr!8enDJEE^zK4uUcWSmeLSh8KbL|NlpVd7UyF++Qz5PcW=+K?#QO#ShRW z-G_I0m@^3+-T}%v(8LqU5fu=QcEwbsAOgl*N&802Fs!umAslQG4$H|89qh)&nJC zpar=9FQD%Yelf$4f#JpC*Z=y7^6U#WdKVFfA`1iZ#>MU9L)!KtdAAF$v(-$?8^~neXy7nB+UdG2l{Uc zG6S+99CV}F1CU$qy#f_q;0s}IfY%9e*L#qNIJWghTsaNu>6{Qs)o|56c9jT6Giz_1VG#DyT@b?E={nC{CjVnGE+ZvjU)+v~vY!_6=FON^Rd zu$3r!lnA}JUI!}Rz$3OT2mk+ny>lPPO$$N9Yq5PGk1hleuetVtyt)uX9Cv*J8bS?# zIK0#K0ccd=0V99QHPGU2*9WESSG}jT)u{?v=5KtU)AdjDZ;leK=HJ{U ztj)h!N~OE~6F~d6-4mLBbClY3`=@mJUO4Wa(!;>O020yZ=8ZVUDA4J9r<*tC7^46) zm}}{Kr&hAt_fGanp6>9NP8UIr|E_ml-+l4!)c^mTY_R+S+6DwNL;inxO7Q>C2a&HO zyJI=JYj1!nM~*Cp7u$Yp25z3wcL);Eehc7SFYtZx)EbsuIvY<;+h zAGGr3#hhQDimvrQsaUVGfc4=bJFsXQRJ26@;D4roUXvY=#_VFS<)s|mwxFc{|9U4| z^D&mN|JN6T9plOYY92y6SYcT#Sqd*cfkH8|`$HI#ez4-!Z>6kR5)pBSo7EUVH^gS~ zWH8)d6nOFf`Tzef_VxY$56*+794|m;NFrZ}3#%HXU;O`n_{H2lSh$1gtL^{)gC~H# z--z@8x5al%xWXu~4@87xz0eTS9N#emqzXjfR0W=xfD(|R&X;3{ZxjPVnk6IXy6@P- z-HtZQ2OBK{Tp1Wj-y2`r$;}9o^pN16a;W=IENt!pWCTRND@V774f6^9?T#YBVf@<* z*^DoR_quRMw0uW_XpoJ;O92f?hm-R10 z;Rd4sWbOpiuw)2me#3#7F!^D8Gypz3(g{8n5;R?q!N9;E@InjRXa!9Sgmk-d=z_!o zATj}98IINiC05-IDhFT4f4|WDfDxLDkmp#yQ!IzUCr}z+YP4i!U^oEwInvn*#s{!y z0A(4F1cx=x(TF3A0wr7y%|FD7-hs-$ZkD4lM;Kw9g2p$0Kzofr^9SG&QP6NGM|jwa zouIMcAI7&MJNL5u`~TniH-AeGXfZnTi&^@hBS?NQ^0%-<1Uf+itp`eZp?h!mTi!7+ zFf{%HZ9L|0F=J$4$e41JQ6NKvnSo({fhUtd_>0eapuRZ|XjknH1_p)<&;bVf7I`uW zgaxN9Uggas@LyD8Bcs4>(1{GK2TJ8LQjUU0lXt`PmGHpM>-e-Abnk1s9s`3zWb+T3 zGOe%|ReB5zy)3Reo=gJWC$!IHF<<KEJz>5jGAge`M50viB2sz3qun**{ zu<#emU~$mxuuK1M1$j9v{Kb16kXn$~#0&uiMurTIql^OKFLJcO!ms1N+p1qyf_8m; zfliHnu>M@)*8J~(iKFp>E*|%e9#)Qj|Np01%D9(sIM{*~LlwOU?rjBG5YXESk_+gF z{r$qO1Jt>I3WPxx+`@-_Rmwp9^(^KW@}M1=%|94RM6;M*2!S}Dz<~yPsaOUm#9yfX z`~M$u)uc42!34eHwcD4Y^<;^3)&t{95#K*ryYdwCzX+@V)k2VFcI;vP^`JiANAMxN z#s`}JJt!9r%i@4-_y?^?Y&}qF)NBj7m68FpzG4EXG;2LjdI{XPDPe=1&ik6T8$9~K zcAODpHmv#E{6;41eQ8~nvu@uf!Ci9Uy>*PuJJ&EU zFfi7MgEsKlWdu!^m1;HoHYnjd?kWO0 z1nsq17rRYG39n5ASO~O;?zQr9R}s*$T(7yBe+ZO{HvBdy6#%PZv56>U>xyRt-H&+U zzwxE-r6S!MK@RH-{lUNOH2-!UzwV38k621L`1hS|erfPpvhg8k*+PjyulM|hpOXCh zPM0$`zf@>Gc+%Qeq*$uiSAxUR^-qyTDXX>bk21w(UkMh_nz>TtZdVEa6&$4sAYKV` zcP$SnxL!whhyLMTA1cCpu`~1!$opNOJ51|Wu#`%#U@Wz5e!x`1*;oroOAY0mtp`fA zEldBDsl8Ax`~Ux&s|4e52GDV|C2Y;UfBwJb|K=*el*JIiKlK3fu};@NkfgxWX#o-k z?NU3g-L8LLOTg3^phy`*=K8_KuJM5Yc;DZW^-TbiKq-619y=z1*Ng!#)`Q2o zL}Cx8@xMP17~FlZ`2pw}-}>%D&5!C2zF;pghu(0V#$V5v#$WF!03sAXgaL?f01*Ko zA^}7c^mc+~^*u^2v|cJP?>=0kJ4Y!5r?~Pe81g%SU~%-@qwW3%gv8Ka*U2%rN0io2I*n! z^UUAEuDTpC1SE340oUy+6ov;ULe&8>I2)r#YnY=jR2a@#mK%r19t1 z6{PXkr-MA45Atw1$iwvoz1u(@?%fX}y-F9io-Fa~zIgBzOY5Z)ab)}I*?Y?bdg}$e zO5Zm>2`8w(;mX0%DctG$r@>AMH0W>X z`e(Be|C9r6p!1b&{tJ%~4CU#P%KLe;FXs~18Z+XeU zz|h5Jqfo+P!&%Da_WHBm>&IXT&`MCyd_)N+XjwnVfdc;-jc+>}pM1^a=Gb5tQ}Pq! zS~bvGXL*n{B|N=uj13k_r7YbaUh^6sFurZ+`UkWV&h^j5m#rsDggn8PGsQUlXKy`O z`Vr(8ww41Wp&-qr95xCi-Zq>i?ya{=Z29*c2xz@jqWoelXd`9offC*>wr*b@i0A&Z zBYgUr_eB>}3A5XO_W$hNp*(IcLARHJ`s2r#7(gKg(jf4X57eS@<%o-JJy62jtibS} z5jvya{Dud-)7VlLlmtszUo7H+HAxougPJ5AvP%L$O%jC-i~*HQZDH?p z{bM0f%G_D{r#tiyXgNV}aBuCG7obxeyIp@YAK?LAlxJV;)$RMG`RD&)r|usIpELEc z@LL}&G7EU|^et$Wd!+T}5>2Rf=6~T~AOlYR1=Wxs13VTlab^GA}J{TDR>Eql6Q{LT6vc;vb~&C<9;p!on(nx#SM4=DX19CFS|SXh|$;joK8 z90G#F!a7~Q{J#!b|8}gzBH%^XThIWD>mTc5C0b!&;Ef`sUxLHJK*y|rhKc^X(8vR+ zDE$-sU(^6(r|X~BoKQBn2@MU=uot}hKt2O)<~0JX;_-{=4&~@R7;Y(0!rOd+r*|jl zyd>-6MeiFAf>{TPSu6!gn2ZmAPD$tv;Ap)BzD)Wns89NfwUh_6H`e%o!^Lw8+@Z65=} zfl|)nZYj;6H9d8<6JUH$C+~Hh?L-(Kw0`q-ob6;7zZIn3&$buFZvye{Z2Mt+&_0RR zdbX2be9%nyYdPDkOi=qk*AKtuv)uyXvmbX$0WJ6aUzU=^@&8g}XI;u`c8~`HL7cdR zXz&JfP)94Y`^^r}k|Rr34)A&H0WaqL19dBr*0VIf5dg1dXiohB&eFl*`~99d?B315 z@c+N{@eEf!TTu=)U)KvxbW01Dq3%zKAOT94O@c;kod)*kj z)%o|iFtRj1aELvu3%ZB1M5*}&ONn^%3*HhQj}m6%1BZ98fELT{5CW4d91IMHcL;*m z-T%YFyFX-r3T)&5y>2q$0WZ!sgEpG*Z$Hs}u(OT@G^$g&lEQJ>jK*Pt~$6nmK1{x;jeX;5} zXzUp@dsLp$S^C7<6nvwAS?jkFsW7BtUH*YmNeO${i%S`x;b)MdPWOy%uA5x^wS{O@0>+;LVA6V%H1 zFY2?AQNSYhPl+pNbx2~t-`6(H|NfQ=AuI9N$SCmocJseKr9$1VAG#e_5R$Ktfb=3( zo56B;^BVxoe5uVO!md;`x%g`76ZJ<2U zQ2UO7zvVLMM7PrSU3@Jk`CE>Gj!xd@_Md;-$(JiZgAoX0Uetpp^O}$FWS=zXEPc?s zae_6IKxgTb*VmOyWx@$R_ zk1>H-lNY*6pZs@y(0q)ex%L4|XX%rom)*xZt67dagD%`)cmX_=-~ay&KFqQ#4%P?DzJZLd`_I7NYViO6 z|6XUgo&P|qnm9NdJWGFeJF~nH{Qdubr!!AyFvn}=&SDNw-aFBK5K>fCgNmxx%Pmbo z>4d*E1T@iq;y)-KfDck!mkQ1ZA{hywjIj6e|NqUt5B|ScvkjCPI&1H|xbf%z|L*vV z?oy5yg`l(4Tsf?rS@>JRK~bRqI);JmDX7%H(R!P|1=M@q*#R0#Ezjuw{dsA#G6QHI zH0Zrd`yFPePdg=fF!>}HcP}biUpv|U{$5}z` zS5SsGJ^-1R0*!BjLMptwmLvTCweI!}pp|$BN|*yc9MH%cs4WcUfR>!WIR_BRdgB9* zyK;aIeQT`#QDVa+P{P^G3{}`%{Rec_hcjr16J%ubVS)d?9HrN~+pjP%FtCD*4ETR7 z9)2eY$QiBw`N5a?fSd-3;{kJ4Ty*#GcxVd)Jm}dC-i&K}JKXwkNxp4_J(EC5Mt2)1 z#HG=g3S7GiMXW%Xo^@W`Tzg_+dwN~Uj!@?6E^)DxD0S#=JHo)gpbN^%0WY>D{r}(Hc7}n00o-BN zJ|6I55m*S+KLhcmfDHrbf6663Y8m;*Rqwz^g)*}H- z0-)v52O-%BRM&zQ<`~}&@2(ZF{#+_({kx2}+m*xma0yeVgzZPQ0yNc}m&%nTtW__^C%=&W) zzxD4j?(pES7YUW1c;$JqAPH0!fVNb=m(eEBI~iZ^ge-|^{!zu>3Yy}u{>9Hm+0lAcoKKWn!MDtI+BJN%f-o~GxX{9pI(IGNfY`uHHnj-gsg75QEs1wtc{=W>` z>H|6r`Nf_XP>Tng@VZ?^x_x79sqly5;RQX`=*<*GxouM0o$!h4H@}cBVmHQp-+M`-kf3-2>u@+14JP1So){WWZ7%dP9G7#{TGL4Ei6C0lMAjFQ{?n`@{M)e+%f| zBI9qbZMuDbbTbC`hJFd^4ZRc88_E&X8~P$B_qEn9M-OXO8L57UsxY6 zXJ>Z3WBsE@5UlZk0BHByZt&LWZr3l?hxuDT8x24MZu0!ipzHAtK41+_Lsd8Mighu_87HYu7JDU%Oq7{pt1< z0c#Nm`d`YU`v3obhX191IvGLRq!|AP6to;DVeYJb5Qea{05sMD^5%a5s3M1M-zVLS z{{t+#L81LWK%?9B0l4S}6;epJJ4A)0+ed}t`ytSA2c0daUCc*1~UIB*OQXH%?FuYcYw1) zvn^=Y8#F8r8sY{IGA4(Ej=8lo-Qmn6P!jyYO$IzG=G6@z4{!Ze;s72ywgN36NX!P+ ztv_C?2fPRaF|Ci6Np`a}*jU(>3WonLN))(i#gH{=_KnnlBX4@_HOalK( zSzoxzFfc3xaX?LL_8tHKGccSyoW%e-2M1KGaRdhZ7mWbtF!)TJ4=sB_@^EU`d`Ws z7?#Beu6RKw9JC(jcKuK)(CsT=4c1bmU=hbyBHi5p(r*#_hrcBaRIftn!k3`+(=T$O zU~L9u$AG3(tU==@2f#Lg=G!>BT|ZtxptjbsvnJ(#_DxqH z>Gn~P2n`Ad49swdgYQuVx5JE&bh@Z;q;!Jz>VS5w8gxf+bo;327)y2+@HGGXS7O~A zqGHng>t6|r^`|nuUX!xkn9Ty++?^gg&DAUnrDwpScHPb#FD6MdFzf{70uX13Gy}uy z4JrMgF%^d9BNFkT^{qZCEZ-ejKs}uuAay0ipu?-XJvfX{yjUFe|9@zAhQ$8@p8o{` z{|iK3*nwAY@Wg`Egs2E~GVK6m)5D>lEZeR5zfk6Xp~U|}jTfR&g@;%eLc_v?yI+Ck zMT{>Qe>497|6(-gSb^?W-5hI=VVaFr@YI!w3 zV2M2pN)Q|`TET5unBq*$QdZXFM4=E zv-2-Nv-7;&jUdh|z&W080X+J4+n`vtrr-~(tyK=kF6%4Lf5w4%TvTfQ;^TK2lqj!6Ma8`8swk?0-SMRK`7Ae%F*kwzxk(RxnT1F1y}$c z2K9Up`#&`xDgP+w<{!`+W6)^~p!+&~@4S%i{{O$%^-h5CZP3b-aL^S5pjdrTsSG-N z{!Opzhp>PbrzAj*)cEH5hPhN5bUB8gMeK_b_HI}3X1*-uh}Zg{J^C^zqAagpd~l>!hbI5X_bwq#S!9zl`gW(ON<~MlUS=@!)ofnmm+*vG3Gk1Ot0=W}h%)p7z z?l<9}&Ku}r-dc|CgWVUIk6HgKJPkNo5)S_W4&0ive)7$eAm#~DEm?10Pm zdNQ;pGYXU>GYaG+GYX_6GYZ5cGYW(xGYa@5GYYtX{NU2}q|>GUNw-Vii%ysR7u_y> zZ#rH2-*mh5edu)Q|IqEy_odUN|4X+^-;Yk0{vX{geSbP#`v3H@fF`&Ym_IQ8fQ80? zkf&E9GYV`;W)wI9GTW5{w7X*h$j9ACY>4lnBE6mrAbE&bmm4TJpfVGBOAP!_5 z#(@gRi;y_r{rh?8pZ_XTby67xy8nN_`Cp~?P70$yuO|a6PJTk-L>m+*1}We;0V$r~ zkisaClENs^lfo#_<;JXhvBRCY`5id)-h)GL6Oz#&w?Wk);_4k!^Lr+6I3uZ?05c2X zgDy9ai5>2Y&F`3---BY7zX>!$jijU3li@)Mqri(4Mu870i~>JW7zG$o83i~}83hDV z83mL2&Fr>2~Rp=yd6q=yvH7>2&EA>2~Q8=yd59=yvJj>2&Gm>2~Sk=yd7l z=yvI2>2&F5L5&-bx4Zw_q%sP)q%sPGq%sO*fXs%)hZ}Q;J2N!IA^z=fXNLF!Dhl!& zTnrLVP#JJh2WlX9M}qeb86U9zP%8G~O(1OeW7ZVV@CW!5S4&r(vZvjl0v&AD7wVY0 z4>?%6@|5$vH~>-$T}FNZw4m!4n?d*S)=PC#pp&Rd*!cIIbbig8Wf0lH;o#MJss3fR zugEb5hSp0JLJiiYHN2qN90QLA3(J!4{}>wV3`<%6JF>hM3V(4w0kk_ZJnY4{-=OMJ z0MxdG9q}F5?aRZz-H|Q0`^Lf7EZ`x9F!26mRk|MuUYr8c4mA8>PD;12!M?aFcRA@^(HZjSDo2VcuGU*f*Ze6sO4sGZu$(HX$f zdadM1w=WO(`S`fBP7jV~c>B}WiUfDZ3WRmK-T_~i4Z1Alh0=e}up(r4RhD4$ z8=l_M3*CQ~E=^ndLPLdtp}X`>r!xotKG#Q`%>4UZA9OmifR@btFO>*;p?Mw@M4(&M zML=@_oy9zH-G@OlV{?5$r7CP?OVybSM^W_^D9L#=rK$=u{W&a)f%$&$i?1o5*yjQ5!ikPO{Nnt%|NmPLlyGE;y|{Sp|NqEutPb`}0!$@> zS&SK?3=9rg%r89p!8)>-Uu;f-Ep~#8{t7j};AphCdzXO$G@Sg#`~QDyO2U&;ZxE&{ZJ?7wKjM)*SI<~J(HX`Umv)Aa%92-NUS*C#L1 zXMvWszhP*$y#X3B>vVllTAPt^no;1tC}_y68$1UETDAop6N@;pi%3_#58AOEj)-)Q|`qSJc2L<6+OrbM#W_fB9IPp|KX zz%0f9(BNJP+c(!AjHR57z5)yk3JkA}7lPbk?fRlbGXQkWM2QmUhK)C%GZPkqTm_Qh zhsbb%PRYE`>H6lsDcC=t+c`i-K>ja%^1t)}SQ0eW4$9-;#@#+oQ1%8VUr>q-Z~ayR z-OwcZLfr?JNU!#R3UP411+>T)GA4=9$?43zT0{m;tr1GGx2^<)W~e+jd9>Fd_prI2oZ>wo@M(E4uU10aJynK~;V zt<%NpU&PCl|NsA|buzu+1~~`flb7J}aL}lBw<||lH`LJ5uiCC07k_~FPK0*9!3f+_ zUa-Ke=!FNas{rWoKv2-$@4lvefqy$kS|<-^DmbkbylV^+lvhAO*#*)Malq?6ke~!D z_O6g?{>fCw)BKaURAk`;@b#i#s*@MOc@g9V&wc-Upt%pM9n^{YUnU#FF=(~>wywjpX*0Q?9XGazZfE4bAdWqKaRQn zgm<(=Kz%LOKLLTD#Yw%O&R4*T$Drfc!9$Y*7Or1FeHU<_&-lP=5&rG2f0SK$P&!)C zCl8-F1MZ3Ys0eVM==M?JDdp^TQDOQ1@Q?sQ@b`z^kYWB56%l3#>ml=H&|OYY-wJ@2 zrGI#_rxrZNz|-yfr7KjVC4^D7R1Um!S_m|D2cLCNb`=0+98i)rKHz|@&z!{q?iyz? zyqE>*CW6LEU{)cOVWAS>Wrx<6>*PVh&8IvqBuY3OtWTGIYd-nE`;hiQ59>pEMrwlxj~!V7`h!;I$c$|O+n*9#s@58HNa!J{8JBr2BTPxg2pZa{+Fu!zt;Vu zv-X4aZ%}4<8N|rIQ0EL;{r?>_if-QPV$WdZ+W$D4s1Z+);_?8P(<28QlG*1tD4sZZdO2lX!S;uAau!vPwD$*TVU|9`1Quj>bpNlcJ)(jw5d{V!(h4}CfK!1Caw?EPqyhy{Di8su0)bw5Dv$xC0_)%40o(dIo5uei z{{OEpHvt77Ebc?pG2>o08YB2_wB9b+)yrZZki`)E;zA6#$_IrHXzC7>0%SlbphWP0 zsYF09Xiu?d#zyc7;J|{+rhp9pU&<5k;x%}b zO9opofr?YmI@4s(Ee&NXFIt^J>(@A5)PiZA7o}iY;6*N&7I~2hrX^m)f@rfBVIZnZ z;e{WVR(atDrZryJfoYu=W?(9R8O%ykG_k zxxD!81agVTi_c)%=f!I<9q{5Ym=1Yy8%#&MxD2LaUYrKg2`>(V>690{!F0xp&0sp` z#cD8J@M1BTE_pE^45n*dbUQ(u0AAkzq822&4YZK;MG=?@T7B~(9n1vn^?VTn zW`ZW3Uj%}gpabw4VCEYTvlh$*uXZQ`GeIlNUZjJWpfmkm#DJNg z8{b|8f|;P@M=#vKOi*3-!Wzs3%^|)p05d^LwO**goD4b!6RDU=a>$ z)&!`?Gl+KPHy&%&6tJp*7jry_T@VowYt|g7NCiYh!kV=JDv|~fk+Ehifr^AcL=>!9E1)7y5D^t?)*7gY0Yt>Y znsowHL=GZiW6e4RD#8O1aj<5c0TuaW2ll;-HR~Lx2x#6QvfItWnsotGV0Z_lMR2X?K$N0d@)u27#u^gppFF?_LoB`aW z(rSh*{9x~f9MA|pujM6Zl+!Ynr{roFc-NQpxiX;_9uDx9;MXS5Eg+y%pCdRxcchC1 zf=csJju&<9J!noBc0f0f`0vFXhMpT?PKXfCoGXZ3$MO06L%m*>&B=Uh_8}_y-Y! zOiaNC#zCzla0{W7;{`8DODQlgIL!E4c(*NRda<+gPdB^uv69qoS|V(F|KA*>s`WsLS+DD#uz(lEs-RU^tp_Rv zEldBD@cb|R^CHw1v^KBwPxEmWkR_!H!DB-w4>zCq|N0w9feA>#>kpuDk1XbZEN0La zUWW{hE1=G04rmh>Xnyf9Y?iTv2Xw+IJM`FTqz%fD*_=>N=Radd1tX}<`C^+614AsR zQhXYFxEp=`$p|zO((Uob_{0lk8}R&-KzD#fPa{+F;>m#FBJ>GqnGb@OcQ^ikmf zO{3LpW??85hs>k7Z030332Nhk81bM5$=gBBh8_tEok%l2kkSo4BDS%T1>}ZOf$l(_ z?jRn^K#o#Y@YLGj;K1Dn?3n~!b3+-R7JH{RN2fDOXEsl0U_eA3fEMy9ZZr_szzKpjAMfz9+gp*gAbbbbB!IZ)a$H3>uMZeEr`Zyh|ax z)AviK>yO6Yj0gVzFI6|bZG6Boru9Q9uS4?>=9(|Y2VTx#0ZqR3)_z#MI*Vy_#LEsA z28QO;I}BmHr5}20pDbOvwA=QEJ(GZcuRG(+=120(hYtQ=?G0yad?e4n&;Ytp+Vw^y zt5@@>2Tr{utsk=FjSoa>pK`eTpwst4D;s|cXn?%i^+xll|Cb+qztkGi`r$Qeb4}|9 zhEfi&8$Q3z>png+OFmNhBG~B0=OA+{S^b;OKX3;*MEkt)C8x`edVN|ybXzk2>a2ax zZE0Eiph(Fw^ucTC){`aLAXQ*#!QLuH&vp~2V12NbtGA~0gZ2sI1D{ue?z}5ubY6JH9=wT~7vy*A zo3*^g2f$&p`Xwjm>a-jD{oG6p4BD<&dP|>lpXjaquoMzFOS>-~{J{zdoTbokX->Vt z09wJ?eExs;smsrgyWUY?U|`S;z4QHgckF}Sn7JRijl0h&pYQa&(tYakyKdh*+@&`< zjk`;)Tz&>xJ6-#LxwG^}&E@V7Sxg6?G4=ZXh|)f1eW8Y<+px1_DkwZtrhZ^(e#zoh z`Lf#-Ai4gGH9>1*nU%y3#7{Tx9eXB$UPq|^dIK5Z z;SKd1$Tjo|?kpzb+Yv8=K`Vd2?y{D>V9zAL-ve4t4@)tPzMvLZL-#>Q3PDOB1K{9p z*(df)0?c4%f;N_c8obP4vBuO7pmWWLaOOEsGJb}XjIVY3KIpFL{m^}0`5Yt>zwh>a zz+HQz6C_xBrQ7$;<>#=JTzjMD0x>Ds_&^*enSz7$^5bsb6W?!2p9CF8%Fw`lvzzyU zJ(B=;?1$!`4yC-^zGu2azjQ{jbPB%aYW}HE%G&LFrc)9$!Ur0^ziW!r=^7LT>W2|n zpuT|?s35-&sR9+`;9*^$ei@_%D#%^dvM&aEf%*qrpn{xU^#}0< zYL5X@fm(yoLNz`aX8f(wwWHgW$1S|mbwY3LgHGR`&e9p3zFRtdS9JO=>GWOD={u*> zcSEP|6iZ)~Qt=mujNrYn{8G^V(w8=%t93&KIz!iVG1(}T@YrycaM*B_u-NdHGCRF~ z9lY6~i^-|^CpUk)3}_9qO0Q}+qd>36`G%hyv6ub zbVb8Yj8TOwz5xMa7DGk>2xBLjntYaN$PiVY)wyCUe6Ahy?? zURm6)TOf|(Z;J;nQ$%qZe@o*3|NpHHL-q`Ghpu@E8dm8nUGZAV#`Y4Ko=gTl?hoZmq+erAur$`P=hAhjy^MT>bC=|6Z?uoxW=z z{wQb9;(R&n-~a#6@Ko^dC=mdMCukLN7RSqppm9sz8UM4`BVPvo`~N?S{bk+1|NlEn z7rbT$yT0V7j-d@l*$%JQY7isK)><8Yy}!9ufuY15Vrs9~|4!dEB}Y6wO15-gY_46w z(Cs>*Q-Hta1~UUgmPCZ5>y|QshT89}HO&89zx)4p-O%Z}1>|G?w!5HX$Ro43BE6b_ za`CsG|NH;{>wTT23ye>`mb9^~SI9oe#lP=EuaDt{#-9Nq3=H+0U`N$He$BcwP=tY@ z(|68mZW~Mfe$ZKV5UGzZ13?>Ge7F2}-QeF~&rtHE*Y!xi|I#g>wFWOjL06c&&N05! ze1yj`bVe=H_Z!Tv8!ST?lrl5BN?3+2DP`7nm9Yic#ow>U%)nr~;r0Lj{5`tN3=BI3 z9GC=}UnX?AuCYGE-vZhmSIhhT#?AsqCV@`ZHO(&*Ku4&vf!5)>9DvM_VGa`mL-qllUKz$6pah*}$y~(O{kgYx$I_)s z|NHLhjor~5dJeRyZ$qc+l}^_iovwHIw>xune=sgRw-eNTRKBQuu<;p4FL-@NFONWH z=>-1mC)2F2^EWFnfi~(K=WiBgVqn<+cRz@Cp1&Q`E!wx=9>hCW&I>aAM5pVKm%L02 z494Hmtgn^IJAQXP18QNGidvSQC>5|QJyOaI_SCtTuRy1pSsyD^bo}nj0ahb!S5-R5z}skN^w!=u?z#fBgtgmsMtA56<^!Ffb6PLe1@*eH9_e&l z((BC8>AIlXbwPLN68`N%{M#9tQ@1eiuRqis{-@W3$@)_fi}eRk$7BnrV**Mro#lT@ znA85VH@hxjYR+B2^xt<*bL||4`rXaPnf|-Z`0u*{l=|u?G#+F+z`&5!da};EyY^1& z{|fEy(i`2bS1zVrIru`p`x^5%?hglF$@j9@b02Cv2wu!m3JTq{)|2(;K!h(uG_Cby zrBnC8?>G3je_%fM&GiZ+=(Nra-L7{)7qExkxR`tA-~;)K@40_;yIwi?NdDpr?n8c^ zwKqCluasW2EZtDTn%3-l1s1!mSIPyuL$81i#%E+`{Ke3~$WUt1Tzl<*sdjVfHHPNe zTmMUCnp1Bv>;kzc%`)^}DSKL{>pjQUd};q(@4e>u?|KVVtX$gxiu*L{6Gh6#2aHdq zS-MW>bUnd8vTQR4N5}Cx?S&dx}Hkw z^gZ`l&iFuD>o@)>2bxn)FnEC0FgLJ*G&ZLmVd!){(_FiTfxp!jeA34m{_Va;x=W9B zhn_n4Lg2Ojan~s;kPmGCq0SbqZ+3{%ii@u2VpMyJKZK zT^IB^{tr{Wkk(wgfFZmycFu9v3!tSz$6c>Ls2gX_{I);t+5>WO3EMGeCf(O8$DElI zz;~i@q_tiuHE;dK-+CUj?esr?>v0CqSWKFw?}D^WX8x(58yJNQE4;A@Uv7U%2(zP&9RJa6~BIru|>e>*6Zb^1Qw-+u#E27bTY>AEGYGxSww z>9fw-N1eVi_*+25S?lde{vDtKvH3*;NX-n;-s)y128L$W86n-L5B_Fue#p+>k`5~Q zKu4d>=swu#y9XRsX_g#Cg54i_YmY2l+Ut5`>3`p2ovwR~Pj>q5==A*n8Xxoh0UA;3 zzGxi!Y9A;IG(G~wacAg??$B4t*Obronh5f5{|A;zv%bdPyc}Etp5t$x$Hc&}4^jgD z<8Pk;;z3Hlf8_$k-#SZQbe2BpEPe3u5+l42bpP)90$jj}T9!Tm6@m|-h2X1~Yruuz zxl&>G?+%bckkhh&rIf|epk#|;>6+fsFUMUEfQvofHQk{Hm``?wZfLz!7uxH?`v5dc z?K`2w}Gz>ru-o?o=r$5YhQ^<;((eA}N`h8>A2`T-2e}PNC zU+&=2?-!`_`vxlgK7osyG|;N(<=|`xE&*3`x;_DyfDh10z!#uHhxl7-|AOWwUL1TW zunRQe3N_;tf75&>(7oHY{=055zSJ4Ip)>SDXXuyC&>!8Qw~TKppHRNg_!yMPdQG@G zW0&x6|CVNbg1^}ZoUktNH-j#y-vLQjANbo%nHU&$KoZu6a&J)8`lQqK!Anb!>TcIh z+_fvxz-z+3bhh&Wy}SiZkrzrO+`hZs0H<0$OV=x-kiGvwBqN#?~dl$9SrsR!PPK4F-~th21$(HK-KUM za5aoADZ*>wW1yJt1SQ08plbMAy>BB}D6REdrBnB@?>G3jA7K6fONbjl39%L_A-?DS ziJTB?VF?jZ4O5&D-Ebtt5AbSu3C=_au7*ExmoC9f0Hv=W)iCI4AJ8%VNYM`(9PM;{ z0XoyLJ9JLB>z3|Xk+e>x5`!7cKU)9SYl0eOOwgvI>w>i7PRxu3h}y`B>6jBM!^>jO zy|n*D>zK1d5P2>TC#Yf^;@OU_j`@c{xdKz)Mz5um@UW0T!0F?K6I(=Upd?~^Ft>K_S_wj=-l_fx;+*!M;UBZsjfU6E{QFLKL@*z7 zVPs+GcI^QTbAlH#epLXqb{eb=N?EAYEd*7#Cdr@$^yX#=YEk2x?i@=rbV z?^MGh-iAX89RWzTHxDZ zMi8y={V)Rq2Lp)7)O~sv12Y4I@~Nb3APfNqgJ4Qfv^GnDXv+8~S!rEJ{CUNdnYiUXO;!cfA>3)(Vw*!aM=LjonN zJ3v#n!C~RMLFRuu#8JY;efZlU7LY0y28M5km_RHJ5Q`DS5@28e9~brg5ZH}O+{e35 z?E^dUT#3B(`4Tbf(th*9#K1ID377T7 zQhr`gARcZ$;c%GOp@9Lqd%gLMNN`yA?3=ZJ(z;{+7+(ShQI3UyknM z-!CX1Qa%r=sChsg#dV-5%^NS+K^^(+2RdDU+|2zk`)2Kr?${sX`$PFyi3Y^=(h%44 zSRX4DYdi(=7rGA`AA&@2`QrOwWIuF<@^l|E{$^<1+S<>SpS z97<$#57aX-fJT)Zz^WM-7{RI;7?{DT85o#A$5~%ccIEhfN% z5&rxCe?X_}mlq#785kn@*TsJ6_WfdfspTZdKT+Qgg6sq(8jkM=p*}jr^8FyxOQ)E= z9|Q*!C{=+s9k!l>L_7yW_d(^upu`07kF+h+JBLe!pwYnvO;AkS*ObpUzjP>(ZF~v! z#!HYlPJvDp21gpi8z4Rt=xkGCUmk8(p6>_cnK{6@m4TrFwwD{UAC^P=MDs&_=7Z8F zxerFZymjRNf4C?+O!UYR$UX|t%3x^kBp@K5JCp~s|AvX{KnZ8_Ax8eyEGn-*c9(KA zpZwqJ$tI1WE)XWpyzc*CgLs;u27T;x`wuY* zx?mOMGA{wp`hr@K?wg&icRE8KwEnNN?>_I@V8Kwr;m~@r^lS6U|IH`d$&=GRX^yFWpzH^Il{wH_#0 z-1@&xx4HHOQ_;hQ+II}SuJ1~2bOkWBTq?QP?R%$-(~6^r#p+bqs^$YspgZcGyx!IN ztYcpFyi|O?q9VIjoPD3%@uEYC#`qJ%GFfcbVb;|Ddg_ z$V+gayoTR4#NPtiIT{RJ&o2V9k`ZhrXj4N!=oBml(DE9I|D_M$ZfU(;Qc-7*WWO=o zeqSv1t6|s=Sw-9Wzr+x! z1YR>Mfo(A;(sHTy{{R1Xhk}+%UB*~0wH>}(%1;_T=KeeOKlq#+)a6p3rIN_YrLL6; zy^us`Iu`r?|7&m17Jd%_>(iy8pp{UdvmrqHZRW9pHnksfAXFN-%!XO7NyjBbk0NJ(n7HnvOx0|hth26%s zM7HHXi6rbag`3^Sv{^xCHyr%I^!)~Cbs2*H`U5!h1H-$27$4|9-}=AA$CAB-v-u#4 zL$f7A>Gv>)ES7*5b)eIVK14=#mi_@BxWxJ5r5L5ac0i)-BFV0;OCp%>V!Y zzZfiXxHJJ2Wh$1kJDiyWN@QNFyb0dL2_lQaX+~Cn(VNYEB(+28dkS2 z&dOp5{|`PxyG#FMF^0X^rw%$S>i|>0 zi)m9KJHxDhmjr}`y;us92!A0k9qgXg1EoQrzTt80W6Z8^IwTxG3lhFxR6Y?H{$EsN z1Nd?@{$}4V|6hC*2SqSwdwuweccPGRDB%MI)dNw`0(y?fPKj>UFE2qye}Oi%2ft7S z9a&NO=fwfIyzzmTpo0maDF}3SJa{eqao0b74h#&P5}gq&oe>HO^LF3}F0^J;- zQLY!D6czCXY{pcl;F z4!)M>KFxfs)1b40rPG7M`dSgIb?6IFA00F$#Q@q9(kW1)-d*|wWMpq0qxC=dQY(at z3YHQU$4(E9?$S44Wisa0_lsDp@1dF#0aeB9`vG)_Z})HJlii=dP6D~{HuEL!dz~H} z;PE*DP}~W0MzHYjGwkIt>MVU>eX5AX`W(o>ACSR=9}JxwB`Uq%pzhk~BG%@g(C*sJ zP7l!e?p)ozUmQDq1v))A{`>ND`wIMbW0-XUYogCJ`OYU@5aCBF2d_NBA5|kQrd$4p% zG`ju)jWl$B?7j^0!hcAA@E`MS?o*u-ogOTmF!d#u)4-j!v`!DM=#z)DI5HN1j(iJh z{QuwhfF&!afm;&x&6UHU#KV&Hi4&7Ri9@IBAO3Zr9Iy32jYx)W23ydTC?#CBvJ4Cd zz=!x6A8-I&j?%0OQqRzw%CR4$9(v^BYYv#e3!_HZ6*tXq1V9lQ92O2a3lzHCcD?8S z|J|`1t+z|nUucMe5;AyE?bUG5)f7lKw?bCcf+}3lbr#=BF6{@65Lwm=lqtUWAOhDn z7hPZX@z=54t{<8!L>NGuO$5S%Uu1%=n1Eib>Igfj)k6Co|27t5(4N467c=&NGAI8w zHVC(K4=6_pxc&DPDB)=RU&3PjqvVqIkM85IcSBdPg7ZzV@omdk4)7)ryRdLjb#Y$# z|Nq_s#;_O8njixsBdvdz$p0_p2n&Cq1-kSSJdO>znU4pOFMGp4Q4$Y7$q`b|3V^EW zb`Nk?Hok3rxK!cAKVi6!SBIh2r^W|f7ld_}ezA<>D76Cb>Hv+qM@GK3<==Mkn~RYw zONo?2mw?sf5-a}gC;7KsI{1)-`;?_GPbs&@Yf1j?7cE11N;n<(w{fIdpDsPkzx^ag zjt^8}io*g6%!5_n#s^Rq8iN`op!L`Pn~(6s9zFv;4&n~yOzA7`|BUc_egxSWaUnbq+kHmk$sOiahD9+Yx7zho@E zXnfM@K?x`5#+?5}Oy6&IAB+Z7dZ0!)67F^SAAJ~lGevKT3WM>1E*BLJ(5CYT9Ni(X zbDl~>ySPASG@ULv-*U1f5PJC2YwjE2-G>i876{|M(ap90|G$5A-3PnB8=q$W!2GlM zUs0AgDZeO0(ll)T;v>qrGfALiio)8v=fMj2Sb}&GX^5%VU4J-{h z%KP;bq=US9U+hIl9P7St@E2?I1Nnmw*t$igDg-hK@b7bBVln>ju6(@Y>T5>F21|vK zUtNqgi6tC1MI~Z3<)!UeOwrxnjV~#N@^H)k2w)O;o!ou@_1o5OC8^-qbcK@O<`>*0 zw#_fhO9VYi)Il7P<`-=ItzMw!SS=4jscL5}&uiV*OQka1*Bc-I-~a#rYx&korF`Aj z8&89Jj;{qAk{{MfG1{wta z&{@jU`mI#p#ccsl$Vf0SFuZ&QS{{)4hv7f#9A_qh&Nv=W^T162T3Wr}28~M`d(mD4 zDym9(!d}di1MM)+lE`=f3U#d-$end}K&kf_XgRV>D=3hfpUEG5#@5Z;kya`8pId z@Ok3kPu5GDEtFCR}H*bKT9Bk;U=TNi~Cie(~X~i3jF5Z|4OA_ zc=G@M->ks!LXRI5S)3r7PW*of>I}5ruHdkiH7-8q%=VAc9V)j_@3pP-Qf=?)B|K?)A!dSu+{6gs;XfJ~6lh@)ew(`L& zezBJi?3QLMx5(yHfszL37TGY6aiHsQ6+r&suL510Y5c#l_DL4w3#o39v9W(Zb8!!> zO&yY$1o(SFV;JBoFMC-wSW1*ccQbT5@T_Pq^;ik6${l;%{x<&*C>QE=69{-A)&=f~ z^0$EYh=FzeH`x&O;*K;Z&cS@x)tfv4VK0njK*DaXCwBkntbOu2Im_b3Z;%1qycR)B z0-Y=ytq+&@Wh~%lV1Q|b+{yU^+;{|wl?sFI<=n~r|3Ca*&eziL%R3u3FbaT`b=z#{ ztbNiM&tv_&M65BL2juk5c$Ut1j!yZ`c$sE@f&Z}70?tgwUszRwLJS-%TAiTW1iD{m z6c7CnP`b^?U~-$0!R0n1gUD@AYnXvy%0otmriY9SGj207EV<3du!?D|p40mob zGQ7FX$nfJfBLmAFMh1a9j0|db7#S??FfzE^VPx>T!^jYKhmj%g4kJU&9Y%(}JB$qT z?l3Z}yTiz^=ME#onLCUO_wFz24z;c(7LGCUigU($>2A8{x3^8{Z8S?HjGBn*~ zWSDT5kzv7IMus(a85wrnWn{Q;myzMoT}Fm~cNrOE?lCeL++$>LxW~v4a*vTA;~pbJ z)jdXr3HKNomfd4y*mjSR;mAEkhD-Mt8SdRS@pMh2Gqj0`gO85wl$Gcvf` zXJiPu&&W`7pOK;GJ|n}t`-}`5?lUqRxX;LN={_UFllzPeU+yz9usvX8kbA($VDx~I z!QlZTL)ZgGhN1_I3_TAR8J0a@WH|7Ek>SDvMuuk(7#Ti2U}X67fRTaYAtQs-Lq-Oz zhl~tX4;dNU9x^fnJ!E7^ddSF7@{o~X&qGKU7u;rKNV?6)Py-EfqK!ddqKAil~?~p5XVHsm?mVLkh1_993YF404A`A=+VVT9H zi8%_++S&@vK_ONOe)%N|sd@RO=@|;Ssk!+@l?o;K3d#Ao1v#lDsS3%7IXNJK;*z4& zqN4mFJqG9eBG-z{l7OQ8 zO|XbYUTIDagvr3hz`zC?Fc)B8W@BJu5@2z4b!7zsQv-$okZKJDi+~7r4hC}rgYW=$ z1_lKM&=ep8M?ywILP7!qLq$PFMMVV%I|l;;I|By?2WZrtg8{^4_~6VW;Nij~5So{r zmtUTz5LB91l9`*T;GB_~oUPyr@(34bYgKSbVsbXj+lfW_rFkg|@rgM(`N@e2<%z`# z$@xV^r3EFaDO|h^jtU^*9EGyPqRhmkoK&!a(&E$P71B!cl0hK>PP=*v3L&5{$}CnW1*Q9(N`*uP zh>aloLCG6rGm@^9)a0DRqSO=xaPZ{kLGl1dQF4A>86=P)a!8sA@-y>FKoN(cIZ+`c zGc7H(C^fGH>=;x-p#Gb%g;AhjGowJjW<~*t&5QzXHZcmE*~ADQ{s3hIQ0ah-A?XR4 zSV3_KPmt;i>fpeEr&>Lben|L1)2uoJgStXyu|iTR$SvSx1@dNoX$e#aDfQ|xFa(w6 z>4qROj$2}8PH9o9g0gCgg06yUF#{}r`R1pT=AGUPI(GAJ-OGh{HNG9)u(Gbk{)F(fi%GUPCng3A#F24x0Sh7<+`23-aP z233Y)xc(BXx?LHH7>dAUkRAgU122ODLnuQYLn1>GLk`%c5{7&R1%?!cOon2H0)`xh zM21QR1%@Do5C&%k1qNS+REA=PVunP9bZ`j<%d_xsRLDzBEK(?i7ACOpW`L=K#s#e4 zfNNu5@X1e3L@F`B;SDP-V8uitD6PWHf)>G0Gcxn@7@VP{1FUQS7YoUWrNy8aL@p8F zX2237k|7KX412aS3QX9}C{VGTQNU+AqkztKMuFhcJm37hkkZuf)Rd5nQn#Ya;KULJ z1_rOhJh#*&-^3!vf+F9Ckz`(%Z znUj;6o|vPMR9TX$P@Gy=nwpoK%D}+jl3A3RTmq_}K$$-^x1gkw!3~zxK|-0Jk}5Yf zuOu}EWS(C>sKCxHR!A&M%*+9m*bEG?G^tRM50cGG2c@f2XwjIGnwXN4pPa3bSeBoe z0yZQFTr@#cCl;lHoWbCdpPQJOrvR=ELH0NmWaecT zGcW|DCZ_1-=jBu?q-Ex$Dil{1m!#%`{Fqmkn3I{JP@I~Y4Kl+oU!k}lF*#KsCpE1^ zAwN$cCABOwITh?)WZOOSkc(?4PzZwD64nZ-6`92) z#SB4+Fb4YzR&Kxo(J3)SAu%PT2-KhmNG;0EEH2IjheBQ^I37TDL)$3ea00mxZe~ep zZb5!gVo_#Jr9x>QQf!0VtdN|MnUkVW0BNnH7Bet_>;lI|N@{U(QD#9&ei4IfMQXA_ zT7FS(VhKEA9gETxax%d+Gd!)J`dJ~r2<~bI2G6{L(h~jr(vpHwxFMdPN&(V>K&_S- zz-}uBg&)*u3^237Zp$xHNP(qy1_lOySnPtU(gIKjm4K4DUq0Nq42}#g3}Fn83;_(D z82PXWT=pW$&Qy4L327AaGJsl$3=AO*J`Da0!3@C+3JeG}5ZPb`PX<2*cPugt3@!}* z489DGU=^4;L9qoY|IHW-7>pT=89?TN%5_JE5{43nREAuJ0)`R>1qMhdUCK}lF0*|Z zKsNd?D1cj+DPXZQa4DY)R+|AX%@r6D!DTnd6i{goD%VrMwMZsI9z!~V0=OOk`MeBl z0;r`5F$*N0$pDf`WXNI2WT;|D1oQG4@))=nLKtA?gVcd)j%2VrsHOnfslbrLPzp95 zB(K0w$&e4W6(m;xu46J8AUZM`Kql%jC@^?}eF>_A6c}V1R@$DE>h11GO!vpDxh- zVF33B%uS%s0;K|MK1czpMudYAg9TUw6yBir7J3^9n;8n=G?&Ow0M-dI3xpx@XT)F* z76q9Ha|dzpSIz*6Cj|yf8xi>o=67WK%*e10l#@Vt6x7Ni)jm_WeFfkg3`!Mc44Di? z3?&Su44@nbO6{Pq1myvcpFp_|l(#|sT!ar`;bj6hkNEJ42bU443>6HZ6siwSPGHDs`a+Xr$ZOeIJcC^YgJ3K&4~1t~>A`8gljCI*#)Ab)~VHa2rn(-Fuu zAd^6004ZBQZFf+b!C&4RBBdXY`!LPPg_d$4pJ#x}y+m-?1t|kSeFOBG2$Wh7{wF8B zfO0)3mqTny0f%=YI2;k?BJv?9OhIK|34;c>90ipN#SEYtAQc|ApmYK%t6{1dE#)zkpt#QfDW4N>ZX!bwLpnn#+{9w2nV=Abq+bIDM0uUY0Ez=h zZp{PNbp;Ih;QS0KjX<>%sHTI245*%klB?GYWtPaTXkA6adWy zcpPQ~k4-TgW)uM3b-v;dqktd-1B1sQMgh>Q1j8Xl0bvFPh7|`H1wa!$76%yxBtZ)t z4=@TyF)%Px9AFfXW?*38IKU_%!vNaVz$hRG65r1#AkVHNe8O})xV(LDnvKPtZZ-%Sa#IG-gg$7j z9%KM$Rv)wt4#b|uz;FP@2HnF9iVu)D=uT%C8+0Etj19Wm8O8?P>kMOq?r4UwLH9eu z*jtzw4#3!;dz)cw(4qdII0qe+#{-IE1`h@+hFpeR1}kt24&;Uu1_;yvw`+VEKoHb# zPhkLwR5Cyy16W)itX3Z^2GSeA;0vM{9HF!ecx(aEE(DbzAW1)ld(TM#>s0o*lYa0JIIsQm?UBPjlp z89>KexiO@IQv@jG6fwZ`fm#;`5r}&k7$9zBV1T%bfdS$k1_p>5V0J_3JO+q8AQK^W zL-<9Y{ukIzkUEe(Aag)Dxs;)Z0aV_BbTBZuF%&UmBJn}38<2`(B(ZP?Pv^6v~Ffa%(w>7jiHHfgXy1Kfywzje|Fc=s#1T=Ioa4=}7 zTLgqRFtCFL)dK=L7#J87G#WY@Ky&X283`E~85|t!6$J(51qCSM>7c$?0D}vI6N4dx z0fPa`7>_GM9s_1wad-IRn&2D_{Vr4+f9rq&w&27pHosRx$*orU#a$7FB{rom?xz zL3RhFru!G9=6R=9LIgmGB*4W9Bo+o5kVz~_H3g}2Ple5IglFcZvJo6wU2ATPJAj92LOMDWGOB|C+GRsl}@(W4}7#Q4Bp?ruj48Ezw#fj;u zPWct#4519c;E^-*F*8OcW)@a9b`DN1ZXRAfegQ!tVG&U=aS2H&X&G5Lc?Cr!WffI5 zbq!4|Z5>@beFH-yV-r&|a|=r=Ya3fTdk04+XBSsDcMnf5Zy#Sj|A4@t;E>R;@QBE$ z=$P2J_=LoyYCcRdXV==G*)0hhZ+nph%SST z@4+B|7q&29LR1}%3<$``KpaFip@{(jnHY$JF#Vv`(uj_JP?3m{0y5~G0vH?Nkda6L zi$M_G#E5`QjKo2ZJ`l#1rb!bclmkGvh%ksCPn>TNsstDiYzPZ< zWjO;wLt|5OOKV$uM`u@ePj6rUgo%?TPnkMx`iz;gX3v>BZ~lUXixw|gx@`H1m8({- zS-WoihK-vxZ`rzS`;MKvcJJA{Z~uXVhYlY(dhGa#lc!FfIeYH>g^QOiU%7hi`i+~n zZr{0k@BV{_j~+jH`t13Om#<#GdHe4DhmW5=fBE|D`;VW$e*gLV?>_^j6#?#oo~Vjo zp#4A4_EQ}Ggeqd(^5BYip#AU6;LqU4;Li}s5W=9sz`$Ur9O&oh2ckXw+`xQKKNm0^ z3>NpGpxRK-XA)?*!YBahBQ_{AGC0IBG90J?&3a=R`VVwlECT}r=Vx z#=inp2jgcSM3qO^|LqV&9%SaBLkt2iedy+RfOnBHFyN9mgs#WIC0~95Vh=8P-IJ*D z2hjY%e+E?^bdUnb%`o$RzCe{nc7Nn627#!On(A+D)3y}w@ z_2EI7c>zt`eG!N{n7`1|kG~kIJi2~qdHnKQj8Ns#{b68&Dvxe{q8X|@a(JyWXB1dx zjw+9yuBKa}%A?!wZH+3AZ2oNw^dJ$!!=kk?toDA02gQD%jJ*70p%WMBc&AO|ooF!bDJ zWT@E4$gluO-MWX23?ds4`k+d|)aY8w!Lb(8fGH?79X_&93*y1nv$$3`GBB8fM4XFK z6H7ot4~`7BAihf~Xa%KrYNaECqf3}$fTyvMUP?|50|OruVjRGc!HBuMxS%LAuO!Wp zp`0l=H7^A!>&UZQ(RJ%Tu|xAz#z=zo>~G~bOl<{0$O+F$iUzWR*{pM z=g7cd0aD>#mRgjPSWu9f0$IqyzyuNnjZ%P@Kr%3>vW6FBmZXBrU^u`Wl$w|VW->&w z1gDm`f)=|mF#Kc*Ps}WF%P$Jf%uCNng^YbL9ApUrtwP94EJ+Q?C`wICVF+RX2arc% zUP?|X!zPyCoYd3;hOG>cV1X)PXoB*=hC4DasDNy8&d<&DO)So4P=oP6i_Y>(ONtp( zVIrU*m{bOa7Do5f5|DZ*zXNI>$lZ<%U%_q#NrTO22w-r|Nlh#Q3xF1VGBBKH@<>fA z053{nm;nk6kWEqfd8tUFISdSyAemtBY8{ZgV=>5h5R2h6$bK-dvLKb=9fK=qg@c;O)fgC(<1YGPR`hPW%pT+qr{N9bY~1__V|Bqkvt7tX*S$%wqb_7rGD zGe0FY0J6l8;WXF>kY!Vs!0ez@2=6G!ZO)}dMX7luknr|QVPI$ji{|I%Cg!F1Wagzh zGF)Z}&C38sQ;KUva%ur+0h(K8P6=qtbSWsjaZ59M7Q3V-m8Pes76lZg7N_QwFuY=L z%>ymhgt_kkB4vPl3R=>`Z~*KEh|6Gt8<~R=GmBHqz7|w!20+QAk7|wxtE~&*OMfsHsM_EF0 ziXlP7a2&*juRl8h;=ytgH%AA0#Pn2#k1S!CMJ1()Ibf?97*rsor!yqJ!Wkr3f=Y5i^U5>x zQW&m)Q($mrRVo96F0+rbZ(;$&SK$l{nIKyrypYO*)Nlrdexy1S%;SdI0gYL-S?Hc6g>I17IUNE?n7UX0mCzhl_;v3wGU|_hvNzV_&lzRdi zO=Ms&XMo@z4;dM*JYrz|bJYz>pxtz_37ufnk9Z14Drd1H*(+28Ijb3=AI>7#Iu`7#JK<7#J2LGcZh0 zU|_f)&%m%Di-F++$UY?o1_e;(Qi_4WK#GCk0cZj^ih$nr;H4rpE5Eq zJ!51LddA40_>7UkR6k>6Xn)4YF!LEB!|G>@4Evrj zGMs(J$Z-1^Bg30#j10e?F*2||XJin5&d8wroRPuoIU|G1b4G^H=Zp+#&q3`*Mg}GZ zW(F1pRt7fEP6!501}+9}1|9}p20jLU1_1^^1|bGv1`!5P1~CS41_=g91}O$<1{nrf z1~~?K1_cI11|=urKV>Vm!uX2rxul^7J)=U^NLW# z;z8!br{<+Fz*))pc}7rPdTL2QVsdtB2|B+#F%u@1lb?>tPJ=4WOD#{%&x3J_Q}a?l z8!Djug3_Y&RD?8|Y;kHyX#rFvnC%$giOkK(Pe%Kt zKuen;Mj#RlsBj)6Sj98NN+GczQ!iP;)WAZ)$iUE0*T78I&`80^*viPr%Ge}IA+e++ zGt~i1>Lusvm1ZlbFfe>z6hJB+xKxX|R6+YTpkfN)nR&)W3NAjNodMP6j%Kb#rjCxf zuFg)Tx+ccv2D%pJPNuq!hAzg27A6L6u13bS3=Gab!JaNLU}In9t-e#dPsLiPfw450kp#@zBsig zGckuDTD90pH6>O-wOB*bNQbeE_GBdHrGR#<78NOE<`tKuCZ;Gd zKuSSyISDUF83xinT3T8P&WU-T-9#z*;4OQgYFkmkFJB=eJP)*Arv&61EiEmm+d=yW zK}r}H2CKWFgGrt)3dN~#$Ac?0B)4OWTYALE#}5Vd0#g zmzJ4c3hI?AK(jMw!)|GDssbW>Z50eER1GX5KZr?F5z}jh6psnor$r|9@ z{-A0WlmQ|jf<~Y=4Y-NPz<@PRK=T-i889(W{9~945hU7Nh;oSAVCEL3f;awy8UjJ7 zC8b4qsVNG6p*}vKHUXrE3ht#a;Bz;qE8!TDlA4oPX{7)f$_G`8pz07-CLk#wHVnY& z0ZAXo)v)kHm!rf?Br8CNLMXU^&mhQG01fUd=qi9lzw=8$HOLV3OHpcZL4ICwDyRWS zYM7yfOHgWZW-92k0Z62hYL;UNFN41~1UrT}27u0>0MSOg3}A7`5NAJ-7)T8)T`B~X z<|#leDk&|{&{RmvEGjNh)YH=gB~4g<1MStvp$xQ7J-?s;lop|ef}3E8dCA}aVPJrc zXn@9I81Sa=&^*w_W{^RkHXO(Rg|wplT<|_wP}oB931M@PNc2b+}w==g%v6!hjQD9^z1 z6T}X1gB03v#~&WZ(Spx>Z071x12EusA1u0qOA?Dfl_w&Oi1%Y~n4bb@m>kqU0)-Ve zzu+qqL3Kw8k_n{hr$#X8!qYhM{zi#gFekUPB()+Q)PK}S&d&q$G#MDEl6LTz50(Nu zTSLsDJ0y?J$JTd{c24n-Q zYz7BBQDu@d=-i(?^%Bq-PPwId;K?A!c{QL?mpn63!W_&g0Z;nJCxh2KXh6nxOHkT+ zq~>$5v0!h)(m8HB2&skWq^*09?EtNi07YP0ei5V&h6IR)Ca4BWN(Hx}Dj`7+t!u#^ zB`Q9BGV`)QW`q2Uz1}9?ENFdTr2wuFQb9v7B)bQ01{K;ReyQc4Ls}AZ6dXg4`f{MH znoxI@K?eK@)N>$nR8w@5GD{SSQgic5Qqjy)&?w0Voqq;NlZ4F#)pJ&=21cO1Jd|Pp zC2mlI3(2k&P!l;hKM&dkRfJhXwA+ca#}jq`i5He zgj6ULmn4>yf=*R~hZwwGCLAZQum`mfbFmvmbhyLJf*t9E6o(|4LCw5CO|u+JK!@U# zKw8krpdE1vnb6^IgjZ3@qtv_<@T4IA_9V7(2rPCbA|)e8)svVE+7$;MQ7s0IqB_Gf z4&;=$q*R3z$ci0s#h99hP_B@XSOyve2c7U#l3J3QLcs0R3`<>ITU&*Y2!#OfbhLsk z63|7oEAv1t6i|79xBrCXWzfnGP>}>pZ-n)O%8F3P5o8RIbHyA(Kw?3#0uQ|Z5mxwV z7@C2KLru^bRB$mf6Q~%tZ;w#I0O7->89eD2kyI*3+?hxkbg0u)00UHlawY%MJ=(%eJ!H0z#Pf|+Q+ zNfw&s5L5d2`fo0oDWKM5dTNP6q5@>jU%^2^H77++)xazVwt9m&v%qMj2gKwZgP9xLY zV%Xd!L{EHq5om>e5#DwKxJpq7%}cE)NKFPc>%fO&YJlb&jUvDk>|hm;c`t~$c+CTs z!$H{EG?4lM$xnFA!B++oYX)dY9W713LS&NTDPnF;5{0yy{s|!6maeIk6}uGcO(57A4ld$SxvQ4@z65 zMuDhs0kuyM?GyzD1qB5gU0p~o51b^6K}TwX#frg)JS!xF*PuhyfewLI(8w)L0`+@Q z#Bxx?$kGdHe<8Ii$}{qFQlU`>$}?6Pu(5k0;tICT2Yj}7acQmy z+_q9cZnP?B7*tp|f`WjkFbDYyyz|70_&D-NEJ`U)EJ_8PN}XE5z!08TlAMu}pRNEp zvAhT-%D~`QT%4Z_I-)xTbQW%LA`+J&xH2y}qbNTwvkKg<1|1X*o-=XI&j%&Z#FBiF zL5Vq;>EQMC49Mc(w2+#@019|mi2@2ckY7;31=i_M2!hV0fdj_^6gb3&vs+POZmOpX zJc@|b=NO^@+QXBWmjbGU!Obk9;{&u&sI(Z|)gVd_$nXB%3=H74O-N_;LLI;WOK$;* zAkTm|%`kvM0yItp3JZ*IgLlwS0?yJL)(#*djguR0kbav2bhi;{VSuf#1?f~$&%D7N zuYURPIKzlzU0q#;U`WA5k|RPc-is2sBdB~POA z04NQUo94*LZ^5O>;L9ZZ@)aDNy%`vsogKlmwWUR=3<03^`YE7;s-Vps21uXVCqExN zE)E)VM;UM`$w&m<#E@44I=~t_qfy93Fwy=R3}Fi?LyEye55%SkSRDlNJZR8SQNb}8v@tEG5;XE!l9&lv zyri0<0BM|oMq7!GC!}%>)a*nmjY#VoplC$nkX^392OvX=67!04GfQB_93p2C8;0bT zZ_ql>B{e6rEVZZ-mL;qdKn(zBrAe$C;Jye-Eh(y00Pk3Y7({%0!5xBB@DgJ`C=Oud zE^>JbOIx4~M9^_s2EWvj^8BJ~P~i+OmqBOpd*y>uT1mbFR0ViUI44aPT3Ue)?}swM z_Y8r{RM0qeerZW6k_b2h2P9U4c9JQ8E->%`^|&z0d{Czg)W@rYMmwlQL#$u@^1*#J zh<4D<_zcjlbp{5Q``~87-G>l?sZmo10=pa`gXu<;atfYa!9EPCKq{q(aW|+Q9_(#6 zM7a)`AaP6v-MN&LnvxERed5zGx;fzGVG2pGiU!&}B;P#OyyT)v&|OX>^exfd1YYcx znwJ8*^n%zpL`f6SNgSl4Ky-Zt&OeAS0WAwa%;zDkfF{;mi210T)Z$`j5Q92`De%z^ z@LW8x=7oYUr2(z31#OCeUwJ{2843!Zt&s{zpw0J4ULrb-6yVk>D3GTgYmky)KXlYc zfjs-cOEW+*o0nP+p8-{XtTq7!B(ZVf2)(05AwM~}6nvi*c&!n*yALuRVxocubcc?A zX$j~;tRm=5EybX1MnwA=e8m>j+n^iEl2TLiz=IH|W1V=%62blk%~*q17J&zGEFwtI z4@rCA3ui#ygC|0WKS7BaE(CXCadJi~=;o>vJ>tt1(AX)`9551-wDu9COdze@2M!|? z-@z_BgA77~(+13&;n0C)0&`G!^Bc)#!Q6u-?UQU4o_t8MSvQz$@e z0GS3&|0MbW+WrGwAqOp?p!pVJB&7U+*cD$68rLL{7Gd);V85lOmJ}tHgJwEFTOQ!+ zY|-TKmMQqm1CQ^4u5fbocT-47Edfugz}f}~yNNd+w2~dPdjzY&Tnz9fBv!EbWzdq5 z_=4;b+-q(?ZFDXLaG07zDAa(KDKIF2&daC(bv6_jKYgPG3=AOq zmP5t2GcYiK+;!43&8P#A&2rE~@ZL-7m-28SRfP*8wr$ceq6+yXg!2Fij{ zpzr|26I=j|0g?w{G~rS1Xb2DtftXE<0zT0DT0v|zvnu!~XP%q~WO6}uP(rtD%AXxPOlP_m0r zAY~V$K*%me0gqjb0v5X%1vGXs3P|i?6yVs!DDY(`qrjD&i~a4Cdtoc{oHRULyvT?3e2!OXyLaPh6h zxFWOhA4H!6l-^3DdPk`GZOjY|=NCO$B*pM_(ThdV5W|p2xcPMp!8!t285ja@#obb1 zh`*I}3w$I4l4=Hq#>+SEJ^0DMU|0vy2|Di^)JNHJl2Ks8Nk)M+Cm97+oMaSOa*|PC z!AVAeIVTwfrkrFH=sC$K&~lPdpyDK>K+Z`F6!1C8DBy9D zQNZOSqkzLnMgg0Xi~<%X83jyEG71=+WE9Xj$ta+4l2Jh6B%^@HNk##llZ*l^Q1yRK zFbaG*!6@+N1f#%%6N~~kPB03bIl(A!-~^+NIAhM5Oac2Amjw2fXfL+0h1Gq0xBmM1q4no3j8_F zDDdVuqri>hi~<*qGYXtI&M0u;IHSOhs7zHwpLC!aa)$Jg+QxliiK>A}aGb!bR+zG-k^FVZjJ(3+~ z83j5nG74~9W)!HfVG?*?3&Eg#1X2sbpmrSSPDpD828Jlm_znXD!*m7)hOG71jJtW_RKlyLzn#=6h6$`TA;mR?a{fJ4i{qosy>zK^Ot`*xE`c!8=)?Ur^ z^BjKeU*Ph#QK-MMX{W<}o2$#t|M!%yICR+||JZ|`mklS~cTHa5xTiHcRNb=od%=Sm zr#*N3?){wm`*q!_K<91A$n!Zld%8jwQ&7SYRnQfP z3}?@rJp%#1?F((Wt}iYC5kKssQmpU$_fF3fYW`|}__9u_`AF{yKpzuF1ecRjB-eM^Z$vB zwiAC@NmeDRIvXBXW24x4(W1$H`VNM_*RAh#L~uxNeDSz#8MF1V7FQ?sZ8J;FZ|hkZ z7s^~xzHRi*b3&Bmvi5xk!f!2YTDEAb(W8t_g3E*EyF_jm&{6e#qHAX2AH42dwrYPQ z%aQ~44%PmCaZGOB^Ny(Si8lKKnw2E{yi=3@HutY*n^G(IMEBnObC*uA++B79)_ZFZ zaDkWC4GacwHY6>Nl6XRZ5pv4pv*ta zCJYP=VGO|x@eE<$8xTSF*MsgFif8a-@MCae@CU2h%kusoD5hAzqbC@p5d#YY3wX>G ztQbnfFoSe}?u!K7YYMuv*o^^n8*3_qCqo_s=ni=X1`Dtn0|pZY(A~@+Iti2{7z!A4 z7#JCx8S=q*(t>VaE@1$jSp~UU7j(lV=uTtMz0Zl@TkA6!av2JscWHy}94`UiD4WKB zewTd&gA)Vjo@fQ|UB8f9EJ5a4Fz~?bD*)d#U&-LdkO;n;8RQ+%jj5m;e^VLs7?K(C z8T1%R8L}Bb0Sy-xbI+LLce2aK0gA+q3 zIL)Pi+Zv#_g2Y4$Ln-*CVdOYQHCKT_mq7s>A|(t33|0*K4Eo@h2f161A)O(gA)g@~ zoZ=uUB9}p*p@2aj&2{<=3=BpL>YE42GcO z2K7rY_`djLhD>l=A^Qb%*B2v07&!c3A%w_P3=Ap^pcxNHNQ3T~&jE)A%r*uFPL2&A z1z@{Ii9s9!0t^g3j0_BN1||kR2GIt226GHn7;H7zYw*v2(@@ng$8e_M7Q;)1Ukn+H z^o*>G?2UYkVvUlF@{Kx-HXH3QI%@RT=#>$Jv9_^+v8}P6akO!v@f_oo#@mgL7~e8x zHQ_Q5H_2=d5rc7p{W-ev{W-(?n&DNORF?(<3X&z(VV?N(p(IUp8!(xgBkEMlWfMuv< zyk&}IhviwTYgT`(IIMZC#jRzm9jt?_E38|rJFO>M&#>NO{o4Aw^&e|C8*UqY8&?}2 zn_!!@Hrs6W+dQ}VZu7^6&6e9%-`3vN)z;Ux)V9^O%XW(GOxwM-XKnx5D%%;`S=c$* zx!Yyi)!41IyKMK^?uFeayYF_=_Hp(Z_J#Ik_O12{?T^@lGSv?T1_lGewT3E20Y))K zy++rKSd0aXZH>E)cNiZrzG*CCqGF{R?pVhHrO`8Hpw>Kw%xYJcAD*M+pV^{Y>(KUw0&!9X;)|0Y}a8o z$!@yc3cFW!BKDE?arVjfx%Nf&&Gtv_?}1hh89@3|tOi~N{stiiaR$i-B?j9J&Ko>6 z2sVr`j5SO*%rUGr+;4cp@SP!xQIt`xQIS!NQKQjPqnAcsjeZ%i7;_ry8s{0e8_zY~ zY#e8jWs+}FX;Nph$mEvEXA@3SdD8^bX{HNIpPIfheQ)~H^q(oeS)f^#S%cX$v+HII z=3M6f<{{>h=E>$6=9T8V&99ihGH0-Gv`DpRv)Ev<$Ks&HX^RUMUn~qPZ7dxvy)6SQ zODr2Lr&unqTxGe@@}T7j%k!3xEMHoFvsAazvx=}vvC6b6wyLn2W3|ERiq!)v4r@K@ zOzTqXD(gn;HtPk}ORd*fZ?wK@{o0z*M%>25=DUrtt(R@AZHMg?+Xc2?Y=7GRvt_m8 zvXi!(W;fezf!%VuHFkULcu6K4BQ3+22uv{28jk)2E7KW3@#cl81fhj8cG`~7zP?f8P*!kGCXMb!SIhEqY<}} zfRTw&vQfTKiBX+Vv(a*+%SQK&o*2C|`fMa_tZ$rS++e&0oR3ZzpE15*e8u>N@g3s_ z#!rl27{4+8Y3yN=WRh-@Z_;DpZ5m@*W7=Ul*YuMqkC~vEq?wu7TC<&I`^-+5oi+Pt z#%}Iuo@YMCe1-X1^X=w)%paHwS!h_;S%g@uurRR9wcKg>!t#ryfR&$BiB+G~WUJX$ z?AF5864pxA>ei0dZq@1H&5z1_ov$ej^bh zIU{8wcO!Y@0OJ_r6yprz9ODAx65|Tv8si4z7UK@%9^(ndQ;b&{3z>MDOfgw!ve{&} z$t{!rCYh!Sz-7j6(*ve&O+T6bF=aGUHG6CJ)9jxahdHmgmASpSr@5bbzIlmxoq4nQ za`V0B7olmu+9JYyDxVC z?3nHM+aIw%XMfrLw>?7v0|N&m1B0N!LW6S#Zw+`1%?-m0OARL(zBT*=P64V$rAAdo zEk>P2YmJ1A#f=q>)!;dx-MGhisqt>(i^k85{~8OM=$rVMXqy_CTAA9LCYq+17MPZr z&Np3Ry3Ta7=~>fDrguyqn*KGFH*+*gHmf(AWA@DawfR@`U*<9viWb@y1{Pix{uYrI zaTW_L)>=HW_;0~t$!95S8D^PeIo!U|`@g5H^rCP%;QIs4@6u@ZW&bkk8Q8(8agmynr=16>X_AOtE*PG zto~RrT60?qSZi79TU%P&Sw~sdTQ9LbY5mSx*v8Z*+@{iIs?8>w^EOv(?%6!H`ER3W z>uj59+h{w_c8=XbyVZ6Z>`vL8x4UU~&rZ=^*FMR<-oC}Y&wjG~HTy^Q3=(0$nfsd;oA;StHGgRS%>0A-S93`VBMTdgDvQY$do3R{d5}tmauQwmNS0&T5bKMeA$U53HYpTM(i)QZ{xrIW}`_ zR@iK@Ibd_f=7!B1n-4Z$Y<}4Mv0<=fu{E`wYrn{Tjr~UZSN8Aif7<`EXIKDfXPFz= z7`PdD8?+d78ca5rVQ|sln!y8urv`F{%7(gzMuypj1%_3I^@jTl4;!8}ykrQf2^5S} zjiQb6jEaqFjhc+M8=W#bZ*o{@;Ly0MP2nX$ETp>d0Gr}1Rt8OCRg9~eJ1esBE6 zSj)uL#L2|RB+z85$y*b7Q&?V!HBB5MX$w1i{lo5EO;$J z^{R(uh~*T^)s`16xvl)H8muN+O}AQTwan_2m9Djib)n@F2Dn+%&=n>w2> z;1&qKt%$9Kt&44%ZHMg&+by>5ZQ1Qi>_Y9z?P~1W?7Hpl+OgZK+PmB5*&nmNWPjcM zq5U&^h7Al1D;O9UxC~ScybN*;Dh=wuscECZ1A{jP9}RvRFc_*B1{=m3rWocK78_1C zJZ5;w@Venc!)JzEMrKA%M(##|Mqx%(MvIKj7+o;BVsyiZ)pW7xO4Hq@*G*l_^3BT4 z+RPT4tvB0YcERkn*+a8n^L+D3=1a{>Ex%eOSQT4MvRY!b-Rh(jr?sZ_Ut4**2)lZ_ z`F1Pq4%j`l#Bu@DX7}yvrGFWSH!r;8Y6@wcFcMSd-s2fHaCK#p|W*Fue78sToRv6Y89y2^+ zc){?B;SIypW(*7r4;a8j#86&1KCM%~j1c&2`NU%}vcM z&27yc&0Wns&3(-S%|p#2&120I%~Q=Y&2!BQ%{Q2DGe2bh(_GL()gsm+$s)&MfyEAs z0~RMOm@LgKZ7iKE!!0{4ms;+%6tJ?jT5P?_TGz(Z#>*zuM$C4d?HSuown27@b~Sbk z9~c;RFfcHjGTddvYT{v1VlvgF)NGo?EQ@&-i!7E|tg={VvB_e;#YKyamb)!aSh891 zT1i?vzlwQ#A>zGW~<#+hpo0*8y2E;<^($);n>3r5HoI-!+t}MC+P2v4w7p|1XQygsXcuai zV7JunzMZhWqP?xXvwg6AynTj!o_&e^T>De@-|az#tOX+j1Cs%V0iS`0fs8?q!Bm6U z1`7;!7+f;=XuxTxVQ6V+XXs)WY8YjhZkTUaXV_{u$#9e59>X_=`o=cK{>CxJIVQy> z^(L(*6HTU@EHGJSvfgC3$w8BoCg)9Vn>;jmXY#}3p9zPlmZ`mIs_9bG)uvZXZ<{_e z{cg%{W@@(H?2_3XGZAwc^9=I}^E&e`^9kk)%$J$3G2d-|()_adP4oBWU(ElQGg)w2 z@LPyk$XKX=>thQGM+;wzAd7H|Sc^o9bc-B|B8zg1Hj6nHi!4@Ithd;1vB%TGaF}{Y@4|@OKl$8XxetzYTJ3+S=*=B?**k{0Y-)l1_lOgLsp}!Mr_71#<9jV z#*!x9CdDRtrq-q*rbVXfOs|<9F*|MMZtiEk()^mazeR%utJQ9+qgIvH9oGM?d2Alq zyte7Ky=yCFr($PgmuffNZogf)eTsdH{VMwl_SZo6D1b(47#J7~Tn&5-5)D=uY%;iO z@YO)r(A+THu+nh3;VHulhEEMyjkJtBj0BApjqQvl8RwXknDm$|H~D8W!Tg!Ijzy2H zuiX>-SN0$5K@(XYK=+Xtlp4G=6gDz8$}}oAI%ag%=(^EMBL-tWV-aI>V>{z;<80$X z<0|7`vWX!^n1!0fnkAcMnB|$>GJ9b5$4te%z`Vx%mxZjQmZh1cou!*) zkY%-Hv*l#V@0LwP4UA2UEsSl99gMq-K|PEa#&e9BOn6N+O!Q4mOe{=nOkz#iO?H|b zFgap!!sLv}1(Pc#H%#uBsF~`R8km}xT9|$?%dk9edDZfXCA*c7m4uasm9CYYm7A51 zRhU(bRhm_fRhd=2RhL!2)eNgeR%@+ZSTR}4Sj*X}+G^Xbu={A&1(hzqST_w zqQPRK#TpAiOIgdQmg_BdSU#{!whFh7w-&JZXwzxiZ);#@X6It}!0x-9q5T$nh6d1x zF$2R2gAE3WhE7Jej2;*@8n+uyH8wQyFljPjH9cg0(_GbB(^}Ws(Aw16(%RPA(c0D8 z)7sZM&^pdq!N$WT!lu;bn9VC2h6#+|IfajAk(L5h%~q4FKiMeT8ry!c{bkE$Cu-+u z7iyPcS73M6j?-SxzRjLt0V8wq>>DQp;_Yhb>Q8-m-iH%3~WK_D(UJVK~Qdf#Dj%4TcvC zj~JaWDljcEtuU=IZ7^*yy55^Bv|_%x{?g zFqg0}v9Pd+u&A(@VX?(xhs6zx9~Kgp7M2Vj7{Tl0Eet?qBNIajBP4D>DHGJg4=@ce zjWCTdO)yO{%`nX|1^IA>*&MS4W=qUgn5{9}V7A3|-99diS76LSl58*>M9 z7xN113Y!|62AdX}4x1jE2{uz~X4uTJSzxooW`)ffn+-NwY zS8Q(B+_8CJ^Tg%_T0elpmd94WR>W4qR>oGr*1*=p*231t*1^`r*2C7vHo!K-Ho`W> zHo-Q{i&VvD;v`#cqe)9=ii}F7^@jG4=`eDfSum zIratiCH58eHTE6$J@ymqr`XT1pJTtkeu@1G`!)6(K)DapQXR#kAut*OqaiRF0;3@? J8UiCA1OP8M`cnV^ 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 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 BQB(i` diff --git a/libs/thirdParty/libxbee/lib/libxbee.map b/libs/thirdParty/libxbee/lib/libxbee.map deleted file mode 100644 index 6adaef145b..0000000000 --- a/libs/thirdParty/libxbee/lib/libxbee.map +++ /dev/null @@ -1,897 +0,0 @@ - libxbee - - Timestamp is 4e2eb2df (Tue Jul 26 14:28:15 2011) - - Preferred load address is 10000000 - - Start Length Name Class - 0001:00000000 00019a38H .text CODE - 0002:00000000 00000188H .idata$5 DATA - 0002:00000188 00000004H .CRT$XCA DATA - 0002:0000018c 00000004H .CRT$XCZ DATA - 0002:00000190 00000004H .CRT$XIA DATA - 0002:00000194 00000010H .CRT$XIC DATA - 0002:000001a4 00000004H .CRT$XIZ DATA - 0002:000001a8 00000004H .CRT$XPA DATA - 0002:000001ac 00000004H .CRT$XPX DATA - 0002:000001b0 00000004H .CRT$XPXA DATA - 0002:000001b4 00000004H .CRT$XPZ DATA - 0002:000001b8 00000004H .CRT$XTA DATA - 0002:000001bc 00000004H .CRT$XTZ DATA - 0002:000001c0 000022c0H .rdata DATA - 0002:00002480 0000000cH .rdata$sxdata DATA - 0002:0000248c 00000004H .rtc$IAA DATA - 0002:00002490 00000000H .rtc$IMZ DATA - 0002:00002490 00000004H .rtc$IZZ DATA - 0002:00002494 00000004H .rtc$TAA DATA - 0002:00002498 00000000H .rtc$TMZ DATA - 0002:00002498 00000004H .rtc$TZZ DATA - 0002:000024a0 0000049cH .xdata$x DATA - 0002:0000293c 0000003cH .idata$2 DATA - 0002:00002978 00000014H .idata$3 DATA - 0002:0000298c 00000188H .idata$4 DATA - 0002:00002b14 000006baH .idata$6 DATA - 0002:000031d0 000005a5H .edata DATA - 0003:00000000 000052f0H .data DATA - 0003:00005300 00001f44H .bss DATA - 0004:00000000 00000058H .rsrc$01 DATA - 0004:00000060 00000400H .rsrc$02 DATA - - Address Publics by Value Rva+Base Lib:Object - - 0000:00000000 __except_list 00000000 - 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 7dcacafa7e..0000000000 --- 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 11b9d3fa62..0000000000 --- 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 6bcb978f6f..0000000000 --- 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 37e7705044..0000000000 --- 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 5fd1de5127..0000000000 --- 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 e64ccedb26..0000000000 --- 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 e64ccedb26..0000000000 --- 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 f7bc1d0c72..0000000000 --- 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 4a0af65b50..0000000000 --- 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 a458e49602..0000000000 --- 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 402a3f266f..0000000000 --- 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 5557111123..0000000000 --- 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 5fd1de5127..0000000000 --- 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 2ec511278d..0000000000 --- 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 b36885d854..0000000000 --- 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 d5e269c13c..0000000000 --- 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 e64ccedb26..0000000000 --- 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 10296bb8c1..0000000000 --- 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 2628c46f71..0000000000 --- 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 56a5fcac56..0000000000 --- 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 56a5fcac56..0000000000 --- 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 56a5fcac56..0000000000 --- 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 b36885d854..0000000000 --- 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 5e56ab9ce3..0000000000 --- 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 cb2e1c923d..0000000000 --- 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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 teuSS4HiIPFGSAL&zE04*Yea5m< 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 diff --git a/libs/thirdParty/libxbee/pdf/api.h.pdf b/libs/thirdParty/libxbee/pdf/api.h.pdf deleted file mode 100644 index 43d99a493e1864ab981612be1960ae6e71117f56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11188 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 b7Ss%@me>W}wdyP7$ 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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 ZMt*TgVo?b=L0TA@8gQwqy863u0RWDHMpys< diff --git a/libs/thirdParty/libxbee/pdf/xbee.h.pdf b/libs/thirdParty/libxbee/pdf/xbee.h.pdf deleted file mode 100644 index a4ee0d754dea35167f2c622db520a173d2fcd0e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11925 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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 84069e39a2..0000000000 --- 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 cff70bf1ae..0000000000 --- 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 66946f1848..0000000000 --- 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 13afbe4169..0000000000 --- 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 9e48276b0f..0000000000 --- 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 4b0c9f6331..0000000000 --- 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 46db084f13..0000000000 --- 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 107f88f6e9..0000000000 --- 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 4546887f9e..0000000000 --- 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 d3360222cc..0000000000 --- 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 8ab776f127..0000000000 --- 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 26750988ab..0000000000 --- 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 fe13a7dfcb..0000000000 --- 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 4c7c6655bc..0000000000 --- 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 b1e51fd907..0000000000 --- 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 815e949e70..0000000000 --- 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 0875907f91..0000000000 --- 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 426a9df939..0000000000 --- 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 dc9f5f76d1..0000000000 --- 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 a80cc9bf25..0000000000 --- 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 223ddcf637..0000000000 --- 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 db49fe6fa6..0000000000 --- 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 597a32377f..0000000000 --- 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 f16dc0d94e..0000000000 --- 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 d909656817..0000000000 --- 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 61331d8443..0000000000 --- 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 48d3928505..0000000000 --- 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 768d5b3d4e..0000000000 --- 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 886bbf5bf1..0000000000 --- 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 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 Dt6OS* diff --git a/libs/thirdParty/libxbee/xsys/pdf/linux.h.pdf b/libs/thirdParty/libxbee/xsys/pdf/linux.h.pdf deleted file mode 100644 index a4c65ae408966bc912ed72e08fc49c4edee516ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5205 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 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 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 IuKsRZ04Ys}#{d8T diff --git a/libs/thirdParty/libxbee/xsys/pdf/win32.h.pdf b/libs/thirdParty/libxbee/xsys/pdf/win32.h.pdf deleted file mode 100644 index b844726f1a19607952831452b160be3896a412b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5959 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 diff --git a/libs/thirdParty/libxbee/xsys/win32.c b/libs/thirdParty/libxbee/xsys/win32.c deleted file mode 100644 index a05950a94b..0000000000 --- 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 888040ee38..0000000000 --- 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 1a9ebcaa85..0000000000 --- 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 65f19f805c..0000000000 --- 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 80f7d33bc4..0000000000 --- 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 878e9409e6..0000000000 --- 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 84703c95c7..0000000000 --- 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 af6b79b4b3..42a181bf48 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 c3a7b15379..0000000000 --- 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 02180dd254..0000000000 --- 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 d359f579d7..0000000000 --- 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 847ab8e93d..0000000000 --- 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 1a6f233fd0..0000000000 --- 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 3cbfb74015..eabeca15f7 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 93bc9b8de2..c5a019acd1 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 2a2b98144d..9e9d318bb7 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 c4482009d7..e014464d30 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 8c0ffc0f65..0000000000 --- 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 a25e792214..0000000000 --- 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 c8597ebdbf..dcfc66ccf6 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 ce9cd426e2..dd0d319fec 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 4801189b02..67666880e0 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 0000000000..9e0cb0c8be --- /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 0000000000..de0775e1c6 --- /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 45cc6b6fdd..fa5069f157 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 96f2f5e747..29e6477a4a 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 a0dcf688a5..a0b553f521 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 b68c7f90ad..6db800532d 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 ca7e7a350b..16129f6c95 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 37d0738ff3..85bd337309 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 0000000000..217ba564dc --- /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 0000000000..54a0a5f365 --- /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 6b946f30f1..24def6a8a3 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 6db800532d..7cc06c5900 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 e798565c47..7a732d786f 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 67666880e0..5146043574 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 f65fb476ce..f707bc32da 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 c88de86dd6..f676e42010 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 1cc89d81c8..8ccdc9270a 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 67666880e0..7634f4901c 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 c7b8bfd148..4ca41805d6 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 42e09581b7..f7e4630156 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 050aa21a54..50c8d837fe 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 99b48a41df..0000000000 --- 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 8392f8abe5..1450309a1c 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 0000000000..6577840424 --- /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 0000000000..06abfda2bf --- /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 e0fc578cf2..c74abab6ef 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 85faa00b0e..bcc4ee330f 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 cce975b99c..f02f022deb 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 7492de0d02..cd460922fb 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 b1bf782aff..ccd5c34b8a 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 96018f1ad3..8d9830e43f 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 0000000000..9fb58839e6 --- /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 a80510eba6..450a51058a 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 d43190b06e..7270ede46c 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 e014464d30..e2fad94e26 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 fa5069f157..51f59ee925 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 a65d3fb315..9361fb2b77 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 63ce682571..477b8a9e49 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 e014464d30..da028d086a 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 4fee754823..17f1e853c4 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 f02f022deb..3ccdeb548c 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 002b1d61de..a349bf947b 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 956115e605..6482a9062f 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 9e999ced5e..dc6c496520 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 f75fc50f14..9383c38a0e 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 94e9b3467b..c792e07248 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 4de149c26c..83ac9ac8c2 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 dd0d319fec..4fa119de46 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 cd40b19277..d2bce2a37a 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 a8851c7e28..dc1706a422 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 472b91e1f4..0000000000 --- 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 2ff6cacab0..0000000000 --- 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 dba1807270..0000000000 --- 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 51f59ee925..ab67ab2f09 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 127c2470c5..a9155de820 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 41cb1195c4..58fbe6ea8e 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 f975f257b6..3ae08c462d 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 51faba4ad3..4828c6f9b7 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 46a27dbef6..2af400c220 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 dcfc66ccf6..5c714bbed2 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 4fa119de46..3560594152 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 618e2a7ab7..4edebf5de4 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 6bd6031526..8ac267b426 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 0000000000..95506a14c8 --- /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 0000000000..68b3eabb1f --- /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 0000000000..df06fec695 --- /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 0000000000..6dcf659fd7 --- /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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 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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 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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 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/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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 SbP)ps1B0ilpUXO@geCwdIQ<*| literal 0 HcmV?d00001 diff --git a/src/FirmwarePlugin/APM/APMResources.qrc b/src/FirmwarePlugin/APM/APMResources.qrc index 9e0cb0c8be..4ca53d7fc4 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 a0b553f521..c538a576a9 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 9f54ee8125..b11b073ab6 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 a349bf947b..738a9864c6 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 bcc4ee330f..ed01d1c6fb 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 cd460922fb..e295ef9d80 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 935a564223..73f876536a 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 24bdd8a1b7..f5009b3f46 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 a0b553f521..75dde2db4d 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 4edebf5de4..23e5daa849 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 6c6d62219d..46d7bdbd74 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 95506a14c8..9d88274446 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 925621b655..a678407b88 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 0192ed38c2..dca4a2b8fc 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 6482a9062f..b8deec86f3 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 b15220ca90..e29a94a899 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 03bf70e4e9..760bbc2488 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 fc1f288d3f..42e495e48e 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 8b7df76ce2..0a50253a94 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 ee52892aee..5d4b0f2dc5 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 a408bf9119..bb7d95e529 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 8572356060..4dd1f6b358 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 0b3bfeb019..3a653e776e 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 35614a1eb5..22ecd62529 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 a079478b24..9d3650c0ce 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 aae975f13f..fd10f9aeb3 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 e0b8faf16a..db8b1ba780 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 6e1fa8daff..32bc65aad0 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 760bbc2488..bb2d1c15e5 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 fd10f9aeb3..0c0a4ff18a 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 bb2d1c15e5..2f51f25c51 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 ab67ab2f09..f0caf147a0 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 e295ef9d80..fa01ef116f 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 de61685db2..ad11cf65be 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 5054eb8e79..b1b8ed8997 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 9d1adec9eb..43eebe1e33 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 c74abab6ef..e5bc994154 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 7b3d2ced12..9d6f40fe2e 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 7306599c4e..6dcfac9727 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 dee9bc7892..33fb7fb026 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 3d43481a14..9eb8e0d73a 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 9fd499eee6..da2380d4f5 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 111abf4fac..8067354b9a 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 5a19fc7771..eb69d9a718 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 640b491c83..096ae71513 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 16d04d301d..3aac9dfa0f 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 c4a340f2d9..ac1ab600ed 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 ed01d1c6fb..be355f7a44 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 0000000000..fbb78e729b --- /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 ccd5c34b8a..bdcc8b713f 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 fc63d80892..ae28afba17 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 34c87bdad6..aff9018179 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 2f51f25c51..ca16bee56e 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 42e495e48e..c03becf26b 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 c384d259e9..1c4d7eba0d 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 63c4abe355..b8ff725d62 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 3a653e776e..f6b6091ba4 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 4585d93e93..1c040a689e 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 0000000000..2aa15bf61b --- /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 0000000000..8054386061 --- /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 1450309a1c..c3d9009102 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 3f546a30ed..84bb0a099f 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 f0caf147a0..f5a8b5b592 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 29e6477a4a..ff299e3486 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 b11b073ab6..dc374d5714 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 3560594152..3ef7283da4 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 7d5ef9527e..0000000000 --- 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 049b818a79..0000000000 --- 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 578634b6d3..8e2295509b 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 002c88e39d..07bc901c3f 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 28e1afbe22..5ca81d9813 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 607ab0a665..dc6ab6d7f3 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 27966dd365..c93c0e5d51 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 b0b01d0921..97e3a33947 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 fcbb4029fb..ec5112e6f2 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 a186a80118..87b4247eb6 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 55a2c2ee7d..6be68ccafc 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 d2bce2a37a..6d925bc463 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 5e79774f6e..8a19f0db53 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 e5bc994154..d032df6f26 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 3ccdeb548c..c7a2258c41 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 8d9830e43f..ed80428026 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 7c0d878beb..26a5dfb6f4 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 738a9864c6..0b180e9101 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 5779dfff8f..0bdd60f77d 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 14698c06b6..7896c6746a 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 0000000000..ebc49222c8 --- /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 4747e3d41b..cfcb5072c8 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 c3d9009102..f1a1caf12d 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 450a51058a..985afec754 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 f5a8b5b592..9b3bc66bee 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 f0caf147a0..8d8e9f4791 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 d032df6f26..b50a6e7fb9 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 0b180e9101..b05d49231c 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 d7be22e884..7761d9660b 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 60c8e09f85..2aad79b34b 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 0000000000..b6a0629249 --- /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 dca4a2b8fc..b5fb661e27 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 cb278c6b98..ad512e6eed 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 e0567057c0..c831f7e1b1 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 ca16bee56e..791e365b2e 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 c03becf26b..bac54c891d 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 1c4d7eba0d..a904796dbd 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 b8ff725d62..a498d07d6c 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 0c0a4ff18a..38ea5f11bc 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 aeb13a15b1..d00974e77f 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 f32ee0b637..f48c1b2a1c 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 9b3bc66bee..1ce4132580 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 ff299e3486..c9d2c01140 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 d1975d1895..d22c60515f 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 8fb9e497c0..13a5c0693d 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 4828c6f9b7..a286a078a0 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 c831f7e1b1..622e1cff1f 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 791e365b2e..38355adc39 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 0a50253a94..bf8de33f96 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 5d4b0f2dc5..5ed2b4ef00 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 22ecd62529..ccfdde11d1 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 9d3650c0ce..7efca0a642 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 d00974e77f..7f46a28ca0 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 32bc65aad0..ab2612b787 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 d6aedc86db..0c60172567 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 5a707b52d6..0b7c6be183 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 29ec60771b..7c1cde362b 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 cc298091a8..6700053979 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 936cd96ee3..47e3835293 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 9097ce44f6..8ba3da326c 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 9d6f40fe2e..54855385b9 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 0000000000..1734b74b8e --- /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 4ca53d7fc4..7e2b6a2120 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 ef24f1de4a..126f1baf42 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 29b4f7d24c..949429e09b 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 c234480265..854ac44078 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 36833d1fe7..2fee9698a7 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 594a028f00..b12189cade 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 6069a99a15..4477b5109f 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 73c339325b..d2bfaa8a2b 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 b12189cade..1f3b0fe1ad 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 4477b5109f..276a031d78 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 d2bfaa8a2b..46b8940f90 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 d920acc4d6..85437d3bdd 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 276a031d78..2a791fe35b 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 1f3b0fe1ad..64202b8523 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 2a791fe35b..847faf749e 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 64202b8523..e382d8ed32 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 46b8940f90..d6d700294e 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 85437d3bdd..943ff18d14 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 2ff5cfac6e..1e1cbbe7be 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 e382d8ed32..e0385816e2 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 847faf749e..27c78a5313 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 d22c60515f..ba381f8f73 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 5c714bbed2..bb2e8de56e 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 0000000000..1509445bd9 --- /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 d6d700294e..c52975b940 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 be355f7a44..b2da710a79 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 943ff18d14..3c64da7b3e 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 e0385816e2..bf192ab0e6 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 c52975b940..73c339325b 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 bf192ab0e6..55e12df294 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 55e12df294..1f79dc5dea 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 27c78a5313..869044d4e5 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 1f79dc5dea..3e1ec6a671 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 3e1ec6a671..d9b36b5690 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 d9b36b5690..a4267d5c06 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 869044d4e5..77a9af33d6 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 dc6ab6d7f3..717bae81ac 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 a4267d5c06..a6d4f07cc4 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 77a9af33d6..bb06fe424f 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 3c64da7b3e..5dc6b63c82 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 a6d4f07cc4..9cba31820f 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 bb06fe424f..082625ffd0 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 9cba31820f..927dbe05e1 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 082625ffd0..8de7bb8738 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 927dbe05e1..d051643504 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 5dc6b63c82..e778dafeb3 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 1e1cbbe7be..c9a5e6291e 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 d051643504..e3e31cdf02 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 8de7bb8738..ad44372562 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 ba381f8f73..1fe8d53778 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 e3e31cdf02..37a423f0a1 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 37a423f0a1..c3ffa5ab7e 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 ad44372562..8a257cd79c 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 c234480265..854ac44078 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 b50a6e7fb9..4b7b893904 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 522f528b89..9703f44e0b 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 55df893480..8ab41ccf21 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 ef24f1de4a..0105b80aad 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 29b4f7d24c..b39fdab303 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 532ca38df2..28797e1493 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 cff613019a..018249cbc0 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 dc6c496520..68d31c11fa 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 c8c0b79c50..eec24e61d3 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 9609a4fa14..51f2728454 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 0000000000..1050c8986d --- /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 eec24e61d3..5e50529b14 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 d22c60515f..43fdeae5b4 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 c3ffa5ab7e..e24f3b3680 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 8a257cd79c..555b3ec8fe 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 e778dafeb3..3eab2d874a 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 e24f3b3680..b196463743 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 555b3ec8fe..b3a1a9db3a 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 b196463743..20aa8713ba 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 b3a1a9db3a..9ec600fedb 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 20aa8713ba..c4f698d6a6 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 b2da710a79..e46cd5da14 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 854ac44078..57ac144057 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 57ac144057..c99c4c6bf4 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 1ef0fc607b..a8d3f6f936 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 26cf0093f9..8460b752f8 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 8e2295509b..d436f68324 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 c99c4c6bf4..303c3005c0 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 4b7b893904..fc3446c49c 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 fbb78e729b..c59f876b8f 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 0000000000..2f7794913b --- /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 f1a1caf12d..c33e30fea2 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 ae28afba17..7dbd479f2f 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 38355adc39..71d2b6fff5 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 bac54c891d..5a341ad197 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 0000000000..36360cbc49 --- /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 f6b6091ba4..a1a7126a02 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 c93c0e5d51..759d6c8534 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 97e3a33947..757573e812 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 1ce4132580..10c7deee44 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 43fdeae5b4..1eacfab917 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 4e4087ffa0..2b4ccf0639 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 c792e07248..8a1a64c376 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 91ec4d160e..391042684e 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 5c714bbed2..3d4d9f3019 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/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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%0Dv~TVNjjj(>dq4dCE5mVh zuBOpUp7&;f?;bq8+cVqbZ1&_=tRbrowqJdcYPMs}+{nsZoYwg-OQgKLy+NDmAAPL2 z8&OcoTJx$`&{ePwd|;If&(?eA8zyY6z2r6jX6LH7TsLFJD@*pWt(iM*|J2v3)44j2 zKMpL>PTKoksFTIMe`AD?p;4l`*YRTJnx!Y67Ci(FqU?S!%J0uZv|6W56@8bL6(YBu`@!mIVMonoq)!S?T;@syOU==f9~pU&BpOo!9q{c7loz$F3x@x zwmDJbGSAJn&D-u*T|e>svkc4Tn>ycPGji*e20!(EV4S(!Cf3i9arX0v1r`AT6^%P+T`b&(34Z5?nRJX86YSB>&g zhKo;&9$r}J+?CcIb^ZL)A`i96HPe`+#ZRcL;H*;%<;wG0F1&W_+6_@_xh5Dt`V49f z8c7~`TnH*jZ{~!Bci-;(ovL0FlfS1v!DL?)ERv$dn5qAD61KMH#bU##Lwt29uY?YI2riQvgT z4_432JZo}T*}ZSU(xt80y{~?ySXONK(&VGooMYBqVzqY0l%rct6dhPDx9j$sEvCQn z-fxx4Ynyv-^1SR;M(N9!ZalHuc}>m#U=X*rm*%wT(>Lzhcdj{qrVpF8wzh@{7jJgj ztOM5552=XLx7Tv6s9QhH zEdgAMCv1#x&~yzB7B(|C-@a7t1i#qlrv}0u0*CkoFH3xT^&w>Ot_7DGeMQTkYJ7}6 zGu2V^X?5b(sLBK0-T^1SJJp_Dx>VI;QcH+dYm8pIi&A56FYlT)YckY+XWd==Yq!+V z{xqih#VjpV>kXgX)oeEEotW9p@MA+J+oU%ICw$e{zE9F^hzLGjVZ-M+X-6e*!tBny z!U?xCcYiF(T77zLTjHUXi8igBYv&(Ldg!_PRq3VA>ra{%JexG(cmCCjm+nSzr?oY3 z*06^O-#IJc^zCxe#szEF`X-j0UVs;MJ6IYi7>c$%8`wX>z|e(n5zZ>3s6)Os~DFM$gc?`wnmJuQNZOtL1GhF(4UTW_wQcUNTkO`0Bl`Ail6XA*)GPhD9Et{x*B`Gpc^&7fv02zl y!BRlJAb5iNPwgf5K8rFYu!{T>VPM$9D_@m2WijuKs0aoI1_n=8KbLh*2~7ZKl0kU@ 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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 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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 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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 L`njxgN@xNA8@nR> 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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 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/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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 L`njxgN@xNA8@nR> 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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 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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 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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%0Dv~TVNjjj(>dq4dCE5mVh zuBOpUp7&;f?;bq8+cVqbZ1&_=tRbrowqJdcYPMs}+{nsZoYwg-OQgKLy+NDmAAPL2 z8&OcoTJx$`&{ePwd|;If&(?eA8zyY6z2r6jX6LH7TsLFJD@*pWt(iM*|J2v3)44j2 zKMpL>PTKoksFTIMe`AD?p;4l`*YRTJnx!Y67Ci(FqU?S!%J0uZv|6W56@8bL6(YBu`@!mIVMonoq)!S?T;@syOU==f9~pU&BpOo!9q{c7loz$F3x@x zwmDJbGSAJn&D-u*T|e>svkc4Tn>ycPGji*e20!(EV4S(!Cf3i9arX0v1r`AT6^%P+T`b&(34Z5?nRJX86YSB>&g zhKo;&9$r}J+?CcIb^ZL)A`i96HPe`+#ZRcL;H*;%<;wG0F1&W_+6_@_xh5Dt`V49f z8c7~`TnH*jZ{~!Bci-;(ovL0FlfS1v!DL?)ERv$dn5qAD61KMH#bU##Lwt29uY?YI2riQvgT z4_432JZo}T*}ZSU(xt80y{~?ySXONK(&VGooMYBqVzqY0l%rct6dhPDx9j$sEvCQn z-fxx4Ynyv-^1SR;M(N9!ZalHuc}>m#U=X*rm*%wT(>Lzhcdj{qrVpF8wzh@{7jJgj ztOM5552=XLx7Tv6s9QhH zEdgAMCv1#x&~yzB7B(|C-@a7t1i#qlrv}0u0*CkoFH3xT^&w>Ot_7DGeMQTkYJ7}6 zGu2V^X?5b(sLBK0-T^1SJJp_Dx>VI;QcH+dYm8pIi&A56FYlT)YckY+XWd==Yq!+V z{xqih#VjpV>kXgX)oeEEotW9p@MA+J+oU%ICw$e{zE9F^hzLGjVZ-M+X-6e*!tBny z!U?xCcYiF(T77zLTjHUXi8igBYv&(Ldg!_PRq3VA>ra{%JexG(cmCCjm+nSzr?oY3 z*06^O-#IJc^zCxe#szEF`X-j0UVs;MJ6IYi7>c$%8`wX>z|e(n5zZ>3s6)Os~DFM$gc?`wnmJuQNZOtL1GhF(4UTW_wQcUNTkO`0Bl`Ail6XA*)GPhD9Et{x*B`Gpc^&7fv02zl y!BRlJAb5iNPwgf5K8rFYu!{T>VPM$9D_@m2WijuKs0aoI1_n=8KbLh*2~7ZKl0kU@ 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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 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/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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%0Dv~TVNjjj(>dq4dCE5mVh zuBOpUp7&;f?;bq8+cVqbZ1&_=tRbrowqJdcYPMs}+{nsZoYwg-OQgKLy+NDmAAPL2 z8&OcoTJx$`&{ePwd|;If&(?eA8zyY6z2r6jX6LH7TsLFJD@*pWt(iM*|J2v3)44j2 zKMpL>PTKoksFTIMe`AD?p;4l`*YRTJnx!Y67Ci(FqU?S!%J0uZv|6W56@8bL6(YBu`@!mIVMonoq)!S?T;@syOU==f9~pU&BpOo!9q{c7loz$F3x@x zwmDJbGSAJn&D-u*T|e>svkc4Tn>ycPGji*e20!(EV4S(!Cf3i9arX0v1r`AT6^%P+T`b&(34Z5?nRJX86YSB>&g zhKo;&9$r}J+?CcIb^ZL)A`i96HPe`+#ZRcL;H*;%<;wG0F1&W_+6_@_xh5Dt`V49f z8c7~`TnH*jZ{~!Bci-;(ovL0FlfS1v!DL?)ERv$dn5qAD61KMH#bU##Lwt29uY?YI2riQvgT z4_432JZo}T*}ZSU(xt80y{~?ySXONK(&VGooMYBqVzqY0l%rct6dhPDx9j$sEvCQn z-fxx4Ynyv-^1SR;M(N9!ZalHuc}>m#U=X*rm*%wT(>Lzhcdj{qrVpF8wzh@{7jJgj ztOM5552=XLx7Tv6s9QhH zEdgAMCv1#x&~yzB7B(|C-@a7t1i#qlrv}0u0*CkoFH3xT^&w>Ot_7DGeMQTkYJ7}6 zGu2V^X?5b(sLBK0-T^1SJJp_Dx>VI;QcH+dYm8pIi&A56FYlT)YckY+XWd==Yq!+V z{xqih#VjpV>kXgX)oeEEotW9p@MA+J+oU%ICw$e{zE9F^hzLGjVZ-M+X-6e*!tBny z!U?xCcYiF(T77zLTjHUXi8igBYv&(Ldg!_PRq3VA>ra{%JexG(cmCCjm+nSzr?oY3 z*06^O-#IJc^zCxe#szEF`X-j0UVs;MJ6IYi7>c$%8`wX>z|e(n5zZ>3s6)Os~DFM$gc?`wnmJuQNZOtL1GhF(4UTW_wQcUNTkO`0Bl`Ail6XA*)GPhD9Et{x*B`Gpc^&7fv02zl y!BRlJAb5iNPwgf5K8rFYu!{T>VPM$9D_@m2WijuKs0aoI1_n=8KbLh*2~7ZKl0kU@ 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 zcmeAS@N?(olHy`uVBq!ia0y~yV6#J|`2Skwkel#|8!l1qM$S$B>F!Z};XF zM2F9k`!UD+k&}>J4$}fx1|d!_l^5GMcKHTfo@TS{#JBc4c_%MVo_FhHT3OKKRWs*# zsm_$Y>6@FCy-Mw7NS#XX=HJTJ_Qxi_$qTya`F7d!#nG=`Zo88=`Q3}f$Cp&8T*?)k z;u0Xlc!@=!n?Z$1;r&hdC)G9YpY9cBuxEU=FH|c?p`h@+<@?He@4u(cDdwA9LSlkz) z)ygfdcVl;`X1-z9dZb(t*Ca(Q-zV6}vx5vbG7aeX0)Y9D)xZzOG!JN*#$yXDveV#pQd)C9FUiT9x z^VUR9)5&r#QQZ(?JojvaS?DA4#Z3&_3s$$r=Wc4uk3P$)eJtp|Z=i@YL++}K#0bud zYqJ{6ZXO6XeUPhawW#_?_B*%P}}ax>_cfy*Vr7gv+8b%AK>a(;}iL7 z|NZyvmtVHre%p5ZG2@17I~onyw%_L6etYfDIg2#Re%wqs5q|&Qx9vyY|E!VQ8N

cv6DSgXp;`Nu!RU$=l+wMcp|4A&Cboum0lO9HFd(t zlmH#EiD%O`ZrRe}w6NjmQP(M6s?tAR9b9wA?`!n@v_qEd#~(Kyf9$9sB9x|SIN@r! zaL2{{HFo+E8JU@xw@uINy~VzGOIOtUt#Ljp-tOo>8@KotgUizIDMpeXtCc1)F!c2I zXWw&{QsT_|<+=X!CY>!uJyaHb6cmyW@>ieidGN9IPQNez-agpAi_NZ9cF{$q3-?!f zaWj1db8I$P8GO4%^kKzVYFF~i#ZakgvD zmeu@E<4ZMVS}GN{e%cj>sU9k3Zy4vcIw&+edzJ=@fwO1Na~Zx34CzYU_M6 z!>L}mH!fs}*X%Yw`lfn&GvgYGeCs7UM47Kop0-^!DsSn2>A$(lzg>Ox`=xrM_LIDw zb$@@c1@N#ngREs?%E-)Yy!=umy~|LIVPl9Er-RS(%a6Wio$tJPE21&^Fw=a&1Cp7s zVL}12wqJk$&B)Dl_4e-Wm?KrIHq%FG{(0rOe#(%Ti%&-MFJW5>e8m}tn_ z?08~RPl1I_O_$5c5U)8KebkzB%%Ue=Kl858T(|w%-2}JITLg@K8?O}XzuzCAq4Gy$ z$;m*GsVY%gQ-!$Wd;62Due^GD<(S!TIpbyPj`k<1&M;%1P|a9%RZh&fVf8hewQ1+e zf~wa)>xtxC=vBFLPO(V0>yjW%!OUg*D{bVqY}uk9z;WRHcVz*N15b-G&n=hYnS4@Z z(@mXc&z`khO5En2_E`Jd+)XbYf;E=l6Gah2Jl=W23kDsR#(%x zbLV&ymTZ$cbM5QL$HyzayqNfdhfycfCF{P}%&sugZPt6muKp5RZ8`g@hW?lTK`UE+ z{;Zs6?XmB$gMtE+x=3o-&X^bP-ibMQD6C*RRQvdGaIm|3dq8(D-~B&-Y$lyldG`Fd z?qvPdAM;-vY&Mx$#cunn?vyG^LxKUvu^DVzR$W}z!?!z-PjRX73eDMPlPW7K50_qf z*1zvH`_dn8p55AcH?LyvJ>Kt!ERQ!bvp@L!)2!y$^Un_tG&0Y+ej;RXb@|Sif}Jta z|MIq;n{s^Rxwlu==NrvjbGnB?+uU*KgXVvmpVZBoIrCzK=#m#K&5k=_^uB!gvS9gg zcMdV5e$CS5?;6kRC|-a4bDoWpdjFopReDr^@_}|aIL9jOHBVyYxJ*mmg{Gim6fgd_w#w{mZkgc>;6cT zzq=#ou>Ag=xszJ1{z%pG+@1BlH;6^Uan`?!4-IdbEWUo_pXMbA5m!NmgUeU{I52fLOxW_l zN3FSNXH4;h)w39m`~BYjQjUYebAy0@oPC{4JD;o*D5j@vx~Y?QcUSApn>Tr{oerDV z$av#YCTFXYMftlsZ0;7haW-q}u3(-DPQr4fpzPinS zRpq?9j|BHJynOM(!9+^*)Z{1wgJT6BA06dAlo{UGZx+Z~Q)ODJZaOK;Z1pt_&A%bj zx3xMcTFCHi*|sff#-$flPuP{~&)vBrqvE;A#4y{_e8b9rEsx6>R?fQO@Z$UL!UqQ! zC&uJFy`9>VSXn8_FK>6`(Icf#KW&U=_6V%7PE~t(N|k` zyGuXV*x3tTU(-Ev=1f9r>e3{;n|`|2(y|&4KQxHZ6Tg#Ze(2$b3om*Y`ilFeP8HSB z)8i7Ja_a7iGi!w&=WN?nxVpVyeYWyz$p>q`rLF$GHTJnfEpP4GN(&jc;$q`Jb^FzJ zY(L!id|owQZfcygklKa$lf0K) z<1n6iW*Sqbb?LwFB0sh@8*Xzxbg8ZFaN>^|J7;^2=Q1~Q!mfy>ZN91d({5W*+54>` zTQk$I=Pv(qGws&nuy@)bT$!_K0*aYxf1UXAXOD*3iftw(yU+jHwr$<*ya%^mGHZBF z$_R2uELhyO@X7fF_0eBBZ=_tl?8MS`FhQWv;Xr_~%IS_vm$TkJ-hSOF)xD->_Q(3j ziRYg?TgbQ>NVr5tOY_OutSEdC6fu95{h3!4xxFXUW@l}Wk>O)^S~y|F$?Ji1E=+4JXx-`~ZaIe$KXRpkm$3%Qi{%$xU5s=8d14*7Genfht!>C}asuY&KHHOt@r zG$)#MU)J9mw{ti9`uJq5%Xr?teLEpFGPr-1*U}?NfyIa4zIva}yv)}uS6^XOXH>AO z(nQ|7(-}T1wXbb@KQE)^>Z;RMwkQa7c6{vVF`RNb_0HaE@y(kzFSBb}yNvNh%4FZ@ zU@5c7x1N6g-8$*=rAvzfTZB4U($donjg5mpp1EEca{YxV)2?&p{48YnK2|LZIB@0+ z&$4C9%uIUk=tbXWbDo`ZOYUr*^?`uc?$z&?SA2gLduMk!e{gW{*Zfzr?k+g~*ilE! zSw-k#%i+&IZG<{o*xr;~`kv4{-`DJI@5yLRi>r6%T#3Ezy;Ml3lSNHU%`D+a-_p%F zUk_c(5V5tjo#o-bGQ`P7PCOx2$iqb! zeWRDL)NC!Y2>jn`wd&iqZ(VA=2cncuynLDIYj145_lVl;oK1(s_TSfE-kO!LFo5Il-@hEKPE1!Oom5d%Q{&lv z^;Xn8Q2a{1_3yRZyLWF=gUE(ypUVFJDm8q)8vDgpS&$#^=wU7ykT|>dU#>mFwKBmDyih<@(v1 zo10&}dL?AH>*dSLn0+;s&erPEXF#3Bw=5RQ7cL#=;$UEBV>_}(-dH=NHumy^V+Rf} zByE(C;bWIbo-u2d(UlL|F86IesWv<37Ef8l-nczqE_s8hG1uA;M~<)@JAS-(J!`{j z{U2}h>!VXbC;#1Z^XB&Iof2DT-0EdHvF*c#?@7D2u39^9<=#5At{>aBYJWjCk z$V?x$`hU;s_dWc3*6me_f&jyF8D(YVj^mGcH%{EjQ)vjQ0Pp0P&#QbUxg$oe!bXlk zpi%a3d2qdh)K_;qcGtioPqunVhwB>OTo7)*CDzyYiU<#zqUWR;Qyn+Y`r7>T>UG8A zb$ZjC7X}U zOOB_3?aQmcO`%-^uf>mctzb`35O7csaEOkcofg}(Zf#xcd68e=FSu+gvC@@V_UiiG zYwx~o7t7dkePI9xsK|c5V$bD{{xfW?!Vyw?{d6B+epwPachcH(J%Vmmw0(VjAD)?M z+$M3*(tY#I#BH-=qt6FjUnM*3U;Y)F#;E5KjJFOQVwz`Dsifp;GU0?zH&6TF2Xne5 zSE#JbR8jElP&YsH=8ev~dwa7h&So9ByX+UU&1xB=>8D$hkM}KHyx5sJ?A2e@)s4%3 zU93qnwO?)TyoYs7skVK^hXn$6a&vPnYJO~RV^E*$c~R`tnG_T2;IBuTuReJ_Q&a5h z)hzGcNeww{GGD6G)75X?zU{iT^H=_o&Xv#mudQmFIdki)g?cPouI%~vbb7p--t@yy zo~WdyrL}Y>9VswlFk^eL^83lRWs5g&7T&veFVlh-?^#@;XYwW=6Ml6vYf0p;yLm2) z11GYZo0_&(e}9*lmbOgIJivChq*mjWV^>U#ub&CITV>HuWj}l7`9n{O9v&5scL@)l zzHHi#0F4Ll?(X(bo4lj^eO&1I;In697H?g6P$z3ykBicx?6t=qA3UwUzbAOPpW;-n z#H_4S7xrg0$0f@+oZGo9v3;MQy6LU8DOGu?X})^Xo%NyN1Z2 zjmgJddTagt`MZuP<=x-cD{Y>)AV5Q6-r_3J17Y8jo=i3|DP>%+awX^L)vKq)2l)5- z8Z+>;Iyvo{n7kuGC(G(+$nMUx^LbB|x6b-I@$^#zL&L_-PR}V>wveg#cvPJC``c@4qotMB+&-v% z`{eE=bAw97cIK`SStEGbcmL~WeewTavbcYJSa8;QxgE2vh-|;z+t|qHD;sYk zcl_*ZbJv1JQo(mUigHx9pFH(`#s3<+`NwbEkjT5Yr_)2F>EJ=f9e48r=UbSWg+B~O3W*nRo>wew*7 z^K)~ZZRDDsng+mAgibXgq8*x>B!+;KE%N7dI=wkoy#C*OZh&dBgk`7O@HTJ!0ox|6}mzfZ#I zc6rsr_f|%(-hHfR`R1ECZtm`eW@c>%4>~U7oOEGt-QQg+KV9?)^KfREFS?nf*^!5r zm$Tl>Xy%a{Hzb}tf9^Xo*Lb3KRf|5ImwZmxB^zx`j4`p@R|%CDB?3RjfJXz{f(@BiYxzeQNxk3-S* zU)0wB>t@C5^Ky{jU~v(wQxFhPT~e~PonJnwvQo0gaLMJ&(~kGTYS!=Xud(aDcrnn& z*Eg}CAmGQG`St%QdwP0u1a|dat6Hbna6ZS;f38*QzI}EYT}?qNTTV~cfBfz3?b$}5 zS7(d!wO_n;P3_YMk44h4!GG(u7KEMWTVT>!V%3{swDO{Z_Dt1R)q2xUOUTO~|MJCT z-?y#nAKuuQY*F}#<<{-ng)c8Ho%L+`jV-3S@7Hd>CzPpaY;3%v?Cq?~l5LZIde%HC z+9|-*D!{?w!o~HItGD8G2YaW6sP^QO33YXH#l^+5H)U8n_gEUVqvE5|u3fu6oYG!@ z;AzprKR-Y3sQj$9E^cqsXUWylj%>|ptWQKtxbogiopROtpmF*+4u@GYXC8dH-(RJdn{sAvYiDC)yKw(LzvrY4d-n8Pycl@q+_}6}yjBc` zl^fQs<&9f^J>avx2v z&S|4_b1a+Bo%3Vduz2y}Y1huiO%m<9YWiWO;pqz(1X3d1R)!pMm#+;e<(v0w)|8N2 zSFftR`gvug+R6~GjOj+pL5m zE=r9TFJAQJPu~+nvGm?R&#>U3Y{r3MN))ecu@7cUrSXNfH!$qmULT1*j+aX%2 zlV(nzHfcqdknVFw|{KJP07R}1a%56?H z>sKZ=`tNUGW@eUNb*j)w&jfB*iS)l?uA9Ik)BVXAa;h>df@CUxXLp5Vur}!!-th5KK=XsK75Ve&Flx+Ods@n=Imf7I~tky z?7GtZQ$IgHzj*5wm**q{BcrDCcE8tTJ^TGM{$J`Gt5UAG_2C{%`0ai$1P2F4#Ko*Yg*ZTS4+&tTE4;3cQNgsZEOpe)CBdM&c zd?LlDZRvt)*VCs@Cw((rnq6rlS5jVn_-&c;RIkv|{ZA&pdGn^__+!WFYHLs>ef4VS z;XI?=N87_Jwkutvpv%G zmAn*s^syq=^55AYK{YkC3Fn`?)^B@!y#Mg~?~nie{k^QmY5b%@=RoDrz_1-{8?=_kOK7^@$z_9)CRW{IjA^C&$_Ienn=_<(X`^?my*q z;=+5C49$aoFZ*P*ck$SSX`ul~eS6TayFJ|1ocfqb}KYCtV4Ua!MQQ18}Yihy6L#)0L_up);+b{oq z-|ux92Q2LD`uOdBBwQA}wfD-O7ynnUUcICKzumjLyFugMpe$=$#^bkK`1||&>vv4+ zbYVJg^SQ@k`Ofn9aj`RheBJb|$MDDBx;-C{NoQnb9lCNw#O~jZ$6Yq;49orJA3LqT zpQrtB*)~pNHuC-me7S?h&QAMbe_fp9JoXwjz zcep5hsM`DD)vH5)e}Dh@%TH2y z`0VU#30c{zJtjFOz2W{VL%3Fka82}JVds}S^6~NUjeGXwyf6;?|L@63VVkNi9I>&n zAAbLBYx=eF+K-+;+VQ_{U1wP1s`CEM&drI5U!_egEHqT|w5A^VzVEwjum7Tpk^yV~ z+E#pE*crq3ut4Ix-EW;fIoqzIM_p~|{@nOv`Tow%=Ed&)haP`?u=Dx6!*_R=Z``yg zX?6AMcN5F+RkG)8mj;cLwQ`GV%}Q%@V94M1Q;kpFuBT{c&-{O1mhUKh+_rMs^%Hw$ zoPPS@X8QbxA0C27x2vnIRXkVC`1a|jc)Y^&(~3f!4?a9ReBx=*EXVuRyfs04b+(;< ze)z)&0}g@OZJe$vTf6`4x?WtZnN?r4n=^an*d1N5eUTrB^0 zh0^B$QTE1rU%q?^F6_8^K3r>R!N*6gOD<;$vgT)IZjAYyVx)QHU+{9j+zTSoXH{;m zxv*5sf4SdWrspvsIxp_8j+;Ggn%Ersdbv$H%5(i5KRVhiy7lni6V;b5UzYl?_=>!7 z@ATq6`M)pRHKv~~*coF{{H!O_;JnwxOpOPtpD|ilSrxpwVW?PoHB;T~+~x4NO4pQG zuO0LEFVB22e|?0`BX;{AjSTzC@7IQJ@iw~M{PAO9;B-%+=>7M8XUcx9EYC9M++b!m zrKRy^akY)yp5O0wGkvN1&@7*Gg1uI%!k@oxPH{)nbXS1NU5f!xw*M*smt%gzda_L=PcRS*&lxY?OHDX_xIPG3%4wr z>BAbyuR+nAmT1(Vgn`;b)CS z^|v)kUCuuLyrbwT7sG=6`{m{O)9?RWcIHp6TtEBYzki*6pM6?1jd9E7^z)#W!?hiP z%wo3}>b}0-)+F9z$ivI4D8d!l^!&yR(8O>`Z&gE`6)JY50%ZdN!*$#Wz$&rh=# zw#_!0DRNftOH<>u4z;g^=Rl!&cEjA|t^2iRE(%{4Be+-Pe$8j!vv;!oR(mfMa&vcQ zbl@?*#69P(kjkYvT@_iMuZ$+jMy8u?>bSYNm8Cp5UpL{z%aSOAYX=TAWSVFG;+d(H zo1436=F2*@S7kou^U8KyRrc~U2(CZ8{NN&kOYhdqn||&2fkx(v&u7hbTb+z%ioCR6 z?mz#Y@$|+m;_Oo|>t4lXJnM|xt=@an7p0|S z)oFPO`OUT3ns1s~wJdnKpW`Hzi>$|eA|}0`$x>`1SHFs3eb{~J(!{v9I5!Our~Lf& zPfd>>Ki(RxCe{0{>iV=;M!UrqzCL)Mz|f(mlJ?|dMaz}FRbN-7{@>#!Q@A~tq2lQ& z(I=lPcf{!J`E*KqTHX4M8x2LeU3c8gyZEBiex0|8RO{`xZW|-6{FvPP;>*j+1-tJ` z#jiLUzG2H2qnSMG;`i&V;jJxeoK>__MonFv(IKI6BJ1S~2bQ}RWkowH|6DU+p2@Tj zt<)PeSJaZ#6gkgGIEROeA9}dq<#Vs4lQNdpeR^`TcKO$fn=fn*TrOv9$J>5b$;tEL z`>bnsmYKS$P4&t&Nn321+|$XnJ8!#mY;5eT{cmSKXp=g5_13(7cXwA6Dox~QI;bF# z+@{HH7U_L5#i(=TN==VRE!X3!bD4!#&Fubm=xGt>+Wh@XHc#00yl7|322<;vImLUv z-O8Sz!WkSK+_pJ+e%agglRo@?vAEym!=yIjm!b;iEW3^_y3u{`;KA7V+p(sl)>c*z zK316UWSC5xdxqEOC|B#DbzaSGYz|*{#qX^WJ(?sa*YEx#sOP|>TX&cRmgfA{oZC7t zGb!m%!H->13VY-9r%j(;`1MujX)nInJ!^M4EmVl%eD$F9;;)GZzE;@GTJo%R>ha?e z!aNfF$8~)N;Rk7yPol{fZ1-;+**J)vZ#~Fp4m7kYY z#O*yWH8oPD*x%1@QHa*fKVi!IFZyql^iIFm;k9%U$C0B+8)L8jD$`x{x@f11-;{gt z%j+LBHZmFEP*qN9=6Vax{sz=o%c(UB` z-$_4M?7V%$h6OL17N)PsFyZPy-W;RXUSYFN)6~egdw$vHZ9!|_-nb#5;cqTH=xPyOnV-YIb9AouD?C!)9Kajvc2 zZt^NNGw8wh->k`!U+=4PwI6;|yQKDoYS-DC*~&>y+ACL;IlmEq5Ij3Lt-b5o5x<$Y zW?i^C|Mbe0nh#$ad#zMj6nQvrd-Rv7cU~Ir(>}>|mFHBuG)?O0`c#v56C3)oJbSnBuD`B*Y1tJ~DYM+USGxSd!>6Y{W#8K$lf;|* zN+@v3g$%3Rx8#$(@6BBtqu0K>{C$_1Q<{^PZNnUfgJ*OD*Z)&vsz_KX?5j83c<*;% zb-xC~w%z6LqskJtWxk%)Hue6D4T;;0xXNUH?1@|dVynHib#})uli5=wuC6jUyX}}^+d{l%qrk@tA)l89Ub5cBQ_V8=g zvrBttGs@c<|D1RF>85A3+jj2W>ykd}*=z~bw&|x+12vszT)c3=eeJ}cl~Xd4of!`O z%oP2zVP%=ah1m`KayAC3PQPqt`ZN{mu{q72Ipu4{Gp20I#d+JKZMr2rCT*zw@ZjLQ zGqn{_9p_Bu7H&Rvutjip;=6YiA10o)>pX0@biEiF$VSAZt76+NlcD0+|e)!=HhOAem%NqSdGB1BP z;Le}#_M}72_wcg0%ubV5ZF!dVUfKS2p20)Ek8^H?uVJ%&WVlKtC|cR(h+Y_@r#{Q>#nSb+_INM9EOmCn#Eu_*yVRs- zhJT4^bxM4{cxR3+qeT2amL*%~^GtYfp>Y2%8}S8YJ8#RHi}m=TR4==nU6CDq=E3LPh40Rae|UA0VIH>}V>D~mQ&pckryjJ+s}=lx zCc9=@c#ey)buimfr|$0Vf}JrRe%3T8ew7MTo6Pt;#^r|8LSw$Wi?6?CeO}`{Ir^*d zgs_b}c4XX;$}OK48ukBOXhDI2Npmu54`Yw^0_z06zR9(=KaRZ?JTmY5;g=tp%o*n| zmwT{${*uCv&G8DePd2YVy!6nHXP>`r(7zfb(CWkpDmF8ug?_ByULE*P&T#hOhYg1x zGCY^zm$&=#Y4bhiAL{%6vei8fuYJLJkIn1xgVpw|Ka>mH1>Wy_b?pC-=)j(yze4}c z%KJY!b^hNqH_OSUW=6@%6Fby$9rD?P76%n97W8r4<-RL@Yrp^d`-_`h8P@MV_KsmM z)4A&|Ht7t{wHc02Z#dd;dHf3WW_;tBr$ zBK+h3KdEB>-`)Q&v2&^V>+2b88-mPjJpQPhHcE-SccFW7MqVCh4ttvHoY(iReEt1b zDJ1vUlJmCTWh|bbNZXvf-f+QD@dNRH+T)M!|J5E@V*5LOfz3XiH98HS-4l2woa)Y~ zv*Eq|ivL6JWc!2j_kS1t^DUo`Ib+ksa|yj)1-+7-POUfbj#TXRlz*^ddB^g9aWCRN z|J?I%gN)YNtlZqY57_V2_U6~lSJ~EAmi=t)k!vNB?*Ef-`}q8QE$e-YpTaxB>KmRN zzdYeocLL9YKY|-JF+Ba43Ub7U_5TvZ>OacY$?EPqaBF4SSLUj!@13DqqD%L@-7-Jl zz483Z5~oQjb2(S5PCw0hOk&!!X$KAnWMm%w{_pW_iN4swULTEArd*c{Y-HioW)NA! zG{-S;)&VciEnfAl_P_q7f4p7)f680tEesA@=kqsQ`0?d)%2j1;$z*AxcdtEvU%uac z_@O{u>gsCMUjO&^Z%l)aW;o}iL>c*dzVCcCc-vNq3) zk?UvgG3@E>ZESwb|NlYgoL4&=r+(5sv07-!RnyKB63t6AGJ+WN7*`9m9}y`yw(vvh z4lnVK>;FgcS5;f*ELpukcH%VW_WN4xzHgsrvjoX&DbF)DD*YbRo!Yb3(1hVbXaU3C zy>EY9|G$QP-6dCz8p%0pKmJ;EJ%d(8{Yl=Rm#JnjZq{@l|s5&)+sD94+*ai8%NxZT)QZ z)xJD)*5}$J3yGXlOx!Emx2dV&vr*mu`SUhS-N%*YzI58FFZEnCD<;o)z0!2X#l08y zt_ohf=1*z$-8<8Y*^XUf666s&&~X3%^5p^ractpX*I4W%M9v+Yu|@5N<+byNe(juK z5w^hL5rcN$Z(e>mX_ z=MJT&p9ymob}}E3da+ThAvyQ3>NOs|zEF(>o`7lji96;mOqSmga?7(`{%1($-|W_m>X-;z_FMknw@v@-RT#yzjm{dIc_ zTittz2v0y&8Z;#5!QgYxrCug_oGa_;?{^=m%bvoBclaevF!%?BmL+h-a#h^K!z-CSMI z_NCgHarxN<7iXsUrOuyTTvTpxQas_C>7eP%bXY6?wo50YZ%^{puHs`B3#LrAxK}5# zZEv(}v1ZpMO|~`Sz4^xPFWP^Ov*l)XF)4q0JxwOto@w7KCsVy$SHEsn+_-h~rc>8- zRhRPY)LeV<+4n2$k<4OmuGPNtVo%%JmSDyp%`l}Xb1vh)9MkhtN;$(_PbsE1ELn5l z{o`zn9h~wP>%Mc82!P=#CYa_0wO^eDmaIfJK<) z0TqTlMRAi3G3c#T_&s6atSw)cubsO0SEXuZ?pn{^E2rIjm3hNY_p$s{h2?A9T~s}9 z18Q6|p51u7)V3tZjs_*R8F1`#AmjhLviIlB{{q_+Ze7S$_3kI)=p7aHSN&UOEF&tecn$%4$kJFlIMZe84I=;15-<7c|}tc5SCSwCd)PS&wDw!56{>dYLk z>UJh=vqjk(35Eyuf1g`xc+_eb?o-=);Mx*J_E|SpE#1zX5HP3xz9NK>T`cgBMGw1Fd?q6riER!Q#lbiK$!&TFU zCo()dJ&dZy0w?_XF#q4@xvP9m-Cg3Ku)uTas;~=YG9R9PuDpD#>C1%Er%z|(=C+=H zet7D9|3{Zn#Z%JtuYLNqkLRsds78VI8{a>-?d7&_-K(`k-*Dbl6YEtguk2qwFS~T* zkGrd`O}@AJww*q6Du2%1Ns-KstX%oW0FUyw|0Gk=_4Q)Q9-ca{ z{yOM~!Lbc{_T=n(cBJ^%IV)b)nvmJ)q2=!Fua0yIM`*5n^u6vsbIrC2t^&4J`R0#b z53v3Y;wU_ve)lw$+q9Dz9m$NiBU1tZcv?f`=wmx?xa_*o|~=cymD{aw5=_tQ?z>T z|2nR`qw@dk467ULZC^RW7hkZC?vMGnbs6g$1+R1EAKU+zbN{>h|C_SLwu#xCX+4G~ zHm!L%F^x?nBs;dhA-(i|$m`6~`(NHz$MVFV^i~gwYSDIYWP0%b$Nl@q_y65L&(-sv z*pz3^@oDLvU+Nfq4sit^IB>CK1HbwEdg1+F``st_q#m$bZGF$W;=_VxXRddN@-o+4 zalW6`@O80!KSPaWWM|{!$@A|YyIudi`p4t_FPy@DR%h5<;5L~4p|?s` z+O=!8tL@Wz)~Da|e0^&^lin@QwNHPU!zyD+C7ny2dJ9&k|2b*uZ z{`n)${p0_?Pi%R2|FLeE$9eD~&#~}V$G!z7JM4_v_T_#czrS|zj(3gz_xIg-p%A3~ zB|u}##6Z!$=#Ty%_y3#ie>Q@BgYWIG13$VWAgO%oBvH$cL`b+v~!nIfN z`=4D2JJV(U`reh;n3xt7PpM}?`DV5s{`}c<;`!={r8y<5otT6Ef2rCF8oDVybmEP| z$=#QEc1&ive&W)F^L)#`-jH7V{`5@g>_)zCOaJp_^IE8UFr6@Y*0tRt6Wx~EGWHlr zYN&5GXCr(tL7>MFw6f2mDSZ9T^Us@AJR@Raa$+86Pd~j#v{&lN70EkKrU!}~-4NUB zdwX%{h4Y8o`I!&2t*NVJIFb=GQ_kj<>*}kMtDNTi+j*sF`mG?zvl91YgF^1FY*_m| zXX4J?({}jFMxKgjW#QvI(B)CJYeBFHLp@E32;J>8WwOgcQig(U= zxTC9$(Qa!>XnEHK>)W1>i+A6hl@Svwvqm;bDYM_jh;zD&QsS)@!MS{w56CTUoAj0C z&8zAq)ysD7tm2(f=dU7EZRHfqbh)QHW^>WW``hE(bvEjYT6!=URe(DVs& zvmWjZ&orO-Rc-B-dsW|=8QC_7%~5hMQTB~I{rvMGOTY6;DPe*?+`p|TT>B|iU-azi zS@V?=J}&)v`g(Tt&QsT(KAOL2b?$TRx!&h2t*oqgTG!6=aK0~oz}((`eWHW$e__x2 z@80EkKVLpEGcEY*0{6v#*A)FY^l*cKTkp1OS&pYH#iiB>7Nl>E6l7HUu=(ezZ(G-u z=z-Qj1ShUmyL91dvZJ)vk7?7U1!#yIkPtqR|D=A_%$bbOWf~n44hzUEcCCtv@_f5x zTe`~D+qZAGzQ4L=v)|rtkAr{rUOk`mMe>|w-u->LTlTqm-d^c$-aPZ`gO3$1g@@j} zS@Uw=#b3`q|CH4azb*QJS;1lL=FOL<6o!X|9ck=2nq;;k*ZAb7o(vN%h8c6_n4~6b zzpeX9H9bAuSKIv6TIS97;w`PMwX@2uHfMkNUibrWN;`i-@deD`^?Q{Yga5^&OWDDgp2jG zhpy+n5-Z(5Db@G3uWNh0q*`~Xm(s56)MIHzk~+G&j+0apC*5S)UeoSCV{Bi@&#b_lwKR)2_HJUAa>8VtM}`AGbYIJX8X-rat&u#k-bw_9}6UEJkAs z3yyP^d3SfEa;3~EUif4}b&~nHAM1Q07dMM^y7objwt1kA*u%2j7cXBqk-aNS- zck`y4PWAEgoAs;RR<8fU`-ux~@!k@Buz1~Sk!9N^tvb@eU|?X7@api!l~()V!^d|7 zt^}-CUs|%FcJ1%RM;~`|-0E5w#%5z;Y1tXHl4bjCT@!)m8`qY7U3tCp+`F^0&2?Kd zOr%6zd1m^g)%Q%lk}Z0qW$)g-O`K=9>?_*I(|^3Vv9U4s?a}@7|LASfNiHY|$f!h&eqJjy-`f$LZJQPn^rzPJ_(tboO1o{ zlY>$}N2hW-=UN^~GSuj5`uMSM%IVb0Cu?M7WkKr;igw;v7cIFv?xYX<>nA5CPkTRm z#ta9u*{zpf9?4$6H?25g>N4Gl_un5saDYL?_2Aa*>t;)qt*GyJTdefM(AGBAJow5s z=LbOt)`Vysd|Je5d@V!PYpKw>*xhWdfuM-E<$j6HskrLx*6R;StR|jKYrP+`SNzr2 z#Shx!G~Tb)%x?K%Y-|jf8Zf>Bg@csA4Q`+k#_FaGG_||I2-8|3=HO8!~$1lDt;ky5Ks&;tG`%3fH)9Zfy zc=7$^%giUAD^G07k=Pk}`ReNMiGIr!PaEB<`<)y8Xx5t5QLpA%6f!XwSXp%~U8;K7 z`PIjY3%74`pWFH2_?z(F^)Y(gE>G{>i@TV3>&0JhZ*K`%S=J3^f6t$g&;7ihOcrF! ziA_BvR=umk*Uy@l37(-+?r#?-F~<+c)6eC zI?kDq#>U1<+=&$x75&Epu9@@l@*aHtd0~iFVoAxBE$5XtTuYqE) z?!Eu=)z#IDe7a05jVFIUHC3BQqrI(-CwRj*ZiW7IzR z6%u3U3XoVbzcsi#JIF*^bZxH#buvk6pMhLBq8A|KIPvi!)!%u{HXrQ5Bib>sJ1W!T6ZT#djVm zNi%LQFW>s-0lR&}B8?6gCD4TS3AYQ@PrhINC21?YbyEH7wcA@xXQiYZnbdxQ*E#p8 zX8!w?Y<3J4|Nnk>{owSrClBzd7*xZ`~Zb+Ng~4CbtiqlH425w;?V=WwK{ae?Rx+>(4(&e)6@Nd+fyv4HZu( z6RD#+i=XqHzV*%ee`$GnH%Fe@#PLTTD?riv<8Pfvw`-&f!B&SLtY|3yD2PuBCu-A~^c z9ipS9K`Tgn)SBJ<n3`*xD>9B-@otm*BxONVde+e*SOx{H~})Z`ujW1HY4S%Oj)kh zBhTe*DmFZ3?)=`Av{B;RJX>zhNfXYd6~4TrnzH$3*Q|FJE`Z#k)vzf*<4yL4)vPSx z;o*kn=IzPH`wl+-c;NBJjw=k7_Zb*O`9S9aL@K9!d2&+t&i;D&c7FM`+i#C`3abZb zP5tom`TY00zkaH)sdzrOyx_}=z=f*uyUW%-O0Tt(2Q7eGwoJ{(-~YYmsgT1A@;~El z-M;v)mc}#3YbnT0UtR<%wuXm?fBgA;et7AE*QH4kf8UkuF8uZ;vgWCF zykJ88%S)1%rM`| z{?)g)x1E};>;70gd-m+ZkB`Zx&rd&{syLB@VddN^tGV00WWU`eVrCVP^?|F^sp9q8 z?H4XzR-W;`-Tsf_ySuy7BYGDufA}zQ;mh8ho{odoWx?Ue^&%fV^rj2Xx^H~mrg`Sf znO)V}H*9FQc{9@H`<-H^7j8;|HdS9dCa(|6JYViSd-Ja~I;SgME}ag_wHF0aYHDQU z>wYALu2bHBzd!!x)Of+G&Y)rxjDraBUbKdSZ&(a`H&=QW;S`87W zg#jx#8lrdpkE-~zkmNOD)z9EJMLfiN!WSv=@(I(bRK`b9)JAf2!-dd;TzHQ$mF6B*0j-D$l1Qbf&|F~M&B{cByVYd>i--jmMX)2Qs;2im#uvqo<5 z;>AC1p07)rQI%A3ZP7)Idlip)dzXdtXKI)7*Muo7`u;QU>cKyM_8coQp1cIK1n$Wb zm7<+8QPI(bFD@vuG&|OOT^%p#cRNNey>jkcrRA49#p5a%^Y?sYtE#R(`ekQ@O`rF4 zJw>^G>2F%bO8v*1^Xop(=3r}Hw7B=}<=eB)>rX$uaPeYhU0vOb{QTp8e}7+Xb;0DW zu3SGigUWw-U%r|zF9gHF!^>;(d;SzG4B%j9X0G`A^|~Ne>xDaadTQ+YwZqpXl$40@ z%iEb;_4=CoBzdC*s2rS_`Qh*T`umr3?)k=TIb_I|(0t;@x$=FG?H^@#uWXo<@ah>$ zh*of?;MePK-o0~+kC$g^Y^boEe_nZ_M?zkn-(zRv-P6xz1bmTXdY@p>(bva!(B){7 zAZW3E-QTLSQH)Q$Pi_J&0DNEn|F@I2nHR(Mm7C7@U+Yqu!q@IRW!K8H_R9aRuHSn6 zv15I`y_L1~!xF1j`RcMVv6UfQoi0h2XC;`{oRV;^uC_k(&_HQo$Fha_R&zl+@jxwg z;ri95RT(?(?8`cLChy}S(Z08qAFg_BjNsAV|HnuI)Vi4Vb>-9=p^Xtf#$^F-m3`DE zpGYxs`jlUG*lD4_ojh}=g%f_n`p)!W6BZT@J-f;D*T2il{TFZF9v;(uDdtSnrQ0FW z69TL*M(6KUot5F^=hw9@e(M^2-kV)VlcazBe)_qR_k5t`!?bs|AAjT!wiV%0l;Dxd zS|ThgJloFt6~l?C+TlFz)8CoCm42}J?9vW(#%o_+UpF)}+xGHxQ2w?83!d$_d0hi} zidh*IW}kg@asp8qRNMpwQ`R+;+Z%kV9{r6u9DXFIW z@5N)+u8-T>m9#N~`7YmwIhS7^SsA?Cu3iK= zxFcnA*F6#_w|5Ttl|$ zr&-ru=Z>6ay3f)0c$w8)G2M!uhTXHCoBWD4kvjU|fkKSl^o)KxE2~wKB6-dC&V9On z<_yoIlPb{t7GJ)9U%Y%ddvbE}w5=7NfOE3V%$HfZM`R5;hdU{%Ezt7*w=*PD$|6N|6>+a4TSNT-* z;U&>9{#~+K{)!cS$doNG=vbxK! zk0u2^S;Nc62P#D+WMo`)#121svc$l3)y)}Es!M+vU(MO|uQ=@O>BA2Ve*OO4dVg7n zR$@|;(`TDsuWvorUhEXUf0@YC-lIt!N0XEqrg|*}?Ql7B_N?T)iIV?wTz^FGT|X^m z|FdUlU%q}-6zg^boit-;Z{NRhqoMDCrI$8evF5*K`eCN-oSUytrU>2t|L=RNr$^SW z0|yvD!xu?2JqtFUT{UguD#p29KKiGPKznBm3>18#CT*J)vTV_(6lFi7Y zt+vYbad7>)=bsm@U++ITE?8?SXw!$k-A|VvxvMN$wjJC4IjsGk?amm!#fuk9NJ}3+ zIa&SU#fyyFZ}%FfpL=j+Ww3{e(2mN_%MztGp8e#bTfO!S2uKK4FY>t&K3xz?1< zo;PQX$i8=fPxznhJ=U;wPdkHw2-m{p%h~VVy_=DldGO`s<%Pe#WU2^pE(y{+bLLD& zluho{Ei1m=v$nDl;A=mssF-m2`R5rkW*oS3+B@2^z?L)_8)CISvg$ohdbOBzj*P&z<1j6C#joNzMW-rtFZ&s9}lc+SO4)* zYp$@-YssG+}s=>!n!nwlV9G>#cHnG#)u=AE(yum*XdMP zOg`DMLQD0u@I-^9w-UUv!X`&D?cKL*`@M7PW^T*=68v|~Bo)cH)i*tsURC!D`^B{Q z`fFEhlPHUX++*yG3JeQdS(_avnm_&g^TFqzF1Cwq=@`6fUmj<@_>OINC`TLLQ< zoR{E~oh^0Q-h-*&5w=i&yb)Rh{U! z+;!f9dp&EP$JnfPNH%3uG-l;vnDv!^U7fRz*lG*r%?saDb)P9QGQYC3G35P=0t-<4 zoWp*Oif6&@yR+U@Z8hUq<2u6)>-*YKOpuKqu@2`~ly<3iP{k7`bddJu8+Ou|&W_EVXZ|BFWx3p*F=2}+IpW7hq??)z@(^}G$zwmCXthaWsxaN?Gq+T!#-)rS6#rf*j@7n01)VOk`rmvR! zQniIwt&DRgY&p5?@5;NDGJGXvWs5d%7JmEo?W{dFDs1{*zI@pgsTO_dN0w{mDX;f$ z-iYjTc=c(5hl7|nB7dde6KlHmr-o_<<7o7KG4A?n>Po4-kxLD4XUK>?X8}* zY(PqbA3kczuehE+d-g4LRgbTkt!|WjX=`SFzPo~eK!Td5P}k8#1@&jooLLZ~m+!gp zrkVWxGRB2hc?0ctB^a!DS>W{EfBEHzh=>Ev=hyeOwzBHz>fVe=o9tW4QN`eLnf2Fl z`|nF`OqjYo{rjHa1KTAhrAi)55O`P+!LV?jv8&yD@w;lPWcTcuS~z)IWp41^hEru9 z9+rJF;?-MjyxPxv;pb_mpMo~9?Jj?>G}Y^BFKc-Cf#vrlBBu%RwO_n+N$J3a7k^n= zu6#SGQJZGG>Q|ZZEZzlXZ%q<%xBXcg6>qDl(&*rDd2L74{iT;P3*{!CT#}Y@C~ZpL za<(5@6VE?C3>rfD{8NCd)r7h7%HQolD@8Wl)Y8O3pkANRfR=-`9#2nAdz36_8)Tgz1FFb1J&EA?++Q>2Hm>ldkvDlP$ zV!8W%aV}6A;G7?$H+^%C+NBk@?(AdQ7bR_Y?UD3KmJYl5{OtU4S4^x;cGYc+a8a8q zxLG74M!_>c`cKX((K~zM^rNDp9+X&_Dx@EM?(OY;;`wKfr9q%_l&iIArPy}K@2As# z?6_A^e?OyUH|w6Pv@cbAKm7iCXpMnXySvczWw8@EeeF9vw|9G%UD~s*Tt0R2%+x1! zg;sMx?SV#Sc9(|6ADx$6dge9j@C$9)8ROSuv-Kp~zF6b6p2jvcKMeR}EEKl5)z)s3 zveDS9F#r7G01budr!&=3CyGV#{1_mx}C zO1Y-RMHHRexM4%X)2FF|HK(6{j);kI*%^};VSJfw9~s3$Pqtq^-(hOmgYUFk!{ysi?aT3yS4w< zzsoO8PCeZup|Gy3|IPISO}sU{Yo6v_Wx2Bdx`#64fVu(;nFn(guLPf%ARWYig}uAR zGKI8Cv+U_%nOdd)<@%iY^V=6` zoB$ur<+1#7L}cW_M~@aIZd!lQ_SOE?fqyd0KG?cCWPFn{e4i#c#l-EB7-L7`WtK@L z7FU){zE~c}t&sItO(&&w1Z+IA`e|%f3wqvSc%a_X!3l?nM8k$_TlHYFcva(sL z)923+d@eJ;{-0%7c(|)=+QtZx``!!=TXyW|m^yW8uJOXXaqE|vOl0uk^19FJ>ZTX(&^k$qb}gE{{2%tK$- z?F=fJq#?kuVCT+A1`gl#%bnMMYYtt!G33+Kr=NfFMoruI^2nb*dl)jl{@!l1rg}L; z>TYH)vlBNDqzBIx`l+$jCrf$%B44w&ET*?79a5W}Q+4abfA;kmG7)!GK6o=QFfe$! L`njxgN@xNA8@nR> 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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 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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 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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 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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 + } + } +} + void JoystickConfigController::_signalAllAttiudeValueChanges(void) { emit rollAxisMappedChanged(rollAxisMapped()); diff --git a/src/VehicleSetup/JoystickConfigController.h b/src/VehicleSetup/JoystickConfigController.h index 9b0a8c8795..ba6ee8960e 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 594a028f00..8631f174cd 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 17ce67b478..19700e9fa9 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 c9915eae20..bef89146f7 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 a8e6c754dc..13bdd56415 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 2e243a7efb..ce68595db9 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 13bdd56415..4af0546ddf 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 ba6ee8960e..c0c709c724 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 19700e9fa9..2b217ae486 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 bef89146f7..521f8866af 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 303c3005c0..59798e2681 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 2c4a9ca6ae..36d966e284 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 ce68595db9..a204c16c14 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 c79c2f186c..d9911f703f 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 eb69d9a718..42aa28387f 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 096ae71513..3e8ededb99 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 2b217ae486..2d08bf6c19 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 521f8866af..0ec6d53abf 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 a204c16c14..69c36928aa 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 b595e720f1..ebbc6516bd 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 50c8d837fe..45f8835531 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 74e2d3e05e..2c8b275fa5 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 fa01ef116f..9e564002b7 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 2fb1d80163..de0c516656 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 5b02b32b33..ff22edf6be 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 eaa1909061..ffe761f37e 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 6d925bc463..5b1fb93b48 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 17ce67b478..f86dbab980 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 c9915eae20..8b8b52490e 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 a8d3f6f936..0e3fa6a2d2 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 8460b752f8..98a3d09ee2 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 303c3005c0..4e074e923c 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 2c4a9ca6ae..e8111a79b9 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 d436f68324..6a7de14ab0 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 10c7deee44..6c7df9c29f 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 c9d2c01140..c291ea210b 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 503c6be535..58069ffa76 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 505478bd73..8111896d96 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 0e3fa6a2d2..9b55ccea4f 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 9b55ccea4f..97a0b10ccb 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 58069ffa76..0b5c3f459b 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 97a0b10ccb..eb9685bf61 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 b2ec571cdb..4cbe2b34f8 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 7761d9660b..c59674d8fd 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 b1b8ed8997..dee03d8f58 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 b05d49231c..e71b230262 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 c59674d8fd..e9f4797ff0 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 b6a0629249..65311e3ad1 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 6e6ea60953..6416801bc3 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 1417af567d..5cff7f1dff 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 c8f24b13bc..4e2150add8 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 759d6c8534..19f0c8ced8 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 757573e812..47a46e647e 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 f70ddd60bd..48c0527d99 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 002b1aa54a..f7de1882b3 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 19fb78e0be..c8f42b284e 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 e71b230262..f2eee50d5b 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 84bb0a099f..9645592735 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 e1e9135120..1845d3abbe 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 1eacfab917..0e7ce90c8f 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 e900e83823..2d1fc417e2 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 5d8914929b..65dc1a16cc 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 eb9685bf61..a36f4a6483 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 47d7dcb012..86e5ee2475 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 9a1c6b19e9..7cb5437a1d 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 8ccdc9270a..5b91083413 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 fc3446c49c..99607527ab 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 db76da950c..9b018b4598 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 e46cd5da14..ec47cffb24 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 3eab2d874a..09a7c3216f 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 c9a5e6291e..912e577157 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 65dc1a16cc..5d6e27f311 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 762a608f59..fb1e58206b 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 be64cafd5f..ed39910a27 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 5cff7f1dff..5238f025a5 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 4e2150add8..116ffb0e61 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 6416801bc3..2c6a304cfe 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 45f8835531..61621b69ed 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 65311e3ad1..a7805187f9 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 b5fb661e27..7d442e427e 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 2d08bf6c19..20093ab2c3 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 eae33e12bf..6d363948b8 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 38cf2c4b85..a63f3ba887 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 1c4005bf18..bc66e1716d 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 fd70dad199..0000000000 --- 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 c9ac663f14..0000000000 --- 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 686381d439..0000000000 --- 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 22012e5661..0000000000 --- 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 f19e27ff1d..0000000000 --- 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 5fdc728643..0000000000 --- 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 42aa28387f..fb6d02816f 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 3e8ededb99..0c0eb0f77f 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 9e564002b7..9743c72f3a 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 c59f876b8f..9278056d67 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 8940e563d6..fa1988a2e7 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 2a3aff1e5d..b941fbf919 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 a5352549c5..879f740c42 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 ad11cf65be..2ce3556632 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 10c7deee44..4876910d66 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 c9d2c01140..a765f876af 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 38ea5f11bc..03a8f4356c 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 6d446ded70..a91e37dda2 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 3e7b1e56a2..677656d768 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 3aac9dfa0f..814a12ac99 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 38edfdc74c..d23351a11e 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 8b38d13000..8ecedd9802 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 1e70939ba7..5765aa8560 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 71f770f3c7..f5c0e10821 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 391042684e..254e4c9e29 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 da63535fa9..1b87e1d4fa 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 066b12356d..c862522665 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 b667910903..46c38e5297 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 485154716e..1b38673cea 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 54f507a7a7..9c86633244 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 fb6d02816f..f42ed6efd9 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 0c0eb0f77f..f5c27eab00 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 9743c72f3a..f60854c4a5 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 4876910d66..7968867902 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 a765f876af..9d168eb004 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 9703f44e0b..268132483a 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 f80ac3d45e..66d6e26976 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 f4596e31fe..6833019317 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 51f2728454..71398d5009 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 1050c8986d..424fb6e019 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 ed39910a27..59618eafe7 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 5d6e27f311..0a58ff8d02 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 7968867902..f9399cbc39 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 1343e18ee0..a7bde7a2ae 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 80a7e03467..c3a7dfdd5b 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 bf0b4f956c..dc9315a86f 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 7270ede46c..41b8966e38 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 732bc35619..6542d290b1 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 13a5c0693d..cd0d25dd73 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 a678407b88..3ad3112ecc 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 1064262772..a2c65c015d 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 6dcfac9727..6ea8bc4d21 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 0c0eb0f77f..81c7dd6231 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 ac1ab600ed..718f8b7f40 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 7dbd479f2f..5a00533dcf 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 6fe546c04c..6613ef4ed3 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 6d363948b8..fdab658a18 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 8dc19950f2..c8d5e31349 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 4876910d66..347abce3b0 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 a765f876af..82bc21a1df 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 17f1e853c4..d32455a9f3 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 346de17fef..3d1cd16579 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 6be68ccafc..bbbd9acc81 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 4dad4d5fda..4820fb41ad 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 8ce2460b97..515fb599b6 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 bbbd9acc81..633ba60dfb 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 633ba60dfb..ebe066245f 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 bf0b4f956c..b2e508728b 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 f2eee50d5b..889f829688 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 9e9d318bb7..e639ac37db 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 42a181bf48..76a4bed125 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 2b78756266..10b55d8bdd 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 d436f68324..baa84cfbe6 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 68d31c11fa..09233d44cc 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 9383c38a0e..8a2031b864 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 4479824747..cd4d75be08 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 df0c58e736..a20efa26e4 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 6ff44a5ceb..0eafaade94 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 42c76d1a24..aefb1d923e 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 3eb129d28a..7712df99c7 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 2bb09e6015..4547fbd3cf 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 2af400c220..f64f29517c 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 2b6062b7c6..57f0ff02c4 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 5dc7788a42..06d0eec0ff 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 7e77599333..b7059d7768 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 54855385b9..1a79754905 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 1734b74b8e..91f87f90fd 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 0000000000..91f87f90fd --- /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 0000000000..91f87f90fd --- /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 7e2b6a2120..941a2a14aa 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 33fb7fb026..3c3ef9e5ef 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 da2380d4f5..88a380a1ba 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 8067354b9a..3ec63a61ad 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 5c4a57599e..bbb7ed961e 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 78a43877e5..88e5e52725 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 ff22edf6be..04603ef0f0 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 ffe761f37e..9f715bace9 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 886330ca93..6c2fc34d40 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 a9155de820..8ac4d9ada7 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 58fbe6ea8e..35f920229c 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 24def6a8a3..b3cd24ff6d 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 3ad3112ecc..0f1c5d83ee 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 7c1cde362b..bcf4cef908 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 23e5daa849..200b04d864 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 a1a7126a02..5a6a090aad 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 ccfdde11d1..355a59c0ac 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 7efca0a642..e3d508ae92 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 097fbec3d3..a517288157 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 19f0c8ced8..fab97270ec 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 47a46e647e..7a912e5746 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 f48c1b2a1c..e58a6e2a6a 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 59618eafe7..a02c919ebe 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 f42ed6efd9..fb6d02816f 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 f5c27eab00..0c0eb0f77f 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 f60854c4a5..b2667bb360 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 f9399cbc39..fa5b2f1257 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 9d168eb004..d880b87690 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 99607527ab..9fe698a3fc 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 0000000000..fe3a43b6d0 --- /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 0000000000..dc76a54fe0 --- /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 c33e30fea2..eed1fdc475 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 0000000000..9c2e7c3ec8 --- /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 c5a019acd1..2ffe1e7258 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 9fe698a3fc..36e85ae3ce 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 ec47cffb24..473136265e 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 2b82520948..86d08aff74 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 9743c72f3a..4654da7ba4 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 9278056d67..a266bf0b6d 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 9f252926c2..b3358db3d0 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 b941fbf919..a8e107180e 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 879f740c42..a5b81c55a2 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 2ce3556632..03d5e34f32 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 c6d02e2cf0..84cdd15c37 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 3ef7283da4..02a09e8e13 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 9fe698a3fc..aae4d36b5a 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 0000000000..e30049adef --- /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 1389766e2b..a71ce19d61 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 e9f4797ff0..25b68ffe36 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 0000000000..0d4f101c7a --- /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 0000000000..55fd5dfb70 --- /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 0000000000..0c9f76b43c --- /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 71d2b6fff5..70011b715d 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 5a341ad197..f1b45fb34c 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 355a59c0ac..5464ddfd3e 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 03a8f4356c..954fc3cf2a 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 ab2612b787..a4ad9419d1 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 4654da7ba4..f6475d47de 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 3b598fe3df..0955cec12d 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 1845d3abbe..dbe141d4fc 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 337aac725d..5599e02528 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 c8f42b284e..bb167cdcbc 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 3ef7283da4..6c2c893acc 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 a530547ddc..89eac8c4c3 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 9d1a1afa03..492da621de 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 6c2c893acc..90d18be9ec 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 d1d0f4debb..e3d48f20d1 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 6c2fc34d40..349e3054c4 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 95e0da21f5..29d8b13507 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 dee03d8f58..8f656ceea5 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 5001be8e38..0000000000 --- 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 bb167cdcbc..0000000000 --- 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 73c339325b..9a0d30b57b 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 955bc2fef6..71fe844ff1 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 48fdc14627..8e9e8fc779 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 51fe0d09f7..6d9f47f58b 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 a3fb1821b9..71b2ddca1e 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 70582454ab..0984d4764e 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 26a5dfb6f4..a861e41d54 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 a7805187f9..3e711443a5 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 1a261c18fa..31f69f52fd 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 624bada907..2ac3237930 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 70011b715d..53a209823b 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 5a6a090aad..66e0b76672 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 5464ddfd3e..3d6c0de22e 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 a517288157..cf453d34ed 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 5ca81d9813..f67e57bae2 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 717bae81ac..762996fd52 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 fab97270ec..df23461f73 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 7a912e5746..bbd0489977 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 0000000000..0408440f95 --- /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 0000000000..bae2fd3b91 --- /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 fdab658a18..45d3656c0e 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 349e3054c4..0ad9378c59 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 29d8b13507..8a8fea4fcd 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 89eac8c4c3..eab76cc0f6 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 492da621de..b668a9d7cb 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 a02c919ebe..f0f231b242 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 2dc61a3a0d..a0f2ab1175 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 9c2e7c3ec8..337b1b40fd 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 a71ce19d61..19c53733fc 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 c7a2258c41..27bdeddd4b 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 8e9e8fc779..b25a9fa69e 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 0000000000..5ee3527d35 --- /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 a71ce19d61..2bcb265e43 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 0000000000..d3a2382cb4 --- /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 ad512e6eed..5aa8b072da 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 0c9f76b43c..26a5c47664 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 7f46a28ca0..6ad612d57e 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 eed1fdc475..2040fedc4f 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 494a73f401..dc6206170b 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 6d9f47f58b..ade759c9a8 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 07d4b4d238..abe3b7a12a 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 e88b8ce552..0b9cb3de3b 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 929fe9e5e3..fa05f49b9c 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 7fcfaeb7b2..3adbce57e0 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 df23461f73..c43db9012b 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 bbd0489977..7eaa1c5eb3 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 0408440f95..42b8422538 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 bae2fd3b91..14c6c4ec51 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 e58a6e2a6a..aca30132df 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 0ad9378c59..5eab8248b2 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 f0f231b242..3783b1ba05 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 8a3828a6f2..ada44fc27b 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 4d50786953..c3db1f46b3 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 06d0eec0ff..f64d20041a 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 dc6206170b..3dd02aa68e 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 ade759c9a8..aa0575d19b 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 abe3b7a12a..3bc51f2954 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 71b2ddca1e..d4915cd0d6 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 fa05f49b9c..ac9a11fda8 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 3adbce57e0..a3c9e1e20a 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 584b5ac5c2..c20f3a0b21 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 4827e4bb3c..9a1e40f9df 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 e3d48f20d1..a92a4ed89d 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 07bc901c3f..c080b24901 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 c43db9012b..66e65e167b 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 7eaa1c5eb3..dc28ca6554 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 42b8422538..115c610c0b 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 14c6c4ec51..1df5d57a09 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 aca30132df..439727ccc0 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 d32455a9f3..3ca92d845b 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 5b1fb93b48..c24a052ed1 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 4d6bb7f531..1e2fcd86fe 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 3783b1ba05..75fe1906ca 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 094e765e63..f29883acd9 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 66d6e26976..838da41339 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 b25a9fa69e..296df8deba 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 eab76cc0f6..b59c274a3d 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 b668a9d7cb..582821a3ac 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 5599e02528..4b161474d0 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 0000000000..344c7714a0 --- /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 af0001d847..0000000000 --- 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 0000000000..d73479069f --- /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 9a0d30b57b..16caea17a6 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 0000000000..aecd246149 --- /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 0000000000..2d8ba75259 --- /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 0000000000..64471abf26 --- /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 0000000000..145fa6519c --- /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 71fe844ff1..f062923408 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 b25a9fa69e..e464e51378 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 aa0575d19b..dc9651422c 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 a861e41d54..8a265d8639 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 c20f3a0b21..480e04dedf 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 3e711443a5..729370c2a8 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 31f69f52fd..f494826b6c 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 53a209823b..39d31974a1 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 66e0b76672..adc43f62fb 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 3d6c0de22e..21a04129fc 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 cf453d34ed..3339cedbcf 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 0000000000..348baf896e --- /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 1df5d57a09..c9bbae84ac 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 0000000000..71de8257b7 --- /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 0000000000..afcd32b633 --- /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 0000000000..0d4f101c7a --- /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 0000000000..c29c425093 --- /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 0000000000..048c0b1132 --- /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 0000000000..0d4f101c7a --- /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 0000000000..0d4f101c7a --- /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 0000000000..35f47cbc7d --- /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 0000000000..40e67555c4 --- /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 0000000000..78c7d6b440 --- /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 0000000000..3ede8765a3 --- /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 0000000000..f518056e1e --- /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 0000000000..e5bc1117d3 --- /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 115c610c0b..0000000000 --- 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 45d3656c0e..105da0786d 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 a98e094a52..1639e05197 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 eab76cc0f6..71653a72ce 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 b668a9d7cb..082c840f3e 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 3ca92d845b..0d031a4f88 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 75fe1906ca..ecaaeb9ddb 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 348baf896e..c0fb64fe8f 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 71de8257b7..16a2c7647c 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 35f47cbc7d..226976f28e 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 78c7d6b440..608e45cfd4 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 f518056e1e..3b9ab513ff 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 71fe844ff1..f062923408 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 296df8deba..1f4bb79242 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 aa0575d19b..dc9651422c 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 a861e41d54..8a265d8639 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 c20f3a0b21..480e04dedf 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 3e711443a5..729370c2a8 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 31f69f52fd..f494826b6c 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 53a209823b..39d31974a1 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 66e0b76672..adc43f62fb 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 3d6c0de22e..21a04129fc 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 cf453d34ed..3339cedbcf 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 0000000000..348baf896e --- /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 1df5d57a09..c9bbae84ac 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 0000000000..71de8257b7 --- /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 0000000000..afcd32b633 --- /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 0000000000..0d4f101c7a --- /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 0000000000..c29c425093 --- /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 0000000000..048c0b1132 --- /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 0000000000..0d4f101c7a --- /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 0000000000..0d4f101c7a --- /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 0000000000..35f47cbc7d --- /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 0000000000..40e67555c4 --- /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 0000000000..78c7d6b440 --- /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 0000000000..3ede8765a3 --- /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 0000000000..f518056e1e --- /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 0000000000..e5bc1117d3 --- /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 115c610c0b..0000000000 --- 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 45d3656c0e..105da0786d 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 a98e094a52..1639e05197 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 b59c274a3d..aee94cde77 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 582821a3ac..eb2ca44aae 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 3ca92d845b..0d031a4f88 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 75fe1906ca..ecaaeb9ddb 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 348baf896e..c0fb64fe8f 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 71de8257b7..16a2c7647c 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 35f47cbc7d..226976f28e 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 78c7d6b440..608e45cfd4 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 f518056e1e..3b9ab513ff 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 eb2ca44aae..76250aa2eb 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 ca96241e5e..76250aa2eb 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 936394e1e9..070923ecb0 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 53a209823b..5058026e24 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 f1b45fb34c..1303229287 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 16a2c7647c..008c08e046 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 afcd32b633..83829fdfe1 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 0d4f101c7a..afce2047f2 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 09233d44cc..0f6868495a 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 8a2031b864..9341b5625a 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 ecaaeb9ddb..9a14487f7c 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 d947991e0e..20841df74e 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 71398d5009..0148da2f07 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 3b9ab513ff..cd0228e57c 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 e5bc1117d3..04bfc6bf82 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 d4915cd0d6..1f673d938f 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 7bab272be5..1ab63b7c4c 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 49aaa2b958..51b5b4eadd 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 7cc06c5900..c951904a0f 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 16129f6c95..16e0fd23e4 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 e6dd01b74a..8f9d2d51d3 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 d9911f703f..c585ba4444 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 fb6d02816f..1ccaea1018 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 81c7dd6231..1ec3d1e93f 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 1639e05197..fd27daee39 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 2963c1b587..9b634e0be3 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 ebe066245f..eb23c566a0 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 515fb599b6..5f35f1408a 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 1f4bb79242..59331e22a4 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 0000000000..01cfb25e77 --- /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 1639e05197..6c9ccaab0a 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 2963c1b587..50f587d62c 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 814a12ac99..31aa6a99a8 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 e82523e8d9..8c78ea79d2 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 7ca1e0f031..3ef5b36156 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 fb6d02816f..33d7d11199 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 81c7dd6231..3e1c2e76d9 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 4cbe2b34f8..6dd4264dc4 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 0e5f1a4f04..bc1607fe81 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 4b742f90bf..9b667ee453 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 254cf7b644..4edbcef77d 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 f494826b6c..cbb41630d3 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 66e65e167b..a3fb27497d 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 1a79754905..ebe40c0838 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 ac42cc7822..e8f2e01eb7 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 0000000000..2ee76518b7 --- /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 941a2a14aa..82e2bfc895 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 e6dd01b74a..aca9380ad2 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 d9911f703f..491c045477 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 1f4bb79242..c61200b5bf 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 e30049adef..6d82d6a46b 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 0000000000..5a6adc4f08 --- /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 0d4f101c7a..7ceddbaa29 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 55fd5dfb70..9303f653ba 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 26a5c47664..c7485c368e 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 2c6d53dc9e..f6f949be16 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 7ceddbaa29..d606af6845 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 dbe141d4fc..c465d5d2be 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 1f673d938f..eb4c4fc86f 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 0984d4764e..cb6e8330f5 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 aee94cde77..78fa0150db 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 76250aa2eb..e9f1a0a9f3 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 9a14487f7c..94f1968214 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 6d82d6a46b..a2f0c5cec1 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 5a6adc4f08..d2c01674b7 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 070923ecb0..c3fe618e25 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 d3a2382cb4..64d261ec5c 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 9303f653ba..799683ece1 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 c7485c368e..4e12c85064 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 f6f949be16..ed4fcd7a17 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 3877ade682..8533f3c787 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 31aa6a99a8..88fd034571 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 473136265e..3682797715 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 dc374d5714..0276d40550 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 51b5b4eadd..1f328647ed 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 74f0678f68..5b0dd15a04 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 435795825f..d44bacf43f 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 2974b73fed..5df28367dd 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 9cf38be3bc..d9aa618034 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 0794337dcc..9abd02ae7a 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 89516ba87c..c5e0d27d57 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 c9620f62cd..870f120571 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 c0fb64fe8f..d2284a7b3e 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 008c08e046..fc1247f84c 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 c29c425093..e0f1ac2313 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 608e45cfd4..0d5c6e0d6d 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 09a7c3216f..e2cf39875c 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 912e577157..f96f1acb09 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 0d4f101c7a..0000000000 --- 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 0000000000..c9835ee056 --- /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 cd0228e57c..ca2a9edbe6 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 04bfc6bf82..df3821720d 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 0a58ff8d02..383f92b666 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 9ec600fedb..2c294cf244 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 94f1968214..a83ceb588b 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 ca2a9edbe6..190c87fff1 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 a2f0c5cec1..af6a9d5a27 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 d606af6845..96e5657ed9 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 799683ece1..3603eba380 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 4e12c85064..a9be9ccfce 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 0794337dcc..dfba32ecc0 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 89516ba87c..91fd880d43 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 aefb1d923e..b2eac54bb6 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 0a58ff8d02..f54059df00 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 f54059df00..1c75e9c1c1 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 a92a4ed89d..2e351302f5 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 c080b24901..f5234c533d 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 4dec260fa0..8c2ff2ddc3 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 116ffb0e61..8b00e725da 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 21a04129fc..d206a0595d 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 88fd034571..142f0f75a1 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 718f8b7f40..b30dfa1924 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 b3e9de3aba..6569ce6e0b 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 718f8b7f40..b290d2b7f9 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 0794337dcc..87ca27c373 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 89516ba87c..7ebf4e3709 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 6dcf659fd7..e2b33b2fa1 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 142f0f75a1..f64b683f81 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 8b65d2dfbb..cdcba598d4 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 a5e7bd16fe..81d6019f7b 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 83682333a9..782fefd920 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 86d08aff74..65d9af211b 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 c9835ee056..17e8254dbc 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 190c87fff1..f36751d24d 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 df3821720d..d914fc7019 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 a83ceb588b..319049398f 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 e2cf39875c..c5c858b593 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 f96f1acb09..aaffec22e3 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 af6a9d5a27..8d26b50b1e 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 d2c01674b7..b393d755f4 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 96e5657ed9..0efffaee46 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 3603eba380..dd1dc98fc4 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 f062923408..51e7f31461 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 870f120571..512e9dcd00 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 200b04d864..c8bf2d92f8 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 8ac267b426..838bbe3939 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 0000000000..75579a5e1f --- /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 0000000000..5a624d6f76 --- /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 0000000000..84034427b3 --- /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 0b96501b34..3036a15767 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 32f63dd14f..e5f0efa6c1 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 319049398f..85186ba5c7 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 75579a5e1f..efe440b97b 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 c465d5d2be..bf070a8dfb 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 a0f2ab1175..0bc38aedd1 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 729370c2a8..b209b6e113 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 7d442e427e..75f952acac 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 337b1b40fd..89ae07a46b 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 8b00e725da..9950708b4b 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 6542d290b1..502e428be1 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 cb827796f4..cd910754a9 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 3682797715..07f4c68327 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 6688eb6d66..222ac38fbf 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 07248d6df1..f10d4e2d25 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 a92a4ed89d..c6e66960cd 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 c080b24901..d1e2cb974e 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 a3fb27497d..af893774ea 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 dc28ca6554..047e3f86e9 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 9645592735..0eb0c55939 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 c0fb64fe8f..7e46463216 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 c9bbae84ac..7e39514cab 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 439727ccc0..a3b074c840 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 54e0e0ffca..ac1156397c 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 736120dcb9..574be4c639 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 339e1f84f7..f0d1a3a3b7 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 94f1968214..682f419982 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 cb827796f4..cd910754a9 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 3682797715..07f4c68327 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 6688eb6d66..222ac38fbf 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 07248d6df1..f10d4e2d25 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 2e351302f5..7933933f9a 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 f5234c533d..91bb3a2874 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 a3fb27497d..af893774ea 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 dc28ca6554..047e3f86e9 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 9645592735..0eb0c55939 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 439727ccc0..a3b074c840 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 d2284a7b3e..74feaa2706 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 c9bbae84ac..7e39514cab 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 54e0e0ffca..ac1156397c 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 736120dcb9..574be4c639 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 339e1f84f7..f0d1a3a3b7 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 a83ceb588b..486ab00cf7 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 74feaa2706..e30ca2c05c 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 78fa0150db..3d7ff10987 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 838da41339..bf83ef8cc5 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 6833019317..1209135dfd 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 bf070a8dfb..cfccc4dbc2 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 a096bf9a6f..a506d86457 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 8533f3c787..bbb2485ea7 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 de0c516656..732b99a00a 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 eabeca15f7..fa0b3e5f67 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 0271713a8b..e558a44ee0 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 ca655d0b79..63fd2c2b10 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 0000000000..548c74dfc3 --- /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 0000000000..b813c5b77b --- /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 0000000000..1e16db34ac --- /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 0000000000..d50e537a2d --- /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 0bc38aedd1..68303bb3b7 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 89ae07a46b..76331e1f5e 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 512e9dcd00..aa49c237be 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 5b91083413..7d992a40f8 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 0000000000..60217970c1 --- /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 2040fedc4f..97d45d6266 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 cec74fee1d..c2d265162d 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 d8f774d6ab..4c6c16dcbb 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 512e9dcd00..53dad4533b 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 0000000000..8e0ec95ce1 --- /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 84cdd15c37..8d6f8ededb 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 8a52ea4f80..22ec90583a 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 e4ba42bfee..6229219493 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 4b9b116634..b1c73ae9d0 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 e4ebade92f..0a7cb00875 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 65d9af211b..275bfd6abd 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 17e8254dbc..457a519c27 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 f36751d24d..52cf596158 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 d914fc7019..48eb53dfc9 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 4993e8c7e0..4506b9174c 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 275bfd6abd..b3ea10af40 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 bc66e1716d..cb4c509dbb 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 5238f025a5..55fe10af65 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 f57d521439..7cf0c4bc64 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 9950708b4b..1a7402167b 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 502e428be1..699a82c646 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 0eb0c55939..0d2d2a27f1 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!ia0y~yU;weXIanAN7~Z5`nasezQ10pC7{VdTdQg$;5Q7NA zh2_PE9Mq!1S(9$^@f!}eG&QQ)BTj5D{?*Kpkb&d_k_@_9LiWpYl* zJPg(gO&J^RoV_jNq4Q)#>oJBKFJeXYth5`>1X}#gSjuqbPU{9%hHLxco-rIaaqy%7 f!-4?D5A5y-CB3>NfAcahFfe$!`njxgN@xNAo;W?I 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 04603ef0f0..e7c814e59b 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 f29883acd9..b5aaac46c4 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 cb4c509dbb..ae67a275de 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 c3fe618e25..d8038782a0 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 25b68ffe36..0ee2e90ac7 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 27bdeddd4b..ed30b1510b 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 fe3a43b6d0..a8adffc0cf 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 dd1dc98fc4..3ec84230b7 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 ed30b1510b..607f1e02ca 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 ed80428026..076641a91b 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 ec325c1ec8..58ad845430 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 b393d755f4..ec4af32361 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 c3fe618e25..d3f1cd6567 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 64d261ec5c..a438fb7077 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 9561f61c05..7e6bb8e74b 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 c2d265162d..2ba2fc255d 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 3dd02aa68e..58a7093609 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 699a82c646..8b5497e905 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 0ee2e90ac7..156345685a 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 ed30b1510b..f10ead0431 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 d8038782a0..87d7e12687 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 76331e1f5e..0df07900f9 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 222ac38fbf..fd553fde2f 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 0f1c5d83ee..4d3e73cd68 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 c2d265162d..9c267d65a0 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