9#include <QtCore/QTextStream>
28#include <QtCore/QEasingCurve>
29#include <QtCore/QFile>
30#include <QtCore/QStandardPaths>
31#include <QtCore/QVariantAnimation>
41 , _logReplay(!vehicle->vehicleLinkManager()->primaryLink().expired() && vehicle->vehicleLinkManager()->primaryLink().lock()->isLogReplay())
42 , _disableAllRetries(_logReplay)
43 , _waitForParamValueAckMs(
QGC::runningUnitTests() ? 50 : kWaitForParamValueAckMs)
44 , _tryftp(vehicle->apmFirmware() || vehicle->px4Firmware())
46 qCDebug(ParameterManagerLog) <<
this;
48 if (_vehicle->isOfflineEditingVehicle()) {
49 _loadOfflineEditingParams();
54 qCDebug(ParameterManagerLog) <<
this <<
"In log replay mode";
57 _hashCheckTimer.setSingleShot(
true);
59 (void) connect(&_hashCheckTimer, &QTimer::timeout,
this, &ParameterManager::_hashCheckTimeout);
61 _paramRequestListTimer.setSingleShot(
true);
62 _paramRequestListTimer.setInterval(
QGC::runningUnitTests() ? kTestInitialRequestIntervalMs : kParamRequestListTimeoutMs);
63 (void) connect(&_paramRequestListTimer, &QTimer::timeout,
this, &ParameterManager::_paramRequestListTimeout);
65 _waitingParamTimeoutTimer.setSingleShot(
true);
68 (void) connect(&_waitingParamTimeoutTimer, &QTimer::timeout,
this, &ParameterManager::_waitingParamTimeout);
72 (void) QDir().mkpath(parameterCacheDir().absolutePath());
77 qCDebug(ParameterManagerLog) <<
this;
80void ParameterManager::_updateProgressBar()
83 if (_initialLoadComplete && !_refreshAllProgressActive) {
87 int waitingReadParamIndexCount = 0;
89 for (
const int compId: _waitingReadParamIndexMap.
keys()) {
90 waitingReadParamIndexCount += _waitingReadParamIndexMap[compId].count();
93 if (waitingReadParamIndexCount == 0) {
94 _refreshAllProgressActive =
false;
95 if (_readParamIndexProgressActive) {
96 _readParamIndexProgressActive =
false;
97 _setLoadProgress(0.0);
101 _readParamIndexProgressActive =
true;
102 _setLoadProgress(
static_cast<double>(_totalParamCount - waitingReadParamIndexCount) /
static_cast<double>(_totalParamCount));
109 if (message.msgid == MAVLINK_MSG_ID_PARAM_VALUE) {
110 mavlink_param_value_t param_value{};
111 mavlink_msg_param_value_decode(&message, ¶m_value);
114 char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {};
115 (void) strncpy(parameterNameWithNull, param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
116 const QString parameterName(parameterNameWithNull);
120 if (_tryftp && (message.compid == MAV_COMP_ID_AUTOPILOT1) && !_initialLoadComplete
121 && (parameterName != QStringLiteral(
"_HASH_CHECK"))) {
126 paramUnion.param_float = param_value.param_value;
127 paramUnion.type = param_value.param_type;
129 QVariant parameterValue;
130 if (!_mavlinkParamUnionToVariant(paramUnion, parameterValue)) {
134 _handleParamValue(message.compid, parameterName, param_value.param_count, param_value.param_index,
static_cast<MAV_PARAM_TYPE
>(param_value.param_type), parameterValue);
138void ParameterManager::_handleParamValue(
int componentId,
const QString ¶meterName,
int parameterCount,
int parameterIndex, MAV_PARAM_TYPE mavParamType,
const QVariant ¶meterValue)
141 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
142 "_parameterUpdate" <<
143 "name:" << parameterName <<
144 "count:" << parameterCount <<
145 "index:" << parameterIndex <<
146 "mavType:" << mavParamType <<
147 "value:" << parameterValue <<
152 if ((parameterIndex == 65535) && (parameterName != QStringLiteral(
"_HASH_CHECK")) && _paramRequestListTimer.isActive()) {
153 qCDebug(ParameterManagerLog) <<
"Disregarding unrequested param prior to initial list response" << parameterName;
157 if (_vehicle->
px4Firmware() && (parameterName ==
"_HASH_CHECK")) {
158 _hashCheckTimer.stop();
159 if (!_initialLoadComplete && !_logReplay) {
161 _tryCacheHashLoad(_vehicle->
id(), componentId, parameterValue);
166 _paramRequestListTimer.stop();
169 if (!_initialLoadComplete && !_logReplay && _debugCacheCRC.contains(componentId) && _debugCacheCRC[componentId]) {
170 if (_debugCacheMap[componentId].contains(parameterName)) {
171 const ParamTypeVal &cacheParamTypeVal = _debugCacheMap[componentId][parameterName];
173 const void *
const cacheData = cacheParamTypeVal.second.constData();
174 const void *
const vehicleData = parameterValue.constData();
176 if (memcmp(cacheData, vehicleData, dataSize) != 0) {
177 qCDebug(ParameterManagerVerbose1Log) <<
"Cache/Vehicle values differ for name:cache:actual" << parameterName << parameterValue << cacheParamTypeVal.second;
179 _debugCacheParamSeen[componentId][parameterName] =
true;
181 qCDebug(ParameterManagerVerbose1Log) <<
"Parameter missing from cache" << parameterName;
185 _waitingParamTimeoutTimer.stop();
187 _paramValuesReceivedThisCycle[componentId]++;
188 _silentCycleCount.remove(componentId);
191 if (!_paramCountMap.contains(componentId)) {
192 _paramCountMap[componentId] = parameterCount;
193 _totalParamCount += parameterCount;
197 if (!_waitingReadParamIndexMap.contains(componentId)) {
199 for (
int waitingIndex = 0; waitingIndex < parameterCount; waitingIndex++) {
201 _waitingReadParamIndexMap[componentId][waitingIndex] = 0;
204 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"Seeing component for first time - paramcount:" << parameterCount;
207 if (!_waitingReadParamIndexMap[componentId].contains(parameterIndex)) {
208 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"Unrequested param update" << parameterName;
212 if (_waitingReadParamIndexMap[componentId].contains(parameterIndex)) {
213 _waitingReadParamIndexMap[componentId].remove(parameterIndex);
214 (void) _indexBatchQueue.removeOne(qMakePair(componentId, parameterIndex));
215 _fillIndexBatchQueue(
false );
219 int waitingReadParamIndexCount = 0;
221 for (
const int waitingComponentId: _waitingReadParamIndexMap.
keys()) {
222 waitingReadParamIndexCount += _waitingReadParamIndexMap[waitingComponentId].count();
224 if (waitingReadParamIndexCount) {
225 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"waitingReadParamIndexCount:" << waitingReadParamIndexCount;
228 const int readWaitingParamCount = waitingReadParamIndexCount;
229 const int totalWaitingParamCount = readWaitingParamCount;
230 if (totalWaitingParamCount) {
232 _waitingParamTimeoutTimer.start();
233 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(-1) <<
"Restarting _waitingParamTimeoutTimer: totalWaitingParamCount:" << totalWaitingParamCount;
236 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Restarting _waitingParamTimeoutTimer (still waiting for default component params)";
237 _waitingParamTimeoutTimer.start();
239 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(-1) <<
"Not restarting _waitingParamTimeoutTimer (all requests satisfied)";
242 _updateProgressBar();
244 Fact *fact =
nullptr;
245 if (_mapCompId2FactMap.contains(componentId) && _mapCompId2FactMap[componentId].contains(parameterName)) {
246 fact = _mapCompId2FactMap[componentId][parameterName];
248 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"Adding new fact" << parameterName;
254 _mapCompId2FactMap[componentId][parameterName] = fact;
268 if (_prevWaitingReadParamIndexCount != 0 && readWaitingParamCount == 0) {
270 _writeLocalParamCache(_vehicle->
id(), componentId);
274 _prevWaitingReadParamIndexCount = waitingReadParamIndexCount;
276 _checkInitialLoadComplete();
278 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"_handleParamValue complete";
281QString ParameterManager::_vehicleAndComponentString(
int componentId)
const
284 QString vehicleIdStr;
286 vehicleIdStr = QStringLiteral(
"veh: %1").arg(_vehicle->
id());
290 QString componentIdStr;
291 if (_mapCompId2FactMap.keys().count() > 1) {
292 componentIdStr = QStringLiteral(
"comp: %1").arg(componentId);
295 if (!vehicleIdStr.isEmpty() && !componentIdStr.isEmpty()) {
296 return vehicleIdStr + QStringLiteral(
" ") + componentIdStr;
297 }
else if (!vehicleIdStr.isEmpty()) {
299 }
else if (!componentIdStr.isEmpty()) {
300 return componentIdStr;
306void ParameterManager::_mavlinkParamSet(
int componentId,
const QString ¶mName,
FactMetaData::ValueType_t valueType,
const QVariant &rawValue)
308 auto paramSetEncoder = [
this, componentId, paramName, valueType, rawValue](uint8_t , uint8_t channel,
mavlink_message_t *message) ->
void {
312 if (!_fillMavlinkParamUnion(valueType, rawValue, union_value)) {
316 char paramId[MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN + 1] = {};
317 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_PARAM_SET_FIELD_PARAM_ID_LEN);
319 (void) mavlink_msg_param_set_pack_chan(
324 static_cast<uint8_t
>(_vehicle->
id()),
325 static_cast<uint8_t
>(componentId),
327 union_value.param_float,
328 static_cast<uint8_t
>(paramType));
331 auto checkForCorrectParamValue = [
this, componentId, paramName, rawValue](
const mavlink_message_t &message) ->
bool {
332 if (message.compid != componentId) {
336 mavlink_param_value_t param_value{};
337 mavlink_msg_param_value_decode(&message, ¶m_value);
340 char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {};
341 (void) strncpy(parameterNameWithNull, param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
342 const QString parameterName(parameterNameWithNull);
344 if (parameterName != paramName) {
349 QVariant receivedValue;
351 param_union.param_float = param_value.param_value;
352 param_union.type = param_value.param_type;
353 if (!_mavlinkParamUnionToVariant(param_union, receivedValue)) {
356 if (rawValue.typeId() != receivedValue.typeId()) {
357 qCWarning(ParameterManagerLog) <<
"QVariant type mismatch on PARAM_VALUE ack for" << paramName <<
": expected type" << rawValue.typeId() <<
"got type" << receivedValue.typeId();
360 if (param_value.param_type == MAV_PARAM_TYPE_REAL32) {
364 return receivedValue == rawValue;
368 auto checkForParamError = [componentId, paramName](
const mavlink_message_t &message) ->
bool {
369 if (message.compid != componentId) {
373 mavlink_param_error_t paramError{};
374 mavlink_msg_param_error_decode(&message, ¶mError);
376 char paramId[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN + 1] = {};
377 (void) strncpy(paramId, paramError.param_id, MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN);
379 return QString(paramId) == paramName;
399 auto incPendingWriteCountState =
new FunctionState(QStringLiteral(
"ParameterManager increment pending write count"), stateMachine, [
this]() {
400 _incrementPendingWriteCount();
402 auto decPendingWriteCountState =
new FunctionState(QStringLiteral(
"ParameterManager decrement pending write count"), stateMachine, [
this]() {
403 _decrementPendingWriteCount();
405 auto retryDecPendingWriteCountState =
new FunctionState(QStringLiteral(
"ParameterManager retry decrement pending write count"), stateMachine, [
this]() {
406 _decrementPendingWriteCount();
408 auto errorDecPendingWriteCountState =
new FunctionState(QStringLiteral(
"ParameterManager error decrement pending write count"), stateMachine, [
this]() {
409 _decrementPendingWriteCount();
411 auto waitAckState =
new WaitForParamResponseState(stateMachine, _waitForParamValueAckMs, checkForCorrectParamValue, checkForParamError);
412 auto paramRefreshState =
new FunctionState(QStringLiteral(
"ParameterManager param refresh"), stateMachine, [
this, waitAckState, componentId, paramName]() {
415 if (waitAckState->lastParamError() == MAV_PARAM_ERROR_DOES_NOT_EXIST) {
416 qCDebug(ParameterManagerLog) <<
"Skipping post-write-failure refresh, param does not exist on vehicle:" << paramName << _vehicleAndComponentString(componentId);
421 auto userNotifyState =
new FunctionState(QStringLiteral(
"ParameterManager user notify"), stateMachine, [waitAckState, paramName,
this, componentId]() {
422 const QString errorDetail = waitAckState->lastParamErrorString();
423 const QString msg = errorDetail.isEmpty()
424 ? QStringLiteral(
"Parameter write failed: param: %1 %2").arg(paramName, _vehicleAndComponentString(componentId))
425 : QStringLiteral(
"Parameter write failed: param: %1 %2 - %3").arg(paramName, _vehicleAndComponentString(componentId), errorDetail);
428 auto logSuccessState =
new FunctionState(QStringLiteral(
"ParameterManager log success"), stateMachine, [
this, componentId, paramName]() {
429 qCDebug(ParameterManagerLog) <<
"Parameter write succeeded: param:" << paramName << _vehicleAndComponentString(componentId);
432 auto logFailureState =
new FunctionState(QStringLiteral(
"ParameterManager log failure"), stateMachine, [
this, componentId, paramName]() {
433 qCDebug(ParameterManagerLog) <<
"Parameter write failed: param:" << paramName << _vehicleAndComponentString(componentId);
439 stateMachine->setInitialState(sendParamSetState);
440 sendParamSetState->addThisTransition (&
QGCState::advance, incPendingWriteCountState);
455 sendParamSetState->addThisTransition(&
QGCState::error, logFailureState);
462 qCDebug(ParameterManagerLog) <<
"Starting state machine for PARAM_SET on: " << paramName << _vehicleAndComponentString(componentId);
463 stateMachine->start();
472 paramUnion.param_uint8 =
static_cast<uint8_t
>(rawValue.toUInt(&ok));
475 paramUnion.param_int8 =
static_cast<int8_t
>(rawValue.toInt(&ok));
478 paramUnion.param_uint16 =
static_cast<uint16_t
>(rawValue.toUInt(&ok));
481 paramUnion.param_int16 =
static_cast<int16_t
>(rawValue.toInt(&ok));
484 paramUnion.param_uint32 =
static_cast<uint32_t
>(rawValue.toUInt(&ok));
487 paramUnion.param_float = rawValue.toFloat(&ok);
490 paramUnion.param_int32 =
static_cast<int32_t
>(rawValue.toInt(&ok));
493 qCCritical(ParameterManagerLog) <<
"Internal Error: Unsupported fact value type" << valueType;
494 paramUnion.param_int32 =
static_cast<int32_t
>(rawValue.toInt(&ok));
499 qCCritical(ParameterManagerLog) <<
"Fact Failed to Convert to Param Type:" << valueType;
506bool ParameterManager::_mavlinkParamUnionToVariant(
const mavlink_param_union_t ¶mUnion, QVariant &outValue)
const
508 switch (paramUnion.type) {
509 case MAV_PARAM_TYPE_REAL32:
510 outValue = QVariant(paramUnion.param_float);
512 case MAV_PARAM_TYPE_UINT8:
513 outValue = QVariant(
static_cast<quint32
>(paramUnion.param_uint8));
515 case MAV_PARAM_TYPE_INT8:
516 outValue = QVariant(
static_cast<qint32
>(paramUnion.param_int8));
518 case MAV_PARAM_TYPE_UINT16:
519 outValue = QVariant(
static_cast<quint32
>(paramUnion.param_uint16));
521 case MAV_PARAM_TYPE_INT16:
522 outValue = QVariant(
static_cast<qint32
>(paramUnion.param_int16));
524 case MAV_PARAM_TYPE_UINT32:
525 outValue = QVariant(paramUnion.param_uint32);
527 case MAV_PARAM_TYPE_INT32:
528 outValue = QVariant(paramUnion.param_int32);
531 qCCritical(ParameterManagerLog) <<
"ParameterManager::_mavlinkParamUnionToVariant - unsupported MAV_PARAM_TYPE" << paramUnion.type;
536void ParameterManager::_factRawValueUpdated(
const QVariant &rawValue)
538 Fact *
const fact = qobject_cast<Fact*>(sender());
540 qCWarning(ParameterManagerLog) <<
"Internal error";
547 "Vehicle reboot required for changes to take effect. Use the power indicator in the toolbar to reboot."));
553void ParameterManager::_ftpDownloadComplete(
const QString &fileName,
const QString &errorMsg)
555 bool continueWithDefaultParameterdownload =
true;
556 bool immediateRetry =
false;
558 _ftpDownloadInProgress =
false;
562 if (errorMsg.isEmpty()) {
563 qCDebug(ParameterManagerLog) <<
"ParameterManager::_ftpDownloadComplete : Parameter file received:" << fileName;
564 if (_parseParamFile(fileName)) {
565 qCDebug(ParameterManagerLog) <<
"ParameterManager::_ftpDownloadComplete : Parsed!";
568 qCDebug(ParameterManagerLog) <<
"ParameterManager::_ftpDownloadComplete : Error in parameter file";
571 }
else if (errorMsg.contains(
"File Not Found")) {
572 qCDebug(ParameterManagerLog) <<
"ParameterManager-ftp: No Parameterfile on vehicle - Start Conventional Parameter Download";
573 if (_initialRequestRetryCount == 0) {
574 immediateRetry =
true;
576 }
else if ((_loadProgress > 0.0001) && (_loadProgress < 0.01)) {
577 qCDebug(ParameterManagerLog) <<
"ParameterManager-ftp progress too slow - Start Conventional Parameter Download";
578 }
else if (_initialRequestRetryCount == 1) {
579 qCDebug(ParameterManagerLog) <<
"ParameterManager-ftp: Too many retries - Start Conventional Parameter Download";
581 qCDebug(ParameterManagerLog) <<
"ParameterManager-ftp Retry:" << _initialRequestRetryCount;
582 continueWithDefaultParameterdownload =
false;
585 if (continueWithDefaultParameterdownload) {
587 _initialRequestRetryCount = 0;
592 if (immediateRetry) {
593 _paramRequestListTimeout();
595 _paramRequestListTimer.start();
598 _paramRequestListTimer.start();
602void ParameterManager::_ftpDownloadProgress(
float progress)
604 qCDebug(ParameterManagerVerbose1Log) <<
"ParameterManager::_ftpDownloadProgress:" << progress;
605 _setLoadProgress(
static_cast<double>(progress));
606 if (progress > 0.001) {
607 _paramRequestListTimer.stop();
611void ParameterManager::_resetHashCheck()
613 _hashCheckTimer.stop();
614 _hashCheckDone =
false;
621 _refreshAllProgressActive =
true;
622 _startParameterDownload(componentId);
628 _cacheOnlyHashCheck =
true;
636 if (sharedLink->linkConfiguration()->isHighLatency() || _logReplay) {
637 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Cache-only hash check: high latency or log replay link, signalling failure";
642 if (_vehicle->
px4Firmware() && !_initialLoadComplete) {
643 _hashCheckTimer.start();
644 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Cache-only hash check: requesting _HASH_CHECK";
645 _requestHashCheck(MAV_COMP_ID_AUTOPILOT1);
647 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Cache-only hash check: not available, signalling failure";
652void ParameterManager::_startParameterDownload(uint8_t componentId)
659 if (sharedLink->linkConfiguration()->isHighLatency() || _logReplay) {
661 _parametersReady =
true;
662 _missingParameters =
true;
663 _initialLoadComplete =
true;
664 _waitingForDefaultComponent =
false;
670 if (_vehicle->
px4Firmware() && !_initialLoadComplete && !_hashCheckDone) {
672 _cacheOnlyHashCheck =
false;
673 _hashCheckTimer.start();
674 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Requesting _HASH_CHECK before full parameter list";
675 const uint8_t hashCheckCompId = (componentId == MAV_COMP_ID_ALL)
676 ?
static_cast<uint8_t
>(MAV_COMP_ID_AUTOPILOT1)
678 _requestHashCheck(hashCheckCompId);
679 }
else if (_tryftp && ((componentId == MAV_COMP_ID_ALL) || (componentId == MAV_COMP_ID_AUTOPILOT1))) {
680 if (_ftpDownloadInProgress) {
682 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Parameter file download already in progress";
683 _refreshAllProgressActive =
false;
686 if (!_initialLoadComplete) {
687 _paramRequestListTimer.start();
692 if (!_anyComponentWaiting()) {
693 _waitingParamTimeoutTimer.stop();
695 if (ftpManager->
download(MAV_COMP_ID_AUTOPILOT1,
696 QStringLiteral(
"@PARAM/param.pck?withdefaults=1"),
697 QStandardPaths::writableLocation(QStandardPaths::TempLocation),
698 QStringLiteral(
"param.pck"),
700 _ftpDownloadInProgress =
true;
703 qCWarning(ParameterManagerLog) <<
"ParameterManager::_startParameterDownload FTPManager::download returned failure";
705 _refreshAllProgressActive =
false;
708 if (!_initialLoadComplete) {
709 _paramRequestListTimer.start();
713 for (
int cid: _paramCountMap.
keys()) {
715 if ((componentId != MAV_COMP_ID_ALL) && (componentId != cid)) {
718 for (
int waitingIndex = 0; waitingIndex < _paramCountMap[cid]; waitingIndex++) {
720 _waitingReadParamIndexMap[cid][waitingIndex] = 0;
727 sharedLink->mavlinkChannel(),
734 const QString what = (componentId == MAV_COMP_ID_ALL) ?
"MAV_COMP_ID_ALL" : QString::
number(componentId);
735 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Request to refresh all parameters for component ID:" << what;
738int ParameterManager::_actualComponentId(
int componentId)
const
743 qCWarning(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Default component id not set";
752 componentId = _actualComponentId(componentId);
754 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"refreshParameter - name:" << paramName <<
")";
756 _mavlinkParamRequestRead(componentId, paramName, -1,
true );
761 componentId = _actualComponentId(componentId);
762 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"refreshParametersPrefix - name:" << namePrefix <<
")";
764 if (!_mapCompId2FactMap.contains(componentId)) {
767 for (
const QString ¶mName: _mapCompId2FactMap[componentId].keys()) {
768 if (paramName.startsWith(namePrefix)) {
776 componentId = _actualComponentId(componentId);
778 if (!_mapCompId2FactMap.contains(componentId)) {
781 const QMap<QString, Fact *> &factMap = _mapCompId2FactMap[componentId];
782 QStringList resolved;
784 for (
const QString &entry : names) {
785 if (entry.endsWith(QLatin1Char(
'*'))) {
786 const QString prefix = entry.chopped(1);
787 if (prefix.isEmpty()) {
788 qCWarning(ParameterManagerLog) <<
"bulkRefresh: ignoring bare '*' entry";
791 for (
auto it = factMap.cbegin(); it != factMap.cend(); ++it) {
792 if (it.key().startsWith(prefix) && !seen.contains(it.key())) {
793 seen.insert(it.key());
794 resolved.append(it.key());
797 }
else if (factMap.contains(entry)) {
798 if (!seen.contains(entry)) {
800 resolved.append(entry);
803 qCWarning(ParameterManagerLog) <<
"bulkRefresh: unknown param name (skipped):" << entry;
807 if (resolved.isEmpty()) {
811 qCDebug(ParameterManagerLog) <<
"bulkRefresh: resolved" << resolved.count() <<
"params";
813 this, componentId, resolved, notifyFailure,
814 [
this, componentId](
const QString &name) {
815 _mavlinkParamRequestRead(componentId, name, -1,
false );
824 componentId = _actualComponentId(componentId);
825 if (_mapCompId2FactMap.contains(componentId)) {
826 ret = _mapCompId2FactMap[componentId].contains(_remapParamNameToVersion(paramName));
834 componentId = _actualComponentId(componentId);
836 const QString mappedParamName = _remapParamNameToVersion(paramName);
837 if (!_mapCompId2FactMap.contains(componentId) || !_mapCompId2FactMap[componentId].contains(mappedParamName)) {
838 qgcApp()->reportMissingParameter(componentId, mappedParamName);
839 return &_defaultFact;
842 return _mapCompId2FactMap[componentId][mappedParamName];
849 const int compId = _actualComponentId(componentId);
850 const QMap<QString, Fact*> &factMap = _mapCompId2FactMap[compId];
851 for (
const QString ¶mName: factMap.keys()) {
858bool ParameterManager::_fillIndexBatchQueue(
bool waitingParamTimeout)
860 if (!_indexBatchQueueActive) {
864 if (waitingParamTimeout) {
866 qCDebug(ParameterManagerLog) <<
"Refilling index based batch queue due to timeout";
867 _indexBatchQueue.clear();
869 qCDebug(ParameterManagerLog) <<
"Refilling index based batch queue due to received parameter";
872 for (
const int componentId: _waitingReadParamIndexMap.
keys()) {
873 if (_waitingReadParamIndexMap[componentId].count()) {
874 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"_waitingReadParamIndexMap count" << _waitingReadParamIndexMap[componentId].count();
875 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"_waitingReadParamIndexMap (index, retry count)" << _waitingReadParamIndexMap[componentId];
878 for (
const int paramIndex: _waitingReadParamIndexMap[componentId].
keys()) {
879 if (_indexBatchQueue.contains(qMakePair(componentId, paramIndex))) {
888 _waitingReadParamIndexMap[componentId][paramIndex]++;
889 if (_disableAllRetries || (_waitingReadParamIndexMap[componentId][paramIndex] > _maxInitialLoadRetrySingleParam)) {
891 _failedReadParamIndexMap[componentId] << paramIndex;
892 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"Giving up on (paramIndex:" << paramIndex <<
"retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] <<
")";
893 (void) _waitingReadParamIndexMap[componentId].remove(paramIndex);
896 _indexBatchQueue.append(qMakePair(componentId, paramIndex));
897 _sendParamRequestReadIndex(componentId, paramIndex);
898 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"Read re-request for (paramIndex:" << paramIndex <<
"retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] <<
")";
903 return (!_indexBatchQueue.isEmpty());
906void ParameterManager::_waitingParamTimeout()
912 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"_waitingParamTimeout after" << _waitingParamTimeoutTimer.interval() <<
"ms";
915 _indexBatchQueueActive =
true;
917 _giveUpOnUnresponsiveComponents();
920 bool paramsRequested = _fillIndexBatchQueue(
true );
921 if (!paramsRequested && !_waitingForDefaultComponent && !_mapCompId2FactMap.contains(_vehicle->
defaultComponentId())) {
924 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Restarting _waitingParamTimeoutTimer - still don't have default component params" << _vehicle->
defaultComponentId();
925 _waitingParamTimeoutTimer.start();
926 _waitingForDefaultComponent =
true;
929 _waitingForDefaultComponent =
false;
931 _checkInitialLoadComplete();
933 _updateProgressBar();
935 if (paramsRequested) {
936 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Restarting _waitingParamTimeoutTimer - re-request";
937 _waitingParamTimeoutTimer.start();
941void ParameterManager::_giveUpOnUnresponsiveComponents()
945 for (
const int componentId: _waitingReadParamIndexMap.
keys()) {
946 if (componentId == defaultCompId) {
950 const int outstanding =
static_cast<int>(std::count_if(_indexBatchQueue.cbegin(), _indexBatchQueue.cend(),
951 [componentId](
const QPair<int, int> &entry) { return entry.first == componentId; }));
960 const QList<int>
remaining = _waitingReadParamIndexMap[componentId].keys();
961 qCWarning(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"Component unresponsive for" <<
kUnresponsiveSilentCycles
962 <<
"cycles, giving up on remaining parameters:" <<
remaining.count();
963 _failedReadParamIndexMap[componentId] <<
remaining;
964 _waitingReadParamIndexMap[componentId].clear();
965 (void) _indexBatchQueue.removeIf([componentId](
const QPair<int, int> &entry) {
return entry.first == componentId; });
968 _paramValuesReceivedThisCycle.clear();
971void ParameterManager::_requestHashCheck(uint8_t componentId)
978 qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) <<
"Sending PARAM_REQUEST_READ for _HASH_CHECK";
980 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1] = {};
981 (void) strncpy(paramId,
"_HASH_CHECK", MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
984 (void) mavlink_msg_param_request_read_pack_chan(
987 sharedLink->mavlinkChannel(),
989 static_cast<uint8_t
>(_vehicle->
id()),
997void ParameterManager::_sendParamRequestReadIndex(
int componentId,
int paramIndex)
1005 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1] = {};
1009 sharedLink->mavlinkChannel(),
1011 static_cast<uint8_t
>(_vehicle->
id()),
1012 static_cast<uint8_t
>(componentId),
1014 static_cast<int16_t
>(paramIndex));
1018void ParameterManager::_mavlinkParamRequestRead(
int componentId,
const QString ¶mName,
int paramIndex,
bool notifyFailure)
1020 auto paramRequestReadEncoder = [
this, componentId, paramName, paramIndex](uint8_t , uint8_t channel,
mavlink_message_t *message) ->
void {
1021 char paramId[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN + 1] = {};
1022 (void) strncpy(paramId, paramName.toLocal8Bit().constData(), MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN);
1028 static_cast<uint8_t
>(_vehicle->
id()),
1029 static_cast<uint8_t
>(componentId),
1031 static_cast<int16_t
>(paramIndex));
1034 auto checkForCorrectParamValue = [componentId, paramName, paramIndex](
const mavlink_message_t &message) ->
bool {
1035 if (message.compid != componentId) {
1039 mavlink_param_value_t param_value{};
1040 mavlink_msg_param_value_decode(&message, ¶m_value);
1043 char parameterNameWithNull[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1] = {};
1044 (void) strncpy(parameterNameWithNull, param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
1045 const QString parameterName(parameterNameWithNull);
1048 if (paramIndex != -1) {
1050 if (param_value.param_index != paramIndex) {
1055 if (parameterName != paramName) {
1063 auto checkForParamError = [componentId, paramName, paramIndex](
const mavlink_message_t &message) ->
bool {
1064 if (message.compid != componentId) {
1068 mavlink_param_error_t paramError{};
1069 mavlink_msg_param_error_decode(&message, ¶mError);
1071 char paramId[MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN + 1] = {};
1072 (void) strncpy(paramId, paramError.param_id, MAVLINK_MSG_PARAM_ERROR_FIELD_PARAM_ID_LEN);
1074 if (paramIndex != -1) {
1075 return paramError.param_index == paramIndex;
1077 return QString(paramId) == paramName;
1094 auto waitAckState =
new WaitForParamResponseState(stateMachine, _waitForParamValueAckMs, checkForCorrectParamValue, checkForParamError);
1095 auto userNotifyState =
new FunctionState(QStringLiteral(
"User notify"), stateMachine, [waitAckState, paramName,
this, componentId]() {
1096 const QString errorDetail = waitAckState->lastParamErrorString();
1097 const QString msg = errorDetail.isEmpty()
1098 ? QStringLiteral(
"Parameter read failed: param: %1 %2").arg(paramName, _vehicleAndComponentString(componentId))
1099 : QStringLiteral(
"Parameter read failed: param: %1 %2 - %3").arg(paramName, _vehicleAndComponentString(componentId), errorDetail);
1102 auto logSuccessState =
new FunctionState(QStringLiteral(
"Log success"), stateMachine, [
this, componentId, paramName, paramIndex]() {
1103 qCDebug(ParameterManagerLog) <<
"PARAM_REQUEST_READ succeeded: name:" << paramName <<
"index" << paramIndex << _vehicleAndComponentString(componentId);
1106 auto logFailureState =
new FunctionState(QStringLiteral(
"Log failure"), stateMachine, [
this, componentId, paramName, paramIndex]() {
1107 qCDebug(ParameterManagerLog) <<
"PARAM_REQUEST_READ failed: param:" << paramName <<
"index" << paramIndex << _vehicleAndComponentString(componentId);
1113 stateMachine->setInitialState(sendParamRequestReadState);
1125 sendParamRequestReadState->addThisTransition(&
QGCState::error, logFailureState);
1128 if (notifyFailure) {
1135 qCDebug(ParameterManagerLog) <<
"Starting state machine for PARAM_REQUEST_READ on: " << paramName << _vehicleAndComponentString(componentId);
1136 stateMachine->start();
1139void ParameterManager::_writeLocalParamCache(
int vehicleId,
int componentId)
1141 CacheMapName2ParamTypeVal cacheMap;
1143 for (
const QString ¶mName: _mapCompId2FactMap[componentId].
keys()) {
1144 const Fact *
const fact = _mapCompId2FactMap[componentId][paramName];
1145 cacheMap[paramName] = ParamTypeVal(fact->
type(), fact->
rawValue());
1149 if (cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
1150 QDataStream ds(&cacheFile);
1152 qCDebug(ParameterManagerLog) <<
"Parameter cache written" << cacheFile.fileName() <<
"paramCount:" << cacheMap.count();
1154 qCWarning(ParameterManagerLog) <<
"Failed to open cache file for writing" << cacheFile.fileName();
1161 const QFileInfo settingsFile(QSettings().fileName());
1162 const QString basePath = settingsFile.dir().absolutePath();
1163 const QString appName = settingsFile.completeBaseName();
1164 return QDir(basePath + QDir::separator() + appName + QDir::separator() + QStringLiteral(
"ParamCache"));
1169 return parameterCacheDir().filePath(QStringLiteral(
"%1_%2.v2").arg(vehicleId).arg(componentId));
1172void ParameterManager::_tryCacheHashLoad(
int vehicleId,
int componentId,
const QVariant &hashValue)
1174 qCDebug(ParameterManagerLog) <<
"Attemping load from cache";
1177 CacheMapName2ParamTypeVal cacheMap;
1179 if (!cacheFile.exists()) {
1180 qCDebug(ParameterManagerLog) <<
"Parameter cache usage failed - No parameter cache file";
1181 if (!_hashCheckDone) {
1182 _hashCheckDone =
true;
1183 if (_cacheOnlyHashCheck) {
1184 qCDebug(ParameterManagerLog) <<
"Cache-only hash check: no cache file, signalling failure";
1189 _startParameterDownload(MAV_COMP_ID_ALL);
1194 (void) cacheFile.open(QIODevice::ReadOnly);
1197 QDataStream ds(&cacheFile);
1201 uint32_t crc32_value = 0;
1202 for (
const QString &name: cacheMap.
keys()) {
1203 const ParamTypeVal ¶mTypeVal = cacheMap[name];
1208 qCDebug(ParameterManagerLog) <<
"Volatile parameter" << name;
1210 const void *
const vdat = paramTypeVal.second.constData();
1212 crc32_value =
QGC::crc32(
reinterpret_cast<const uint8_t *
>(qPrintable(name)), name.length(), crc32_value);
1218 if (crc32_value == hashValue.toUInt()) {
1219 _hashCheckDone =
true;
1220 _paramRequestListTimer.stop();
1221 qCDebug(ParameterManagerLog) <<
"Parameters loaded from cache" << qPrintable(QFileInfo(cacheFile).absoluteFilePath());
1223 const int count = cacheMap.count();
1225 for (
const QString &name: cacheMap.
keys()) {
1226 const ParamTypeVal ¶mTypeVal = cacheMap[name];
1229 _handleParamValue(componentId, name, count, index++, mavParamType, paramTypeVal.second);
1234 mavlink_param_set_t p{};
1238 p.param_type = MAV_PARAM_TYPE_UINT32;
1239 (void) strncpy(p.param_id,
"_HASH_CHECK",
sizeof(p.param_id));
1240 union_value.param_uint32 = crc32_value;
1241 p.param_value = union_value.param_float;
1242 p.target_system =
static_cast<uint8_t
>(_vehicle->
id());
1243 p.target_component =
static_cast<uint8_t
>(componentId);
1248 sharedLink->mavlinkChannel(),
1255 QVariantAnimation *
const ani =
new QVariantAnimation(
this);
1256 ani->setEasingCurve(QEasingCurve::OutCubic);
1257 ani->setStartValue(0.0);
1258 ani->setEndValue(1.0);
1259 ani->setDuration(750);
1261 (void) connect(ani, &QVariantAnimation::valueChanged,
this, [
this](
const QVariant &value) {
1262 _setLoadProgress(value.toDouble());
1266 connect(ani, &QVariantAnimation::finished,
this, [
this] {
1267 QTimer::singleShot(500,
this, [
this] {
1268 _setLoadProgress(0);
1272 ani->start(QAbstractAnimation::DeleteWhenStopped);
1274 qCDebug(ParameterManagerLog) <<
"Parameters cache match failed" << qPrintable(QFileInfo(cacheFile).absoluteFilePath());
1275 if (ParameterManagerDebugCacheFailureLog().isDebugEnabled()) {
1276 _debugCacheCRC[componentId] =
true;
1277 _debugCacheMap[componentId] = cacheMap;
1278 for (
const QString &name: cacheMap.
keys()) {
1279 _debugCacheParamSeen[componentId][name] =
false;
1283 if (!_hashCheckDone) {
1284 _hashCheckDone =
true;
1285 if (_cacheOnlyHashCheck) {
1286 qCDebug(ParameterManagerLog) <<
"Cache-only hash check: CRC mismatch, signalling failure";
1291 _startParameterDownload(MAV_COMP_ID_ALL);
1299 stream <<
"# Onboard parameters for Vehicle " << _vehicle->
id() <<
"\n";
1304 stream <<
"# Version: "
1309 stream <<
"# Git Revision: " << _vehicle->
gitHash() <<
"\n";
1312 stream <<
"# Vehicle-Id Component-Id Name Value Type\n";
1314 for (
const int componentId: _mapCompId2FactMap.keys()) {
1315 for (
const QString ¶mName: _mapCompId2FactMap[componentId].keys()) {
1316 const Fact *
const fact = _mapCompId2FactMap[componentId][paramName];
1320 qCWarning(ParameterManagerLog) <<
"Internal error: missing fact";
1332 return MAV_PARAM_TYPE_UINT8;
1334 return MAV_PARAM_TYPE_INT8;
1336 return MAV_PARAM_TYPE_UINT16;
1338 return MAV_PARAM_TYPE_INT16;
1340 return MAV_PARAM_TYPE_UINT32;
1342 return MAV_PARAM_TYPE_UINT64;
1344 return MAV_PARAM_TYPE_INT64;
1346 return MAV_PARAM_TYPE_REAL32;
1348 return MAV_PARAM_TYPE_REAL64;
1350 qCWarning(ParameterManagerLog) <<
"Unsupported fact type" << factType;
1353 return MAV_PARAM_TYPE_INT32;
1360 case MAV_PARAM_TYPE_UINT8:
1362 case MAV_PARAM_TYPE_INT8:
1364 case MAV_PARAM_TYPE_UINT16:
1366 case MAV_PARAM_TYPE_INT16:
1368 case MAV_PARAM_TYPE_UINT32:
1370 case MAV_PARAM_TYPE_UINT64:
1372 case MAV_PARAM_TYPE_INT64:
1374 case MAV_PARAM_TYPE_REAL32:
1376 case MAV_PARAM_TYPE_REAL64:
1379 qCWarning(ParameterManagerLog) <<
"Unsupported mav param type" << mavType;
1381 case MAV_PARAM_TYPE_INT32:
1386void ParameterManager::_checkInitialLoadComplete()
1388 if (_initialLoadComplete) {
1389 _checkOtherComponentsLoadComplete();
1396 if (_waitingReadParamIndexMap.contains(defaultCompId) && !_waitingReadParamIndexMap[defaultCompId].isEmpty()) {
1400 if (!_mapCompId2FactMap.contains(defaultCompId)) {
1406 _initialLoadComplete =
true;
1407 _refreshAllProgressActive =
false;
1408 _setLoadProgress(0.0);
1411 for (
const int componentId: _debugCacheParamSeen.
keys()) {
1412 if (!_logReplay && _debugCacheCRC.contains(componentId) && _debugCacheCRC[componentId]) {
1413 for (
const QString ¶mName: _debugCacheParamSeen[componentId].
keys()) {
1414 if (!_debugCacheParamSeen[componentId][paramName]) {
1415 qCDebug(ParameterManagerLog) <<
"Parameter in cache but not on vehicle componentId:Name" << componentId << paramName;
1420 _debugCacheCRC.clear();
1422 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Initial load complete";
1424 _missingParameters = !_failedReadParamIndexMap.value(defaultCompId).isEmpty();
1425 if (_missingParameters) {
1426 QStringList failedIndices;
1427 for (
const int paramIndex: _failedReadParamIndexMap[defaultCompId]) {
1428 failedIndices.append(QString::number(paramIndex));
1430 qCWarning(ParameterManagerLog) << _logVehiclePrefix(defaultCompId) <<
"The following parameter indices could not be loaded after the maximum number of retries:" << failedIndices.join(QStringLiteral(
", "));
1431 const QString errorMsg = tr(
"%1 was unable to retrieve the full set of parameters from vehicle %2. "
1432 "This will cause %1 to be unable to display its full user interface. "
1433 "If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. "
1434 "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue.").arg(QCoreApplication::applicationName()).arg(_vehicle->
id());
1435 qCDebug(ParameterManagerLog) << errorMsg;
1440 _parametersReady =
true;
1445 _checkOtherComponentsLoadComplete();
1448bool ParameterManager::_anyComponentWaiting()
const
1450 for (
auto it = _waitingReadParamIndexMap.cbegin(); it != _waitingReadParamIndexMap.cend(); ++it) {
1451 if (!it.value().isEmpty()) {
1458void ParameterManager::_checkOtherComponentsLoadComplete()
1460 if (_otherComponentsReported) {
1464 if (_anyComponentWaiting()) {
1469 QStringList failedIndices;
1470 QStringList failedComponentIds;
1471 for (
const int componentId: _failedReadParamIndexMap.
keys()) {
1472 if ((componentId == defaultCompId) || _failedReadParamIndexMap[componentId].isEmpty()) {
1475 failedComponentIds.append(QString::number(componentId));
1476 for (
const int paramIndex: _failedReadParamIndexMap[componentId]) {
1477 failedIndices.append(QStringLiteral(
"%1:%2").arg(componentId).arg(paramIndex));
1480 if (failedComponentIds.isEmpty()) {
1483 _otherComponentsReported =
true;
1485 qCWarning(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"The following parameter indices could not be loaded after the maximum number of retries:" << failedIndices.join(QStringLiteral(
", "));
1486 QString vehicleIdPrefix;
1488 vehicleIdPrefix = tr(
"Vehicle %1: ").arg(_vehicle->
id());
1490 const QString errorMsg = vehicleIdPrefix + tr(
"%1 was unable to retrieve the full set of parameters from component(s) %2.").arg(QCoreApplication::applicationName(), failedComponentIds.join(QStringLiteral(
", ")));
1491 qCDebug(ParameterManagerLog) << errorMsg;
1495void ParameterManager::_hashCheckTimeout()
1497 _hashCheckDone =
true;
1498 if (_cacheOnlyHashCheck) {
1499 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"_HASH_CHECK timed out in cache-only mode, signalling failure";
1503 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"_HASH_CHECK timed out, falling back to parameter download";
1504 _startParameterDownload(MAV_COMP_ID_ALL);
1507void ParameterManager::_paramRequestListTimeout()
1511 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"_paramRequestListTimeout (log replay): Signalling load complete";
1512 _initialLoadComplete =
true;
1513 _missingParameters =
false;
1514 _parametersReady =
true;
1521 if (!_disableAllRetries && (++_initialRequestRetryCount <= _maxInitialRequestListRetry)) {
1522 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Retrying initial parameter request list";
1523 _startParameterDownload(MAV_COMP_ID_ALL);
1527 qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) <<
"Initial parameter request list retries exhausted, giving up";
1529 const QString errorMsg = tr(
"Vehicle %1 did not respond to request for parameters. "
1530 "This will cause %2 to be unable to display its full user interface.").arg(_vehicle->
id()).arg(QCoreApplication::applicationName());
1531 qCDebug(ParameterManagerLog) << errorMsg;
1537QString ParameterManager::_remapParamNameToVersion(
const QString ¶mName)
const
1539 static const QString noRemapPrefix = QStringLiteral(
"noremap.");
1540 if (paramName.startsWith(noRemapPrefix)) {
1541 return paramName.mid(noRemapPrefix.length());
1553 if (!majorVersionRemap.contains(majorVersion)) {
1555 qCDebug(ParameterManagerVerbose1Log) <<
"_remapParamNameToVersion: no major version mapping";
1561 QString mappedParamName = paramName;
1563 if (remapMinorVersion.contains(currentMinorVersion)) {
1565 if (remap.contains(mappedParamName)) {
1566 const QString toParamName = remap[mappedParamName];
1567 qCDebug(ParameterManagerLog) <<
"_remapParamNameToVersion: remapped currentMinor:from:to" << currentMinorVersion << mappedParamName << toParamName;
1568 mappedParamName = toParamName;
1573 return mappedParamName;
1576void ParameterManager::_loadOfflineEditingParams()
1579 if (paramFilename.isEmpty()) {
1583 QFile paramFile(paramFilename);
1584 if (!paramFile.open(QFile::ReadOnly)) {
1585 qCWarning(ParameterManagerLog) <<
"Unable to open offline editing params file" << paramFilename;
1588 QTextStream paramStream(¶mFile);
1589 while (!paramStream.atEnd()) {
1590 const QString
line = paramStream.readLine();
1591 if (
line.startsWith(
"#")) {
1595 const QStringList paramData =
line.split(
"\t");
1596 Q_ASSERT(paramData.count() == 5);
1598 const int offlineDefaultComponentId = paramData.at(1).toInt();
1600 const QString paramName = paramData.at(2);
1601 const QString valStr = paramData.at(3);
1602 const MAV_PARAM_TYPE paramType =
static_cast<MAV_PARAM_TYPE
>(paramData.at(4).toUInt());
1604 QVariant paramValue;
1605 switch (paramType) {
1606 case MAV_PARAM_TYPE_REAL32:
1607 paramValue = QVariant(valStr.toFloat());
1609 case MAV_PARAM_TYPE_UINT32:
1610 paramValue = QVariant(valStr.toUInt());
1612 case MAV_PARAM_TYPE_UINT16:
1613 paramValue = QVariant((quint16)valStr.toUInt());
1615 case MAV_PARAM_TYPE_INT16:
1616 paramValue = QVariant((qint16)valStr.toInt());
1618 case MAV_PARAM_TYPE_UINT8:
1619 paramValue = QVariant((quint8)valStr.toUInt());
1621 case MAV_PARAM_TYPE_INT8:
1622 paramValue = QVariant((qint8)valStr.toUInt());
1625 qCCritical(ParameterManagerLog) <<
"Unknown type" << paramType;
1627 case MAV_PARAM_TYPE_INT32:
1628 paramValue = QVariant(valStr.toInt());
1640 _parametersReady =
true;
1641 _initialLoadComplete =
true;
1642 _debugCacheCRC.clear();
1648 MAV_CMD_PREFLIGHT_STORAGE,
1658 if (sysAutoConfigFact) {
1663QString ParameterManager::_logVehiclePrefix(
int componentId)
const
1665 if (componentId == -1) {
1666 return QStringLiteral(
"V:%1").arg(_vehicle->
id());
1668 return QStringLiteral(
"V:%1 C:%2").arg(_vehicle->
id()).arg(componentId);
1672void ParameterManager::_setLoadProgress(
double loadProgress)
1682 if (_parameterDownloadSkipped != skipped) {
1683 _parameterDownloadSkipped = skipped;
1690 return _paramCountMap.keys();
1695 return _pendingWritesCount > 0;
1698#ifdef QGC_UNITTEST_BUILD
1699void ParameterManager::setPendingWritesForTest(
bool pending)
1701 const bool wasPending = (_pendingWritesCount > 0);
1702 _pendingWritesCount = pending ? 1 : 0;
1703 if (wasPending != pending) {
1715bool ParameterManager::_parseParamFile(
const QString& filename)
1717 constexpr quint16 magic_standard = 0x671B;
1718 constexpr quint16 magic_withdefaults = 0x671C;
1719 quint32 no_of_parameters_found = 0;
1720 constexpr int componentId = MAV_COMP_ID_AUTOPILOT1;
1731 qCDebug(ParameterManagerLog) <<
"_parseParamFile:" << filename;
1732 QFile file(filename);
1733 if (!file.open(QIODevice::ReadOnly)) {
1734 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: Could not open downloaded parameter file.";
1738 QDataStream in(&file);
1739 in.setByteOrder(QDataStream::LittleEndian);
1741 quint16 magic, num_params, total_params;
1746 if (in.status() != QDataStream::Ok) {
1747 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: Could not read Header";
1751 qCDebug(ParameterManagerVerbose2Log) <<
"_parseParamFile: magic: 0x" << Qt::hex << magic;
1752 qCDebug(ParameterManagerVerbose2Log) <<
"_parseParamFile: num_params:" << num_params
1753 <<
"total_params:" << total_params;
1755 if ((magic != magic_standard) && (magic != magic_withdefaults)) {
1756 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: File does not start with Magic";
1759 if (num_params > total_params) {
1760 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: total_params > num_params";
1763 if (num_params != total_params) {
1765 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: total_params != num_params";
1769 while (in.status() == QDataStream::Ok) {
1773 quint8 name_len = 0;
1774 quint8 common_len = 0;
1775 bool withdefault =
false;
1777 char name_buffer[17];
1779 while (
byte == 0x0) {
1781 if (in.status() != QDataStream::Ok) {
1782 if (no_of_parameters_found == num_params) {
1785 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: unexpected EOF"
1786 <<
"number of parameters expected:" << num_params
1787 <<
"actual:" << no_of_parameters_found;
1792 ptype =
byte & 0x0F;
1793 flags = (
byte >> 4) & 0x0F;
1794 withdefault = (flags & 0x01) == 0x01;
1796 if (in.status() != QDataStream::Ok) {
1797 qCCritical(ParameterManagerLog) <<
"_parseParamFile: Error: Unexpected EOF while reading flags";
1800 name_len = ((
byte >> 4) & 0x0F) + 1;
1801 common_len =
byte & 0x0F;
1802 if ((name_len + common_len) > 16) {
1803 qCCritical(ParameterManagerLog) <<
"_parseParamFile: Error: common_len + name_len > 16"
1804 <<
"name_len" << name_len
1805 <<
"common_len" << common_len;
1808 no_read = in.readRawData(&name_buffer[common_len],
static_cast<int>(name_len));
1809 if (no_read != name_len) {
1810 qCCritical(ParameterManagerLog) <<
"_parseParamFile: Error: Unexpected EOF while reading parameterName"
1811 <<
"Expected:" << name_len
1812 <<
"Actual:" << no_read;
1815 name_buffer[common_len + name_len] =
'\0';
1816 const QString parameterName(name_buffer);
1817 qCDebug(ParameterManagerVerbose2Log) <<
"_parseParamFile: parameter" << parameterName
1818 <<
"name_len" << name_len
1819 <<
"common_len" << common_len
1821 <<
"flags" << flags;
1823 QVariant parameterValue = 0;
1824 QVariant defaultValue;
1825 switch (
static_cast<ap_var_type
>(ptype)) {
1832 parameterValue = data8;
1835 defaultValue = data8;
1838 case AP_PARAM_INT16:
1840 parameterValue = data16;
1843 defaultValue = data16;
1846 case AP_PARAM_INT32:
1848 parameterValue = data32;
1851 defaultValue = data32;
1854 case AP_PARAM_FLOAT:
1856 (void) memcpy(&dfloat, &data32, 4);
1857 parameterValue = dfloat;
1861 (void) memcpy(&ddefault, &data32, 4);
1862 defaultValue = ddefault;
1866 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: type is out of range" << ptype;
1870 qCDebug(ParameterManagerVerbose2Log) <<
"paramValue" << parameterValue;
1872 if (++no_of_parameters_found > num_params) {
1873 qCDebug(ParameterManagerLog) <<
"_parseParamFile: Error: more parameters in file than expected."
1874 <<
"Expected:" << num_params
1875 <<
"Actual:" << no_of_parameters_found;
1880 (ptype == AP_PARAM_INT16) ?
FactMetaData::valueTypeInt16 :
1881 (ptype == AP_PARAM_INT32) ?
FactMetaData::valueTypeInt32 :
1884 Fact *fact =
nullptr;
1885 if (_mapCompId2FactMap.contains(componentId) && _mapCompId2FactMap[componentId].contains(parameterName)) {
1886 fact = _mapCompId2FactMap[componentId][parameterName];
1887 if (withdefault && defaultValue.isValid()) {
1894 qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"Adding new fact" << parameterName;
1896 fact =
new Fact(componentId, parameterName, factType,
this);
1900 _mapCompId2FactMap[componentId][parameterName] = fact;
1906 if (withdefault && defaultValue.isValid()) {
1918 _paramCountMap[componentId] = num_params;
1919 _totalParamCount += num_params;
1920 _waitingReadParamIndexMap[componentId] = QMap<int, int>();
1922 _writeLocalParamCache(_vehicle->
id(), componentId);
1924 _checkInitialLoadComplete();
1925 _refreshAllProgressActive =
false;
1926 _setLoadProgress(0.0);
1934void ParameterManager::_incrementPendingWriteCount()
1936 _pendingWritesCount++;
1937 if (_pendingWritesCount == 1) {
1942void ParameterManager::_decrementPendingWriteCount()
1944 if (_pendingWritesCount == 0) {
1945 qCWarning(ParameterManagerLog) <<
"Internal Error: _pendingWriteCount == 0";
1949 _pendingWritesCount--;
1950 if (_pendingWritesCount == 0) {
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct param_union mavlink_param_union_t
std::array< MsgOutKey, 4 > keys
virtual void parametersReadyPreChecks()
FactMetaData * factMetaDataForName(const QString &name, FactMetaData::ValueType_t valueType)
void commandProgress(float value)
void downloadComplete(const QString &file, const QString &errorMsg)
bool download(uint8_t fromCompId, const QString &fromURI, const QString &toDir, const QString &fileName="", bool checksize=true)
A Fact is used to hold a single value within the system.
void setMetaData(FactMetaData *metaData, bool setDefaultFromMetaData=false)
void containerSetRawValue(const QVariant &value)
Value coming from Vehicle. This does NOT send a _containerRawValueChanged signal.
FactMetaData * metaData()
FactMetaData::ValueType_t type() const
void setRawValue(const QVariant &value)
void containerRawValueChanged(const QVariant &value)
This signal is meant for use by Fact container implementations. Used to send changed values to vehicl...
QString rawValueStringFullPrecision() const
Returns the values as a string with full 18 digit precision if float/double.
bool vehicleRebootRequired() const
QVariant rawValue() const
QMap< int, remapParamNameMap_t > remapParamNameMinorVersionRemapMap_t
virtual int remapParamNameHigestMinorVersionNumber(int) const
QMap< QString, QString > remapParamNameMap_t
virtual QString offlineEditingParamFile(Vehicle *) const
Return the resource file which contains the set of params loaded for offline editing.
QMap< int, remapParamNameMinorVersionRemapMap_t > remapParamNameMajorVersionMap_t
virtual const remapParamNameMajorVersionMap_t & paramNameRemapMajorVersionMap() const
static int getComponentId()
static MAVLinkProtocol * instance()
static MultiVehicleManager * instance()
bool parameterExists(int componentId, const QString ¶mName) const
void parameterDownloadSkippedChanged()
void mavlinkMessageReceived(const mavlink_message_t &message)
Fact * getParameter(int componentId, const QString ¶mName)
void factAdded(int componentId, Fact *fact)
static FactMetaData::ValueType_t mavTypeToFactType(MAV_PARAM_TYPE mavType)
void setParameterDownloadSkipped(bool skipped)
QList< int > componentIds() const
void refreshParametersPrefix(int componentId, const QString &namePrefix)
Request a refresh on all parameters that begin with the specified prefix.
void initialParametersRequestFailed()
Vehicle never responded to PARAM_REQUEST_LIST, all retries exhausted.
void parametersReadyChanged(bool parametersReady)
double loadProgress() const
void bulkRefresh(int componentId, const QStringList &names, bool notifyFailure=true)
void tryHashCheckCacheLoad()
bool pendingWrites() const
static QDir parameterCacheDir()
void missingParametersChanged(bool missingParameters)
static constexpr int defaultComponentId
void resetAllParametersToDefaults()
QStringList parameterNames(int componentId) const
Returns all parameter names.
void _paramRequestReadFailure(int componentId, const QString ¶mName, int paramIndex)
void _paramRequestReadSuccess(int componentId, const QString ¶mName, int paramIndex)
static constexpr int kIndexBatchMaxOutstanding
Maximum index re-reads in flight at once.
static constexpr int kParamSetRetryCount
Number of retries for PARAM_SET.
void _paramSetSuccess(int componentId, const QString ¶mName)
static constexpr int kUnresponsiveMinOutstanding
Minimum in-flight re-reads to a component before a silent cycle counts against it.
void loadProgressChanged(float value)
void cacheCheckOnlyFailed()
void refreshParameter(int componentId, const QString ¶mName)
Request a refresh on the specific parameter.
void pendingWritesChanged(bool pendingWrites)
static MAV_PARAM_TYPE factTypeToMavType(FactMetaData::ValueType_t factType)
static constexpr int kParamRequestReadRetryCount
Number of retries for PARAM_REQUEST_READ.
void resetAllToVehicleConfiguration()
Q_INVOKABLE void refreshAllParameters()
static QString parameterCacheFile(int vehicleId, int componentId)
void writeParametersToStream(QTextStream &stream) const
static constexpr int kUnresponsiveSilentCycles
Consecutive silent cycles before a non-default component is given up on.
void _paramSetFailure(int componentId, const QString ¶mName)
Final state for a QGCStateMachine with logging support.
static VehicleClass_t vehicleClass(MAV_TYPE mavType)
static const char * vehicleClassToCanonicalString(VehicleClass_t vehicleClass)
static FirmwareClass_t firmwareClass(MAV_AUTOPILOT autopilot)
static const char * firmwareClassToCanonicalString(FirmwareClass_t firmwareClass)
QGroundControl specific state machine with enhanced error handling.
QSignalTransition * addThisTransition(PointerToMemberFunction signal, QAbstractState *target)
Simpler version of QState::addTransition which assumes the sender is this.
Sends the specified MAVLink message to the vehicle.
WeakLinkInterfacePtr primaryLink() const
void sendMavCommand(int compId, 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)
QString firmwareVersionTypeString() const
MAV_TYPE vehicleType() const
VehicleLinkManager * vehicleLinkManager()
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
ComponentInformationManager * compInfoManager()
int firmwareMinorVersion() const
MAV_AUTOPILOT firmwareType() const
void setRebootRequired()
Latches rebootRequired until the vehicle reboots.
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
int defaultComponentId() const
bool genericFirmware() const
AutoPilotPlugin * autopilotPlugin()
Provides access to AutoPilotPlugin for this vehicle.
void setOfflineEditingDefaultComponentId(int defaultComponentId)
Sets the default component id for an offline editing vehicle.
int firmwarePatchVersion() const
FTPManager * ftpManager()
int firmwareMajorVersion() const
Waits for either PARAM_VALUE (success) or PARAM_ERROR (rejection) from the vehicle.
QByteArray line(std::string_view command)
command as one CR/LF-terminated line.
constexpr std::chrono::microseconds remaining(uint64_t timestampUs, uint64_t nowUs, std::chrono::microseconds lifetime)
Time left before a receipt reaches lifetime; zero for a missing, future or expired one.
std::optional< T > number(std::string_view field, int base=DECIMAL_BASE)
quint32 crc32(const quint8 *src, unsigned len, unsigned state)
bool fuzzyCompare(double value1, double value2)
Returns true if the two values are equal or close. Correctly handles 0 and NaN values.
void showRebootAppMessage(const QString &message, const QString &title)
Modal reboot-required message. Debounced within 2 minutes.
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
static const int versionNotSetValue