QGCParamWidget.cc 30.1 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);
pixhawk's avatar
pixhawk committed
164
    if (components->contains(component))
pixhawk's avatar
pixhawk committed
165
    {
pixhawk's avatar
pixhawk committed
166 167 168 169 170 171 172 173 174 175 176 177 178 179
        // Update existing
        components->value(component)->setData(0, Qt::DisplayRole, componentName);
        components->value(component)->setData(1, Qt::DisplayRole, QString::number(component));
    }
    else
    {
        // 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
180 181
        tree->addTopLevelItem(comp);
        tree->update();
182 183 184 185 186 187 188 189 190 191
        // Create map in parameters
        if (!parameters.contains(component))
        {
            parameters.insert(component, new QMap<QString, float>());
        }
        // Create map in changed parameters
        if (!changedValues.contains(component))
        {
            changedValues.insert(component, new QMap<QString, float>());
        }
pixhawk's avatar
pixhawk committed
192 193 194
    }
}

lm's avatar
lm committed
195 196 197 198 199 200 201 202 203
/**
 * @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
204 205 206 207 208 209
    // Missing packets list has to be instantiated for all components
    if (!transmissionMissingPackets.contains(component))
    {
        transmissionMissingPackets.insert(component, new QList<int>());
    }

lm's avatar
lm committed
210 211 212 213 214 215 216
    // List mode is different from single parameter transfers
    if (transmissionListMode)
    {
        // Only accept the list size once on the first packet from
        // each component
        if (!transmissionListSizeKnown.contains(component))
        {
lm's avatar
lm committed
217 218
            // Mark list size as known
            transmissionListSizeKnown.insert(component, true);
lm's avatar
lm committed
219

220
            // Mark all parameters as missing
lm's avatar
lm committed
221 222 223 224 225 226 227
            for (int i = 0; i < paramCount; ++i)
            {
                if (!transmissionMissingPackets.value(component)->contains(i))
                {
                    transmissionMissingPackets.value(component)->append(i);
                }
            }
228

lm's avatar
lm committed
229 230 231 232 233 234 235 236
            // 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);
            if (thisTransmissionTimeout > transmissionTimeout)
            {
                transmissionTimeout = thisTransmissionTimeout;
            }
lm's avatar
lm committed
237
        }
lm's avatar
lm committed
238 239 240 241

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

lm's avatar
lm committed
244
    // Mark this parameter as received in read list
lm's avatar
lm committed
245 246 247 248
    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
249 250
    bool justWritten = false;
    bool writeMismatch = false;
251
    //bool lastWritten = false;
lm's avatar
lm committed
252 253 254 255 256 257 258 259 260 261 262 263
    // Mark this parameter as received in write ACK list
    QMap<QString, float>* map = transmissionMissingWriteAckPackets.value(component);
    if (map && map->contains(parameterName))
    {
        justWritten = true;
        if (map->value(parameterName) != value)
        {
            writeMismatch = true;
        }
        map->remove(parameterName);
    }

264 265 266 267 268 269
    int missCount = 0;
    foreach (int key, transmissionMissingPackets.keys())
    {
        missCount +=  transmissionMissingPackets.value(key)->count();
    }

lm's avatar
lm committed
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    int missWriteCount = 0;
    foreach (int key, transmissionMissingWriteAckPackets.keys())
    {
        missWriteCount += transmissionMissingWriteAckPackets.value(key)->count();
    }

    if (justWritten && !writeMismatch && missWriteCount == 0)
    {
        // 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);
    }
    else if (justWritten && !writeMismatch)
lm's avatar
lm committed
285 286
    {
        statusLabel->setText(tr("SUCCESS: Wrote %2 (#%1/%4): %3").arg(paramId+1).arg(parameterName).arg(value).arg(paramCount));
lm's avatar
lm committed
287 288 289
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorGreen);
        statusLabel->setPalette(pal);
lm's avatar
lm committed
290 291 292 293
    }
    else if (justWritten && writeMismatch)
    {
        // Mismatch, tell user
lm's avatar
lm committed
294 295 296
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorRed);
        statusLabel->setPalette(pal);
lm's avatar
lm committed
297
        statusLabel->setText(tr("FAILURE: Wrote %1: sent %2 != onboard %3").arg(parameterName).arg(map->value(parameterName)).arg(value));
lm's avatar
lm committed
298 299 300
    }
    else
    {
lm's avatar
lm committed
301 302 303 304 305 306 307 308 309 310 311 312
        if (missCount > 0)
        {
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorOrange);
            statusLabel->setPalette(pal);
        }
        else
        {
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorGreen);
            statusLabel->setPalette(pal);
        }
lm's avatar
lm committed
313 314
        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
315 316

    // Check if last parameter was received
lm's avatar
lm committed
317
    if (missCount == 0 && missWriteCount == 0)
lm's avatar
lm committed
318 319 320 321 322 323 324 325 326 327 328
    {
        this->transmissionActive = false;
        this->transmissionListMode = false;
        transmissionListSizeKnown.clear();
        foreach (int key, transmissionMissingPackets.keys())
        {
            transmissionMissingPackets.value(key)->clear();
        }
    }
}

329 330 331 332 333 334
/**
 * @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
335
{
336
    Q_UNUSED(uas);
337
    // Reference to item in tree
338
    QTreeWidgetItem* parameterItem = NULL;
pixhawk's avatar
pixhawk committed
339 340 341 342

    // Get component
    if (!components->contains(component))
    {
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
        QString componentName;
        switch (component)
        {
        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
358
    }
359

360 361 362 363 364 365 366
    // 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
367 368 369
    QString splitToken = "_";
    // Check if auto-grouping can work
    if (parameterName.contains(splitToken))
370
    {
pixhawk's avatar
pixhawk committed
371 372 373
        QString parent = parameterName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(component);
        if (!compParamGroups->contains(parent))
374
        {
pixhawk's avatar
pixhawk committed
375 376 377 378 379 380
            // Insert group item
            QStringList glist;
            glist.append(parent);
            QTreeWidgetItem* item = new QTreeWidgetItem(glist);
            compParamGroups->insert(parent, item);
            components->value(component)->addChild(item);
381
        }
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

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

        if (!found)
        {
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
            plist.append(QString::number(value));
405
            // CREATE PARAMETER ITEM
406
            parameterItem = new QTreeWidgetItem(plist);
407
            // CONFIGURE PARAMETER ITEM
408 409

            compParamGroups->value(parent)->addChild(parameterItem);
lm's avatar
lm committed
410
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
411
        }
412
    }
pixhawk's avatar
pixhawk committed
413
    else
414
    {
pixhawk's avatar
pixhawk committed
415 416 417 418 419 420 421 422 423
        bool found = false;
        QTreeWidgetItem* parent = components->value(component);
        for (int i = 0; i < parent->childCount(); i++)
        {
            QTreeWidgetItem* child = parent->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
            if (key == parameterName)
            {
                //qDebug() << "UPDATED CHILD";
424 425
                parameterItem = child;
                parameterItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
426 427 428 429 430 431
                found = true;
            }
        }

        if (!found)
        {
432 433 434 435
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
            plist.append(QString::number(value));
436
            // CREATE PARAMETER ITEM
437
            parameterItem = new QTreeWidgetItem(plist);
438
            // CONFIGURE PARAMETER ITEM
439

lm's avatar
lm committed
440 441
            components->value(component)->addChild(parameterItem);
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
pixhawk's avatar
pixhawk committed
442
        }
443
        //tree->expandAll();
444
    }
445
    // Reset background color
446 447 448
    parameterItem->setBackground(0, QBrush(QColor(0, 0, 0)));
    parameterItem->setBackground(1, Qt::NoBrush);
    //tree->update();
pixhawk's avatar
pixhawk committed
449
    if (changedValues.contains(component)) changedValues.value(component)->remove(parameterName);
pixhawk's avatar
pixhawk committed
450 451
}

452 453 454 455
/**
 * Send a request to deliver the list of onboard parameters
 * to the MAV.
 */
456 457
void QGCParamWidget::requestParameterList()
{
458 459 460 461 462 463 464
    // 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
465
    // Clear view and request param list
466
    clear();
467
    parameters.clear();
lm's avatar
lm committed
468
    received.clear();
lm's avatar
lm committed
469 470 471 472 473 474 475 476
    // Clear transmission state
    transmissionListMode = true;
    transmissionListSizeKnown.clear();
    foreach (int key, transmissionMissingPackets.keys())
    {
        transmissionMissingPackets.value(key)->clear();
    }
    transmissionActive = true;
lm's avatar
lm committed
477 478 479 480 481 482 483

    // 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
484
    mav->requestParameters();
485 486
}

487
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column)
lm's avatar
lm committed
488
{
489
    if (current && column > 0)
lm's avatar
lm committed
490
    {
491 492 493 494 495 496 497 498 499 500 501 502 503
        QTreeWidgetItem* parent = current->parent();
        while (parent->parent() != NULL)
        {
            parent = parent->parent();
        }
        // Parent is now top-level component
        int key = components->key(parent);
        if (!changedValues.contains(key))
        {
            changedValues.insert(key, new QMap<QString, float>());
        }
        QMap<QString, float>* map = changedValues.value(key, NULL);
        if (map)
lm's avatar
lm committed
504
        {
505 506 507
            bool ok;
            QString str = current->data(0, Qt::DisplayRole).toString();
            float value = current->data(1, Qt::DisplayRole).toDouble(&ok);
lm's avatar
lm committed
508
            // Set parameter on changed list to be transmitted to MAV
509
            if (ok)
lm's avatar
lm committed
510
            {
511 512
                if (ok)
                {
lm's avatar
lm committed
513 514
                    statusLabel->setText(tr("Changed Param %1:%2: %3").arg(key).arg(str).arg(value));
                    //qDebug() << "PARAM CHANGED: COMP:" << key << "KEY:" << str << "VALUE:" << value;
515
                    // Changed values list
516
                    if (map->contains(str)) map->remove(str);
517
                    map->insert(str, value);
518 519 520 521

                    // Check if the value was numerically changed
                    if (!parameters.value(key)->contains(str) || parameters.value(key)->value(str, 0.0f) != value)
                    {
lm's avatar
lm committed
522 523
                        current->setBackground(0, QBrush(QColor(QGC::colorOrange)));
                        current->setBackground(1, QBrush(QColor(QGC::colorOrange)));
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
                    }

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

void QGCParamWidget::saveParameters()
{    
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "./parameters.txt", tr("Parameter File (*.txt)"));
    QFile file(fileName);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        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;
    for (i = parameters.begin(); i != parameters.end(); ++i)
    {
        // Iterate through the parameters of the component
        int compid = i.key();
        QMap<QString, float>* comp = i.value();
        {
            QMap<QString, float>::iterator j;
            for (j = comp->begin(); j != comp->end(); ++j)
            {
561
                QString paramValue("%1");
lm's avatar
lm committed
562
                paramValue = paramValue.arg(j.value(), 25, 'g', 12);
563
                in << mav->getUASID() << "\t" << compid << "\t" << j.key() << "\t" << paramValue << "\n";
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
                in.flush();
            }
        }
    }
    file.close();
}

void QGCParamWidget::loadParameters()
{
    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);
    while (!in.atEnd())
    {
        QString line = in.readLine();
        if (!line.startsWith("#"))
        {
            QStringList wpParams = line.split("\t");
            if (wpParams.size() == 4)
            {
                // Only load parameters for right mav
                if (mav->getUASID() == wpParams.at(0).toInt())
                {
pixhawk's avatar
pixhawk committed
593

lm's avatar
lm committed
594 595 596 597 598 599 600 601 602
                    bool changed = false;
                    int component = wpParams.at(1).toInt();
                    QString parameterName = wpParams.at(2);
                    if (!parameters.contains(component) || parameters.value(component)->value(parameterName, 0.0f) != (float)wpParams.at(3).toDouble())
                    {
                        changed = true;
                    }

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

                    if (changed)
606
                    {
lm's avatar
lm committed
607 608
                        // Create changed values data structure if necessary
                        if (!changedValues.contains(wpParams.at(1).toInt()))
609
                        {
lm's avatar
lm committed
610
                            changedValues.insert(wpParams.at(1).toInt(), new QMap<QString, float>());
611 612
                        }

lm's avatar
lm committed
613 614 615 616 617 618 619 620
                        // Add to changed values
                        if (changedValues.value(wpParams.at(1).toInt())->contains(wpParams.at(2)))
                        {
                            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());

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

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

pixhawk's avatar
pixhawk committed
625

626
                    }
627
                }
lm's avatar
lm committed
628 629
            }
        }
630
    }
631 632
    file.close();

lm's avatar
lm committed
633 634
}

635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
/**
 * 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)
{
    if (enabled)
    {
        retransmissionTimer.start(retransmissionTimeout);
    }
    else
    {
        retransmissionTimer.stop();
    }
}

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

lm's avatar
lm committed
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
        // Check for timeout
        // stop retransmission attempts on timeout
        if (QGC::groundTimeMilliseconds() > transmissionTimeout)
        {
            setRetransmissionGuardEnabled(false);
            transmissionActive = false;

            // Empty read retransmission list
            // Empty write retransmission list
            int missingReadCount = 0;
            QList<int> readKeys = transmissionMissingPackets.keys();
            foreach (int component, readKeys)
            {
                missingReadCount += transmissionMissingPackets.value(component)->count();
                transmissionMissingPackets.value(component)->clear();
            }

            // Empty write retransmission list
            int missingWriteCount = 0;
            QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
            foreach (int component, writeKeys)
            {
                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
690 691 692 693 694
        QMap<int, QMap<QString, float>*>::iterator i;
        for (i = parameters.begin(); i != parameters.end(); ++i)
        {
            // Iterate through the parameters of the component
            int component = i.key();
lm's avatar
lm committed
695
            // Request n parameters from this component (at maximum)
696 697 698 699 700 701
            QList<int> * paramList = transmissionMissingPackets.value(component, NULL);
            if (paramList)
            {
                int count = 0;
                foreach (int id, *paramList)
                {
lm's avatar
lm committed
702
                    if (count < retransmissionBurstRequestSize)
703 704 705
                    {
                        qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD REQUESTS RETRANSMISSION OF PARAM #" << id << "FROM COMPONENT #" << component;
                        emit requestParameter(component, id);
lm's avatar
lm committed
706
                        statusLabel->setText(tr("Requested retransmission of #%1").arg(id+1));
707 708 709 710 711 712 713 714 715
                        count++;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
lm's avatar
lm committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730

        // Re-request at maximum retransmissionBurstRequestSize parameters at once
        // to prevent write-request link flooding
        // Empty write retransmission list
        QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
        foreach (int component, writeKeys)
        {
            int count = 0;
            QMap <QString, float>* missingParams = transmissionMissingWriteAckPackets.value(component);
            foreach (QString key, missingParams->keys())
            {
                if (count < retransmissionBurstRequestSize)
                {
                    // Re-request write operation
                    emit parameterChanged(component, key, missingParams->value(key));
lm's avatar
lm committed
731
                    statusLabel->setText(tr("Requested rewrite of: %1: %2").arg(key).arg(missingParams->value(key)));
lm's avatar
lm committed
732 733 734 735 736 737 738 739
                    count++;
                }
                else
                {
                    break;
                }
            }
        }
740 741 742 743 744 745 746 747
    }
    else
    {
        qDebug() << __FILE__ << __LINE__ << "STOPPING RETRANSMISSION GUARD GRACEFULLY";
        setRetransmissionGuardEnabled(false);
    }
}

748

749 750 751 752 753 754 755 756 757
/**
 * The .. signal is emitted
 */
void QGCParamWidget::requestParameterUpdate(int component, const QString& parameter)
{

}


758 759 760 761 762 763 764
/**
 * @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)
{
765
    emit parameterChanged(component, parameterName, value);
lm's avatar
lm committed
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
    // Wait for parameter to be written back
    // mark it therefore as missing
    if (!transmissionMissingWriteAckPackets.contains(component))
    {
        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;
    if (newTransmissionTimeout > transmissionTimeout)
    {
        transmissionTimeout = newTransmissionTimeout;
    }
    // Enable guard / reset timeouts
    setRetransmissionGuardEnabled(true);
785 786
}

787 788 789
/**
 * Set all parameter in the parameter tree on the MAV
 */
790 791
void QGCParamWidget::setParameters()
{
792
    // Iterate through all components, through all parameters and emit them
793
    int parametersSent = 0;
794 795 796 797 798 799 800 801 802 803
    QMap<int, QMap<QString, float>*>::iterator i;
    for (i = changedValues.begin(); i != changedValues.end(); ++i)
    {
        // Iterate through the parameters of the component
        int compid = i.key();
        QMap<QString, float>* comp = i.value();
        {
            QMap<QString, float>::iterator j;
            for (j = comp->begin(); j != comp->end(); ++j)
            {
804
                setParameter(compid, j.key(), j.value());
805
                parametersSent++;
806 807 808 809
            }
        }
    }

lm's avatar
lm committed
810
    // Change transmission status if necessary
811 812 813 814 815 816 817
    if (parametersSent == 0)
    {
        statusLabel->setText(tr("No transmission: No changed values."));
    }
    else
    {
        statusLabel->setText(tr("Transmitting %1 parameters.").arg(parametersSent));
lm's avatar
lm committed
818 819 820 821 822 823 824 825 826
        // Set timeouts
        transmissionActive = true;
        quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + (parametersSent/retransmissionBurstRequestSize+5)*rewriteTimeout;
        if (newTransmissionTimeout > transmissionTimeout)
        {
            transmissionTimeout = newTransmissionTimeout;
        }
        // Enable guard
        setRetransmissionGuardEnabled(true);
827 828
    }

lm's avatar
lm committed
829
    changedValues.clear();
830 831
}

832 833 834 835
/**
 * Write the current onboard parameters from RAM into
 * permanent storage, e.g. EEPROM or harddisk
 */
836 837
void QGCParamWidget::writeParameters()
{
838 839 840 841 842 843
    mav->writeParametersToStorage();
}

void QGCParamWidget::readParameters()
{
    mav->readParametersFromStorage();
844 845
}

846 847 848
/**
 * Clear all data in the parameter widget
 */
pixhawk's avatar
pixhawk committed
849 850 851
void QGCParamWidget::clear()
{
    tree->clear();
lm's avatar
lm committed
852
    components->clear();
pixhawk's avatar
pixhawk committed
853
}