Newer
Older
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
#include "QGCApplication.h"
Nate Weibley
committed
#include <QEasingCurve>
#include <QFile>
#include <QDebug>
Nate Weibley
committed
#include <QVariantAnimation>
QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "ParameterManagerVerbose1Log")
QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "ParameterManagerVerbose2Log")
QGC_LOGGING_CATEGORY(ParameterManagerDebugCacheFailureLog, "ParameterManagerDebugCacheFailureLog") // Turn on to debug parameter cache crc misses
olliw42
committed
const QHash<int, QString> _mavlinkCompIdHash {
{ MAV_COMP_ID_CAMERA, "Camera1" },
{ MAV_COMP_ID_CAMERA2, "Camera2" },
{ MAV_COMP_ID_CAMERA3, "Camera3" },
{ MAV_COMP_ID_CAMERA4, "Camera4" },
{ MAV_COMP_ID_CAMERA5, "Camera5" },
{ MAV_COMP_ID_CAMERA6, "Camera6" },
{ MAV_COMP_ID_SERVO1, "Servo1" },
{ MAV_COMP_ID_SERVO2, "Servo2" },
{ MAV_COMP_ID_SERVO3, "Servo3" },
{ MAV_COMP_ID_SERVO4, "Servo4" },
{ MAV_COMP_ID_SERVO5, "Servo5" },
{ MAV_COMP_ID_SERVO6, "Servo6" },
{ MAV_COMP_ID_SERVO7, "Servo7" },
{ MAV_COMP_ID_SERVO8, "Servo8" },
{ MAV_COMP_ID_SERVO9, "Servo9" },
{ MAV_COMP_ID_SERVO10, "Servo10" },
{ MAV_COMP_ID_SERVO11, "Servo11" },
{ MAV_COMP_ID_SERVO12, "Servo12" },
{ MAV_COMP_ID_SERVO13, "Servo13" },
{ MAV_COMP_ID_SERVO14, "Servo14" },
{ MAV_COMP_ID_GIMBAL, "Gimbal1" },
{ MAV_COMP_ID_ADSB, "ADSB" },
{ MAV_COMP_ID_OSD, "OSD" },
{ MAV_COMP_ID_FLARM, "FLARM" },
{ MAV_COMP_ID_GIMBAL2, "Gimbal2" },
{ MAV_COMP_ID_GIMBAL3, "Gimbal3" },
{ MAV_COMP_ID_GIMBAL4, "Gimbal4" },
{ MAV_COMP_ID_GIMBAL5, "Gimbal5" },
{ MAV_COMP_ID_GIMBAL6, "Gimbal6" },
olliw42
committed
{ MAV_COMP_ID_IMU, "IMU1" },
{ MAV_COMP_ID_IMU_2, "IMU2" },
{ MAV_COMP_ID_IMU_3, "IMU3" },
{ MAV_COMP_ID_GPS, "GPS1" },
{ MAV_COMP_ID_GPS2, "GPS2" }
};
const char* ParameterManager::_jsonParametersKey = "parameters";
const char* ParameterManager::_jsonCompIdKey = "compId";
const char* ParameterManager::_jsonParamNameKey = "name";
const char* ParameterManager::_jsonParamValueKey = "value";
ParameterManager::ParameterManager(Vehicle* vehicle)
: QObject (vehicle)
, _vehicle (vehicle)
, _loadProgress (0.0)
, _parametersReady (false)
, _missingParameters (false)
, _initialLoadComplete (false)
, _waitingForDefaultComponent (false)
, _saveRequired (false)
, _metaDataAddedToFacts (false)
, _logReplay (vehicle->vehicleLinkManager()->primaryLink() && vehicle->vehicleLinkManager()->primaryLink()->isLogReplay())
, _prevWaitingReadParamIndexCount (0)
, _prevWaitingReadParamNameCount (0)
, _prevWaitingWriteParamNameCount (0)
, _initialRequestRetryCount (0)
, _disableAllRetries (false)
, _indexBatchQueueActive (false)
, _totalParamCount (0)
if (_vehicle->isOfflineEditingVehicle()) {
_loadOfflineEditingParams();
return;
}
_mavlink = qgcApp()->toolbox()->mavlinkProtocol();
_initialRequestTimeoutTimer.setSingleShot(true);
_initialRequestTimeoutTimer.setInterval(5000);
connect(&_initialRequestTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_initialRequestTimeout);
_waitingParamTimeoutTimer.setSingleShot(true);
_waitingParamTimeoutTimer.setInterval(3000);
connect(&_waitingParamTimeoutTimer, &QTimer::timeout, this, &ParameterManager::_waitingParamTimeout);
// Ensure the cache directory exists
QFileInfo(QSettings().fileName()).dir().mkdir("ParamCache");
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
void ParameterManager::_updateProgressBar(void)
{
int waitingReadParamIndexCount = 0;
int waitingReadParamNameCount = 0;
int waitingWriteParamCount = 0;
for (int compId: _waitingReadParamIndexMap.keys()) {
waitingReadParamIndexCount += _waitingReadParamIndexMap[compId].count();
}
for(int compId: _waitingReadParamNameMap.keys()) {
waitingReadParamNameCount += _waitingReadParamNameMap[compId].count();
}
for(int compId: _waitingWriteParamNameMap.keys()) {
waitingWriteParamCount += _waitingWriteParamNameMap[compId].count();
}
if (waitingReadParamIndexCount == 0) {
if (_readParamIndexProgressActive) {
_readParamIndexProgressActive = false;
_setLoadProgress(0.0);
return;
}
} else {
_readParamIndexProgressActive = true;
_setLoadProgress((double)(_totalParamCount - waitingReadParamIndexCount) / (double)_totalParamCount);
return;
}
if (waitingWriteParamCount == 0) {
if (_writeParamProgressActive) {
_writeParamProgressActive = false;
_waitingWriteParamBatchCount = 0;
_setLoadProgress(0.0);
emit pendingWritesChanged(false);
return;
}
} else {
_writeParamProgressActive = true;
_setLoadProgress((double)(qMax(_waitingWriteParamBatchCount - waitingWriteParamCount, 1)) / (double)(_waitingWriteParamBatchCount + 1));
emit pendingWritesChanged(true);
return;
}
if (waitingReadParamNameCount == 0) {
if (_readParamNameProgressActive) {
_readParamNameProgressActive = false;
_waitingReadParamNameBatchCount = 0;
_setLoadProgress(0.0);
return;
}
} else {
_readParamNameProgressActive = true;
_setLoadProgress((double)(qMax(_waitingReadParamNameBatchCount - waitingReadParamNameCount, 1)) / (double)(_waitingReadParamNameBatchCount + 1));
return;
}
}
void ParameterManager::mavlinkMessageReceived(mavlink_message_t message)
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
if (message.msgid == MAVLINK_MSG_ID_PARAM_VALUE) {
mavlink_param_value_t param_value;
mavlink_msg_param_value_decode(&message, ¶m_value);
// This will null terminate the name string
QByteArray bytes(param_value.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
QString parameterName(bytes);
mavlink_param_union_t paramUnion;
paramUnion.param_float = param_value.param_value;
paramUnion.type = param_value.param_type;
QVariant parameterValue;
switch (paramUnion.type) {
case MAV_PARAM_TYPE_REAL32:
parameterValue = QVariant(paramUnion.param_float);
break;
case MAV_PARAM_TYPE_UINT8:
parameterValue = QVariant(paramUnion.param_uint8);
break;
case MAV_PARAM_TYPE_INT8:
parameterValue = QVariant(paramUnion.param_int8);
break;
case MAV_PARAM_TYPE_UINT16:
parameterValue = QVariant(paramUnion.param_uint16);
break;
case MAV_PARAM_TYPE_INT16:
parameterValue = QVariant(paramUnion.param_int16);
break;
case MAV_PARAM_TYPE_UINT32:
parameterValue = QVariant(paramUnion.param_uint32);
break;
case MAV_PARAM_TYPE_INT32:
parameterValue = QVariant(paramUnion.param_int32);
break;
default:
qCritical() << "ParameterManager::_handleParamValue - unsupported MAV_PARAM_TYPE" << paramUnion.type;
break;
}
_handleParamValue(message.compid, parameterName, param_value.param_count, param_value.param_index, static_cast<MAV_PARAM_TYPE>(param_value.param_type), parameterValue);
}
/// Called whenever a parameter is updated or first seen.
void ParameterManager::_handleParamValue(int componentId, QString parameterName, int parameterCount, int parameterIndex, MAV_PARAM_TYPE mavParamType, QVariant parameterValue)
{
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) <<
"_parameterUpdate" <<
"name:" << parameterName <<
"count:" << parameterCount <<
"index:" << parameterIndex <<
"mavType:" << mavParamType <<
"value:" << parameterValue <<
// ArduPilot has this strange behavior of streaming parameters that we didn't ask for. This even happens before it responds to the
// PARAM_REQUEST_LIST. We disregard any of this until the initial request is responded to.
if (parameterIndex == 65535 && parameterName != "_HASH_CHECK" && _initialRequestTimeoutTimer.isActive()) {
qCDebug(ParameterManagerVerbose1Log) << "Disregarding unrequested param prior to initial list response" << parameterName;
if (!_initialLoadComplete && !_indexBatchQueueActive) {
// Handy for testing retry logic
static int counter = 0;
if (counter++ & 0x8) {
qCDebug(ParameterManagerLog) << "Artificial discard" << counter;
return;
}
#if 0
// Use this to test missing default component id
if (componentId == 50) {
return;
}
#endif
if (_vehicle->px4Firmware() && parameterName == "_HASH_CHECK") {
if (!_initialLoadComplete && !_logReplay) {
/* we received a cache hash, potentially load from cache */
_tryCacheHashLoad(_vehicle->id(), componentId, parameterValue);
// Used to debug cache crc misses (turn on ParameterManagerDebugCacheFailureLog)
if (!_initialLoadComplete && !_logReplay && _debugCacheCRC.contains(componentId) && _debugCacheCRC[componentId]) {
if (_debugCacheMap[componentId].contains(parameterName)) {
const ParamTypeVal& cacheParamTypeVal = _debugCacheMap[componentId][parameterName];
size_t dataSize = FactMetaData::typeToSize(static_cast<FactMetaData::ValueType_t>(cacheParamTypeVal.first));
const void* cacheData = cacheParamTypeVal.second.constData();
const void* vehicleData = parameterValue.constData();
if (memcmp(cacheData, vehicleData, dataSize)) {
qDebug() << "Cache/Vehicle values differ for name:cache:actual" << parameterName << parameterValue << cacheParamTypeVal.second;
}
_debugCacheParamSeen[componentId][parameterName] = true;
} else {
qDebug() << "Parameter missing from cache" << parameterName;
}
}
_waitingParamTimeoutTimer.stop();
// Update our total parameter counts
if (!_paramCountMap.contains(componentId)) {
_paramCountMap[componentId] = parameterCount;
_totalParamCount += parameterCount;
}
// If we've never seen this component id before, setup the index wait lists.
if (!_waitingReadParamIndexMap.contains(componentId)) {
// Add all indices to the wait list, parameter index is 0-based
for (int waitingIndex=0; waitingIndex<parameterCount; waitingIndex++) {
// This will add the new component id, as well as the the new waiting index and set the retry count for that index to 0
_waitingReadParamIndexMap[componentId][waitingIndex] = 0;
// The read and write waiting lists for this component are initialized the empty
_waitingReadParamNameMap[componentId] = QMap<QString, int>();
_waitingWriteParamNameMap[componentId] = QMap<QString, int>();
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Seeing component for first time - paramcount:" << parameterCount;
if (!_waitingReadParamIndexMap[componentId].contains(parameterIndex) &&
!_waitingReadParamNameMap[componentId].contains(parameterName) &&
!_waitingWriteParamNameMap[componentId].contains(parameterName)) {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "Unrequested param update" << parameterName;
// Remove this parameter from the waiting lists
if (_waitingReadParamIndexMap[componentId].contains(parameterIndex)) {
_waitingReadParamIndexMap[componentId].remove(parameterIndex);
_indexBatchQueue.removeOne(parameterIndex);
_fillIndexBatchQueue(false /* waitingParamTimeout */);
}
_waitingReadParamNameMap[componentId].remove(parameterName);
_waitingWriteParamNameMap[componentId].remove(parameterName);
if (_waitingReadParamIndexMap[componentId].count()) {
qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamIndexMap:" << _waitingReadParamIndexMap[componentId];
}
if (_waitingReadParamNameMap[componentId].count()) {
qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingReadParamNameMap" << _waitingReadParamNameMap[componentId];
}
if (_waitingWriteParamNameMap[componentId].count()) {
qCDebug(ParameterManagerVerbose2Log) << _logVehiclePrefix(componentId) << "_waitingWriteParamNameMap" << _waitingWriteParamNameMap[componentId];
}
// Track how many parameters we are still waiting for
int waitingReadParamIndexCount = 0;
int waitingReadParamNameCount = 0;
int waitingWriteParamNameCount = 0;
for(int waitingComponentId: _waitingReadParamIndexMap.keys()) {
waitingReadParamIndexCount += _waitingReadParamIndexMap[waitingComponentId].count();
}
if (waitingReadParamIndexCount) {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamIndexCount:" << waitingReadParamIndexCount;
for(int waitingComponentId: _waitingReadParamNameMap.keys()) {
waitingReadParamNameCount += _waitingReadParamNameMap[waitingComponentId].count();
}
if (waitingReadParamNameCount) {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingReadParamNameCount:" << waitingReadParamNameCount;
for(int waitingComponentId: _waitingWriteParamNameMap.keys()) {
waitingWriteParamNameCount += _waitingWriteParamNameMap[waitingComponentId].count();
}
if (waitingWriteParamNameCount) {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "waitingWriteParamNameCount:" << waitingWriteParamNameCount;
int readWaitingParamCount = waitingReadParamIndexCount + waitingReadParamNameCount;
int totalWaitingParamCount = readWaitingParamCount + waitingWriteParamNameCount;
if (totalWaitingParamCount) {
// More params to wait for, restart timer
_waitingParamTimeoutTimer.start();
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(-1) << "Restarting _waitingParamTimeoutTimer: totalWaitingParamCount:" << totalWaitingParamCount;
if (!_mapCompId2FactMap.contains(_vehicle->defaultComponentId())) {
// Still waiting for parameters from default component
qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Restarting _waitingParamTimeoutTimer (still waiting for default component params)";
_waitingParamTimeoutTimer.start();
} else {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(-1) << "Not restarting _waitingParamTimeoutTimer (all requests satisfied)";
Fact* fact = nullptr;
if (_mapCompId2FactMap.contains(componentId) && _mapCompId2FactMap[componentId].contains(parameterName)) {
fact = _mapCompId2FactMap[componentId][parameterName];
} else {
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "Adding new fact" << parameterName;
fact = new Fact(componentId, parameterName, mavTypeToFactType(mavParamType), this);
FactMetaData* factMetaData = _vehicle->compInfoManager()->compInfoParam(componentId)->factMetaDataForName(parameterName, fact->type());
fact->setMetaData(factMetaData);
_mapCompId2FactMap[componentId][parameterName] = fact;
// We need to know when the fact value changes so we can update the vehicle
connect(fact, &Fact::_containerRawValueChanged, this, &ParameterManager::_factRawValueUpdated);
emit factAdded(componentId, fact);
fact->_containerSetRawValue(parameterValue);
// Update param cache. The param cache is only used on PX4 Firmware since ArduPilot and Solo have volatile params
// which invalidate the cache. The Solo also streams param updates in flight for things like gimbal values
// which in turn causes a perf problem with all the param cache updates.
if (_prevWaitingReadParamIndexCount + _prevWaitingReadParamNameCount != 0 && readWaitingParamCount == 0) {
// All reads just finished, update the cache
_writeLocalParamCache(_vehicle->id(), componentId);
}
}
_prevWaitingReadParamIndexCount = waitingReadParamIndexCount;
_prevWaitingReadParamNameCount = waitingReadParamNameCount;
_prevWaitingWriteParamNameCount = waitingWriteParamNameCount;
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "_parameterUpdate complete";
/// Writes the parameter update to mavlink, sets up for write wait
void ParameterManager::_factRawValueUpdateWorker(int componentId, const QString& name, FactMetaData::ValueType_t valueType, const QVariant& rawValue)
if (_waitingWriteParamNameMap[componentId].contains(name)) {
_waitingWriteParamNameMap[componentId].remove(name);
} else {
_waitingWriteParamBatchCount++;
}
_waitingWriteParamNameMap[componentId][name] = 0; // Add new entry and set retry count
_updateProgressBar();
_waitingParamTimeoutTimer.start();
_saveRequired = true;
} else {
qWarning() << "Internal error ParameterManager::_factValueUpdateWorker: component id not found" << componentId;
_sendParamSetToVehicle(componentId, name, valueType, rawValue);
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Update parameter (_waitingParamTimeoutTimer started) - compId:name:rawValue" << componentId << name << rawValue;
}
void ParameterManager::_factRawValueUpdated(const QVariant& rawValue)
{
Fact* fact = qobject_cast<Fact*>(sender());
if (!fact) {
qWarning() << "Internal error";
return;
}
_factRawValueUpdateWorker(fact->componentId(), fact->name(), fact->type(), rawValue);
void ParameterManager::refreshAllParameters(uint8_t componentId)
if (!_vehicle->vehicleLinkManager()->primaryLink()) {
return;
}
if (_vehicle->vehicleLinkManager()->primaryLink()->linkConfiguration()->isHighLatency() || _logReplay) {
// These links don't load params
_parametersReady = true;
_missingParameters = true;
_initialLoadComplete = true;
_waitingForDefaultComponent = false;
emit parametersReadyChanged(_parametersReady);
emit missingParametersChanged(_missingParameters);
if (!_initialLoadComplete) {
_initialRequestTimeoutTimer.start();
}
for (int cid: _paramCountMap.keys()) {
// Add/Update all indices to the wait list, parameter index is 0-based
if(componentId != MAV_COMP_ID_ALL && componentId != cid)
continue;
for (int waitingIndex = 0; waitingIndex < _paramCountMap[cid]; waitingIndex++) {
// This will add a new waiting index if needed and set the retry count for that index to 0
MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol();
mavlink_msg_param_request_list_pack_chan(mavlink->getSystemId(),
mavlink->getComponentId(),
_vehicle->vehicleLinkManager()->primaryLink()->mavlinkChannel(),
&msg,
_vehicle->id(),
_vehicle->sendMessageOnLinkThreadSafe(_vehicle->vehicleLinkManager()->primaryLink(), msg);
QString what = (componentId == MAV_COMP_ID_ALL) ? "MAV_COMP_ID_ALL" : QString::number(componentId);
qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Request to refresh all parameters for component ID:" << what;
}
/// Translates FactSystem::defaultComponentId to real component id if needed
int ParameterManager::_actualComponentId(int componentId)
{
if (componentId == FactSystem::defaultComponentId) {
componentId = _vehicle->defaultComponentId();
return componentId;
}
olliw42
committed
void ParameterManager::refreshParameter(int componentId, const QString& paramName)
componentId = _actualComponentId(componentId);
olliw42
committed
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParameter - name:" << paramName << ")";
if (_waitingReadParamNameMap.contains(componentId)) {
olliw42
committed
QString mappedParamName = _remapParamNameToVersion(paramName);
if (_waitingReadParamNameMap[componentId].contains(mappedParamName)) {
_waitingReadParamNameMap[componentId].remove(mappedParamName);
} else {
_waitingReadParamNameBatchCount++;
}
_waitingReadParamNameMap[componentId][mappedParamName] = 0; // Add new wait entry and update retry count
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "restarting _waitingParamTimeout";
_waitingParamTimeoutTimer.start();
olliw42
committed
_readParameterRaw(componentId, paramName, -1);
void ParameterManager::refreshParametersPrefix(int componentId, const QString& namePrefix)
{
componentId = _actualComponentId(componentId);
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "refreshParametersPrefix - name:" << namePrefix << ")";
for (const QString ¶mName: _mapCompId2FactMap[componentId].keys()) {
olliw42
committed
if (paramName.startsWith(namePrefix)) {
refreshParameter(componentId, paramName);
olliw42
committed
bool ParameterManager::parameterExists(int componentId, const QString& paramName)
componentId = _actualComponentId(componentId);
if (_mapCompId2FactMap.contains(componentId)) {
ret = _mapCompId2FactMap[componentId].contains(_remapParamNameToVersion(paramName));
olliw42
committed
Fact* ParameterManager::getParameter(int componentId, const QString& paramName)
{
componentId = _actualComponentId(componentId);
olliw42
committed
QString mappedParamName = _remapParamNameToVersion(paramName);
if (!_mapCompId2FactMap.contains(componentId) || !_mapCompId2FactMap[componentId].contains(mappedParamName)) {
qgcApp()->reportMissingParameter(componentId, mappedParamName);
return _mapCompId2FactMap[componentId][mappedParamName];
QStringList ParameterManager::parameterNames(int componentId)
for(const QString ¶mName: _mapCompId2FactMap[_actualComponentId(componentId)].keys()) {
/// Requests missing index based parameters from the vehicle.
/// @param waitingParamTimeout: true: being called due to timeout, false: being called to re-fill the batch queue
/// return true: Parameters were requested, false: No more requests needed
bool ParameterManager::_fillIndexBatchQueue(bool waitingParamTimeout)
if (!_indexBatchQueueActive) {
return false;
}
if (waitingParamTimeout) {
// We timed out, clear the queue and try again
qCDebug(ParameterManagerLog) << "Refilling index based batch queue due to timeout";
_indexBatchQueue.clear();
} else {
qCDebug(ParameterManagerLog) << "Refilling index based batch queue due to received parameter";
}
for(int componentId: _waitingReadParamIndexMap.keys()) {
if (_waitingReadParamIndexMap[componentId].count()) {
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "_waitingReadParamIndexMap count" << _waitingReadParamIndexMap[componentId].count();
qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix(componentId) << "_waitingReadParamIndexMap" << _waitingReadParamIndexMap[componentId];
for(int paramIndex: _waitingReadParamIndexMap[componentId].keys()) {
if (_indexBatchQueue.contains(paramIndex)) {
// Don't add more than once
continue;
}
if (_indexBatchQueue.count() > maxBatchSize) {
break;
_waitingReadParamIndexMap[componentId][paramIndex]++; // Bump retry count
if (_disableAllRetries || _waitingReadParamIndexMap[componentId][paramIndex] > _maxInitialLoadRetrySingleParam) {
// Give up on this index
_failedReadParamIndexMap[componentId] << paramIndex;
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Giving up on (paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")";
_waitingReadParamIndexMap[componentId].remove(paramIndex);
} else {
// Retry again
_indexBatchQueue.append(paramIndex);
_readParameterRaw(componentId, "", paramIndex);
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")";
return _indexBatchQueue.count() != 0;
}
void ParameterManager::_waitingParamTimeout(void)
{
bool paramsRequested = false;
const int maxBatchSize = 10;
int batchCount = 0;
qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "_waitingParamTimeout";
// Now that we have timed out for possibly the first time we can activate the index batch queue
_indexBatchQueueActive = true;
// First check for any missing parameters from the initial index based load
paramsRequested = _fillIndexBatchQueue(true /* waitingParamTimeout */);
if (!paramsRequested && !_waitingForDefaultComponent && !_mapCompId2FactMap.contains(_vehicle->defaultComponentId())) {
// Initial load is complete but we still don't have any default component params. Wait one more cycle to see if the
// any show up.
qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Restarting _waitingParamTimeoutTimer - still don't have default component params" << _vehicle->defaultComponentId();
_waitingParamTimeoutTimer.start();
_waitingForDefaultComponent = true;
return;
}
_waitingForDefaultComponent = false;
for(int componentId: _waitingWriteParamNameMap.keys()) {
for(const QString ¶mName: _waitingWriteParamNameMap[componentId].keys()) {
_waitingWriteParamNameMap[componentId][paramName]++; // Bump retry count
if (_waitingWriteParamNameMap[componentId][paramName] <= _maxReadWriteRetry) {
Fact* fact = getParameter(componentId, paramName);
_sendParamSetToVehicle(componentId, paramName, fact->type(), fact->rawValue());
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Write resend for (paramName:" << paramName << "retryCount:" << _waitingWriteParamNameMap[componentId][paramName] << ")";
goto Out;
}
} else {
// Exceeded max retry count, notify user
_waitingWriteParamNameMap[componentId].remove(paramName);
QString errorMsg = tr("Parameter write failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName);
qCDebug(ParameterManagerLog) << errorMsg;
for(int componentId: _waitingReadParamNameMap.keys()) {
for(const QString ¶mName: _waitingReadParamNameMap[componentId].keys()) {
_waitingReadParamNameMap[componentId][paramName]++; // Bump retry count
if (_waitingReadParamNameMap[componentId][paramName] <= _maxReadWriteRetry) {
_readParameterRaw(componentId, paramName, -1);
qCDebug(ParameterManagerLog) << _logVehiclePrefix(componentId) << "Read re-request for (paramName:" << paramName << "retryCount:" << _waitingReadParamNameMap[componentId][paramName] << ")";
goto Out;
}
} else {
// Exceeded max retry count, notify user
_waitingReadParamNameMap[componentId].remove(paramName);
QString errorMsg = tr("Parameter read failed: veh:%1 comp:%2 param:%3").arg(_vehicle->id()).arg(componentId).arg(paramName);
qCDebug(ParameterManagerLog) << errorMsg;
qCDebug(ParameterManagerLog) << _logVehiclePrefix(-1) << "Restarting _waitingParamTimeoutTimer - re-request";
_waitingParamTimeoutTimer.start();
}
}
void ParameterManager::_readParameterRaw(int componentId, const QString& paramName, int paramIndex)
{
mavlink_message_t msg;
char fixedParamName[MAVLINK_MSG_PARAM_REQUEST_READ_FIELD_PARAM_ID_LEN];
strncpy(fixedParamName, paramName.toStdString().c_str(), sizeof(fixedParamName));
mavlink_msg_param_request_read_pack_chan(_mavlink->getSystemId(), // QGC system id
_mavlink->getComponentId(), // QGC component id
_vehicle->vehicleLinkManager()->primaryLink()->mavlinkChannel(),
&msg, // Pack into this mavlink_message_t
_vehicle->id(), // Target system id
componentId, // Target component id
fixedParamName, // Named parameter being requested
paramIndex); // Parameter index being requested, -1 for named
_vehicle->sendMessageOnLinkThreadSafe(_vehicle->vehicleLinkManager()->primaryLink(), msg);
void ParameterManager::_sendParamSetToVehicle(int componentId, const QString& paramName, FactMetaData::ValueType_t valueType, const QVariant& value)
{
mavlink_param_set_t p;
mavlink_param_union_t union_value;
p.param_type = factTypeToMavType(valueType);
switch (valueType) {
case FactMetaData::valueTypeUint8:
union_value.param_uint8 = (uint8_t)value.toUInt();
break;
case FactMetaData::valueTypeInt8:
union_value.param_int8 = (int8_t)value.toInt();
break;
case FactMetaData::valueTypeUint16:
union_value.param_uint16 = (uint16_t)value.toUInt();
break;
case FactMetaData::valueTypeInt16:
union_value.param_int16 = (int16_t)value.toInt();
break;
case FactMetaData::valueTypeUint32:
union_value.param_uint32 = (uint32_t)value.toUInt();
break;
case FactMetaData::valueTypeFloat:
union_value.param_float = value.toFloat();
break;
qCritical() << "Unsupported fact falue type" << valueType;
case FactMetaData::valueTypeInt32:
union_value.param_int32 = (int32_t)value.toInt();
break;
p.param_value = union_value.param_float;
p.target_system = (uint8_t)_vehicle->id();
p.target_component = (uint8_t)componentId;
strncpy(p.param_id, paramName.toStdString().c_str(), sizeof(p.param_id));
mavlink_msg_param_set_encode_chan(_mavlink->getSystemId(),
_mavlink->getComponentId(),
_vehicle->vehicleLinkManager()->primaryLink()->mavlinkChannel(),
_vehicle->sendMessageOnLinkThreadSafe(_vehicle->vehicleLinkManager()->primaryLink(), msg);
void ParameterManager::_writeLocalParamCache(int vehicleId, int componentId)
CacheMapName2ParamTypeVal cacheMap;
for (const QString& paramName: _mapCompId2FactMap[componentId].keys()) {
const Fact *fact = _mapCompId2FactMap[componentId][paramName];
olliw42
committed
cacheMap[paramName] = ParamTypeVal(fact->type(), fact->rawValue());
}
QFile cacheFile(parameterCacheFile(vehicleId, componentId));
cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
QDataStream ds(&cacheFile);
ds << cacheMap;
}
QDir ParameterManager::parameterCacheDir()
{
const QString spath(QFileInfo(QSettings().fileName()).dir().absolutePath());
return spath + QDir::separator() + "ParamCache";
}
QString ParameterManager::parameterCacheFile(int vehicleId, int componentId)
return parameterCacheDir().filePath(QString("%1_%2.v2").arg(vehicleId).arg(componentId));
void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, QVariant hash_value)
qCInfo(ParameterManagerLog) << "Attemping load from cache";
uint32_t crc32_value = 0;
/* The datastructure of the cache table */
CacheMapName2ParamTypeVal cacheMap;
QFile cacheFile(parameterCacheFile(vehicleId, componentId));
if (!cacheFile.exists()) {
/* no local cache, just wait for them to come in*/
return;
}
cacheFile.open(QIODevice::ReadOnly);
/* Deserialize the parameter cache table */
QDataStream ds(&cacheFile);
ds >> cacheMap;
/* compute the crc of the local cache to check against the remote */
for (const QString& name: cacheMap.keys()) {
const ParamTypeVal& paramTypeVal = cacheMap[name];
const FactMetaData::ValueType_t fact_type = static_cast<FactMetaData::ValueType_t>(paramTypeVal.first);
if (_vehicle->compInfoManager()->compInfoParam(MAV_COMP_ID_AUTOPILOT1)->factMetaDataForName(name, fact_type)->volatileValue()) {
// Does not take part in CRC
qCDebug(ParameterManagerLog) << "Volatile parameter" << name;
} else {
const void *vdat = paramTypeVal.second.constData();
const FactMetaData::ValueType_t fact_type = static_cast<FactMetaData::ValueType_t>(paramTypeVal.first);
crc32_value = QGC::crc32((const uint8_t *)qPrintable(name), name.length(), crc32_value);
crc32_value = QGC::crc32((const uint8_t *)vdat, FactMetaData::typeToSize(fact_type), crc32_value);
}
}
/* if the two param set hashes match, just load from the disk */
if (crc32_value == hash_value.toUInt()) {
qCInfo(ParameterManagerLog) << "Parameters loaded from cache" << qPrintable(QFileInfo(cacheFile).absoluteFilePath());
int count = cacheMap.count();
int index = 0;
for (const QString& name: cacheMap.keys()) {
const ParamTypeVal& paramTypeVal = cacheMap[name];
const FactMetaData::ValueType_t fact_type = static_cast<FactMetaData::ValueType_t>(paramTypeVal.first);
const MAV_PARAM_TYPE mavParamType = factTypeToMavType(fact_type);
_handleParamValue(componentId, name, count, index++, mavParamType, paramTypeVal.second);
// Return the hash value to notify we don't want any more updates
mavlink_param_set_t p;
mavlink_param_union_t union_value;
p.param_type = MAV_PARAM_TYPE_UINT32;
strncpy(p.param_id, "_HASH_CHECK", sizeof(p.param_id));
union_value.param_uint32 = crc32_value;
p.param_value = union_value.param_float;
p.target_system = (uint8_t)_vehicle->id();
p.target_component = (uint8_t)componentId;
mavlink_message_t msg;
mavlink_msg_param_set_encode_chan(_mavlink->getSystemId(),
_mavlink->getComponentId(),
_vehicle->vehicleLinkManager()->primaryLink()->mavlinkChannel(),
_vehicle->sendMessageOnLinkThreadSafe(_vehicle->vehicleLinkManager()->primaryLink(), msg);
Nate Weibley
committed
// Give the user some feedback things loaded properly
QVariantAnimation *ani = new QVariantAnimation(this);
ani->setEasingCurve(QEasingCurve::OutCubic);
ani->setStartValue(0.0);
ani->setEndValue(1.0);
ani->setDuration(750);
connect(ani, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
Nate Weibley
committed
});
// Hide 500ms after animation finishes
connect(ani, &QVariantAnimation::finished, this, [this] {
QTimer::singleShot(500, [this] {
Nate Weibley
committed
});
});
ani->start(QAbstractAnimation::DeleteWhenStopped);
qCInfo(ParameterManagerLog) << "Parameters cache match failed" << qPrintable(QFileInfo(cacheFile).absoluteFilePath());
if (ParameterManagerDebugCacheFailureLog().isDebugEnabled()) {
_debugCacheCRC[componentId] = true;
_debugCacheMap[componentId] = cacheMap;
for (const QString& name: cacheMap.keys()) {
_debugCacheParamSeen[componentId][name] = false;
}
QString ParameterManager::readParametersFromStream(QTextStream& stream)
while (!stream.atEnd()) {
QString line = stream.readLine();
if (!line.startsWith("#")) {
QStringList wpParams = line.split("\t");
int lineMavId = wpParams.at(0).toInt();
if (wpParams.size() == 5) {
if (_vehicle->id() != lineMavId) {
return QString("The parameters in the stream have been saved from System Id %1, but the current vehicle has the System Id %2.").arg(lineMavId).arg(_vehicle->id());
int componentId = wpParams.at(1).toInt();
QString paramName = wpParams.at(2);
QString valStr = wpParams.at(3);
uint mavType = wpParams.at(4).toUInt();
if (!parameterExists(componentId, paramName)) {
error += QStringLiteral("%1:%2 ").arg(componentId).arg(paramName);
missingErrors += error;
qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to missing: %1").arg(error);
Fact* fact = getParameter(componentId, paramName);
if (fact->type() != mavTypeToFactType((MAV_PARAM_TYPE)mavType)) {
error = QStringLiteral("%1:%2 ").arg(componentId).arg(paramName);
typeErrors += error;
qCDebug(ParameterManagerLog) << QStringLiteral("Skipped due to type mismatch: %1").arg(error);
qCDebug(ParameterManagerLog) << "Updating parameter" << componentId << paramName << valStr;
QString errors;
if (!missingErrors.isEmpty()) {
errors = tr("Parameters not loaded since they are not currently on the vehicle: %1\n").arg(missingErrors);
}
if (!typeErrors.isEmpty()) {
errors += tr("Parameters not loaded due to type mismatch: %1").arg(typeErrors);
}
olliw42
committed
void ParameterManager::writeParametersToStream(QTextStream& stream)
stream << "# Onboard parameters for Vehicle " << _vehicle->id() << "\n";
stream << "#\n";
stream << "# Stack: " << _vehicle->firmwareTypeString() << "\n";
stream << "# Vehicle: " << _vehicle->vehicleTypeString() << "\n";
stream << "# Version: "
<< _vehicle->firmwareMajorVersion() << "."
<< _vehicle->firmwareMinorVersion() << "."
<< _vehicle->firmwarePatchVersion() << " "
<< _vehicle->firmwareVersionTypeString() << "\n";
stream << "# Git Revision: " << _vehicle->gitHash() << "\n";
for (int componentId: _mapCompId2FactMap.keys()) {
for (const QString ¶mName: _mapCompId2FactMap[componentId].keys()) {
Fact* fact = _mapCompId2FactMap[componentId][paramName];
stream << _vehicle->id() << "\t" << componentId << "\t" << paramName << "\t" << fact->rawValueStringFullPrecision() << "\t" << QString("%1").arg(factTypeToMavType(fact->type())) << "\n";
} else {
qWarning() << "Internal error: missing fact";
}
MAV_PARAM_TYPE ParameterManager::factTypeToMavType(FactMetaData::ValueType_t factType)