ParameterLoader.cc 33.9 KB
Newer Older
1
/*=====================================================================
dogmaphobic's avatar
dogmaphobic committed
2

3
 QGroundControl Open Source Ground Control Station
dogmaphobic's avatar
dogmaphobic committed
4

5
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
dogmaphobic's avatar
dogmaphobic committed
6

7
 This file is part of the QGROUNDCONTROL project
dogmaphobic's avatar
dogmaphobic committed
8

9 10 11 12
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
dogmaphobic's avatar
dogmaphobic committed
13

14 15 16 17
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
dogmaphobic's avatar
dogmaphobic committed
18

19 20
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
dogmaphobic's avatar
dogmaphobic committed
21

22 23 24 25 26 27 28 29
 ======================================================================*/

/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "ParameterLoader.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
30
#include "QGCApplication.h"
31
#include "UASMessageHandler.h"
Don Gagne's avatar
Don Gagne committed
32
#include "FirmwarePlugin.h"
33
#include "UAS.h"
34 35 36 37

#include <QFile>
#include <QDebug>

38 39 40 41 42
/* types for local parameter cache */
typedef QPair<int, QVariant> ParamTypeVal;
typedef QPair<QString, ParamTypeVal> NamedParam;
typedef QMap<int, NamedParam> MapID2NamedParam;

43
QGC_LOGGING_CATEGORY(ParameterLoaderLog, "ParameterLoaderLog")
44
QGC_LOGGING_CATEGORY(ParameterLoaderVerboseLog, "ParameterLoaderVerboseLog")
45

Don Gagne's avatar
Don Gagne committed
46 47
Fact ParameterLoader::_defaultFact;

48 49 50 51 52 53 54
ParameterLoader::ParameterLoader(AutoPilotPlugin* autopilot, Vehicle* vehicle, QObject* parent)
    : QObject(parent)
    , _autopilot(autopilot)
    , _vehicle(vehicle)
    , _mavlink(qgcApp()->toolbox()->mavlinkProtocol())
    , _parametersReady(false)
    , _initialLoadComplete(false)
55
    , _saveRequired(false)
56 57
    , _defaultComponentId(FactSystem::defaultComponentId)
    , _totalParamCount(0)
58
{
59
    Q_ASSERT(_autopilot);
60
    Q_ASSERT(_vehicle);
61
    Q_ASSERT(_mavlink);
dogmaphobic's avatar
dogmaphobic committed
62

63 64
    // We signal this to ouselves in order to start timer on our thread
    connect(this, &ParameterLoader::restartWaitingParamTimer, this, &ParameterLoader::_restartWaitingParamTimer);
dogmaphobic's avatar
dogmaphobic committed
65

66 67 68 69
    _initialRequestTimeoutTimer.setSingleShot(true);
    _initialRequestTimeoutTimer.setInterval(6000);
    connect(&_initialRequestTimeoutTimer, &QTimer::timeout, this, &ParameterLoader::_initialRequestTimeout);

70
    _waitingParamTimeoutTimer.setSingleShot(true);
Don Gagne's avatar
Don Gagne committed
71
    _waitingParamTimeoutTimer.setInterval(1000);
72
    connect(&_waitingParamTimeoutTimer, &QTimer::timeout, this, &ParameterLoader::_waitingParamTimeout);
73 74 75

    _cacheTimeoutTimer.setSingleShot(true);
    _cacheTimeoutTimer.setInterval(2500);
dogmaphobic's avatar
dogmaphobic committed
76
    connect(&_cacheTimeoutTimer, &QTimer::timeout, this, &ParameterLoader::_timeoutRefreshAll);
dogmaphobic's avatar
dogmaphobic committed
77

78
    connect(_vehicle->uas(), &UASInterface::parameterUpdate, this, &ParameterLoader::_parameterUpdate);
79 80 81

    /* Initially attempt a local cache load, refresh over the link if it fails */
    _tryCacheLookup();
82 83 84 85 86 87 88 89
}

ParameterLoader::~ParameterLoader()
{

}

/// Called whenever a parameter is updated or first seen.
90
void ParameterLoader::_parameterUpdate(int uasId, int componentId, QString parameterName, int parameterCount, int parameterId, int mavType, QVariant value)
91 92
{
    bool setMetaData = false;
dogmaphobic's avatar
dogmaphobic committed
93

94
    // Is this for our uas?
95
    if (uasId != _vehicle->id()) {
96 97
        return;
    }
dogmaphobic's avatar
dogmaphobic committed
98

99 100
    _initialRequestTimeoutTimer.stop();

101 102 103 104 105 106 107 108
    qCDebug(ParameterLoaderLog) << "_parameterUpdate (usaId:" << uasId <<
                                    "componentId:" << componentId <<
                                    "name:" << parameterName <<
                                    "count:" << parameterCount <<
                                    "index:" << parameterId <<
                                    "mavType:" << mavType <<
                                    "value:" << value <<
                                    ")";
dogmaphobic's avatar
dogmaphobic committed
109

110 111 112 113 114 115 116 117
#if 0
    // Handy for testing retry logic
    static int counter = 0;
    if (counter++ & 0x3) {
        qCDebug(ParameterLoaderLog) << "Artificial discard" << counter;
        return;
    }
#endif
118 119 120 121 122 123 124

    if (parameterName == "_HASH_CHECK") {
        /* we received a cache hash, potentially load from cache */
        _cacheTimeoutTimer.stop();
        _tryCacheHashLoad(uasId, value);
        return;
    }
125
    _dataMutex.lock();
dogmaphobic's avatar
dogmaphobic committed
126

127 128
    // Restart our waiting for param timer
    _waitingParamTimeoutTimer.start();
dogmaphobic's avatar
dogmaphobic committed
129

130 131 132 133 134
    // Update our total parameter counts
    if (!_paramCountMap.contains(componentId)) {
        _paramCountMap[componentId] = parameterCount;
        _totalParamCount += parameterCount;
    }
135 136

    _mapParameterId2Name[componentId][parameterId] = parameterName;
dogmaphobic's avatar
dogmaphobic committed
137

138 139
    // If we've never seen this component id before, setup the wait lists.
    if (!_waitingReadParamIndexMap.contains(componentId)) {
140 141 142 143
        // 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;
144
        }
dogmaphobic's avatar
dogmaphobic committed
145

146 147 148
        // The read and write waiting lists for this component are initialized the empty
        _waitingReadParamNameMap[componentId] = QMap<QString, int>();
        _waitingWriteParamNameMap[componentId] = QMap<QString, int>();
dogmaphobic's avatar
dogmaphobic committed
149

150 151
        qCDebug(ParameterLoaderLog) << "Seeing component for first time, id:" << componentId << "parameter count:" << parameterCount;
    }
dogmaphobic's avatar
dogmaphobic committed
152

153
    // Remove this parameter from the waiting lists
154 155 156
    _waitingReadParamIndexMap[componentId].remove(parameterId);
    _waitingReadParamNameMap[componentId].remove(parameterName);
    _waitingWriteParamNameMap[componentId].remove(parameterName);
157
    qCDebug(ParameterLoaderVerboseLog) << "_waitingReadParamIndexMap:" << _waitingReadParamIndexMap[componentId];
158 159 160 161
    qCDebug(ParameterLoaderLog) << "_waitingReadParamNameMap" << _waitingReadParamNameMap[componentId];
    qCDebug(ParameterLoaderLog) << "_waitingWriteParamNameMap" << _waitingWriteParamNameMap[componentId];

    // Track how many parameters we are still waiting for
dogmaphobic's avatar
dogmaphobic committed
162

163 164 165
    int waitingReadParamIndexCount = 0;
    int waitingReadParamNameCount = 0;
    int waitingWriteParamNameCount = 0;
dogmaphobic's avatar
dogmaphobic committed
166

167 168 169 170 171 172 173
    foreach(int waitingComponentId, _waitingReadParamIndexMap.keys()) {
        waitingReadParamIndexCount += _waitingReadParamIndexMap[waitingComponentId].count();
    }
    if (waitingReadParamIndexCount) {
        qCDebug(ParameterLoaderLog) << "waitingReadParamIndexCount:" << waitingReadParamIndexCount;
    }

dogmaphobic's avatar
dogmaphobic committed
174

175 176 177 178 179 180
    foreach(int waitingComponentId, _waitingReadParamNameMap.keys()) {
        waitingReadParamNameCount += _waitingReadParamNameMap[waitingComponentId].count();
    }
    if (waitingReadParamNameCount) {
        qCDebug(ParameterLoaderLog) << "waitingReadParamNameCount:" << waitingReadParamNameCount;
    }
dogmaphobic's avatar
dogmaphobic committed
181

182 183 184 185 186 187
    foreach(int waitingComponentId, _waitingWriteParamNameMap.keys()) {
        waitingWriteParamNameCount += _waitingWriteParamNameMap[waitingComponentId].count();
    }
    if (waitingWriteParamNameCount) {
        qCDebug(ParameterLoaderLog) << "waitingWriteParamNameCount:" << waitingWriteParamNameCount;
    }
dogmaphobic's avatar
dogmaphobic committed
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202
    int waitingParamCount = waitingReadParamIndexCount + waitingReadParamNameCount + waitingWriteParamNameCount;
    if (waitingParamCount) {
        qCDebug(ParameterLoaderLog) << "waitingParamCount:" << waitingParamCount;
    } else {
        // No more parameters to wait for, stop the timeout
        _waitingParamTimeoutTimer.stop();
    }

    // Update progress bar
    if (waitingParamCount == 0) {
        emit parameterListProgress(0);
    } else {
        emit parameterListProgress((float)(_totalParamCount - waitingParamCount) / (float)_totalParamCount);
    }
dogmaphobic's avatar
dogmaphobic committed
203

204 205
    // Attempt to determine default component id
    if (_defaultComponentId == FactSystem::defaultComponentId && _defaultComponentIdParam.isEmpty()) {
206
        _defaultComponentIdParam = _vehicle->firmwarePlugin()->getDefaultComponentIdParam();
207 208 209 210
    }
    if (!_defaultComponentIdParam.isEmpty() && _defaultComponentIdParam == parameterName) {
        _defaultComponentId = componentId;
    }
dogmaphobic's avatar
dogmaphobic committed
211

212
    if (!_mapParameterName2Variant.contains(componentId) || !_mapParameterName2Variant[componentId].contains(parameterName)) {
213
        qCDebug(ParameterLoaderLog) << "Adding new fact";
dogmaphobic's avatar
dogmaphobic committed
214

215 216 217 218 219 220
        FactMetaData::ValueType_t factType;
        switch (mavType) {
            case MAV_PARAM_TYPE_UINT8:
                factType = FactMetaData::valueTypeUint8;
                break;
            case MAV_PARAM_TYPE_INT8:
221
                factType = FactMetaData::valueTypeInt8;
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
                break;
            case MAV_PARAM_TYPE_UINT16:
                factType = FactMetaData::valueTypeUint16;
                break;
            case MAV_PARAM_TYPE_INT16:
                factType = FactMetaData::valueTypeInt16;
                break;
            case MAV_PARAM_TYPE_UINT32:
                factType = FactMetaData::valueTypeUint32;
                break;
            case MAV_PARAM_TYPE_INT32:
                factType = FactMetaData::valueTypeInt32;
                break;
            case MAV_PARAM_TYPE_REAL32:
                factType = FactMetaData::valueTypeFloat;
                break;
            case MAV_PARAM_TYPE_REAL64:
                factType = FactMetaData::valueTypeDouble;
                break;
            default:
                factType = FactMetaData::valueTypeInt32;
                qCritical() << "Unsupported fact type" << mavType;
                break;
        }
dogmaphobic's avatar
dogmaphobic committed
246

247 248
        Fact* fact = new Fact(componentId, parameterName, factType, this);
        setMetaData = true;
dogmaphobic's avatar
dogmaphobic committed
249

250
        _mapParameterName2Variant[componentId][parameterName] = QVariant::fromValue(fact);
dogmaphobic's avatar
dogmaphobic committed
251

252
        // We need to know when the fact changes from QML so that we can send the new value to the parameter manager
Don Gagne's avatar
Don Gagne committed
253
        connect(fact, &Fact::_containerRawValueChanged, this, &ParameterLoader::_valueUpdated);
254
    }
dogmaphobic's avatar
dogmaphobic committed
255

Don Gagne's avatar
Don Gagne committed
256 257
    _dataMutex.unlock();

258
    Q_ASSERT(_mapParameterName2Variant[componentId].contains(parameterName));
dogmaphobic's avatar
dogmaphobic committed
259

260 261
    Fact* fact = _mapParameterName2Variant[componentId][parameterName].value<Fact*>();
    Q_ASSERT(fact);
Don Gagne's avatar
Don Gagne committed
262
    fact->_containerSetRawValue(value);
dogmaphobic's avatar
dogmaphobic committed
263

264
    if (setMetaData) {
265
        _vehicle->firmwarePlugin()->addMetaDataToFact(fact, _vehicle->vehicleType());
266
    }
dogmaphobic's avatar
dogmaphobic committed
267

268 269 270
    if (waitingParamCount == 0) {
        // Now that we know vehicle is up to date persist
        _saveToEEPROM();
271
        _writeLocalParamCache();
272
    }
dogmaphobic's avatar
dogmaphobic committed
273

274
    _checkInitialLoadComplete();
275 276 277 278
}

/// Connected to Fact::valueUpdated
///
279
/// Writes the parameter to mavlink, sets up for write wait
280 281 282 283
void ParameterLoader::_valueUpdated(const QVariant& value)
{
    Fact* fact = qobject_cast<Fact*>(sender());
    Q_ASSERT(fact);
dogmaphobic's avatar
dogmaphobic committed
284

285
    int componentId = fact->componentId();
286
    QString name = fact->name();
dogmaphobic's avatar
dogmaphobic committed
287

288
    _dataMutex.lock();
dogmaphobic's avatar
dogmaphobic committed
289

290
    Q_ASSERT(_waitingWriteParamNameMap.contains(componentId));
291 292
    _waitingWriteParamNameMap[componentId].remove(name);    // Remove any old entry
    _waitingWriteParamNameMap[componentId][name] = 0;       // Add new entry and set retry count
293
    _waitingParamTimeoutTimer.start();
294
    _saveRequired = true;
dogmaphobic's avatar
dogmaphobic committed
295

296
    _dataMutex.unlock();
dogmaphobic's avatar
dogmaphobic committed
297

298 299
    _writeParameterRaw(componentId, fact->name(), value);
    qCDebug(ParameterLoaderLog) << "Set parameter (componentId:" << componentId << "name:" << name << value << ")";
300

301 302
    if (fact->rebootRequired() && !qgcApp()->runningUnitTests()) {
        qgcApp()->showMessage(QStringLiteral("Change of parameter %1 requires a Vehicle reboot to take effect").arg(name));
303
    }
304 305
}

dogmaphobic's avatar
dogmaphobic committed
306
void ParameterLoader::refreshAllParameters(uint8_t componentID)
307
{
308
    _dataMutex.lock();
dogmaphobic's avatar
dogmaphobic committed
309

310 311 312 313
    if (!_initialLoadComplete) {
        _initialRequestTimeoutTimer.start();
    }

314
    // Reset index wait lists
dogmaphobic's avatar
dogmaphobic committed
315
    foreach (int cid, _paramCountMap.keys()) {
316
        // Add/Update all indices to the wait list, parameter index is 0-based
dogmaphobic's avatar
dogmaphobic committed
317 318 319
        if(componentID != MAV_COMP_ID_ALL && componentID != cid)
            continue;
        for (int waitingIndex = 0; waitingIndex < _paramCountMap[cid]; waitingIndex++) {
320
            // This will add a new waiting index if needed and set the retry count for that index to 0
dogmaphobic's avatar
dogmaphobic committed
321
            _waitingReadParamIndexMap[cid][waitingIndex] = 0;
322 323
        }
    }
dogmaphobic's avatar
dogmaphobic committed
324

325
    _dataMutex.unlock();
dogmaphobic's avatar
dogmaphobic committed
326

327
    MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol();
328
    Q_ASSERT(mavlink);
dogmaphobic's avatar
dogmaphobic committed
329

330
    mavlink_message_t msg;
dogmaphobic's avatar
dogmaphobic committed
331
    mavlink_msg_param_request_list_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, _vehicle->id(), componentID);
332
    _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
dogmaphobic's avatar
dogmaphobic committed
333

dogmaphobic's avatar
dogmaphobic committed
334 335
    QString what = (componentID == MAV_COMP_ID_ALL) ? "MAV_COMP_ID_ALL" : QString::number(componentID);
    qCDebug(ParameterLoaderLog) << "Request to refresh all parameters for component ID:" << what;
336 337 338 339 340 341 342 343
}

void ParameterLoader::_determineDefaultComponentId(void)
{
    if (_defaultComponentId == FactSystem::defaultComponentId) {
        // We don't have a default component id yet. That means the plugin can't provide
        // the param to trigger off of. Instead we use the most prominent component id in
        // the set of parameters. Better than nothing!
dogmaphobic's avatar
dogmaphobic committed
344

345
        _defaultComponentId = -1;
346
        int largestCompParamCount = 0;
347
        foreach(int componentId, _mapParameterName2Variant.keys()) {
348 349 350
            int compParamCount = _mapParameterName2Variant[componentId].count();
            if (compParamCount > largestCompParamCount) {
                largestCompParamCount = compParamCount;
351 352 353
                _defaultComponentId = componentId;
            }
        }
354 355 356 357

        if (_defaultComponentId == -1) {
            qWarning() << "All parameters missing, unable to determine default componet id";
        }
358 359 360 361 362 363 364 365
    }
}

/// Translates FactSystem::defaultComponentId to real component id if needed
int ParameterLoader::_actualComponentId(int componentId)
{
    if (componentId == FactSystem::defaultComponentId) {
        componentId = _defaultComponentId;
Don Gagne's avatar
Don Gagne committed
366
        if (componentId == FactSystem::defaultComponentId) {
Don Gagne's avatar
Don Gagne committed
367 368
            qWarning() << "Default component id not set";
        }
369
    }
dogmaphobic's avatar
dogmaphobic committed
370

371 372 373 374 375
    return componentId;
}

void ParameterLoader::refreshParameter(int componentId, const QString& name)
{
376 377
    componentId = _actualComponentId(componentId);
    qCDebug(ParameterLoaderLog) << "refreshParameter (component id:" << componentId << "name:" << name << ")";
dogmaphobic's avatar
dogmaphobic committed
378

379 380 381
    _dataMutex.lock();

    Q_ASSERT(_waitingReadParamNameMap.contains(componentId));
dogmaphobic's avatar
dogmaphobic committed
382

383
    if (_waitingReadParamNameMap.contains(componentId)) {
384 385
        _waitingReadParamNameMap[componentId].remove(name); // Remove old wait entry if there
        _waitingReadParamNameMap[componentId][name] = 0;    // Add new wait entry and update retry count
386 387
        emit restartWaitingParamTimer();
    }
dogmaphobic's avatar
dogmaphobic committed
388

389 390 391
    _dataMutex.unlock();

    _readParameterRaw(componentId, name, -1);
392 393 394 395 396
}

void ParameterLoader::refreshParametersPrefix(int componentId, const QString& namePrefix)
{
    componentId = _actualComponentId(componentId);
397 398
    qCDebug(ParameterLoaderLog) << "refreshParametersPrefix (component id:" << componentId << "name:" << namePrefix << ")";

399
    foreach(const QString &name, _mapParameterName2Variant[componentId].keys()) {
400 401 402 403 404 405
        if (name.startsWith(namePrefix)) {
            refreshParameter(componentId, name);
        }
    }
}

406
bool ParameterLoader::parameterExists(int componentId, const QString&  name)
407
{
408
    bool ret = false;
dogmaphobic's avatar
dogmaphobic committed
409

410 411
    componentId = _actualComponentId(componentId);
    if (_mapParameterName2Variant.contains(componentId)) {
412
        ret = _mapParameterName2Variant[componentId].contains(name);
413
    }
414 415

    return ret;
416 417 418 419 420
}

Fact* ParameterLoader::getFact(int componentId, const QString& name)
{
    componentId = _actualComponentId(componentId);
dogmaphobic's avatar
dogmaphobic committed
421

422
    if (!_mapParameterName2Variant.contains(componentId) || !_mapParameterName2Variant[componentId].contains(name)) {
Don Gagne's avatar
Don Gagne committed
423 424
        qgcApp()->reportMissingParameter(componentId, name);
        return &_defaultFact;
425
    }
dogmaphobic's avatar
dogmaphobic committed
426

Don Gagne's avatar
Don Gagne committed
427
    return _mapParameterName2Variant[componentId][name].value<Fact*>();
428
}
429

Don Gagne's avatar
Don Gagne committed
430
QStringList ParameterLoader::parameterNames(int componentId)
431
{
dogmaphobic's avatar
dogmaphobic committed
432 433
    QStringList names;

434
    foreach(const QString &paramName, _mapParameterName2Variant[_actualComponentId(componentId)].keys()) {
dogmaphobic's avatar
dogmaphobic committed
435 436 437 438
        names << paramName;
    }

    return names;
439 440 441 442 443
}

void ParameterLoader::_setupGroupMap(void)
{
    foreach (int componentId, _mapParameterName2Variant.keys()) {
444
        foreach (const QString &name, _mapParameterName2Variant[componentId].keys()) {
445 446 447 448 449 450 451 452 453 454
            Fact* fact = _mapParameterName2Variant[componentId][name].value<Fact*>();
            _mapGroup2ParameterName[componentId][fact->group()] += name;
        }
    }
}

const QMap<int, QMap<QString, QStringList> >& ParameterLoader::getGroupMap(void)
{
    return _mapGroup2ParameterName;
}
455 456 457 458 459 460

void ParameterLoader::_waitingParamTimeout(void)
{
    bool paramsRequested = false;
    const int maxBatchSize = 10;
    int batchCount = 0;
dogmaphobic's avatar
dogmaphobic committed
461

462
    // We timed out waiting for some parameters from the initial set. Re-request those.
dogmaphobic's avatar
dogmaphobic committed
463

464 465
    batchCount = 0;
    foreach(int componentId, _waitingReadParamIndexMap.keys()) {
466 467 468 469 470 471 472 473 474 475 476 477
        foreach(int paramIndex, _waitingReadParamIndexMap[componentId].keys()) {
            _waitingReadParamIndexMap[componentId][paramIndex]++;   // Bump retry count
            if (_waitingReadParamIndexMap[componentId][paramIndex] > _maxInitialLoadRetry) {
                // Give up on this index
                _failedReadParamIndexMap[componentId] << paramIndex;
                qCDebug(ParameterLoaderLog) << "Giving up on (componentId:" << componentId << "paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")";
                _waitingReadParamIndexMap[componentId].remove(paramIndex);
            } else {
                // Retry again
                paramsRequested = true;
                _readParameterRaw(componentId, "", paramIndex);
                qCDebug(ParameterLoaderLog) << "Read re-request for (componentId:" << componentId << "paramIndex:" << paramIndex << "retryCount:" << _waitingReadParamIndexMap[componentId][paramIndex] << ")";
dogmaphobic's avatar
dogmaphobic committed
478

479 480 481
                if (++batchCount > maxBatchSize) {
                    goto Out;
                }
482 483 484
            }
        }
    }
485 486
    // We need to check for initial load complete here as well, since it could complete on a max retry failure
    _checkInitialLoadComplete();
dogmaphobic's avatar
dogmaphobic committed
487

488 489
    if (!paramsRequested) {
        foreach(int componentId, _waitingWriteParamNameMap.keys()) {
490
            foreach(const QString &paramName, _waitingWriteParamNameMap[componentId].keys()) {
491
                paramsRequested = true;
492
                _waitingWriteParamNameMap[componentId][paramName]++;   // Bump retry count
Don Gagne's avatar
Don Gagne committed
493
                _writeParameterRaw(componentId, paramName, _autopilot->getFact(FactSystem::ParameterProvider, componentId, paramName)->rawValue());
494
                qCDebug(ParameterLoaderLog) << "Write resend for (componentId:" << componentId << "paramName:" << paramName << "retryCount:" << _waitingWriteParamNameMap[componentId][paramName] << ")";
dogmaphobic's avatar
dogmaphobic committed
495

496 497 498 499 500 501
                if (++batchCount > maxBatchSize) {
                    goto Out;
                }
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
502

503 504
    if (!paramsRequested) {
        foreach(int componentId, _waitingReadParamNameMap.keys()) {
505
            foreach(const QString &paramName, _waitingReadParamNameMap[componentId].keys()) {
506
                paramsRequested = true;
507
                _waitingReadParamNameMap[componentId][paramName]++;   // Bump retry count
508
                _readParameterRaw(componentId, paramName, -1);
509
                qCDebug(ParameterLoaderLog) << "Read re-request for (componentId:" << componentId << "paramName:" << paramName << "retryCount:" << _waitingReadParamNameMap[componentId][paramName] << ")";
dogmaphobic's avatar
dogmaphobic committed
510

511 512 513 514 515 516
                if (++batchCount > maxBatchSize) {
                    goto Out;
                }
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
517

518 519 520 521 522 523
Out:
    if (paramsRequested) {
        _waitingParamTimeoutTimer.start();
    }
}

524 525 526 527 528
void ParameterLoader::_tryCacheLookup()
{
    /* Start waiting for 2.5 seconds to get a cache hit and avoid loading all params over the radio */
    _cacheTimeoutTimer.start();

529
    MAVLinkProtocol* mavlink = qgcApp()->toolbox()->mavlinkProtocol();
530 531 532 533
    Q_ASSERT(mavlink);

    mavlink_message_t msg;
    mavlink_msg_param_request_read_pack(mavlink->getSystemId(), mavlink->getComponentId(), &msg, _vehicle->id(), MAV_COMP_ID_ALL, "_HASH_CHECK", -1);
534
    _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
535 536
}

537 538 539 540 541 542 543 544 545
void ParameterLoader::_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(_mavlink->getSystemId(),    // Our system id
                                        _mavlink->getComponentId(), // Our component id
                                        &msg,                       // Pack into this mavlink_message_t
546
                                        _vehicle->id(),             // Target system id
547 548 549
                                        componentId,                // Target component id
                                        fixedParamName,             // Named parameter being requested
                                        paramIndex);                // Parameter index being requested, -1 for named
550
    _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
551 552 553 554 555 556
}

void ParameterLoader::_writeParameterRaw(int componentId, const QString& paramName, const QVariant& value)
{
    mavlink_param_set_t     p;
    mavlink_param_union_t   union_value;
dogmaphobic's avatar
dogmaphobic committed
557

558 559
    FactMetaData::ValueType_t factType = _autopilot->getFact(FactSystem::ParameterProvider, componentId, paramName)->type();
    p.param_type = _factTypeToMavType(factType);
dogmaphobic's avatar
dogmaphobic committed
560

561 562
    switch (factType) {
        case FactMetaData::valueTypeUint8:
Don Gagne's avatar
Don Gagne committed
563
            union_value.param_uint8 = (uint8_t)value.toUInt();
564
            break;
dogmaphobic's avatar
dogmaphobic committed
565

566
        case FactMetaData::valueTypeInt8:
Don Gagne's avatar
Don Gagne committed
567
            union_value.param_int8 = (int8_t)value.toInt();
568
            break;
dogmaphobic's avatar
dogmaphobic committed
569

570
        case FactMetaData::valueTypeUint16:
Don Gagne's avatar
Don Gagne committed
571
            union_value.param_uint16 = (uint16_t)value.toUInt();
572
            break;
dogmaphobic's avatar
dogmaphobic committed
573

574
        case FactMetaData::valueTypeInt16:
Don Gagne's avatar
Don Gagne committed
575
            union_value.param_int16 = (int16_t)value.toInt();
576
            break;
dogmaphobic's avatar
dogmaphobic committed
577

578
        case FactMetaData::valueTypeUint32:
Don Gagne's avatar
Don Gagne committed
579
            union_value.param_uint32 = (uint32_t)value.toUInt();
580
            break;
dogmaphobic's avatar
dogmaphobic committed
581

582 583 584
        case FactMetaData::valueTypeFloat:
            union_value.param_float = value.toFloat();
            break;
dogmaphobic's avatar
dogmaphobic committed
585

586 587 588
        default:
            qCritical() << "Unsupported fact type" << factType;
            // fall through
dogmaphobic's avatar
dogmaphobic committed
589

590
        case FactMetaData::valueTypeInt32:
Don Gagne's avatar
Don Gagne committed
591
            union_value.param_int32 = (int32_t)value.toInt();
592 593
            break;
    }
dogmaphobic's avatar
dogmaphobic committed
594

595
    p.param_value = union_value.param_float;
596
    p.target_system = (uint8_t)_vehicle->id();
597
    p.target_component = (uint8_t)componentId;
dogmaphobic's avatar
dogmaphobic committed
598

599
    strncpy(p.param_id, paramName.toStdString().c_str(), sizeof(p.param_id));
dogmaphobic's avatar
dogmaphobic committed
600

601 602
    mavlink_message_t msg;
    mavlink_msg_param_set_encode(_mavlink->getSystemId(), _mavlink->getComponentId(), &msg, &p);
603
    _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
604 605
}

606 607 608 609 610 611 612 613
void ParameterLoader::_writeLocalParamCache()
{
    QMap<int, MapID2NamedParam> cache_map;

    foreach(int component, _mapParameterId2Name.keys()) {
        foreach(int id, _mapParameterId2Name[component].keys()) {
            const QString name(_mapParameterId2Name[component][id]);
            const Fact *fact = _mapParameterName2Variant[component][name].value<Fact*>();
Don Gagne's avatar
Don Gagne committed
614
            cache_map[component][id] = NamedParam(name, ParamTypeVal(fact->type(), fact->rawValue()));
615 616 617 618 619 620 621 622 623 624
        }
    }

    QFile cache_file(QFileInfo(QSettings().fileName()).path() + QDir::separator() + "param_cache");
    cache_file.open(QIODevice::WriteOnly | QIODevice::Truncate);

    QDataStream ds(&cache_file);
    ds << cache_map;
}

625 626 627 628 629 630
QString ParameterLoader::parameterCacheFile(void)
{
    const QDir settingsDir(QFileInfo(QSettings().fileName()).dir());
    return settingsDir.filePath("param_cache");
}

631 632 633 634 635
void ParameterLoader::_tryCacheHashLoad(int uasId, QVariant hash_value)
{
    uint32_t crc32_value = 0;
    /* The datastructure of the cache table */
    QMap<int, MapID2NamedParam> cache_map;
636
    QFile cache_file(parameterCacheFile());
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
    if (!cache_file.exists()) {
        /* no local cache, immediately refresh all params */
        refreshAllParameters();
        return;
    }
    cache_file.open(QIODevice::ReadOnly);

    /* Deserialize the parameter cache table */
    QDataStream ds(&cache_file);
    ds >> cache_map;

    /* compute the crc of the local cache to check against the remote */
    foreach(int component, cache_map.keys()) {
        foreach(int id, cache_map[component].keys()) {
            const QString name(cache_map[component][id].first);
            const void *vdat = cache_map[component][id].second.second.constData();
            crc32_value = QGC::crc32((const uint8_t *)qPrintable(name), name.length(),  crc32_value);
            crc32_value = QGC::crc32((const uint8_t *)vdat, sizeof(uint32_t), crc32_value);
        }
    }

    if (crc32_value == hash_value.toUInt()) {
        /* if the two param set hashes match, just load from the disk */
        foreach(int component, cache_map.keys()) {
            int count = cache_map[component].count();
            foreach(int id, cache_map[component].keys()) {
                const QString &name = cache_map[component][id].first;
                const QVariant &value = cache_map[component][id].second.second;
665
                const int mavType = _factTypeToMavType(static_cast<FactMetaData::ValueType_t>(cache_map[component][id].second.first));
666 667 668 669 670 671 672 673 674
                _parameterUpdate(uasId, component, name, count, id, mavType, value);
            }
        }
    } else {
        /* cache and remote hashes differ. Immediately request all params */
        refreshAllParameters();
    }
}

675 676
void ParameterLoader::_saveToEEPROM(void)
{
677 678 679 680 681 682 683 684 685 686
    if (_saveRequired) {
        _saveRequired = false;
        if (_vehicle->firmwarePlugin()->isCapable(FirmwarePlugin::MavCmdPreflightStorageCapability)) {
            mavlink_message_t msg;
            mavlink_msg_command_long_pack(_mavlink->getSystemId(), _mavlink->getComponentId(), &msg, _vehicle->id(), 0, MAV_CMD_PREFLIGHT_STORAGE, 1, 1, -1, -1, -1, 0, 0, 0);
            _vehicle->sendMessageOnLink(_vehicle->priorityLink(), msg);
            qCDebug(ParameterLoaderLog) << "_saveToEEPROM";
        } else {
            qCDebug(ParameterLoaderLog) << "_saveToEEPROM skipped due to FirmwarePlugin::isCapable";
        }
Don Gagne's avatar
Don Gagne committed
687
    }
688 689
}

690
QString ParameterLoader::readParametersFromStream(QTextStream& stream)
691
{
692
    QString errors;
dogmaphobic's avatar
dogmaphobic committed
693

694 695 696 697 698 699
    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) {
Don Gagne's avatar
Don Gagne committed
700 701
                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());
dogmaphobic's avatar
dogmaphobic committed
702 703
                }

704 705 706 707
                int     componentId = wpParams.at(1).toInt();
                QString paramName = wpParams.at(2);
                QString valStr = wpParams.at(3);
                uint    mavType = wpParams.at(4).toUInt();
dogmaphobic's avatar
dogmaphobic committed
708

709
                if (!_autopilot->factExists(FactSystem::ParameterProvider, componentId, paramName)) {
710 711 712 713
                    QString error;
                    error = QString("Skipped parameter %1:%2 - does not exist on this vehicle\n").arg(componentId).arg(paramName);
                    errors += error;
                    qCDebug(ParameterLoaderLog) << error;
714 715
                    continue;
                }
dogmaphobic's avatar
dogmaphobic committed
716

717 718
                Fact* fact = _autopilot->getFact(FactSystem::ParameterProvider, componentId, paramName);
                if (fact->type() != _mavTypeToFactType((MAV_PARAM_TYPE)mavType)) {
719 720 721 722
                    QString error;
                    error  = QString("Skipped parameter %1:%2 - type mismatch %3:%4\n").arg(componentId).arg(paramName).arg(fact->type()).arg(_mavTypeToFactType((MAV_PARAM_TYPE)mavType));
                    errors += error;
                    qCDebug(ParameterLoaderLog) << error;
723 724
                    continue;
                }
dogmaphobic's avatar
dogmaphobic committed
725

726
                qCDebug(ParameterLoaderLog) << "Updating parameter" << componentId << paramName << valStr;
Don Gagne's avatar
Don Gagne committed
727
                fact->setRawValue(valStr);
728 729 730
            }
        }
    }
dogmaphobic's avatar
dogmaphobic committed
731

732
    return errors;
733 734
}

735
void ParameterLoader::writeParametersToStream(QTextStream &stream)
736
{
737
    stream << "# Onboard parameters for vehicle " << _vehicle->id() << "\n";
738 739 740 741
    stream << "#\n";
    stream << "# MAV ID  COMPONENT ID  PARAM NAME  VALUE (FLOAT)\n";

    foreach (int componentId, _mapParameterName2Variant.keys()) {
742
        foreach (const QString &paramName, _mapParameterName2Variant[componentId].keys()) {
743 744
            Fact* fact = _mapParameterName2Variant[componentId][paramName].value<Fact*>();
            Q_ASSERT(fact);
dogmaphobic's avatar
dogmaphobic committed
745

746
            stream << _vehicle->id() << "\t" << componentId << "\t" << paramName << "\t" << fact->rawValueString() << "\t" << QString("%1").arg(_factTypeToMavType(fact->type())) << "\n";
747 748
        }
    }
dogmaphobic's avatar
dogmaphobic committed
749

750 751 752 753 754 755 756 757
    stream.flush();
}

MAV_PARAM_TYPE ParameterLoader::_factTypeToMavType(FactMetaData::ValueType_t factType)
{
    switch (factType) {
        case FactMetaData::valueTypeUint8:
            return MAV_PARAM_TYPE_UINT8;
dogmaphobic's avatar
dogmaphobic committed
758

759 760
        case FactMetaData::valueTypeInt8:
            return MAV_PARAM_TYPE_INT8;
dogmaphobic's avatar
dogmaphobic committed
761

762 763
        case FactMetaData::valueTypeUint16:
            return MAV_PARAM_TYPE_UINT16;
dogmaphobic's avatar
dogmaphobic committed
764

765 766
        case FactMetaData::valueTypeInt16:
            return MAV_PARAM_TYPE_INT16;
dogmaphobic's avatar
dogmaphobic committed
767

768 769
        case FactMetaData::valueTypeUint32:
            return MAV_PARAM_TYPE_UINT32;
dogmaphobic's avatar
dogmaphobic committed
770

771 772
        case FactMetaData::valueTypeFloat:
            return MAV_PARAM_TYPE_REAL32;
dogmaphobic's avatar
dogmaphobic committed
773

774 775 776
        default:
            qWarning() << "Unsupported fact type" << factType;
            // fall through
dogmaphobic's avatar
dogmaphobic committed
777

778 779 780 781 782 783 784 785 786 787
        case FactMetaData::valueTypeInt32:
            return MAV_PARAM_TYPE_INT32;
    }
}

FactMetaData::ValueType_t ParameterLoader::_mavTypeToFactType(MAV_PARAM_TYPE mavType)
{
    switch (mavType) {
        case MAV_PARAM_TYPE_UINT8:
            return FactMetaData::valueTypeUint8;
dogmaphobic's avatar
dogmaphobic committed
788

789 790
        case MAV_PARAM_TYPE_INT8:
            return FactMetaData::valueTypeInt8;
dogmaphobic's avatar
dogmaphobic committed
791

792 793
        case MAV_PARAM_TYPE_UINT16:
            return FactMetaData::valueTypeUint16;
dogmaphobic's avatar
dogmaphobic committed
794

795 796
        case MAV_PARAM_TYPE_INT16:
            return FactMetaData::valueTypeInt16;
dogmaphobic's avatar
dogmaphobic committed
797

798 799
        case MAV_PARAM_TYPE_UINT32:
            return FactMetaData::valueTypeUint32;
dogmaphobic's avatar
dogmaphobic committed
800

801 802
        case MAV_PARAM_TYPE_REAL32:
            return FactMetaData::valueTypeFloat;
dogmaphobic's avatar
dogmaphobic committed
803

804 805 806
        default:
            qWarning() << "Unsupported mav param type" << mavType;
            // fall through
dogmaphobic's avatar
dogmaphobic committed
807

808 809 810 811 812 813 814 815 816
        case MAV_PARAM_TYPE_INT32:
            return FactMetaData::valueTypeInt32;
    }
}

void ParameterLoader::_restartWaitingParamTimer(void)
{
    _waitingParamTimeoutTimer.start();
}
817 818 819 820 821 822 823

void ParameterLoader::_checkInitialLoadComplete(void)
{
    // Already processed?
    if (_initialLoadComplete) {
        return;
    }
dogmaphobic's avatar
dogmaphobic committed
824

825 826 827 828 829 830
    foreach (int componentId, _waitingReadParamIndexMap.keys()) {
        if (_waitingReadParamIndexMap[componentId].count()) {
            // We are still waiting on some parameters, not done yet
            return;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
831 832


833 834
    // We aren't waiting for any more initial parameter updates, initial parameter loading is complete
    _initialLoadComplete = true;
dogmaphobic's avatar
dogmaphobic committed
835

836 837 838 839 840 841 842 843 844 845 846 847 848
    // Check for load failures
    QString indexList;
    bool initialLoadFailures = false;
    foreach (int componentId, _failedReadParamIndexMap.keys()) {
        foreach (int paramIndex, _failedReadParamIndexMap[componentId]) {
            if (initialLoadFailures) {
                indexList += ", ";
            }
            indexList += QString("%1").arg(paramIndex);
            initialLoadFailures = true;
            qCDebug(ParameterLoaderLog) << "Gave up on initial load after max retries (componentId:" << componentId << "paramIndex:" << paramIndex << ")";
        }
    }
dogmaphobic's avatar
dogmaphobic committed
849

850
    // Check for any errors during vehicle boot
dogmaphobic's avatar
dogmaphobic committed
851

852
    UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler();
853
    if (msgHandler->getErrorCountTotal()) {
854
        QString errors;
855
        bool firstError = true;
856
        bool errorsFound = false;
dogmaphobic's avatar
dogmaphobic committed
857

858 859 860
        msgHandler->lockAccess();
        foreach (UASMessage* msg, msgHandler->messages()) {
            if (msg->severityIsError()) {
861
                if (!firstError) {
dogmaphobic's avatar
dogmaphobic committed
862
                    errors += "<br>";
863 864
                }
                errors += " - ";
865
                errors += msg->getText();
866
                firstError = false;
867
                errorsFound = true;
868 869
            }
        }
870
        msgHandler->showErrorsInToolbar();
871
        msgHandler->unlockAccess();
dogmaphobic's avatar
dogmaphobic committed
872

873
        if (errorsFound) {
dogmaphobic's avatar
dogmaphobic committed
874
            QString errorMsg = QString("<b>Critical safety issue detected:</b><br>%1").arg(errors);
875
            qgcApp()->showMessage(errorMsg);
876
        }
877
    }
dogmaphobic's avatar
dogmaphobic committed
878

879
    // Warn of parameter load failure
dogmaphobic's avatar
dogmaphobic committed
880

881
    if (initialLoadFailures) {
882
        qgcApp()->showMessage("QGroundControl was unable to retrieve the full set of parameters from the vehicle. "
dogmaphobic's avatar
dogmaphobic committed
883
                              "This will cause QGroundControl to be unable to display its full user interface. "
884 885
                              "If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. "
                              "If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue.");
886
        qCWarning(ParameterLoaderLog) << "The following parameter indices could not be loaded after the maximum number of retries: " << indexList;
887
        emit parametersReady(true);
888 889 890 891 892
    } else {
        // No failed parameters, ok to signal ready
        _parametersReady = true;
        _determineDefaultComponentId();
        _setupGroupMap();
893
        emit parametersReady(false);
894
    }
895
}
896 897 898 899 900 901 902

void ParameterLoader::_initialRequestTimeout(void)
{
    qgcApp()->showMessage("Vehicle did not respond to request for parameters, retrying");
    refreshAllParameters();
    _initialRequestTimeoutTimer.start();
}
dogmaphobic's avatar
dogmaphobic committed
903 904 905 906 907 908

void ParameterLoader::_timeoutRefreshAll()
{
    refreshAllParameters();
}