Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/AutoPilotPlugins/APM/APMSensorsComponent.qml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ SetupPage {
}
}

onCalibrationComplete: {
onCalibrationComplete: (calType) => {
switch (calType) {
case MAVLink.CalibrationAccel:
case MAVLink.CalibrationMag:
Expand All @@ -158,7 +158,7 @@ SetupPage {
}
}

onSetAllCalButtonsEnabled: {
onSetAllCalButtonsEnabled: (enabled) => {
buttonColumn.enabled = enabled
}
}
Expand Down
16 changes: 5 additions & 11 deletions src/AutoPilotPlugins/APM/APMSensorsComponentController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 &paramName : 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)
Expand Down
17 changes: 6 additions & 11 deletions src/AutoPilotPlugins/PX4/SensorsComponentController.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 &paramName : 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)
Expand Down
92 changes: 92 additions & 0 deletions src/FactSystem/BulkRefreshJob.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#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<void(const QString &)> requestFn,
QObject *parent)
: QObject(parent)
, _mgr(mgr)
, _componentId(componentId)
, _notifyFailure(notifyFailure)
, _requestFn(std::move(requestFn))
, _pending(QSet<QString>(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 &paramName, int /*paramIndex*/)
{
if (componentId != _componentId) {
return;
}
const bool wasPending = _pending.remove(paramName);
const bool wasFailed = _failed.remove(paramName);
if (!wasPending && !wasFailed) {
return;
}
_checkRoundComplete();
}

void BulkRefreshJob::_onParamFailure(int componentId, const QString &paramName, 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(", "))),
_mgr->tr("Parameter Bulk Refresh"));
}
deleteLater();
}
}
40 changes: 40 additions & 0 deletions src/FactSystem/BulkRefreshJob.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

#include <QtCore/QObject>
#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTimer>

#include <functional>

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<void(const QString &)> 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 &paramName, int paramIndex);
void _onParamFailure(int componentId, const QString &paramName, int paramIndex);
void _checkRoundComplete();

ParameterManager *_mgr;
int _componentId;
bool _notifyFailure;
std::function<void(const QString&)> _requestFn;
QSet<QString> _pending;
QSet<QString> _failed;
int _round = 0;
const int _retryBaseDelayMs; ///< 50 ms in unit tests, kRetryBaseDelayMs otherwise
QTimer _retryTimer;
};
2 changes: 2 additions & 0 deletions src/FactSystem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

target_sources(${CMAKE_PROJECT_NAME}
PRIVATE
BulkRefreshJob.cc
BulkRefreshJob.h
Fact.cc
Fact.h
FactGroup.cc
Expand Down
58 changes: 54 additions & 4 deletions src/FactSystem/ParameterManager.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "QmlObjectListModel.h"
#include "ParameterManager.h"
#include "BulkRefreshJob.h"

#include <QtCore/QDir>
#include <QtCore/QSet>
#include <QtCore/QTextStream>

#include "AutoPilotPlugin.h"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -390,7 +393,7 @@ void ParameterManager::_mavlinkParamSet(int componentId, const QString &paramNam
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);
});
Expand Down Expand Up @@ -718,13 +721,62 @@ 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 &paramName: _mapCompId2FactMap[componentId].keys()) {
if (paramName.startsWith(namePrefix)) {
refreshParameter(componentId, paramName);
}
}
}

void ParameterManager::bulkRefresh(int componentId, const QStringList &names, bool notifyFailure)
{
componentId = _actualComponentId(componentId);

if (!_mapCompId2FactMap.contains(componentId)) {
return;
}
const QMap<QString, Fact *> &factMap = _mapCompId2FactMap[componentId];
QStringList resolved;
QSet<QString> seen;
for (const QString &entry : names) {
if (entry.endsWith(QLatin1Char('*'))) {
const QString prefix = entry.chopped(1);
Comment on lines +741 to +746
if (prefix.isEmpty()) {
qCWarning(ParameterManagerLog) << "bulkRefresh: ignoring bare '*' entry";
continue;
}
for (auto it = factMap.cbegin(); it != factMap.cend(); ++it) {
if (it.key().startsWith(prefix) && !seen.contains(it.key())) {
seen.insert(it.key());
resolved.append(it.key());
}
}
} else if (factMap.contains(entry)) {
if (!seen.contains(entry)) {
seen.insert(entry);
resolved.append(entry);
}
} else {
qCWarning(ParameterManagerLog) << "bulkRefresh: unknown param name (skipped):" << entry;
}
}

if (resolved.isEmpty()) {
return;
}

qCDebug(ParameterManagerLog) << "bulkRefresh: resolved" << resolved.count() << "params";
new BulkRefreshJob(
this, componentId, resolved, notifyFailure,
[this, componentId](const QString &name) {
_mavlinkParamRequestRead(componentId, name, -1, false /* notifyFailure */);
},
this);
}

bool ParameterManager::parameterExists(int componentId, const QString &paramName) const
{
bool ret = false;
Expand Down Expand Up @@ -946,7 +998,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()
Expand Down Expand Up @@ -1410,8 +1462,6 @@ QString ParameterManager::_remapParamNameToVersion(const QString &paramName) 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;
Expand Down
12 changes: 11 additions & 1 deletion src/FactSystem/ParameterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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 &paramName);
void _paramSetFailure(int componentId, const QString &paramName);
void _paramRequestReadSuccess(int componentId, const QString &paramName, int paramIndex);
Expand Down Expand Up @@ -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<int> _indexBatchQueue; ///< The current queue of index re-requests
Expand Down
Loading
Loading