UASParameterCommsMgr.cc 16.9 KB
Newer Older
1 2
#include "UASParameterCommsMgr.h"

tstellanova's avatar
tstellanova committed
3 4
#include <QSettings>

5 6 7
#include "QGCUASParamManager.h"
#include "UASInterface.h"

8 9


10 11
#define RC_CAL_CHAN_MAX 8

12
UASParameterCommsMgr::UASParameterCommsMgr(QObject *parent) :
tstellanova's avatar
tstellanova committed
13
    QObject(parent),
14
    mav(NULL),
15
    maxSilenceTimeout(30000),
16
    paramDataModel(NULL),
17 18 19
    retransmitBurstLimit(5),
    silenceTimeout(1000),
    transmissionListMode(false)
20
{
21 22 23 24 25 26 27


}

UASParameterCommsMgr* UASParameterCommsMgr::initWithUAS(UASInterface* uas)
{
    mav = uas;
28
    paramDataModel = mav->getParamManager()->dataModel();
tstellanova's avatar
tstellanova committed
29 30 31 32 33
    loadParamCommsSettings();

    //Requesting parameters one-by-one from mav
    connect(this, SIGNAL(parameterUpdateRequestedById(int,int)),
            mav, SLOT(requestParameter(int,int)));
34 35

    // Sending params to the UAS
36
    connect(this, SIGNAL(commitPendingParameter(int,QString,QVariant)),
37
            mav, SLOT(setParameter(int,QString,QVariant)));
38

39
    // Received parameter updates from UAS
40 41
    connect(mav, SIGNAL(parameterChanged(int,int,int,int,QString,QVariant)),
            this, SLOT(receivedParameterUpdate(int,int,int,int,QString,QVariant)));
42

43 44
    connect(&silenceTimer, SIGNAL(timeout()),
            this,SLOT(silenceTimerExpired()));
tstellanova's avatar
tstellanova committed
45

46
    return this;
47 48 49
}


tstellanova's avatar
tstellanova committed
50

51

tstellanova's avatar
tstellanova committed
52 53 54
void UASParameterCommsMgr::loadParamCommsSettings()
{
    QSettings settings;
55
    //TODO these are duplicates of MAVLinkProtocol settings...seems wrong to use them in two places
tstellanova's avatar
tstellanova committed
56
    settings.beginGroup("QGC_MAVLINK_PROTOCOL");
tstellanova's avatar
tstellanova committed
57
    bool ok;
58
    int val = settings.value("PARAMETER_RETRANSMISSION_TIMEOUT", 1000).toInt(&ok);
tstellanova's avatar
tstellanova committed
59
    if (ok) {
60 61
        silenceTimeout = val;
        qDebug() << "silenceTimeout" << silenceTimeout;
tstellanova's avatar
tstellanova committed
62
    }
63

tstellanova's avatar
tstellanova committed
64 65 66
    settings.endGroup();
}

67 68


69 70 71 72 73 74 75 76 77 78
/**
 * Send a request to deliver the list of onboard parameters
 * from the MAV.
 */
void UASParameterCommsMgr::requestParameterList()
{
    if (!mav) {
        return;
    }

79
    if (!transmissionListMode) {
80 81 82
        transmissionListMode = true;//TODO eliminate?
        //we use (compId 0, paramId 0) as  indicating all params for the system
        markReadParamWaiting(0,0);
83
        mav->requestParameters();
84
        updateSilenceTimer();
85 86 87
    }
    else {
        qDebug() << __FILE__ << __LINE__ << "Ignoring requestParameterList because we're receiving params list";
88 89 90 91 92
    }

}


93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
void UASParameterCommsMgr::markReadParamWaiting(int compId, int paramId)
{
    if (!readsWaiting.contains(compId)) {
        readsWaiting.insert(compId, new QSet<int>());
    }

    readsWaiting.value(compId)->insert(paramId);
}

void UASParameterCommsMgr::markWriteParamWaiting(int compId, QString paramName, QVariant value)
{
    //ensure we have a map for this compId
    if (!writesWaiting.contains(compId)) {
        writesWaiting.insert(compId, new QMap<QString, QVariant>());
    }

    // Insert it in missing write ACK list
    writesWaiting.value(compId)->insert(paramName, value);
}

113 114 115 116
/*
 Empty read retransmission list
 Empty write retransmission list
*/
tstellanova's avatar
tstellanova committed
117
void UASParameterCommsMgr::clearRetransmissionLists(int& missingReadCount, int& missingWriteCount )
118 119 120
{
    qDebug() << __FILE__ << __LINE__ << "clearRetransmissionLists";

tstellanova's avatar
tstellanova committed
121
    missingReadCount = 0;
122 123 124 125
    QList<int> compIds = readsWaiting.keys();
    foreach (int compId, compIds) {
        missingReadCount += readsWaiting.value(compId)->count();
        readsWaiting.value(compId)->clear();
126 127
    }

tstellanova's avatar
tstellanova committed
128
    missingWriteCount = 0;
129 130 131 132
    compIds = writesWaiting.keys();
    foreach (int compId, compIds) {
        missingWriteCount += writesWaiting.value(compId)->count();
        writesWaiting.value(compId)->clear();
133 134 135 136 137
    }

}


138
void UASParameterCommsMgr::emitPendingParameterCommit(int compId, const QString& key, QVariant& value)
139 140 141 142 143 144 145
{
    int paramType = (int)value.type();
    switch (paramType)
    {
    case QVariant::Char:
    {
        QVariant fixedValue(QChar((unsigned char)value.toInt()));
146
        emit commitPendingParameter(compId, key, fixedValue);
147 148 149 150 151
    }
        break;
    case QVariant::Int:
    {
        QVariant fixedValue(value.toInt());
152
        emit commitPendingParameter(compId, key, fixedValue);
153 154 155 156 157
    }
        break;
    case QVariant::UInt:
    {
        QVariant fixedValue(value.toUInt());
158
        emit commitPendingParameter(compId, key, fixedValue);
159 160 161 162 163
    }
        break;
    case QMetaType::Float:
    {
        QVariant fixedValue(value.toFloat());
164
        emit commitPendingParameter(compId, key, fixedValue);
165 166 167
    }
        break;
    default:
168
        qCritical() << "ABORTED PARAM SEND, INVALID QVARIANT TYPE" << paramType;
169 170 171
        return;
    }

172
    setParameterStatusMsg(tr("Writing %1: %2 for comp. %3").arg(key).arg(value.toDouble()).arg(compId));
173 174 175 176 177 178 179 180 181

}


void UASParameterCommsMgr::resendReadWriteRequests()
{
    int compId;
    QList<int> compIds;

182
    // Re-request at maximum retransmitBurstLimit parameters at once
183 184
    // to prevent link flooding'
    int requestedReadCount = 0;
185
    compIds = readsWaiting.keys();
186 187
    foreach (compId, compIds) {
        // Request n parameters from this component (at maximum)
188 189
        QSet<int>* missingReadParams = readsWaiting.value(compId, NULL);
        qDebug() << "compId " << compId << "readsWaiting:" << missingReadParams->count();
190
        foreach (int paramId, *missingReadParams) {
191 192 193 194 195 196
            if (0 == paramId && 0 == compId) {
                mav->requestParameters();
                //don't request any other params individually for this component
                break;
            }
            if (requestedReadCount < retransmitBurstLimit) {
197
                //qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD REQUESTS RETRANSMISSION OF PARAM #" << paramId << "FROM COMPONENT #" << compId;
198 199 200 201 202 203 204 205 206 207 208
                emit parameterUpdateRequestedById(compId, paramId);
                setParameterStatusMsg(tr("Requested retransmission of #%1").arg(paramId+1));
                requestedReadCount++;
            }
            else {
                qDebug() << "Throttling read retransmit requests at" << requestedReadCount;
                break;
            }
        }
    }

209
    // Re-request at maximum retransmitBurstLimit parameters at once
210 211
    // to prevent write-request link flooding
    int requestedWriteCount = 0;
212
    compIds = writesWaiting.keys();
213
    foreach (compId, compIds) {
214 215 216
        QMap <QString, QVariant>* missingWriteParams = writesWaiting.value(compId);
        foreach (QString key, missingWriteParams->keys()) {
            if (requestedWriteCount < retransmitBurstLimit) {
217
                // Re-request write operation
218
                QVariant value = missingWriteParams->value(key);
219
                emitPendingParameterCommit(compId, key, value);
220 221 222 223 224 225 226 227 228
                requestedWriteCount++;
            }
            else {
                qDebug() << "Throttling write retransmit requests at" << requestedWriteCount;
                break;
            }
        }
    }

229 230
    updateSilenceTimer();

231 232
}

233 234 235
void UASParameterCommsMgr::resetAfterListReceive()
{
    transmissionListMode = false;
236
    knownParamListSize.clear();
237 238
}

239
void UASParameterCommsMgr::silenceTimerExpired()
240
{
241
    quint64 curTime = QGC::groundTimeMilliseconds();
242 243
    int elapsed = (int)(curTime - lastSilenceTimerReset);
    qDebug() << "silenceTimerExpired elapsed:" << elapsed;
244

245
    if (elapsed < silenceTimeout) {
246
        //reset the guard timer: it fired prematurely
247
        updateSilenceTimer();
248 249
        return;
    }
250

251 252
    int totalElapsed = (int)(curTime - lastReceiveTime);
    if (totalElapsed > maxSilenceTimeout) {
253
        qDebug() << "maxSilenceTimeout exceeded: " << totalElapsed;
254 255
        int missingReads, missingWrites;
        clearRetransmissionLists(missingReads,missingWrites);
256 257
        silenceTimer.stop();
        lastSilenceTimerReset = curTime;
258
        setParameterStatusMsg(tr("TIMEOUT: Abandoning %1 reads %2 writes after %3 seconds").arg(missingReads).arg(missingWrites).arg(totalElapsed/1000));
259 260
    }
    else {
261
        resendReadWriteRequests();
262 263 264 265
    }
}


266
void UASParameterCommsMgr::requestParameterUpdate(int compId, const QString& paramName)
267 268
{
    if (mav) {
269 270 271
        mav->requestParameter(compId, paramName);
        //TODO track these read requests with a paramName but no param ID  : use index in getOnboardParamsForComponent?
        //ensure we keep track of every single read request
272 273 274
    }
}

275 276 277 278 279 280 281 282 283 284 285
void UASParameterCommsMgr::requestRcCalibrationParamsUpdate()
{
    if (!transmissionListMode) {
        QString minTpl("RC%1_MIN");
        QString maxTpl("RC%1_MAX");
        QString trimTpl("RC%1_TRIM");
        QString revTpl("RC%1_REV");

        // Do not request the RC type, as these values depend on this
        // active onboard parameter

286 287

        int defCompId = paramDataModel->getDefaultComponentId();
288 289
        for (unsigned int i = 1; i < (RC_CAL_CHAN_MAX+1); ++i)  {
            qDebug() << "Request RC " << i;
290 291 292 293
            requestParameterUpdate(defCompId, minTpl.arg(i));
            requestParameterUpdate(defCompId, trimTpl.arg(i));
            requestParameterUpdate(defCompId, maxTpl.arg(i));
            requestParameterUpdate(defCompId, revTpl.arg(i));
294 295 296 297 298 299 300 301 302
            QGC::SLEEP::usleep(5000);
        }
    }
    else {
        qDebug() << __FILE__ << __LINE__ << "Ignoring requestRcCalibrationParamsUpdate because we're receiving params list";
    }
}


303 304 305 306 307
/**
 * @param component the subsystem which has the parameter
 * @param parameterName name of the parameter, as delivered by the system
 * @param value value of the parameter
 */
308
void UASParameterCommsMgr::setParameter(int compId, QString paramName, QVariant value)
309
{
310
    if (paramName.isEmpty()) {
311 312 313 314 315
        return;
    }

    double dblValue = value.toDouble();

316 317
    if (paramDataModel->isValueLessThanParamMin(paramName,dblValue)) {
        setParameterStatusMsg(tr("REJ. %1, %2 < min").arg(paramName).arg(dblValue),
tstellanova's avatar
tstellanova committed
318 319
                              ParamCommsStatusLevel_Error
                              );
320 321
        return;
    }
322 323
    if (paramDataModel->isValueGreaterThanParamMax(paramName,dblValue)) {
        setParameterStatusMsg(tr("REJ. %1, %2 > max").arg(paramName).arg(dblValue),
tstellanova's avatar
tstellanova committed
324 325
                              ParamCommsStatusLevel_Error
                              );
326 327
        return;
    }
328

329
    QVariant onboardVal;
330
    paramDataModel->getOnboardParamValue(compId,paramName,onboardVal);
331
    if (onboardVal == value) {
332
        setParameterStatusMsg(tr("REJ. %1 already %2").arg(paramName).arg(dblValue),
tstellanova's avatar
tstellanova committed
333 334
                              ParamCommsStatusLevel_Warning
                              );
335 336 337
        return;
    }

338 339 340
    emitPendingParameterCommit(compId, paramName, value);

    //Add this request to list of writes not yet ack'd
341

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    markWriteParamWaiting( compId,  paramName,  value);
    updateSilenceTimer();


}

void UASParameterCommsMgr::updateSilenceTimer()
{
    //if there are pending reads or writes, ensure we timeout in a little while
    //if we hear nothing but silence from our partner

    int missReadCount = 0;
    foreach (int key, readsWaiting.keys()) {
        missReadCount +=  readsWaiting.value(key)->count();
    }

    int missWriteCount = 0;
    foreach (int key, writesWaiting.keys()) {
        missWriteCount += writesWaiting.value(key)->count();
361 362 363
    }


364
    if (missReadCount > 0 || missWriteCount > 0) {
365
        silenceTimer.start(silenceTimeout);
366
        lastSilenceTimerReset = QGC::groundTimeMilliseconds();
367
    }
368
    else {
369 370 371 372
        //all parameters have been received, broadcast to UI
        emit parameterListUpToDate();
        resetAfterListReceive();
        silenceTimer.stop();
373 374
    }

375 376


377 378
}

379

tstellanova's avatar
tstellanova committed
380
void UASParameterCommsMgr::setParameterStatusMsg(const QString& msg, ParamCommsStatusLevel_t level)
381 382
{
    qDebug() << "parameterStatusMsg: " << msg;
tstellanova's avatar
tstellanova committed
383
    emit parameterStatusMsgUpdated(msg,level);
384
}
tstellanova's avatar
tstellanova committed
385 386 387

void UASParameterCommsMgr::receivedParameterUpdate(int uas, int compId, int paramCount, int paramId, QString paramName, QVariant value)
{
388
    Q_UNUSED(uas); //this object is assigned to one UAS only
389
    lastReceiveTime = QGC::groundTimeMilliseconds();
390 391
    qDebug() << "compId" << compId << "receivedParameterUpdate:" << paramName;

392
    //notify the data model that we have an updated param
393
    paramDataModel->handleParamUpdate(compId,paramName,value);
tstellanova's avatar
tstellanova committed
394 395


396 397 398 399 400 401 402
    // Ensure we have missing read/write lists for this compId
    if (!readsWaiting.contains(compId)) {
        readsWaiting.insert(compId, new QSet<int>());
    }
    if (!writesWaiting.contains(compId) ) {
        writesWaiting.insert(compId,new QMap<QString,QVariant>());
    }
403

404
    QSet<int>* compMissingReads =  readsWaiting.value(compId);
tstellanova's avatar
tstellanova committed
405 406
    // List mode is different from single parameter transfers
    if (transmissionListMode) {
407 408
        // Only accept the list size once on the first packet from each component
        if (!knownParamListSize.contains(compId)) {
tstellanova's avatar
tstellanova committed
409
            // Mark list size as known
410
            knownParamListSize.insert(compId,paramCount);
tstellanova's avatar
tstellanova committed
411

412 413
            //remove our placeholder read request for all params
            readsWaiting.value(0)->remove(0);
tstellanova's avatar
tstellanova committed
414

415 416 417
            qDebug() << "Mark all parameters as missing: " << paramCount;
            for (int i = 1; i < paramCount; ++i) { //param Id 0 is  "all parameters" and not valid
                compMissingReads->insert(i);
tstellanova's avatar
tstellanova committed
418 419 420 421
            }
        }
    }

422

tstellanova's avatar
tstellanova committed
423
    // Mark this parameter as received in read list
424 425
    compMissingReads->remove(paramId);

tstellanova's avatar
tstellanova committed
426 427 428

    bool justWritten = false;
    bool writeMismatch = false;
429

tstellanova's avatar
tstellanova committed
430
    // Mark this parameter as received in write ACK list
431 432
    QMap<QString, QVariant>* compMissingWrites = writesWaiting.value(compId);
    if (!compMissingWrites) {
433
        //we sometimes send a write request on compId 0 and get a response on a nonzero compId eg 50
434
        compMissingWrites = writesWaiting.value(0);
435
    }
436
    if (compMissingWrites && compMissingWrites->contains(paramName)) {
tstellanova's avatar
tstellanova committed
437
        justWritten = true;
438
        if (compMissingWrites->value(paramName) != value) {
tstellanova's avatar
tstellanova committed
439 440
            writeMismatch = true;
        }
441
        compMissingWrites->remove(paramName);
tstellanova's avatar
tstellanova committed
442 443 444
    }


445
    if (justWritten) {
446
        int waitingWritesCount = compMissingWrites->count();
447
        if (!writeMismatch) {
448 449 450 451 452 453
            setParameterStatusMsg(tr("SUCCESS: Wrote %2 (#%1): %3").arg(paramId+1).arg(paramName).arg(value.toDouble()));
        }

        if (!writeMismatch) {
            if (0 == waitingWritesCount) {
                setParameterStatusMsg(tr("SUCCESS: Wrote all params for component %1").arg(compId));
454 455 456 457
                if (persistParamsAfterSend) {
                    writeParamsToPersistentStorage();
                    persistParamsAfterSend = false;
                }
458 459 460 461
            }
        }
        else  {
            // Mismatch, tell user
462
            setParameterStatusMsg(tr("FAILURE: Wrote %1: sent %2 != onboard %3").arg(paramName).arg(compMissingWrites->value(paramName).toDouble()).arg(value.toDouble()),
463 464
                                  ParamCommsStatusLevel_Warning);
        }
tstellanova's avatar
tstellanova committed
465 466
    }
    else {
467 468 469
        int waitingReadsCount = compMissingReads->count();

        if (0 == waitingReadsCount) {
tstellanova's avatar
tstellanova committed
470 471 472 473 474 475
            // Transmission done
            QTime time = QTime::currentTime();
            QString timeString = time.toString();
            setParameterStatusMsg(tr("All received. (updated at %1)").arg(timeString));
        }
        else {
476
            // Waiting to receive more
477
            QString val = QString("%1").arg(value.toFloat(), 5, 'f', 1, QChar(' '));
478 479
            setParameterStatusMsg(tr("OK: %1 %2 (%3/%4)").arg(paramName).arg(val).arg(paramCount-waitingReadsCount).arg(paramCount),
                                  ParamCommsStatusLevel_OK);
tstellanova's avatar
tstellanova committed
480 481 482
        }
    }

483 484 485
    updateSilenceTimer();


tstellanova's avatar
tstellanova committed
486 487 488 489 490 491 492
}


void UASParameterCommsMgr::writeParamsToPersistentStorage()
{
    if (mav) {
        mav->writeParametersToStorage(); //TODO track timeout, retransmit etc?
493
        persistParamsAfterSend = false; //done
tstellanova's avatar
tstellanova committed
494 495 496 497
    }
}


498
void UASParameterCommsMgr::sendPendingParameters(bool copyToPersistent)
tstellanova's avatar
tstellanova committed
499
{
500 501
    persistParamsAfterSend |= copyToPersistent;

tstellanova's avatar
tstellanova committed
502 503
    // Iterate through all components, through all pending parameters and send them to UAS
    int parametersSent = 0;
504
    QMap<int, QMap<QString, QVariant>*>* changedValues = paramDataModel->getAllPendingParams();
tstellanova's avatar
tstellanova committed
505
    QMap<int, QMap<QString, QVariant>*>::iterator i;
506
    for (i = changedValues->begin(); i != changedValues->end(); ++i) {
tstellanova's avatar
tstellanova committed
507
        // Iterate through the parameters of the component
tstellanova's avatar
tstellanova committed
508 509 510 511 512 513 514 515
        int compId = i.key();
        QMap<QString, QVariant>* paramList = i.value();
        QMap<QString, QVariant>::iterator j;
        setParameterStatusMsg(tr("%1 pending params for component %2").arg(paramList->count()).arg(compId));

        for (j = paramList->begin(); j != paramList->end(); ++j) {
            setParameter(compId, j.key(), j.value());
            parametersSent++;
tstellanova's avatar
tstellanova committed
516 517 518 519
        }
    }

    // Change transmission status if necessary
520
    if (0 == parametersSent) {
tstellanova's avatar
tstellanova committed
521
        setParameterStatusMsg(tr("No transmission: No changed values."),ParamCommsStatusLevel_Warning);
522 523
    }
    else {
tstellanova's avatar
tstellanova committed
524
        setParameterStatusMsg(tr("Transmitting %1 parameters.").arg(parametersSent));
525
        qDebug() << "Pending parameters now:" << paramDataModel->countPendingParams();
tstellanova's avatar
tstellanova committed
526
    }
527 528 529


    updateSilenceTimer();
tstellanova's avatar
tstellanova committed
530 531
}

532 533
UASParameterCommsMgr::~UASParameterCommsMgr()
{
534
    silenceTimer.stop();
535 536 537 538 539

    QString ptrStr;
    ptrStr.sprintf("%8p", this);
    qDebug() <<  "UASParameterCommsMgr destructor: " << ptrStr ;

540 541
}