QGCParamWidget.cc 29.5 KB
Newer Older
1 2 3 4
/*=====================================================================

QGroundControl Open Source Ground Control Station

pixhawk's avatar
pixhawk committed
5
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

This file is part of the QGROUNDCONTROL project

    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.

    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.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/
/**
 * @file
 *   @brief Implementation of class QGCParamWidget
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
 */

29 30
#include <QGridLayout>
#include <QPushButton>
31 32
#include <QFileDialog>
#include <QFile>
33
#include <QList>
34
#include <QSettings>
pixhawk's avatar
pixhawk committed
35 36 37 38

#include "QGCParamWidget.h"
#include "UASInterface.h"
#include <QDebug>
39
#include "QGC.h"
pixhawk's avatar
pixhawk committed
40

41 42 43 44
/**
 * @param uas MAV to set the parameters on
 * @param parent Parent widget
 */
pixhawk's avatar
pixhawk committed
45
QGCParamWidget::QGCParamWidget(UASInterface* uas, QWidget *parent) :
46 47
    QGCUASParamManager(uas, parent),
    components(new QMap<int, QTreeWidgetItem*>())
pixhawk's avatar
pixhawk committed
48
{
49 50 51
    // Load settings
    loadSettings();

pixhawk's avatar
pixhawk committed
52 53
    // Create tree widget
    tree = new QTreeWidget(this);
lm's avatar
lm committed
54
    statusLabel = new QLabel();
lm's avatar
lm committed
55
    statusLabel->setAutoFillBackground(true);
56
    tree->setColumnWidth(0, 150);
pixhawk's avatar
pixhawk committed
57 58

    // Set tree widget as widget onto this component
59
    QGridLayout* horizontalLayout;
pixhawk's avatar
pixhawk committed
60
    //form->setAutoFillBackground(false);
61 62
    horizontalLayout = new QGridLayout(this);
    horizontalLayout->setSpacing(6);
pixhawk's avatar
pixhawk committed
63 64 65
    horizontalLayout->setMargin(0);
    horizontalLayout->setSizeConstraint(QLayout::SetMinimumSize);

lm's avatar
lm committed
66
    // Parameter tree
67
    horizontalLayout->addWidget(tree, 0, 0, 1, 3);
lm's avatar
lm committed
68 69

    // Status line
lm's avatar
lm committed
70
    statusLabel->setText(tr("Click refresh to download parameters"));
lm's avatar
lm committed
71 72 73 74
    horizontalLayout->addWidget(statusLabel, 1, 0, 1, 3);


    // BUTTONS
75
    QPushButton* refreshButton = new QPushButton(tr("Refresh"));
76 77
    refreshButton->setToolTip(tr("Load parameters currently in non-permanent memory of aircraft."));
    refreshButton->setWhatsThis(tr("Load parameters currently in non-permanent memory of aircraft."));
78
    connect(refreshButton, SIGNAL(clicked()), this, SLOT(requestParameterList()));
lm's avatar
lm committed
79
    horizontalLayout->addWidget(refreshButton, 2, 0);
80

81
    QPushButton* setButton = new QPushButton(tr("Transmit"));
82 83
    setButton->setToolTip(tr("Set current parameters in non-permanent onboard memory"));
    setButton->setWhatsThis(tr("Set current parameters in non-permanent onboard memory"));
84
    connect(setButton, SIGNAL(clicked()), this, SLOT(setParameters()));
lm's avatar
lm committed
85
    horizontalLayout->addWidget(setButton, 2, 1);
86

87
    QPushButton* writeButton = new QPushButton(tr("Write (ROM)"));
88 89
    writeButton->setToolTip(tr("Copy current parameters in non-permanent memory of the aircraft to permanent memory. Transmit your parameters first to write these."));
    writeButton->setWhatsThis(tr("Copy current parameters in non-permanent memory of the aircraft to permanent memory. Transmit your parameters first to write these."));
90
    connect(writeButton, SIGNAL(clicked()), this, SLOT(writeParameters()));
lm's avatar
lm committed
91
    horizontalLayout->addWidget(writeButton, 2, 2);
92

93
    QPushButton* loadFileButton = new QPushButton(tr("Load File"));
94 95
    loadFileButton->setToolTip(tr("Load parameters from a file on this computer in the view. To write them to the aircraft, use transmit after loading them."));
    loadFileButton->setWhatsThis(tr("Load parameters from a file on this computer in the view. To write them to the aircraft, use transmit after loading them."));
96
    connect(loadFileButton, SIGNAL(clicked()), this, SLOT(loadParameters()));
lm's avatar
lm committed
97
    horizontalLayout->addWidget(loadFileButton, 3, 0);
98 99

    QPushButton* saveFileButton = new QPushButton(tr("Save File"));
100 101
    saveFileButton->setToolTip(tr("Save parameters in this view to a file on this computer."));
    saveFileButton->setWhatsThis(tr("Save parameters in this view to a file on this computer."));
102
    connect(saveFileButton, SIGNAL(clicked()), this, SLOT(saveParameters()));
lm's avatar
lm committed
103 104 105 106 107 108 109
    horizontalLayout->addWidget(saveFileButton, 3, 1);

    QPushButton* readButton = new QPushButton(tr("Read (ROM)"));
    readButton->setToolTip(tr("Copy parameters from permanent memory to non-permanent current memory of aircraft. DOES NOT update the parameters in this view, click refresh after copying them to get them."));
    readButton->setWhatsThis(tr("Copy parameters from permanent memory to non-permanent current memory of aircraft. DOES NOT update the parameters in this view, click refresh after copying them to get them."));
    connect(readButton, SIGNAL(clicked()), this, SLOT(readParameters()));
    horizontalLayout->addWidget(readButton, 3, 2);
110

111
    // Set layout
pixhawk's avatar
pixhawk committed
112 113 114 115 116 117 118 119
    this->setLayout(horizontalLayout);

    // Set header
    QStringList headerItems;
    headerItems.append("Parameter");
    headerItems.append("Value");
    tree->setHeaderLabels(headerItems);
    tree->setColumnCount(2);
120
    tree->setExpandsOnDoubleClick(true);
121 122 123

    // Connect signals/slots
    connect(this, SIGNAL(parameterChanged(int,QString,float)), mav, SLOT(setParameter(int,QString,float)));
124
    connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(parameterItemChanged(QTreeWidgetItem*,int)));
lm's avatar
lm committed
125

126
    // New parameters from UAS
lm's avatar
lm committed
127
    connect(uas, SIGNAL(parameterChanged(int,int,int,int,QString,float)), this, SLOT(addParameter(int,int,int,int,QString,float)));
128 129 130 131

    // Connect retransmission guard
    connect(this, SIGNAL(requestParameter(int,int)), uas, SLOT(requestParameter(int,int)));
    connect(&retransmissionTimer, SIGNAL(timeout()), this, SLOT(retransmissionGuardTick()));
pixhawk's avatar
pixhawk committed
132 133
}

134 135 136 137 138 139 140 141 142 143 144 145
void QGCParamWidget::loadSettings()
{
    QSettings settings;
    settings.beginGroup("QGC_MAVLINK_PROTOCOL");
    bool ok;
    int temp = settings.value("PARAMETER_RETRANSMISSION_TIMEOUT", retransmissionTimeout).toInt(&ok);
    if (ok) retransmissionTimeout = temp;
    temp = settings.value("PARAMETER_REWRITE_TIMEOUT", rewriteTimeout).toInt(&ok);
    if (ok) rewriteTimeout = temp;
    settings.endGroup();
}

146 147 148 149
/**
 * @return The MAV of this widget. Unless the MAV object has been destroyed, this
 *         pointer is never zero.
 */
pixhawk's avatar
pixhawk committed
150 151 152 153 154 155 156 157 158 159 160
UASInterface* QGCParamWidget::getUAS()
{
    return mav;
}

/**
 *
 * @param uas System which has the component
 * @param component id of the component
 * @param componentName human friendly name of the component
 */
161
void QGCParamWidget::addComponent(int uas, int component, QString componentName)
pixhawk's avatar
pixhawk committed
162
{
163
    Q_UNUSED(uas);
164
    if (components->contains(component)) {
pixhawk's avatar
pixhawk committed
165 166 167
        // Update existing
        components->value(component)->setData(0, Qt::DisplayRole, componentName);
        components->value(component)->setData(1, Qt::DisplayRole, QString::number(component));
168
    } else {
pixhawk's avatar
pixhawk committed
169 170 171 172 173 174 175 176
        // Add new
        QStringList list;
        list.append(componentName);
        list.append(QString::number(component));
        QTreeWidgetItem* comp = new QTreeWidgetItem(list);
        components->insert(component, comp);
        // Create grouping and update maps
        paramGroups.insert(component, new QMap<QString, QTreeWidgetItem*>());
pixhawk's avatar
pixhawk committed
177 178
        tree->addTopLevelItem(comp);
        tree->update();
179
        // Create map in parameters
180
        if (!parameters.contains(component)) {
181 182 183
            parameters.insert(component, new QMap<QString, float>());
        }
        // Create map in changed parameters
184
        if (!changedValues.contains(component)) {
185 186
            changedValues.insert(component, new QMap<QString, float>());
        }
pixhawk's avatar
pixhawk committed
187 188 189
    }
}

lm's avatar
lm committed
190 191 192 193 194 195 196 197 198
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
void QGCParamWidget::addParameter(int uas, int component, int paramCount, int paramId, QString parameterName, float value)
{
    addParameter(uas, component, parameterName, value);

lm's avatar
lm committed
199
    // Missing packets list has to be instantiated for all components
200
    if (!transmissionMissingPackets.contains(component)) {
lm's avatar
lm committed
201 202 203
        transmissionMissingPackets.insert(component, new QList<int>());
    }

lm's avatar
lm committed
204
    // List mode is different from single parameter transfers
205
    if (transmissionListMode) {
lm's avatar
lm committed
206 207
        // Only accept the list size once on the first packet from
        // each component
208
        if (!transmissionListSizeKnown.contains(component)) {
lm's avatar
lm committed
209 210
            // Mark list size as known
            transmissionListSizeKnown.insert(component, true);
lm's avatar
lm committed
211

212
            // Mark all parameters as missing
213 214
            for (int i = 0; i < paramCount; ++i) {
                if (!transmissionMissingPackets.value(component)->contains(i)) {
lm's avatar
lm committed
215 216 217
                    transmissionMissingPackets.value(component)->append(i);
                }
            }
218

lm's avatar
lm committed
219 220 221 222
            // There is only one transmission timeout for all components
            // since components do not manage their transmission,
            // the longest timeout is safe for all components.
            quint64 thisTransmissionTimeout = QGC::groundTimeMilliseconds() + ((paramCount/retransmissionBurstRequestSize+5)*retransmissionTimeout);
223
            if (thisTransmissionTimeout > transmissionTimeout) {
lm's avatar
lm committed
224 225
                transmissionTimeout = thisTransmissionTimeout;
            }
lm's avatar
lm committed
226
        }
lm's avatar
lm committed
227 228 229 230

        // Start retransmission guard
        // or reset timer
        setRetransmissionGuardEnabled(true);
lm's avatar
lm committed
231 232
    }

lm's avatar
lm committed
233
    // Mark this parameter as received in read list
lm's avatar
lm committed
234 235 236 237
    int index = transmissionMissingPackets.value(component)->indexOf(paramId);
    // If the MAV sent the parameter without request, it wont be in missing list
    if (index != -1) transmissionMissingPackets.value(component)->removeAt(index);

lm's avatar
lm committed
238 239
    bool justWritten = false;
    bool writeMismatch = false;
240
    //bool lastWritten = false;
lm's avatar
lm committed
241 242
    // Mark this parameter as received in write ACK list
    QMap<QString, float>* map = transmissionMissingWriteAckPackets.value(component);
243
    if (map && map->contains(parameterName)) {
lm's avatar
lm committed
244
        justWritten = true;
245
        if (map->value(parameterName) != value) {
lm's avatar
lm committed
246 247 248 249 250
            writeMismatch = true;
        }
        map->remove(parameterName);
    }

251
    int missCount = 0;
252
    foreach (int key, transmissionMissingPackets.keys()) {
253 254 255
        missCount +=  transmissionMissingPackets.value(key)->count();
    }

lm's avatar
lm committed
256
    int missWriteCount = 0;
257
    foreach (int key, transmissionMissingWriteAckPackets.keys()) {
lm's avatar
lm committed
258 259 260
        missWriteCount += transmissionMissingWriteAckPackets.value(key)->count();
    }

261
    if (justWritten && !writeMismatch && missWriteCount == 0) {
lm's avatar
lm committed
262 263 264 265 266
        // Just wrote one and count went to 0 - this was the last missing write parameter
        statusLabel->setText(tr("SUCCESS: WROTE ALL PARAMETERS"));
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorGreen);
        statusLabel->setPalette(pal);
267
    } else if (justWritten && !writeMismatch) {
lm's avatar
lm committed
268
        statusLabel->setText(tr("SUCCESS: Wrote %2 (#%1/%4): %3").arg(paramId+1).arg(parameterName).arg(value).arg(paramCount));
lm's avatar
lm committed
269 270 271
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorGreen);
        statusLabel->setPalette(pal);
272
    } else if (justWritten && writeMismatch) {
lm's avatar
lm committed
273
        // Mismatch, tell user
lm's avatar
lm committed
274 275 276
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorRed);
        statusLabel->setPalette(pal);
lm's avatar
lm committed
277
        statusLabel->setText(tr("FAILURE: Wrote %1: sent %2 != onboard %3").arg(parameterName).arg(map->value(parameterName)).arg(value));
278 279
    } else {
        if (missCount > 0) {
lm's avatar
lm committed
280 281 282
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorOrange);
            statusLabel->setPalette(pal);
283
        } else {
lm's avatar
lm committed
284 285 286 287
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorGreen);
            statusLabel->setPalette(pal);
        }
lm's avatar
lm committed
288 289
        statusLabel->setText(tr("Got %2 (#%1/%5): %3 (%4 missing)").arg(paramId+1).arg(parameterName).arg(value).arg(missCount).arg(paramCount));
    }
lm's avatar
lm committed
290 291

    // Check if last parameter was received
292
    if (missCount == 0 && missWriteCount == 0) {
lm's avatar
lm committed
293 294 295
        this->transmissionActive = false;
        this->transmissionListMode = false;
        transmissionListSizeKnown.clear();
296
        foreach (int key, transmissionMissingPackets.keys()) {
lm's avatar
lm committed
297 298 299 300 301
            transmissionMissingPackets.value(key)->clear();
        }
    }
}

302 303 304 305 306 307
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
void QGCParamWidget::addParameter(int uas, int component, QString parameterName, float value)
pixhawk's avatar
pixhawk committed
308
{
309
    Q_UNUSED(uas);
310
    // Reference to item in tree
311
    QTreeWidgetItem* parameterItem = NULL;
pixhawk's avatar
pixhawk committed
312 313

    // Get component
314
    if (!components->contains(component)) {
315
        QString componentName;
316
        switch (component) {
317 318 319 320 321 322 323 324 325 326 327 328
        case MAV_COMP_ID_CAMERA:
            componentName = tr("Camera (#%1)").arg(component);
            break;
        case MAV_COMP_ID_IMU:
            componentName = tr("IMU (#%1)").arg(component);
            break;
        default:
            componentName = tr("Component #").arg(component);
            break;
        }

        addComponent(uas, component, componentName);
pixhawk's avatar
pixhawk committed
329
    }
330

331 332 333 334 335 336 337
    // Replace value in map

    // FIXME
    if (parameters.value(component)->contains(parameterName)) parameters.value(component)->remove(parameterName);
    parameters.value(component)->insert(parameterName, value);


pixhawk's avatar
pixhawk committed
338 339
    QString splitToken = "_";
    // Check if auto-grouping can work
340
    if (parameterName.contains(splitToken)) {
pixhawk's avatar
pixhawk committed
341 342
        QString parent = parameterName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(component);
343
        if (!compParamGroups->contains(parent)) {
pixhawk's avatar
pixhawk committed
344 345 346 347 348 349
            // Insert group item
            QStringList glist;
            glist.append(parent);
            QTreeWidgetItem* item = new QTreeWidgetItem(glist);
            compParamGroups->insert(parent, item);
            components->value(component)->addChild(item);
350
        }
351 352 353 354

        // Append child to group
        bool found = false;
        QTreeWidgetItem* parentItem = compParamGroups->value(parent);
355
        for (int i = 0; i < parentItem->childCount(); i++) {
356 357
            QTreeWidgetItem* child = parentItem->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
358
            if (key == parameterName) {
359 360 361 362 363 364 365
                //qDebug() << "UPDATED CHILD";
                parameterItem = child;
                parameterItem->setData(1, Qt::DisplayRole, value);
                found = true;
            }
        }

366
        if (!found) {
367 368 369 370
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
            plist.append(QString::number(value));
371
            // CREATE PARAMETER ITEM
372
            parameterItem = new QTreeWidgetItem(plist);
373
            // CONFIGURE PARAMETER ITEM
374 375

            compParamGroups->value(parent)->addChild(parameterItem);
lm's avatar
lm committed
376
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
377
        }
378
    } else {
pixhawk's avatar
pixhawk committed
379 380
        bool found = false;
        QTreeWidgetItem* parent = components->value(component);
381
        for (int i = 0; i < parent->childCount(); i++) {
pixhawk's avatar
pixhawk committed
382 383
            QTreeWidgetItem* child = parent->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
384
            if (key == parameterName) {
pixhawk's avatar
pixhawk committed
385
                //qDebug() << "UPDATED CHILD";
386 387
                parameterItem = child;
                parameterItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
388 389 390 391
                found = true;
            }
        }

392
        if (!found) {
393 394 395 396
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
            plist.append(QString::number(value));
397
            // CREATE PARAMETER ITEM
398
            parameterItem = new QTreeWidgetItem(plist);
399
            // CONFIGURE PARAMETER ITEM
400

lm's avatar
lm committed
401 402
            components->value(component)->addChild(parameterItem);
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
pixhawk's avatar
pixhawk committed
403
        }
404
        //tree->expandAll();
405
    }
406
    // Reset background color
407 408 409
    parameterItem->setBackground(0, QBrush(QColor(0, 0, 0)));
    parameterItem->setBackground(1, Qt::NoBrush);
    //tree->update();
pixhawk's avatar
pixhawk committed
410
    if (changedValues.contains(component)) changedValues.value(component)->remove(parameterName);
pixhawk's avatar
pixhawk committed
411 412
}

413 414 415 416
/**
 * Send a request to deliver the list of onboard parameters
 * to the MAV.
 */
417 418
void QGCParamWidget::requestParameterList()
{
419
    if (!mav) return;
420 421 422 423 424 425 426
    // FIXME This call does not belong here
    // Once the comm handling is moved to a new
    // Param manager class the settings can be directly
    // loaded from MAVLink protocol
    loadSettings();
    // End of FIXME

lm's avatar
lm committed
427
    // Clear view and request param list
428
    clear();
429
    parameters.clear();
lm's avatar
lm committed
430
    received.clear();
lm's avatar
lm committed
431 432 433
    // Clear transmission state
    transmissionListMode = true;
    transmissionListSizeKnown.clear();
434
    foreach (int key, transmissionMissingPackets.keys()) {
lm's avatar
lm committed
435 436 437
        transmissionMissingPackets.value(key)->clear();
    }
    transmissionActive = true;
lm's avatar
lm committed
438 439 440 441 442 443 444

    // Set status text
    statusLabel->setText(tr("Requested param list.. waiting"));

    // Request twice as mean of forward error correction
    mav->requestParameters();
    QGC::SLEEP::msleep(10);
lm's avatar
lm committed
445
    mav->requestParameters();
446 447
}

448
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column)
lm's avatar
lm committed
449
{
450
    if (current && column > 0) {
451
        QTreeWidgetItem* parent = current->parent();
452
        while (parent->parent() != NULL) {
453 454 455 456
            parent = parent->parent();
        }
        // Parent is now top-level component
        int key = components->key(parent);
457
        if (!changedValues.contains(key)) {
458 459 460
            changedValues.insert(key, new QMap<QString, float>());
        }
        QMap<QString, float>* map = changedValues.value(key, NULL);
461
        if (map) {
462 463 464
            bool ok;
            QString str = current->data(0, Qt::DisplayRole).toString();
            float value = current->data(1, Qt::DisplayRole).toDouble(&ok);
lm's avatar
lm committed
465
            // Set parameter on changed list to be transmitted to MAV
466 467
            if (ok) {
                if (ok) {
lm's avatar
lm committed
468 469
                    statusLabel->setText(tr("Changed Param %1:%2: %3").arg(key).arg(str).arg(value));
                    //qDebug() << "PARAM CHANGED: COMP:" << key << "KEY:" << str << "VALUE:" << value;
470
                    // Changed values list
471
                    if (map->contains(str)) map->remove(str);
472
                    map->insert(str, value);
473 474

                    // Check if the value was numerically changed
475
                    if (!parameters.value(key)->contains(str) || parameters.value(key)->value(str, 0.0f) != value) {
lm's avatar
lm committed
476 477
                        current->setBackground(0, QBrush(QColor(QGC::colorOrange)));
                        current->setBackground(1, QBrush(QColor(QGC::colorOrange)));
478 479 480 481 482 483 484 485 486 487 488 489
                    }

                    // All parameters list
                    if (parameters.value(key)->contains(str)) parameters.value(key)->remove(str);
                    parameters.value(key)->insert(str, value);
                }
            }
        }
    }
}

void QGCParamWidget::saveParameters()
490
{
491
    if (!mav) return;
492 493
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "./parameters.txt", tr("Parameter File (*.txt)"));
    QFile file(fileName);
494
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
495 496 497 498 499 500 501 502 503 504 505
        return;
    }

    QTextStream in(&file);

    in << "# Onboard parameters for system " << mav->getUASName() << "\n";
    in << "#\n";
    in << "# MAV ID  COMPONENT ID  PARAM NAME  VALUE (FLOAT)\n";

    // Iterate through all components, through all parameters and emit them
    QMap<int, QMap<QString, float>*>::iterator i;
506
    for (i = parameters.begin(); i != parameters.end(); ++i) {
507 508 509 510 511
        // Iterate through the parameters of the component
        int compid = i.key();
        QMap<QString, float>* comp = i.value();
        {
            QMap<QString, float>::iterator j;
512
            for (j = comp->begin(); j != comp->end(); ++j) {
513
                QString paramValue("%1");
lm's avatar
lm committed
514
                paramValue = paramValue.arg(j.value(), 25, 'g', 12);
515
                in << mav->getUASID() << "\t" << compid << "\t" << j.key() << "\t" << paramValue << "\n";
516 517 518 519 520 521 522 523 524
                in.flush();
            }
        }
    }
    file.close();
}

void QGCParamWidget::loadParameters()
{
525
    if (!mav) return;
526 527 528 529 530 531 532 533 534
    QString fileName = QFileDialog::getOpenFileName(this, tr("Load File"), ".", tr("Parameter file (*.txt)"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    // Clear list
    clear();

    QTextStream in(&file);
535
    while (!in.atEnd()) {
536
        QString line = in.readLine();
537
        if (!line.startsWith("#")) {
538
            QStringList wpParams = line.split("\t");
539
            if (wpParams.size() == 4) {
540
                // Only load parameters for right mav
541
                if (mav->getUASID() == wpParams.at(0).toInt()) {
pixhawk's avatar
pixhawk committed
542

lm's avatar
lm committed
543 544 545
                    bool changed = false;
                    int component = wpParams.at(1).toInt();
                    QString parameterName = wpParams.at(2);
546
                    if (!parameters.contains(component) || parameters.value(component)->value(parameterName, 0.0f) != (float)wpParams.at(3).toDouble()) {
lm's avatar
lm committed
547 548 549 550
                        changed = true;
                    }

                    // Set parameter value
551
                    addParameter(wpParams.at(0).toInt(), wpParams.at(1).toInt(), wpParams.at(2), wpParams.at(3).toDouble());
lm's avatar
lm committed
552

553
                    if (changed) {
lm's avatar
lm committed
554
                        // Create changed values data structure if necessary
555
                        if (!changedValues.contains(wpParams.at(1).toInt())) {
lm's avatar
lm committed
556
                            changedValues.insert(wpParams.at(1).toInt(), new QMap<QString, float>());
557 558
                        }

lm's avatar
lm committed
559
                        // Add to changed values
560
                        if (changedValues.value(wpParams.at(1).toInt())->contains(wpParams.at(2))) {
lm's avatar
lm committed
561 562 563 564 565
                            changedValues.value(wpParams.at(1).toInt())->remove(wpParams.at(2));
                        }

                        changedValues.value(wpParams.at(1).toInt())->insert(wpParams.at(2), (float)wpParams.at(3).toDouble());

566
                        //qDebug() << "MARKING COMP" << wpParams.at(1).toInt() << "PARAM" << wpParams.at(2) << "VALUE" << (float)wpParams.at(3).toDouble() << "AS CHANGED";
lm's avatar
lm committed
567 568

                        // Mark in UI
pixhawk's avatar
pixhawk committed
569

pixhawk's avatar
pixhawk committed
570

571
                    }
572
                }
lm's avatar
lm committed
573 574
            }
        }
575
    }
576 577
    file.close();

lm's avatar
lm committed
578 579
}

580 581 582 583 584 585 586 587 588
/**
 * Enabling the retransmission guard enables the parameter widget to track
 * dropped parameters and to re-request them. This works for both individual
 * parameter reads as well for whole list requests.
 *
 * @param enabled True if retransmission checking should be enabled, false else
 */
void QGCParamWidget::setRetransmissionGuardEnabled(bool enabled)
{
589
    if (enabled) {
590
        retransmissionTimer.start(retransmissionTimeout);
591
    } else {
592 593 594 595 596 597
        retransmissionTimer.stop();
    }
}

void QGCParamWidget::retransmissionGuardTick()
{
598
    if (transmissionActive) {
599 600
        qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD ACTIVE, CHECKING FOR DROPS..";

lm's avatar
lm committed
601 602
        // Check for timeout
        // stop retransmission attempts on timeout
603
        if (QGC::groundTimeMilliseconds() > transmissionTimeout) {
lm's avatar
lm committed
604 605 606 607 608 609 610
            setRetransmissionGuardEnabled(false);
            transmissionActive = false;

            // Empty read retransmission list
            // Empty write retransmission list
            int missingReadCount = 0;
            QList<int> readKeys = transmissionMissingPackets.keys();
611
            foreach (int component, readKeys) {
lm's avatar
lm committed
612 613 614 615 616 617 618
                missingReadCount += transmissionMissingPackets.value(component)->count();
                transmissionMissingPackets.value(component)->clear();
            }

            // Empty write retransmission list
            int missingWriteCount = 0;
            QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
619
            foreach (int component, writeKeys) {
lm's avatar
lm committed
620 621 622 623 624 625 626 627
                missingWriteCount += transmissionMissingWriteAckPackets.value(component)->count();
                transmissionMissingWriteAckPackets.value(component)->clear();
            }
            statusLabel->setText(tr("TIMEOUT! MISSING: %1 read, %2 write.").arg(missingReadCount).arg(missingWriteCount));
        }

        // Re-request at maximum retransmissionBurstRequestSize parameters at once
        // to prevent link flooding
628
        QMap<int, QMap<QString, float>*>::iterator i;
629
        for (i = parameters.begin(); i != parameters.end(); ++i) {
630 631
            // Iterate through the parameters of the component
            int component = i.key();
lm's avatar
lm committed
632
            // Request n parameters from this component (at maximum)
633
            QList<int> * paramList = transmissionMissingPackets.value(component, NULL);
634
            if (paramList) {
635
                int count = 0;
636 637
                foreach (int id, *paramList) {
                    if (count < retransmissionBurstRequestSize) {
638 639
                        qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD REQUESTS RETRANSMISSION OF PARAM #" << id << "FROM COMPONENT #" << component;
                        emit requestParameter(component, id);
lm's avatar
lm committed
640
                        statusLabel->setText(tr("Requested retransmission of #%1").arg(id+1));
641
                        count++;
642
                    } else {
643 644 645 646 647
                        break;
                    }
                }
            }
        }
lm's avatar
lm committed
648 649 650 651 652

        // Re-request at maximum retransmissionBurstRequestSize parameters at once
        // to prevent write-request link flooding
        // Empty write retransmission list
        QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
653
        foreach (int component, writeKeys) {
lm's avatar
lm committed
654 655
            int count = 0;
            QMap <QString, float>* missingParams = transmissionMissingWriteAckPackets.value(component);
656 657
            foreach (QString key, missingParams->keys()) {
                if (count < retransmissionBurstRequestSize) {
lm's avatar
lm committed
658 659
                    // Re-request write operation
                    emit parameterChanged(component, key, missingParams->value(key));
lm's avatar
lm committed
660
                    statusLabel->setText(tr("Requested rewrite of: %1: %2").arg(key).arg(missingParams->value(key)));
lm's avatar
lm committed
661
                    count++;
662
                } else {
lm's avatar
lm committed
663 664 665 666
                    break;
                }
            }
        }
667
    } else {
668 669 670 671 672
        qDebug() << __FILE__ << __LINE__ << "STOPPING RETRANSMISSION GUARD GRACEFULLY";
        setRetransmissionGuardEnabled(false);
    }
}

673

674 675 676 677 678 679 680 681 682
/**
 * The .. signal is emitted
 */
void QGCParamWidget::requestParameterUpdate(int component, const QString& parameter)
{

}


683 684 685 686 687 688 689
/**
 * @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
 */
void QGCParamWidget::setParameter(int component, QString parameterName, float value)
{
690
    emit parameterChanged(component, parameterName, value);
lm's avatar
lm committed
691 692
    // Wait for parameter to be written back
    // mark it therefore as missing
693
    if (!transmissionMissingWriteAckPackets.contains(component)) {
lm's avatar
lm committed
694 695 696 697 698 699 700 701 702
        transmissionMissingWriteAckPackets.insert(component, new QMap<QString, float>());
    }

    // Insert it in missing write ACK list
    transmissionMissingWriteAckPackets.value(component)->insert(parameterName, value);

    // Set timeouts
    transmissionActive = true;
    quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + 5*rewriteTimeout;
703
    if (newTransmissionTimeout > transmissionTimeout) {
lm's avatar
lm committed
704 705 706 707
        transmissionTimeout = newTransmissionTimeout;
    }
    // Enable guard / reset timeouts
    setRetransmissionGuardEnabled(true);
708 709
}

710 711 712
/**
 * Set all parameter in the parameter tree on the MAV
 */
713 714
void QGCParamWidget::setParameters()
{
715
    // Iterate through all components, through all parameters and emit them
716
    int parametersSent = 0;
717
    QMap<int, QMap<QString, float>*>::iterator i;
718
    for (i = changedValues.begin(); i != changedValues.end(); ++i) {
719 720 721 722 723
        // Iterate through the parameters of the component
        int compid = i.key();
        QMap<QString, float>* comp = i.value();
        {
            QMap<QString, float>::iterator j;
724
            for (j = comp->begin(); j != comp->end(); ++j) {
725
                setParameter(compid, j.key(), j.value());
726
                parametersSent++;
727 728 729 730
            }
        }
    }

lm's avatar
lm committed
731
    // Change transmission status if necessary
732
    if (parametersSent == 0) {
733
        statusLabel->setText(tr("No transmission: No changed values."));
734
    } else {
735
        statusLabel->setText(tr("Transmitting %1 parameters.").arg(parametersSent));
lm's avatar
lm committed
736 737 738
        // Set timeouts
        transmissionActive = true;
        quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + (parametersSent/retransmissionBurstRequestSize+5)*rewriteTimeout;
739
        if (newTransmissionTimeout > transmissionTimeout) {
lm's avatar
lm committed
740 741 742 743
            transmissionTimeout = newTransmissionTimeout;
        }
        // Enable guard
        setRetransmissionGuardEnabled(true);
744 745
    }

lm's avatar
lm committed
746
    changedValues.clear();
747 748
}

749 750 751 752
/**
 * Write the current onboard parameters from RAM into
 * permanent storage, e.g. EEPROM or harddisk
 */
753 754
void QGCParamWidget::writeParameters()
{
755
    if (!mav) return;
756 757 758 759 760
    mav->writeParametersToStorage();
}

void QGCParamWidget::readParameters()
{
761
    if (!mav) return;
762
    mav->readParametersFromStorage();
763 764
}

765 766 767
/**
 * Clear all data in the parameter widget
 */
pixhawk's avatar
pixhawk committed
768 769 770
void QGCParamWidget::clear()
{
    tree->clear();
lm's avatar
lm committed
771
    components->clear();
pixhawk's avatar
pixhawk committed
772
}