From b1b45f3d92376f77dcc62574462f94d1332ce561 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 19 May 2026 13:39:14 -0700 Subject: [PATCH 1/2] feat(FactSystem): add BulkRefreshJob and ParameterManager::bulkRefresh() Add a BulkRefreshJob class that fires a batch of PARAM_REQUEST_READs with exponential-backoff retry. Owned by ParameterManager as a QObject child; self-deletes on completion. Add ParameterManager::bulkRefresh() which resolves a mix of exact names and wildcard prefixes (e.g. "COMPASS_*") against the known parameter set, deduplicates, and delegates to BulkRefreshJob. Timeouts (_waitForParamValueAckMs, BulkRefreshJob::_retryBaseDelayMs) are reduced to 50 ms under QGC::runningUnitTests() so the test suite stays fast. Add five unit tests covering: exact names, prefix expansion, unknown name skipped, round-0-failure-then-retry-succeeds, and all-rounds-exhausted. --- src/FactSystem/BulkRefreshJob.cc | 86 +++++++++++++ src/FactSystem/BulkRefreshJob.h | 40 ++++++ src/FactSystem/CMakeLists.txt | 2 + src/FactSystem/ParameterManager.cc | 53 +++++++- src/FactSystem/ParameterManager.h | 12 +- test/FactSystem/ParameterManagerTest.cc | 154 ++++++++++++++++++++++++ test/FactSystem/ParameterManagerTest.h | 5 + 7 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 src/FactSystem/BulkRefreshJob.cc create mode 100644 src/FactSystem/BulkRefreshJob.h diff --git a/src/FactSystem/BulkRefreshJob.cc b/src/FactSystem/BulkRefreshJob.cc new file mode 100644 index 000000000000..111787b4c03e --- /dev/null +++ b/src/FactSystem/BulkRefreshJob.cc @@ -0,0 +1,86 @@ +#include "BulkRefreshJob.h" +#include "ParameterManager.h" + +#include "AppMessages.h" +#include "QGCLoggingCategory.h" + +Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog) + +BulkRefreshJob::BulkRefreshJob(ParameterManager *mgr, int componentId, const QStringList &resolvedNames, + bool notifyFailure, std::function requestFn, + QObject *parent) + : QObject(parent) + , _mgr(mgr) + , _componentId(componentId) + , _notifyFailure(notifyFailure) + , _requestFn(std::move(requestFn)) + , _pending(QSet(resolvedNames.cbegin(), resolvedNames.cend())) + , _retryBaseDelayMs(QGC::runningUnitTests() ? 50 : kRetryBaseDelayMs) +{ + _retryTimer.setSingleShot(true); + connect(&_retryTimer, &QTimer::timeout, this, &BulkRefreshJob::_sendPendingRequests); + connect(_mgr, &ParameterManager::_paramRequestReadSuccess, this, &BulkRefreshJob::_onParamSuccess); + connect(_mgr, &ParameterManager::_paramRequestReadFailure, this, &BulkRefreshJob::_onParamFailure); + _sendPendingRequests(); +} + +void BulkRefreshJob::_sendPendingRequests() +{ + qCDebug(ParameterManagerLog) << "BulkRefreshJob: round" << _round + << "\u2014 firing" << _pending.count() << "requests"; + for (const QString &name : std::as_const(_pending)) { + _requestFn(name); + } +} + +void BulkRefreshJob::_onParamSuccess(int componentId, const QString ¶mName, int /*paramIndex*/) +{ + if (componentId != _componentId || !_pending.remove(paramName)) { + return; + } + _checkRoundComplete(); +} + +void BulkRefreshJob::_onParamFailure(int componentId, const QString ¶mName, int /*paramIndex*/) +{ + if (componentId != _componentId || !_pending.remove(paramName)) { + return; + } + _failed.insert(paramName); + _checkRoundComplete(); +} + +void BulkRefreshJob::_checkRoundComplete() +{ + if (!_pending.isEmpty()) { + return; + } + + _retryTimer.stop(); + + if (_failed.isEmpty()) { + qCDebug(ParameterManagerLog) << "BulkRefreshJob: complete after round" << _round + << "\u2014 all params refreshed successfully"; + deleteLater(); + return; + } + + if (_round < kMaxRetryRounds) { + const int delayMs = _retryBaseDelayMs << _round; + qCDebug(ParameterManagerLog) << "BulkRefreshJob: round" << _round + << "finished with" << _failed.count() + << "failures \u2014 retrying in" << delayMs << "ms"; + _pending = std::move(_failed); + _failed.clear(); + ++_round; + _retryTimer.start(delayMs); + } else { + qCDebug(ParameterManagerLog) << "BulkRefreshJob: complete after round" << _round + << "\u2014" << _failed.count() << "params still failed"; + if (_notifyFailure) { + const QStringList failedList(_failed.cbegin(), _failed.cend()); + QGC::showAppMessage(_mgr->tr("Parameter refresh failed for: %1").arg(failedList.join(QStringLiteral(", ")))); + } + deleteLater(); + } +} diff --git a/src/FactSystem/BulkRefreshJob.h b/src/FactSystem/BulkRefreshJob.h new file mode 100644 index 000000000000..46500df38d90 --- /dev/null +++ b/src/FactSystem/BulkRefreshJob.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +class ParameterManager; + +/// Fires a batch of PARAM_REQUEST_READs with exponential backoff retry. +/// Owned by ParameterManager (as QObject parent); self-deletes on completion. +class BulkRefreshJob : public QObject +{ +public: + BulkRefreshJob(ParameterManager *mgr, int componentId, const QStringList &resolvedNames, + bool notifyFailure, std::function requestFn, + QObject *parent); + + static constexpr int kMaxRetryRounds = 3; ///< Number of batch-level retry rounds before giving up + static constexpr int kRetryBaseDelayMs = 1000; ///< Base retry interval; doubled each round + +private: + void _sendPendingRequests(); + void _onParamSuccess(int componentId, const QString ¶mName, int paramIndex); + void _onParamFailure(int componentId, const QString ¶mName, int paramIndex); + void _checkRoundComplete(); + + ParameterManager *_mgr; + int _componentId; + bool _notifyFailure; + std::function _requestFn; + QSet _pending; + QSet _failed; + int _round = 0; + const int _retryBaseDelayMs; ///< 50 ms in unit tests, kRetryBaseDelayMs otherwise + QTimer _retryTimer; +}; diff --git a/src/FactSystem/CMakeLists.txt b/src/FactSystem/CMakeLists.txt index 7f3ec2802efd..fa99b4be5e95 100644 --- a/src/FactSystem/CMakeLists.txt +++ b/src/FactSystem/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources(${CMAKE_PROJECT_NAME} PRIVATE + BulkRefreshJob.cc + BulkRefreshJob.h Fact.cc Fact.h FactGroup.cc diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 5380c4ffe661..c31badeff54d 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -1,7 +1,9 @@ #include "QmlObjectListModel.h" #include "ParameterManager.h" +#include "BulkRefreshJob.h" #include +#include #include #include "AutoPilotPlugin.h" @@ -36,6 +38,7 @@ ParameterManager::ParameterManager(Vehicle *vehicle) , _vehicle(vehicle) , _logReplay(!vehicle->vehicleLinkManager()->primaryLink().expired() && vehicle->vehicleLinkManager()->primaryLink().lock()->isLogReplay()) , _disableAllRetries(_logReplay) + , _waitForParamValueAckMs(QGC::runningUnitTests() ? 50 : kWaitForParamValueAckMs) , _tryftp(vehicle->apmFirmware()) { qCDebug(ParameterManagerLog) << this; @@ -390,7 +393,7 @@ void ParameterManager::_mavlinkParamSet(int componentId, const QString ¶mNam auto errorDecPendingWriteCountState = new FunctionState(QStringLiteral("ParameterManager error decrement pending write count"), stateMachine, [this]() { _decrementPendingWriteCount(); }); - auto waitAckState = new WaitForParamResponseState(stateMachine, kWaitForParamValueAckMs, checkForCorrectParamValue, checkForParamError); + auto waitAckState = new WaitForParamResponseState(stateMachine, _waitForParamValueAckMs, checkForCorrectParamValue, checkForParamError); auto paramRefreshState = new FunctionState(QStringLiteral("ParameterManager param refresh"), stateMachine, [this, componentId, paramName]() { refreshParameter(componentId, paramName); }); @@ -718,6 +721,9 @@ void ParameterManager::refreshParametersPrefix(int componentId, const QString &n componentId = _actualComponentId(componentId); qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParametersPrefix - name:" << namePrefix << ")"; + if (!_mapCompId2FactMap.contains(componentId)) { + return; + } for (const QString ¶mName: _mapCompId2FactMap[componentId].keys()) { if (paramName.startsWith(namePrefix)) { refreshParameter(componentId, paramName); @@ -725,6 +731,47 @@ void ParameterManager::refreshParametersPrefix(int componentId, const QString &n } } +void ParameterManager::bulkRefresh(int componentId, const QStringList &names, bool notifyFailure) +{ + componentId = _actualComponentId(componentId); + + if (!_mapCompId2FactMap.contains(componentId)) { + return; + } + const QMap &factMap = _mapCompId2FactMap[componentId]; + QSet resolvedSet; + for (const QString &entry : names) { + if (entry.endsWith(QLatin1Char('*'))) { + const QString prefix = entry.chopped(1); + if (prefix.isEmpty()) { + qCWarning(ParameterManagerLog) << "bulkRefresh: ignoring bare '*' entry"; + continue; + } + for (auto it = factMap.cbegin(); it != factMap.cend(); ++it) { + if (it.key().startsWith(prefix)) { + resolvedSet.insert(it.key()); + } + } + } else if (factMap.contains(entry)) { + resolvedSet.insert(entry); + } else { + qCWarning(ParameterManagerLog) << "bulkRefresh: unknown param name (skipped):" << entry; + } + } + + if (resolvedSet.isEmpty()) { + return; + } + + qCDebug(ParameterManagerLog) << "bulkRefresh: resolved" << resolvedSet.count() << "params"; + new BulkRefreshJob( + this, componentId, QStringList(resolvedSet.cbegin(), resolvedSet.cend()), notifyFailure, + [this, componentId](const QString &name) { + _mavlinkParamRequestRead(componentId, name, -1, false /* notifyFailure */); + }, + this); +} + bool ParameterManager::parameterExists(int componentId, const QString ¶mName) const { bool ret = false; @@ -946,7 +993,7 @@ void ParameterManager::_mavlinkParamRequestRead(int componentId, const QString & // Create states auto stateMachine = new QGCStateMachine(QStringLiteral("PARAM_REQUEST_READ"), vehicle(), this); auto sendParamRequestReadState = new SendMavlinkMessageState(stateMachine, paramRequestReadEncoder, kParamRequestReadRetryCount); - auto waitAckState = new WaitForParamResponseState(stateMachine, kWaitForParamValueAckMs, checkForCorrectParamValue, checkForParamError); + auto waitAckState = new WaitForParamResponseState(stateMachine, _waitForParamValueAckMs, checkForCorrectParamValue, checkForParamError); auto userNotifyState = new FunctionState(QStringLiteral("User notify"), stateMachine, [waitAckState, paramName, this, componentId]() { const QString errorDetail = waitAckState->lastParamErrorString(); const QString msg = errorDetail.isEmpty() @@ -1410,8 +1457,6 @@ QString ParameterManager::_remapParamNameToVersion(const QString ¶mName) con const int majorVersion = _vehicle->firmwareMajorVersion(); const int minorVersion = _vehicle->firmwareMinorVersion(); - qCDebug(ParameterManagerLog) << "_remapParamNameToVersion" << paramName << majorVersion << minorVersion; - if (majorVersion == Vehicle::versionNotSetValue) { // Vehicle version unknown return paramName; diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 0dc70e69f15e..3626be65daa8 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -61,6 +61,14 @@ class ParameterManager : public QObject /// Request a refresh on all parameters that begin with the specified prefix void refreshParametersPrefix(int componentId, const QString &namePrefix); + /// Refresh a set of parameters in a single batch with exponential-backoff retry. + /// @param names Mix of exact param names and prefix patterns ending in '*' (e.g. "COMPASS_*"). + /// Prefix entries are expanded against the known parameter set at call time. + /// Duplicate names are deduplicated automatically. + /// @param notifyFailure When true (default), shows a single user-visible message if any + /// parameters still fail to respond after all retry rounds. + void bulkRefresh(int componentId, const QStringList &names, bool notifyFailure = true); + void resetAllParametersToDefaults(); void resetAllToVehicleConfiguration(); @@ -112,7 +120,8 @@ class ParameterManager : public QObject void parameterDownloadSkippedChanged(); void factAdded(int componentId, Fact *fact); - // These signals are used to verify unit tests + // Internal signals — emitted by PARAM_SET / PARAM_REQUEST_READ state machines. + // Also consumed by BulkRefreshJob and unit tests. void _paramSetSuccess(int componentId, const QString ¶mName); void _paramSetFailure(int componentId, const QString ¶mName); void _paramRequestReadSuccess(int componentId, const QString ¶mName, int paramIndex); @@ -203,6 +212,7 @@ private slots: int _initialRequestRetryCount = 0; ///< Current retry count for request list static constexpr int _maxInitialLoadRetrySingleParam = 5; ///< Maximum retries for initial index based load of a single param bool _disableAllRetries = false; ///< true: Don't retry any requests (used for testing and logReplay) + const int _waitForParamValueAckMs; ///< 50 ms in unit tests, kWaitForParamValueAckMs otherwise bool _indexBatchQueueActive = false; ///< true: we are actively batching re-requests for missing index base params, false: index based re-request has not yet started QList _indexBatchQueue; ///< The current queue of index re-requests diff --git a/test/FactSystem/ParameterManagerTest.cc b/test/FactSystem/ParameterManagerTest.cc index 8da746c7e2e1..e00ae9bcca3c 100644 --- a/test/FactSystem/ParameterManagerTest.cc +++ b/test/FactSystem/ParameterManagerTest.cc @@ -6,6 +6,7 @@ #include #include +#include "BulkRefreshJob.h" #include "MockLinkFTP.h" #include "MultiVehicleManager.h" #include "ParameterManager.h" @@ -415,3 +416,156 @@ void ParameterManagerTest::_FTPChangeParam() } UT_REGISTER_TEST(ParameterManagerTest, TestLabel::Integration, TestLabel::Vehicle, TestLabel::Serial) + +// --------------------------------------------------------------------------- +// bulkRefresh tests +// --------------------------------------------------------------------------- + +// Two exact param names — both should resolve and succeed on round 0. +void ParameterManagerTest::_bulkRefreshExactNamesAllSucceed() +{ + _connectMockLink(); + QVERIFY(_vehicle); + ParameterManager* const paramManager = _vehicle->parameterManager(); + QVERIFY(paramManager); + + QSignalSpy successSpy(paramManager, &ParameterManager::_paramRequestReadSuccess); + QSignalSpy failureSpy(paramManager, &ParameterManager::_paramRequestReadFailure); + QVERIFY(successSpy.isValid()); + QVERIFY(failureSpy.isValid()); + + paramManager->bulkRefresh(MAV_COMP_ID_AUTOPILOT1, + {QStringLiteral("BAT1_V_CHARGED"), QStringLiteral("BAT1_N_CELLS")}); + + const int maxWaitMs = ParameterManager::kWaitForParamValueAckMs + * (ParameterManager::kParamRequestReadRetryCount + 1) + + TestTimeout::shortMs(); + QVERIFY_SIGNAL_COUNT_WAIT(successSpy, 2, maxWaitMs); + QCOMPARE(failureSpy.count(), 0); + + QStringList succeededNames; + for (int i = 0; i < successSpy.count(); ++i) { + succeededNames << successSpy.at(i).at(1).toString(); + } + QVERIFY(succeededNames.contains(QStringLiteral("BAT1_V_CHARGED"))); + QVERIFY(succeededNames.contains(QStringLiteral("BAT1_N_CELLS"))); + + _disconnectMockLink(); +} + +// A wildcard prefix "BAT1_*" should expand to all BAT1_ parameters and all should succeed. +void ParameterManagerTest::_bulkRefreshPrefixExpansion() +{ + _connectMockLink(); + QVERIFY(_vehicle); + ParameterManager* const paramManager = _vehicle->parameterManager(); + QVERIFY(paramManager); + + // Count BAT1_ params actually present on the mock vehicle. + int bat1Count = 0; + for (const QString &name : paramManager->parameterNames(MAV_COMP_ID_AUTOPILOT1)) { + if (name.startsWith(QStringLiteral("BAT1_"))) { + ++bat1Count; + } + } + QVERIFY2(bat1Count > 0, "Mock link must have at least one BAT1_ parameter"); + + QSignalSpy successSpy(paramManager, &ParameterManager::_paramRequestReadSuccess); + QSignalSpy failureSpy(paramManager, &ParameterManager::_paramRequestReadFailure); + QVERIFY(successSpy.isValid()); + QVERIFY(failureSpy.isValid()); + + paramManager->bulkRefresh(MAV_COMP_ID_AUTOPILOT1, {QStringLiteral("BAT1_*")}); + + const int maxWaitMs = ParameterManager::kWaitForParamValueAckMs + * (ParameterManager::kParamRequestReadRetryCount + 1) + + TestTimeout::shortMs(); + QVERIFY_SIGNAL_COUNT_WAIT(successSpy, bat1Count, maxWaitMs); + QCOMPARE(failureSpy.count(), 0); + QCOMPARE(successSpy.count(), bat1Count); + + _disconnectMockLink(); +} + +// Passing only non-existent names should resolve to an empty set — no requests are sent. +void ParameterManagerTest::_bulkRefreshUnknownNameSkipped() +{ + _connectMockLink(); + QVERIFY(_vehicle); + ParameterManager* const paramManager = _vehicle->parameterManager(); + QVERIFY(paramManager); + + QSignalSpy successSpy(paramManager, &ParameterManager::_paramRequestReadSuccess); + QSignalSpy failureSpy(paramManager, &ParameterManager::_paramRequestReadFailure); + QVERIFY(successSpy.isValid()); + QVERIFY(failureSpy.isValid()); + + paramManager->bulkRefresh(MAV_COMP_ID_AUTOPILOT1, {QStringLiteral("ZZZZ_DOES_NOT_EXIST")}); + + QVERIFY_NO_SIGNAL_WAIT(successSpy, TestTimeout::shortMs()); + QCOMPARE(failureSpy.count(), 0); + + _disconnectMockLink(); +} + +// Round 0 fails (no response from MockLink), round 1 succeeds after failure mode is cleared. +void ParameterManagerTest::_bulkRefreshRetrySucceeds() +{ + _connectMockLink(); + QVERIFY(_mockLink); + QVERIFY(_vehicle); + ParameterManager* const paramManager = _vehicle->parameterManager(); + QVERIFY(paramManager); + + QSignalSpy successSpy(paramManager, &ParameterManager::_paramRequestReadSuccess); + QSignalSpy failureSpy(paramManager, &ParameterManager::_paramRequestReadFailure); + QVERIFY(successSpy.isValid()); + QVERIFY(failureSpy.isValid()); + + // Round 0: MockLink drops all responses — per-param SM exhausts retries → _paramRequestReadFailure + _mockLink->setParamRequestReadFailureMode(MockLink::FailParamRequestReadNoResponse); + paramManager->bulkRefresh(MAV_COMP_ID_AUTOPILOT1, {QStringLiteral("BAT1_V_CHARGED")}); + + const int roundTimeMs = ParameterManager::kWaitForParamValueAckMs + * (ParameterManager::kParamRequestReadRetryCount + 1) + + TestTimeout::shortMs(); + QVERIFY_SIGNAL_WAIT(failureSpy, roundTimeMs); + QCOMPARE(failureSpy.count(), 1); + + // Allow BulkRefreshJob's retry round to succeed + _mockLink->setParamRequestReadFailureMode(MockLink::FailParamRequestReadNone); + QVERIFY_SIGNAL_WAIT(successSpy, roundTimeMs); + QCOMPARE(successSpy.count(), 1); + QCOMPARE(successSpy.at(0).at(1).toString(), QStringLiteral("BAT1_V_CHARGED")); + + _disconnectMockLink(); +} + +// All kMaxRetryRounds+1 rounds fail — BulkRefreshJob gives up without a success signal. +void ParameterManagerTest::_bulkRefreshAllRetriesExhausted() +{ + _connectMockLink(); + QVERIFY(_mockLink); + QVERIFY(_vehicle); + ParameterManager* const paramManager = _vehicle->parameterManager(); + QVERIFY(paramManager); + + QSignalSpy successSpy(paramManager, &ParameterManager::_paramRequestReadSuccess); + QSignalSpy failureSpy(paramManager, &ParameterManager::_paramRequestReadFailure); + QVERIFY(successSpy.isValid()); + QVERIFY(failureSpy.isValid()); + + _mockLink->setParamRequestReadFailureMode(MockLink::FailParamRequestReadNoResponse); + paramManager->bulkRefresh(MAV_COMP_ID_AUTOPILOT1, {QStringLiteral("BAT1_V_CHARGED")}); + + // Each round exhausts the per-param SM retries. Upper bound uses production constants; + // actual duration is ~1s in test mode (kWaitForParamValueAckMs and kRetryBaseDelayMs are + // reduced to 50ms when QGC::runningUnitTests() is true). + const int roundTimeMs = ParameterManager::kWaitForParamValueAckMs + * (ParameterManager::kParamRequestReadRetryCount + 1); + const int maxWaitMs = roundTimeMs * (BulkRefreshJob::kMaxRetryRounds + 1) + TestTimeout::mediumMs(); + QVERIFY_SIGNAL_COUNT_WAIT(failureSpy, BulkRefreshJob::kMaxRetryRounds + 1, maxWaitMs); + QCOMPARE(successSpy.count(), 0); + + _disconnectMockLink(); +} diff --git a/test/FactSystem/ParameterManagerTest.h b/test/FactSystem/ParameterManagerTest.h index 148ee1958e5c..e181867a9283 100644 --- a/test/FactSystem/ParameterManagerTest.h +++ b/test/FactSystem/ParameterManagerTest.h @@ -21,6 +21,11 @@ private slots: void _paramReadParamError(); void _FTPnoFailure(); void _FTPChangeParam(); + void _bulkRefreshExactNamesAllSucceed(); + void _bulkRefreshPrefixExpansion(); + void _bulkRefreshUnknownNameSkipped(); + void _bulkRefreshRetrySucceeds(); + void _bulkRefreshAllRetriesExhausted(); private: void _noFailureWorker(MockConfiguration::FailureMode_t failureMode); From 6b5d3e2ab0b78d688f8f2773553fc478c3a5f293 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 19 May 2026 13:39:24 -0700 Subject: [PATCH 2/2] fix(Sensors): use bulkRefresh to suppress post-cal refresh failure popups After compass calibration stops/cancels the FC is often slow to answer. The old code called refreshParameter + refreshParametersPrefix individually, causing a modal "Parameter read failed" popup for every timed-out request which could stack dozens of dialogs and freeze the UI (#14346). Replace _refreshParams() in both APM and PX4 sensor controllers with a single bulkRefresh() call (notifyFailure=false). Retry logic and logging are preserved inside BulkRefreshJob; only the user-visible modal is suppressed. Also fix QML signal handler syntax (arrow-function form) in APMSensorsComponent.qml to silence qmllint warnings. Addresses the compass-cal refresh spam described in #14347. --- .../APM/APMSensorsComponent.qml | 4 ++-- .../APM/APMSensorsComponentController.cc | 16 +++++----------- .../PX4/SensorsComponentController.cc | 17 ++++++----------- src/FactSystem/BulkRefreshJob.cc | 10 ++++++++-- src/FactSystem/ParameterManager.cc | 19 ++++++++++++------- 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml index 71b01700af64..9302f2608263 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSensorsComponent.qml @@ -148,7 +148,7 @@ SetupPage { } } - onCalibrationComplete: { + onCalibrationComplete: (calType) => { switch (calType) { case MAVLink.CalibrationAccel: case MAVLink.CalibrationMag: @@ -158,7 +158,7 @@ SetupPage { } } - onSetAllCalButtonsEnabled: { + onSetAllCalButtonsEnabled: (enabled) => { buttonColumn.enabled = enabled } } diff --git a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc index 367aa4a51a3b..33916f631f55 100644 --- a/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMSensorsComponentController.cc @@ -376,18 +376,12 @@ void APMSensorsComponentController::_handleTextMessage(int sysid, int componenti void APMSensorsComponentController::_refreshParams() { - static const QStringList fastRefreshList = { + _vehicle->parameterManager()->bulkRefresh(ParameterManager::defaultComponentId, { QStringLiteral("COMPASS_OFS_X"), QStringLiteral("COMPASS_OFS_Y"), QStringLiteral("COMPASS_OFS_Z"), - QStringLiteral("INS_ACCOFFS_X"), QStringLiteral("INS_ACCOFFS_Y"), QStringLiteral("INS_ACCOFFS_Z") - }; - - for (const QString ¶mName : fastRefreshList) { - _vehicle->parameterManager()->refreshParameter(ParameterManager::defaultComponentId, paramName); - } - - // Now ask for all to refresh - _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, QStringLiteral("COMPASS_")); - _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, QStringLiteral("INS_")); + QStringLiteral("INS_ACCOFFS_X"), QStringLiteral("INS_ACCOFFS_Y"), QStringLiteral("INS_ACCOFFS_Z"), + QStringLiteral("COMPASS_*"), + QStringLiteral("INS_*"), + }, false /* notifyFailure */); } void APMSensorsComponentController::_updateAndEmitShowOrientationCalArea(bool show) diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentController.cc b/src/AutoPilotPlugins/PX4/SensorsComponentController.cc index be34615ceea9..5e5cb5a3fda3 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentController.cc +++ b/src/AutoPilotPlugins/PX4/SensorsComponentController.cc @@ -432,17 +432,12 @@ void SensorsComponentController::_handleUASTextMessage(int uasId, int compId, in void SensorsComponentController::_refreshParams(void) { - QStringList fastRefreshList; - - // We ask for a refresh on these first so that the rotation combo show up as fast as possible - fastRefreshList << "CAL_MAG0_ID" << "CAL_MAG1_ID" << "CAL_MAG2_ID" << "CAL_MAG0_ROT" << "CAL_MAG1_ROT" << "CAL_MAG2_ROT"; - for (const QString ¶mName : std::as_const(fastRefreshList)) { - _vehicle->parameterManager()->refreshParameter(ParameterManager::defaultComponentId, paramName); - } - - // Now ask for all to refresh - _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, "CAL_"); - _vehicle->parameterManager()->refreshParametersPrefix(ParameterManager::defaultComponentId, "SENS_"); + _vehicle->parameterManager()->bulkRefresh(ParameterManager::defaultComponentId, { + QStringLiteral("CAL_MAG0_ID"), QStringLiteral("CAL_MAG1_ID"), QStringLiteral("CAL_MAG2_ID"), + QStringLiteral("CAL_MAG0_ROT"), QStringLiteral("CAL_MAG1_ROT"), QStringLiteral("CAL_MAG2_ROT"), + QStringLiteral("CAL_*"), + QStringLiteral("SENS_*"), + }, false /* notifyFailure */); } void SensorsComponentController::_updateAndEmitShowOrientationCalArea(bool show) diff --git a/src/FactSystem/BulkRefreshJob.cc b/src/FactSystem/BulkRefreshJob.cc index 111787b4c03e..2abd552f660f 100644 --- a/src/FactSystem/BulkRefreshJob.cc +++ b/src/FactSystem/BulkRefreshJob.cc @@ -35,7 +35,12 @@ void BulkRefreshJob::_sendPendingRequests() void BulkRefreshJob::_onParamSuccess(int componentId, const QString ¶mName, int /*paramIndex*/) { - if (componentId != _componentId || !_pending.remove(paramName)) { + if (componentId != _componentId) { + return; + } + const bool wasPending = _pending.remove(paramName); + const bool wasFailed = _failed.remove(paramName); + if (!wasPending && !wasFailed) { return; } _checkRoundComplete(); @@ -79,7 +84,8 @@ void BulkRefreshJob::_checkRoundComplete() << "\u2014" << _failed.count() << "params still failed"; if (_notifyFailure) { const QStringList failedList(_failed.cbegin(), _failed.cend()); - QGC::showAppMessage(_mgr->tr("Parameter refresh failed for: %1").arg(failedList.join(QStringLiteral(", ")))); + QGC::showAppMessage(_mgr->tr("Parameter refresh failed for: %1").arg(failedList.join(QStringLiteral(", "))), + _mgr->tr("Parameter Bulk Refresh")); } deleteLater(); } diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index c31badeff54d..75001fc3fe9b 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -739,7 +739,8 @@ void ParameterManager::bulkRefresh(int componentId, const QStringList &names, bo return; } const QMap &factMap = _mapCompId2FactMap[componentId]; - QSet resolvedSet; + QStringList resolved; + QSet seen; for (const QString &entry : names) { if (entry.endsWith(QLatin1Char('*'))) { const QString prefix = entry.chopped(1); @@ -748,24 +749,28 @@ void ParameterManager::bulkRefresh(int componentId, const QStringList &names, bo continue; } for (auto it = factMap.cbegin(); it != factMap.cend(); ++it) { - if (it.key().startsWith(prefix)) { - resolvedSet.insert(it.key()); + if (it.key().startsWith(prefix) && !seen.contains(it.key())) { + seen.insert(it.key()); + resolved.append(it.key()); } } } else if (factMap.contains(entry)) { - resolvedSet.insert(entry); + if (!seen.contains(entry)) { + seen.insert(entry); + resolved.append(entry); + } } else { qCWarning(ParameterManagerLog) << "bulkRefresh: unknown param name (skipped):" << entry; } } - if (resolvedSet.isEmpty()) { + if (resolved.isEmpty()) { return; } - qCDebug(ParameterManagerLog) << "bulkRefresh: resolved" << resolvedSet.count() << "params"; + qCDebug(ParameterManagerLog) << "bulkRefresh: resolved" << resolved.count() << "params"; new BulkRefreshJob( - this, componentId, QStringList(resolvedSet.cbegin(), resolvedSet.cend()), notifyFailure, + this, componentId, resolved, notifyFailure, [this, componentId](const QString &name) { _mavlinkParamRequestRead(componentId, name, -1, false /* notifyFailure */); },