diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 322d87e001ebda247166fe2fb56908999fca0c24..55fb1a35b32ccf3ba6d43a69bfe481658d78a3d9 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -94,6 +94,63 @@ ParameterManager::~ParameterManager() delete _parameterMetaData; } +void ParameterManager::_updateProgressBar(void) +{ + int waitingReadParamIndexCount = 0; + int waitingReadParamNameCount = 0; + int waitingWriteParamCount = 0; + + for (int compId: _waitingReadParamIndexMap.keys()) { + waitingReadParamIndexCount += _waitingReadParamIndexMap[compId].count(); + } + for(int compId: _waitingReadParamNameMap.keys()) { + waitingReadParamNameCount += _waitingReadParamNameMap[compId].count(); + } + for(int compId: _waitingWriteParamNameMap.keys()) { + waitingWriteParamCount += _waitingWriteParamNameMap[compId].count(); + } + + if (waitingReadParamIndexCount == 0) { + if (_readParamIndexProgressActive) { + _readParamIndexProgressActive = false; + _setLoadProgress(0.0); + return; + } + } else { + _readParamIndexProgressActive = true; + _setLoadProgress((double)(_totalParamCount - waitingReadParamIndexCount) / (double)_totalParamCount); + return; + } + + if (waitingWriteParamCount == 0) { + if (_writeParamProgressActive) { + _writeParamProgressActive = false; + _waitingWriteParamBatchCount = 0; + _setLoadProgress(0.0); + emit pendingWritesChanged(false); + return; + } + } else { + _writeParamProgressActive = true; + _setLoadProgress((double)(qMax(_waitingWriteParamBatchCount - waitingWriteParamCount, 1)) / (double)(_waitingWriteParamBatchCount + 1)); + emit pendingWritesChanged(true); + return; + } + + if (waitingReadParamNameCount == 0) { + if (_readParamNameProgressActive) { + _readParamNameProgressActive = false; + _waitingReadParamNameBatchCount = 0; + _setLoadProgress(0.0); + return; + } + } else { + _readParamNameProgressActive = true; + _setLoadProgress((double)(qMax(_waitingReadParamNameBatchCount - waitingReadParamNameCount, 1)) / (double)(_waitingReadParamNameBatchCount + 1)); + return; + } +} + /// Called whenever a parameter is updated or first seen. void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString parameterName, int parameterCount, int parameterId, int mavType, QVariant value) { @@ -262,17 +319,8 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(-1) << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)"; } } - - // Update progress bar for waiting reads - if (readWaitingParamCount == 0) { - // We are no longer waiting for any reads to complete - if (_prevWaitingReadParamIndexCount + _prevWaitingReadParamNameCount != 0) { - // Set progress to 0 if not already there - _setLoadProgress(0.0); - } - } else { - _setLoadProgress((double)(_totalParamCount - readWaitingParamCount) / (double)_totalParamCount); - } +\ + _updateProgressBar(); // Get parameter set version if (!_versionParam.isEmpty() && _versionParam == parameterName) { @@ -389,8 +437,13 @@ void ParameterManager::_valueUpdated(const QVariant& value) _dataMutex.lock(); if (_waitingWriteParamNameMap.contains(componentId)) { - _waitingWriteParamNameMap[componentId].remove(name); // Remove any old entry - _waitingWriteParamNameMap[componentId][name] = 0; // Add new entry and set retry count + if (_waitingWriteParamNameMap[componentId].contains(name)) { + _waitingWriteParamNameMap[componentId].remove(name); + } else { + _waitingWriteParamBatchCount++; + } + _waitingWriteParamNameMap[componentId][name] = 0; // Add new entry and set retry count + _updateProgressBar(); _waitingParamTimeoutTimer.start(); _saveRequired = true; } else { @@ -466,8 +519,13 @@ void ParameterManager::refreshParameter(int componentId, const QString& name) if (_waitingReadParamNameMap.contains(componentId)) { QString mappedParamName = _remapParamNameToVersion(name); - _waitingReadParamNameMap[componentId].remove(mappedParamName); // Remove old wait entry if there + if (_waitingReadParamNameMap[componentId].contains(mappedParamName)) { + _waitingReadParamNameMap[componentId].remove(mappedParamName); + } else { + _waitingReadParamNameBatchCount++; + } _waitingReadParamNameMap[componentId][mappedParamName] = 0; // Add new wait entry and update retry count + _updateProgressBar(); qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "restarting _waitingParamTimeout"; _waitingParamTimeoutTimer.start(); } else { @@ -896,7 +954,8 @@ void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, QVarian QString ParameterManager::readParametersFromStream(QTextStream& stream) { - QString errors; + QString missingErrors; + QString typeErrors; while (!stream.atEnd()) { QString line = stream.readLine(); @@ -915,18 +974,18 @@ QString ParameterManager::readParametersFromStream(QTextStream& stream) if (!parameterExists(componentId, paramName)) { QString error; - error = QString("Skipped parameter %1:%2 - does not exist on this vehicle\n").arg(componentId).arg(paramName); - errors += error; - qCDebug(ParameterManagerLog) << error; + error += QStringLiteral("%1:%2 ").arg(componentId).arg(paramName); + missingErrors += error; + qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to missing: %1").arg(error); continue; } Fact* fact = getParameter(componentId, paramName); if (fact->type() != _mavTypeToFactType((MAV_PARAM_TYPE)mavType)) { QString error; - error = QString("Skipped parameter %1:%2 - type mismatch %3:%4\n").arg(componentId).arg(paramName).arg(fact->type()).arg(_mavTypeToFactType((MAV_PARAM_TYPE)mavType)); - errors += error; - qCDebug(ParameterManagerLog) << error; + error = QStringLiteral("%1:%2 ").arg(componentId).arg(paramName); + typeErrors += error; + qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to type mismatch: %1").arg(error); continue; } @@ -936,6 +995,16 @@ QString ParameterManager::readParametersFromStream(QTextStream& stream) } } + QString errors; + + if (!missingErrors.isEmpty()) { + errors = tr("Parameters not loaded since they are not currently on the vehicle: %1\n").arg(missingErrors); + } + + if (!typeErrors.isEmpty()) { + errors += tr("Parameters not loaded due to type mismatch: %1").arg(typeErrors); + } + return errors; } @@ -1579,11 +1648,24 @@ QString ParameterManager::_logVehiclePrefix(int componentId) void ParameterManager::_setLoadProgress(double loadProgress) { - _loadProgress = loadProgress; - emit loadProgressChanged(static_cast(loadProgress)); + if (_loadProgress != loadProgress) { + _loadProgress = loadProgress; + emit loadProgressChanged(static_cast(loadProgress)); + } } QList ParameterManager::componentIds(void) { return _paramCountMap.keys(); } + +bool ParameterManager::pendingWrites(void) +{ + for (int compId: _waitingWriteParamNameMap.keys()) { + if (_waitingWriteParamNameMap[compId].count()) { + return true; + } + } + + return false; +} diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 9527b9612fbe18fc1312bc4718e92c69b9de69bc..be1ae4cb0dcde2cc9768d404d21ba9856ff813a7 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -39,6 +39,7 @@ public: Q_PROPERTY(bool parametersReady READ parametersReady NOTIFY parametersReadyChanged) ///< true: Parameters are ready for use Q_PROPERTY(bool missingParameters READ missingParameters NOTIFY missingParametersChanged) ///< true: Parameters are missing from firmware response, false: all parameters received from firmware Q_PROPERTY(double loadProgress READ loadProgress NOTIFY loadProgressChanged) + Q_PROPERTY(bool pendingWrites READ pendingWrites NOTIFY pendingWritesChanged) ///< true: There are still pending write updates against the vehicle bool parametersReady (void) const { return _parametersReady; } bool missingParameters (void) const { return _missingParameters; } @@ -111,12 +112,15 @@ public: /// @return true: success, false: failure (errorString set) bool loadFromJson(const QJsonObject& json, bool required, QString& errorString); + bool pendingWrites(void); + Vehicle* vehicle(void) { return _vehicle; } signals: - void parametersReadyChanged(bool parametersReady); - void missingParametersChanged(bool missingParameters); - void loadProgressChanged(float value); + void parametersReadyChanged (bool parametersReady); + void missingParametersChanged (bool missingParameters); + void loadProgressChanged (float value); + void pendingWritesChanged (bool pendingWrites); protected: Vehicle* _vehicle; @@ -146,6 +150,7 @@ private: QString _logVehiclePrefix(int componentId); void _setLoadProgress(double loadProgress); bool _fillIndexBatchQueue(bool waitingParamTimeout); + void _updateProgressBar(void); MAV_PARAM_TYPE _factTypeToMavType(FactMetaData::ValueType_t factType); FactMetaData::ValueType_t _mavTypeToFactType(MAV_PARAM_TYPE mavType); @@ -178,9 +183,13 @@ private: QMap> _debugCacheParamSeen; // Wait counts from previous parameter update cycle - int _prevWaitingReadParamIndexCount; - int _prevWaitingReadParamNameCount; - int _prevWaitingWriteParamNameCount; + int _prevWaitingReadParamIndexCount; + int _prevWaitingReadParamNameCount; + int _prevWaitingWriteParamNameCount; + + bool _readParamIndexProgressActive = false; + bool _readParamNameProgressActive = false; + bool _writeParamProgressActive = false; static const int _maxInitialRequestListRetry = 4; ///< Maximum retries for request list int _initialRequestRetryCount; ///< Current retry count for request list @@ -197,7 +206,9 @@ private: QMap > _waitingWriteParamNameMap; ///< Key: Component id, Value: Map { Key: parameter name still waiting for, Value: retry count } QMap > _failedReadParamIndexMap; ///< Key: Component id, Value: failed parameter index - int _totalParamCount; ///< Number of parameters across all components + int _totalParamCount; ///< Number of parameters across all components + int _waitingWriteParamBatchCount = 0; ///< Number of parameters which are batched up waiting on write responses + int _waitingReadParamNameBatchCount = 0; ///< Number of parameters which are batched up waiting on read responses QTimer _initialRequestTimeoutTimer; QTimer _waitingParamTimeoutTimer; diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 40c3a62d9dadb43ea2f377048b0ea78eb913e59c..c2b744209e1121622c442896f9d6541bec7de1cf 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -158,12 +158,8 @@ static QObject* shapeFileHelperSingletonFactory(QQmlEngine*, QJSEngine*) } QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) - #if defined(__mobile__) - : QGuiApplication (argc, argv) - #else - : QApplication (argc, argv) - #endif - , _runningUnitTests (unitTesting) + : QGuiApplication (argc, argv) + , _runningUnitTests (unitTesting) { _app = this; _msecsElapsedTime.start(); diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 19b293883e9d3ea0a28a9d4e961bf3068c55bef3..364da2d10866ba52ff126b0b678e7fae543af5cd 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -7,17 +7,7 @@ * ****************************************************************************/ - -/** - * @file - * @brief Definition of main class - * - * @author Lorenz Meier - * - */ - -#ifndef QGCAPPLICATION_H -#define QGCAPPLICATION_H +#pragma once #include #include @@ -45,21 +35,7 @@ class QGCSingleton; class QGCToolbox; class QGCFileDownload; -/** - * @brief The main application and management class. - * - * This class is started by the main method and provides - * the central management unit of the groundstation application. - * - * Needs QApplication base to support QtCharts drawing module and - * avoid application crashing on 5.12. Enforce no widget on mobile -**/ -class QGCApplication : - #if defined(__mobile__) - public QGuiApplication - #else - public QApplication - #endif +class QGCApplication : public QGuiApplication { Q_OBJECT @@ -214,5 +190,3 @@ private: /// @brief Returns the QGCApplication object singleton. QGCApplication* qgcApp(void); - -#endif diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index f7537f3fb5f6575ef1c5dc1cbba0a90d774c345d..811f26192abdd1c8778a1d1e738aba69fe6472bf 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -32,10 +32,8 @@ Item { property var _appSettings: QGroundControl.settingsManager.appSettings ParameterEditorController { - id: controller; - onShowErrorMessage: { - mainWindow.showMessageDialog(qsTr("Parameter Editor"), qsTr("Parameter Load Errors")) - } + id: controller + onShowErrorMessage: mainWindow.showMessageDialog(qsTr("Parameter Load Errors"), errorMsg) } ExclusiveGroup { id: sectionGroup } diff --git a/src/ui/MainRootWindow.qml b/src/ui/MainRootWindow.qml index 8111f221af6b367701ccc3bd1715ce5843bdfa69..0468d92cbfbad93c81d9b6e0d2ba6e55bb82508a 100644 --- a/src/ui/MainRootWindow.qml +++ b/src/ui/MainRootWindow.qml @@ -196,47 +196,66 @@ ApplicationWindow { mainWindow.close() } + // On attempting an application close we check for: + // Unsaved missions - then + // Pending parameter writes - then + // Active connections + onClosing: { + if (!_forceClose) { + unsavedMissionCloseDialog.check() + close.accepted = false + } + } + MessageDialog { - id: activeConnectionsCloseDialog + id: unsavedMissionCloseDialog title: qsTr("%1 close").arg(QGroundControl.appName) - text: qsTr("There are still active connections to vehicles. Are you sure you want to exit?") - standardButtons: StandardButton.Yes | StandardButton.Cancel + text: qsTr("You have a mission edit in progress which has not been saved/sent. If you close you will lose changes. Are you sure you want to close?") + standardButtons: StandardButton.Yes | StandardButton.No modality: Qt.ApplicationModal visible: false - onYes: finishCloseProcess() + onYes: pendingParameterWritesCloseDialog.check() function check() { - if (QGroundControl.multiVehicleManager.activeVehicle) { - activeConnectionsCloseDialog.open() + if (planMasterControllerPlan && planMasterControllerPlan.dirty) { + unsavedMissionCloseDialog.open() } else { - finishCloseProcess() + pendingParameterWritesCloseDialog.check() } } } - //------------------------------------------------------------------------- - //-- Check for unsaved missions - - onClosing: { - // Check first for unsaved missions and active connections - if (!_forceClose) { - unsavedMissionCloseDialog.check() - close.accepted = false + MessageDialog { + id: pendingParameterWritesCloseDialog + title: qsTr("%1 close").arg(QGroundControl.appName) + text: qsTr("You have pending parameter updates to a vehicle. If you close you will lose changes. Are you sure you want to close?") + standardButtons: StandardButton.Yes | StandardButton.No + modality: Qt.ApplicationModal + visible: false + onYes: activeConnectionsCloseDialog.check() + function check() { + for (var index=0; index