QGCParamWidget.cc 43.2 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

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>
 */
Lorenz Meier's avatar
Lorenz Meier committed
28 29
#include <cmath>
#include <float.h>
30 31
#include <QGridLayout>
#include <QPushButton>
32 33
#include <QFileDialog>
#include <QFile>
34
#include <QList>
35
#include <QTime>
36
#include <QSettings>
37
#include <QMessageBox>
LM's avatar
LM committed
38
#include <QApplication>
pixhawk's avatar
pixhawk committed
39 40 41

#include "QGCParamWidget.h"
#include "UASInterface.h"
Lorenz Meier's avatar
Lorenz Meier committed
42
#include "MainWindow.h"
pixhawk's avatar
pixhawk committed
43
#include <QDebug>
44
#include "QGC.h"
pixhawk's avatar
pixhawk committed
45

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

lm's avatar
lm committed
57
    // Load default values and tooltips
58 59 60
    QString hey(uas->getAutopilotTypeName());
    QString hey2(uas->getSystemTypeName());
    loadParameterInfoCSV(hey, hey2);
lm's avatar
lm committed
61

pixhawk's avatar
pixhawk committed
62 63
    // Create tree widget
    tree = new QTreeWidget(this);
lm's avatar
lm committed
64
    statusLabel = new QLabel();
lm's avatar
lm committed
65
    statusLabel->setAutoFillBackground(true);
66
    tree->setColumnWidth(70, 30);
pixhawk's avatar
pixhawk committed
67 68

    // Set tree widget as widget onto this component
69
    QGridLayout* horizontalLayout;
pixhawk's avatar
pixhawk committed
70
    //form->setAutoFillBackground(false);
71
    horizontalLayout = new QGridLayout(this);
72 73
    horizontalLayout->setHorizontalSpacing(6);
    horizontalLayout->setVerticalSpacing(6);
pixhawk's avatar
pixhawk committed
74
    horizontalLayout->setMargin(0);
75 76
    horizontalLayout->setSizeConstraint(QLayout::SetMinimumSize);
    //horizontalLayout->setSizeConstraint( QLayout::SetFixedSize );
pixhawk's avatar
pixhawk committed
77

lm's avatar
lm committed
78
    // Parameter tree
79
    horizontalLayout->addWidget(tree, 0, 0, 1, 3);
lm's avatar
lm committed
80 81

    // Status line
lm's avatar
lm committed
82
    statusLabel->setText(tr("Click refresh to download parameters"));
lm's avatar
lm committed
83 84 85 86
    horizontalLayout->addWidget(statusLabel, 1, 0, 1, 3);


    // BUTTONS
Jessica's avatar
Jessica committed
87
    QPushButton* refreshButton = new QPushButton(tr("Get"));
88 89
    refreshButton->setToolTip(tr("Load parameters currently in non-permanent memory of aircraft."));
    refreshButton->setWhatsThis(tr("Load parameters currently in non-permanent memory of aircraft."));
90
    connect(refreshButton, SIGNAL(clicked()), this, SLOT(requestParameterList()));
lm's avatar
lm committed
91
    horizontalLayout->addWidget(refreshButton, 2, 0);
92

Jessica's avatar
Jessica committed
93
    QPushButton* setButton = new QPushButton(tr("Set"));
94 95
    setButton->setToolTip(tr("Set current parameters in non-permanent onboard memory"));
    setButton->setWhatsThis(tr("Set current parameters in non-permanent onboard memory"));
96
    connect(setButton, SIGNAL(clicked()), this, SLOT(setParameters()));
lm's avatar
lm committed
97
    horizontalLayout->addWidget(setButton, 2, 1);
98

99
    QPushButton* writeButton = new QPushButton(tr("Write (ROM)"));
100 101
    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."));
102
    connect(writeButton, SIGNAL(clicked()), this, SLOT(writeParameters()));
lm's avatar
lm committed
103
    horizontalLayout->addWidget(writeButton, 2, 2);
104

105
    QPushButton* loadFileButton = new QPushButton(tr("Load File"));
106 107
    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."));
108
    connect(loadFileButton, SIGNAL(clicked()), this, SLOT(loadParameters()));
lm's avatar
lm committed
109
    horizontalLayout->addWidget(loadFileButton, 3, 0);
110 111

    QPushButton* saveFileButton = new QPushButton(tr("Save File"));
112 113
    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."));
114
    connect(saveFileButton, SIGNAL(clicked()), this, SLOT(saveParameters()));
lm's avatar
lm committed
115 116 117 118 119 120 121
    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);
122

123 124 125 126 127 128
    // Set correct vertical scaling
    horizontalLayout->setRowStretch(0, 100);
    horizontalLayout->setRowStretch(1, 10);
    horizontalLayout->setRowStretch(2, 10);
    horizontalLayout->setRowStretch(3, 10);

129
    // Set layout
pixhawk's avatar
pixhawk committed
130 131 132 133 134 135 136 137
    this->setLayout(horizontalLayout);

    // Set header
    QStringList headerItems;
    headerItems.append("Parameter");
    headerItems.append("Value");
    tree->setHeaderLabels(headerItems);
    tree->setColumnCount(2);
138
    tree->setExpandsOnDoubleClick(true);
139 140

    // Connect signals/slots
141
    connect(this, SIGNAL(parameterChanged(int,QString,QVariant)), mav, SLOT(setParameter(int,QString,QVariant)));
142
    connect(tree, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(parameterItemChanged(QTreeWidgetItem*,int)));
lm's avatar
lm committed
143

144
    // New parameters from UAS
145
    connect(uas, SIGNAL(parameterChanged(int,int,int,int,QString,QVariant)), this, SLOT(addParameter(int,int,int,int,QString,QVariant)));
146 147

    // Connect retransmission guard
148
    connect(this, SIGNAL(requestParameter(int,QString)), uas, SLOT(requestParameter(int,QString)));
149 150
    connect(this, SIGNAL(requestParameter(int,int)), uas, SLOT(requestParameter(int,int)));
    connect(&retransmissionTimer, SIGNAL(timeout()), this, SLOT(retransmissionGuardTick()));
151 152

    // Get parameters
153
    if (uas) requestParameterList();
pixhawk's avatar
pixhawk committed
154 155
}

156 157 158 159 160 161 162 163 164 165 166 167
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();
}

lm's avatar
lm committed
168 169
void QGCParamWidget::loadParameterInfoCSV(const QString& autopilot, const QString& airframe)
{
170 171 172 173
    Q_UNUSED(airframe);

    qDebug() << "ATTEMPTING TO LOAD CSV";

LM's avatar
LM committed
174
    QDir appDir = QApplication::applicationDirPath();
175
    appDir.cd("files");
176
    QString fileName = QString("%1/%2/parameter_tooltips/tooltips.txt").arg(appDir.canonicalPath()).arg(autopilot.toLower());
LM's avatar
LM committed
177
    QFile paramMetaFile(fileName);
lm's avatar
lm committed
178

179 180 181
    qDebug() << "AUTOPILOT:" << autopilot;
    qDebug() << "FILENAME: " << fileName;

lm's avatar
lm committed
182 183 184
    // Load CSV data
    if (!paramMetaFile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
185
        //qDebug() << "COULD NOT OPEN PARAM META INFO FILE:" << fileName;
lm's avatar
lm committed
186 187 188 189 190 191 192 193 194 195
        return;
    }

    // Extract header

    // Read in values
    // Find all keys
    QTextStream in(&paramMetaFile);

    // First line is header
196 197
    // there might be more lines, but the first
    // line is assumed to be at least header
lm's avatar
lm committed
198 199
    QString header = in.readLine();

200
    // Ignore top-level comment lines
201 202
    while (header.startsWith('#') || header.startsWith('/')
           || header.startsWith('=') || header.startsWith('^'))
203 204 205 206
    {
        header = in.readLine();
    }

lm's avatar
lm committed
207 208 209 210 211 212
    bool charRead = false;
    QString separator = "";
    QList<QChar> sepCandidates;
    sepCandidates << '\t';
    sepCandidates << ',';
    sepCandidates << ';';
213
    //sepCandidates << ' ';
lm's avatar
lm committed
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    sepCandidates << '~';
    sepCandidates << '|';

    // Iterate until separator is found
    // or full header is parsed
    for (int i = 0; i < header.length(); i++)
    {
        if (sepCandidates.contains(header.at(i)))
        {
            // Separator found
            if (charRead)
            {
                separator += header[i];
            }
        }
        else
        {
            // Char found
            charRead = true;
            // If the separator is not empty, this char
            // has been read after a separator, so detection
            // is now complete
            if (separator != "") break;
        }
    }

240 241 242 243 244 245 246 247 248
    bool stripFirstSeparator = false;
    bool stripLastSeparator = false;

    // Figure out if the lines start with the separator (e.g. wiki syntax)
    if (header.startsWith(separator)) stripFirstSeparator = true;

    // Figure out if the lines end with the separator (e.g. wiki syntax)
    if (header.endsWith(separator)) stripLastSeparator = true;

lm's avatar
lm committed
249 250
    QString out = separator;
    out.replace("\t", "<tab>");
251
    //qDebug() << " Separator: \"" << out << "\"";
lm's avatar
lm committed
252 253 254 255 256 257 258 259
    //qDebug() << "READING CSV:" << header;


    // Read data
    while (!in.atEnd())
    {
        QString line = in.readLine();

260 261 262 263 264 265 266 267
        //qDebug() << "LINE PRE-STRIP" << line;

        // Strip separtors if necessary
        if (stripFirstSeparator) line.remove(0, separator.length());
        if (stripLastSeparator) line.remove(line.length()-separator.length(), line.length()-1);

        //qDebug() << "LINE POST-STRIP" << line;

lm's avatar
lm committed
268 269 270 271 272 273 274 275 276 277 278
        // Keep empty parts here - we still have to act on them
        QStringList parts = line.split(separator, QString::KeepEmptyParts);

        // Each line is:
        // variable name, Min, Max, Default, Multiplier, Enabled (0 = no, 1 = yes), Comment


        // Fill in min, max and default values
        if (parts.count() > 1)
        {
            // min
279
            paramMin.insert(parts.at(0).trimmed(), parts.at(1).toDouble());
lm's avatar
lm committed
280 281 282 283
        }
        if (parts.count() > 2)
        {
            // max
284
            paramMax.insert(parts.at(0).trimmed(), parts.at(2).toDouble());
lm's avatar
lm committed
285 286 287 288
        }
        if (parts.count() > 3)
        {
            // default
289
            paramDefault.insert(parts.at(0).trimmed(), parts.at(3).toDouble());
lm's avatar
lm committed
290 291 292 293 294
        }
        // IGNORING 4 and 5 for now
        if (parts.count() > 6)
        {
            // tooltip
295 296
            paramToolTips.insert(parts.at(0).trimmed(), parts.at(6).trimmed());
            qDebug() << "PARAM META:" << parts.at(0).trimmed();
lm's avatar
lm committed
297 298 299 300
        }
    }
}

301 302 303 304
/**
 * @return The MAV of this widget. Unless the MAV object has been destroyed, this
 *         pointer is never zero.
 */
pixhawk's avatar
pixhawk committed
305 306 307 308 309 310 311 312 313 314 315
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
 */
316
void QGCParamWidget::addComponent(int uas, int component, QString componentName)
pixhawk's avatar
pixhawk committed
317
{
318
    Q_UNUSED(uas);
319
    if (components->contains(component)) {
pixhawk's avatar
pixhawk committed
320
        // Update existing
321 322 323
        components->value(component)->setData(0, Qt::DisplayRole, QString("%1 (#%2)").arg(componentName).arg(component));
        //components->value(component)->setData(1, Qt::DisplayRole, QString::number(component));
        components->value(component)->setFirstColumnSpanned(true);
324
    } else {
pixhawk's avatar
pixhawk committed
325
        // Add new
lm's avatar
lm committed
326
        QStringList list(QString("%1 (#%2)").arg(componentName).arg(component));
pixhawk's avatar
pixhawk committed
327
        QTreeWidgetItem* comp = new QTreeWidgetItem(list);
lm's avatar
lm committed
328
        comp->setFirstColumnSpanned(true);
pixhawk's avatar
pixhawk committed
329 330 331
        components->insert(component, comp);
        // Create grouping and update maps
        paramGroups.insert(component, new QMap<QString, QTreeWidgetItem*>());
pixhawk's avatar
pixhawk committed
332 333
        tree->addTopLevelItem(comp);
        tree->update();
334
        // Create map in parameters
335
        if (!parameters.contains(component)) {
336
            parameters.insert(component, new QMap<QString, QVariant>());
337 338
        }
        // Create map in changed parameters
339
        if (!changedValues.contains(component)) {
340
            changedValues.insert(component, new QMap<QString, QVariant>());
341
        }
pixhawk's avatar
pixhawk committed
342 343 344
    }
}

lm's avatar
lm committed
345 346 347 348 349
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
350
void QGCParamWidget::addParameter(int uas, int component, int paramCount, int paramId, QString parameterName, QVariant value)
lm's avatar
lm committed
351 352 353
{
    addParameter(uas, component, parameterName, value);

lm's avatar
lm committed
354
    // Missing packets list has to be instantiated for all components
355
    if (!transmissionMissingPackets.contains(component)) {
lm's avatar
lm committed
356 357 358
        transmissionMissingPackets.insert(component, new QList<int>());
    }

lm's avatar
lm committed
359
    // List mode is different from single parameter transfers
360
    if (transmissionListMode) {
lm's avatar
lm committed
361 362
        // Only accept the list size once on the first packet from
        // each component
lm's avatar
lm committed
363 364
        if (!transmissionListSizeKnown.contains(component))
        {
lm's avatar
lm committed
365 366
            // Mark list size as known
            transmissionListSizeKnown.insert(component, true);
lm's avatar
lm committed
367

368
            // Mark all parameters as missing
lm's avatar
lm committed
369 370 371 372
            for (int i = 0; i < paramCount; ++i)
            {
                if (!transmissionMissingPackets.value(component)->contains(i))
                {
lm's avatar
lm committed
373 374 375
                    transmissionMissingPackets.value(component)->append(i);
                }
            }
376

lm's avatar
lm committed
377 378 379
            // There is only one transmission timeout for all components
            // since components do not manage their transmission,
            // the longest timeout is safe for all components.
Lorenz Meier's avatar
Lorenz Meier committed
380
            quint64 thisTransmissionTimeout = QGC::groundTimeMilliseconds() + ((paramCount)*retransmissionTimeout);
lm's avatar
lm committed
381 382
            if (thisTransmissionTimeout > transmissionTimeout)
            {
lm's avatar
lm committed
383 384
                transmissionTimeout = thisTransmissionTimeout;
            }
lm's avatar
lm committed
385
        }
lm's avatar
lm committed
386 387 388 389

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

lm's avatar
lm committed
392
    // Mark this parameter as received in read list
lm's avatar
lm committed
393 394 395 396
    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
397 398
    bool justWritten = false;
    bool writeMismatch = false;
399
    //bool lastWritten = false;
lm's avatar
lm committed
400
    // Mark this parameter as received in write ACK list
401
    QMap<QString, QVariant>* map = transmissionMissingWriteAckPackets.value(component);
lm's avatar
lm committed
402 403
    if (map && map->contains(parameterName))
    {
lm's avatar
lm committed
404
        justWritten = true;
405
        QVariant newval = map->value(parameterName);
lm's avatar
lm committed
406 407
        if (map->value(parameterName) != value)
        {
lm's avatar
lm committed
408 409 410 411 412
            writeMismatch = true;
        }
        map->remove(parameterName);
    }

413
    int missCount = 0;
lm's avatar
lm committed
414 415
    foreach (int key, transmissionMissingPackets.keys())
    {
416 417 418
        missCount +=  transmissionMissingPackets.value(key)->count();
    }

lm's avatar
lm committed
419
    int missWriteCount = 0;
lm's avatar
lm committed
420 421
    foreach (int key, transmissionMissingWriteAckPackets.keys())
    {
lm's avatar
lm committed
422 423 424
        missWriteCount += transmissionMissingWriteAckPackets.value(key)->count();
    }

lm's avatar
lm committed
425 426
    if (justWritten && !writeMismatch && missWriteCount == 0)
    {
lm's avatar
lm committed
427 428 429 430 431
        // 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);
lm's avatar
lm committed
432 433
    } else if (justWritten && !writeMismatch)
    {
434
        statusLabel->setText(tr("SUCCESS: Wrote %2 (#%1/%4): %3").arg(paramId+1).arg(parameterName).arg(value.toDouble()).arg(paramCount));
lm's avatar
lm committed
435 436 437
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorGreen);
        statusLabel->setPalette(pal);
lm's avatar
lm committed
438 439
    } else if (justWritten && writeMismatch)
    {
lm's avatar
lm committed
440
        // Mismatch, tell user
lm's avatar
lm committed
441 442 443
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorRed);
        statusLabel->setPalette(pal);
444
        statusLabel->setText(tr("FAILURE: Wrote %1: sent %2 != onboard %3").arg(parameterName).arg(map->value(parameterName).toDouble()).arg(value.toDouble()));
lm's avatar
lm committed
445 446 447 448 449
    }
    else
    {
        if (missCount > 0)
        {
lm's avatar
lm committed
450 451 452
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorOrange);
            statusLabel->setPalette(pal);
lm's avatar
lm committed
453 454 455
        }
        else
        {
lm's avatar
lm committed
456 457 458 459
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorGreen);
            statusLabel->setPalette(pal);
        }
460 461
        QString val = QString("%1").arg(value.toFloat(), 5, 'f', 1, QChar(' '));
        //statusLabel->setText(tr("OK: %1 %2 #%3/%4, %5 miss").arg(parameterName).arg(val).arg(paramId+1).arg(paramCount).arg(missCount));
462 463 464 465 466 467 468 469 470 471 472 473
        if (missCount == 0)
        {
            // Transmission done
            QTime time = QTime::currentTime();
            QString timeString = time.toString();
            statusLabel->setText(tr("All received. (updated at %1)").arg(timeString));
        }
        else
        {
            // Transmission in progress
            statusLabel->setText(tr("OK: %1 %2 (%3/%4)").arg(parameterName).arg(val).arg(paramCount-missCount).arg(paramCount));
        }
lm's avatar
lm committed
474
    }
lm's avatar
lm committed
475 476

    // Check if last parameter was received
lm's avatar
lm committed
477 478
    if (missCount == 0 && missWriteCount == 0)
    {
lm's avatar
lm committed
479 480 481
        this->transmissionActive = false;
        this->transmissionListMode = false;
        transmissionListSizeKnown.clear();
lm's avatar
lm committed
482 483
        foreach (int key, transmissionMissingPackets.keys())
        {
lm's avatar
lm committed
484 485
            transmissionMissingPackets.value(key)->clear();
        }
486 487 488

        // Expand visual tree
        tree->expandItem(tree->topLevelItem(0));
lm's avatar
lm committed
489 490 491
    }
}

492 493 494 495 496
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
497
void QGCParamWidget::addParameter(int uas, int component, QString parameterName, QVariant value)
pixhawk's avatar
pixhawk committed
498
{
499
    //qDebug() << "PARAM WIDGET GOT PARAM:" << value;
500
    Q_UNUSED(uas);
501
    // Reference to item in tree
502
    QTreeWidgetItem* parameterItem = NULL;
pixhawk's avatar
pixhawk committed
503 504

    // Get component
lm's avatar
lm committed
505 506
    if (!components->contains(component))
    {
Lorenz Meier's avatar
Lorenz Meier committed
507 508 509 510 511 512 513 514 515 516 517 518 519
        //        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;
        //        }
520
        QString componentName = tr("Component #%1").arg(component);
521
        addComponent(uas, component, componentName);
pixhawk's avatar
pixhawk committed
522
    }
523

524 525 526 527 528 529 530
    // 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
531 532
    QString splitToken = "_";
    // Check if auto-grouping can work
lm's avatar
lm committed
533 534
    if (parameterName.contains(splitToken))
    {
pixhawk's avatar
pixhawk committed
535 536
        QString parent = parameterName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(component);
537 538
        if (!compParamGroups->contains(parent))
        {
pixhawk's avatar
pixhawk committed
539 540 541 542 543 544
            // Insert group item
            QStringList glist;
            glist.append(parent);
            QTreeWidgetItem* item = new QTreeWidgetItem(glist);
            compParamGroups->insert(parent, item);
            components->value(component)->addChild(item);
545
        }
546 547 548 549

        // Append child to group
        bool found = false;
        QTreeWidgetItem* parentItem = compParamGroups->value(parent);
550
        for (int i = 0; i < parentItem->childCount(); i++) {
551 552
            QTreeWidgetItem* child = parentItem->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
553 554
            if (key == parameterName)
            {
555 556
                //qDebug() << "UPDATED CHILD";
                parameterItem = child;
557 558 559 560 561 562 563 564
                if (value.type() == QVariant::Char)
                {
                    parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
                }
                else
                {
                    parameterItem->setData(1, Qt::DisplayRole, value);
                }
565 566 567 568
                found = true;
            }
        }

lm's avatar
lm committed
569 570
        if (!found)
        {
571 572 573
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
574
            // CREATE PARAMETER ITEM
575
            parameterItem = new QTreeWidgetItem(plist);
576
            // CONFIGURE PARAMETER ITEM
577 578 579 580 581 582 583 584
            if (value.type() == QVariant::Char)
            {
                parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
            }
            else
            {
                parameterItem->setData(1, Qt::DisplayRole, value);
            }
585 586

            compParamGroups->value(parent)->addChild(parameterItem);
lm's avatar
lm committed
587
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
588
        }
lm's avatar
lm committed
589 590 591
    }
    else
    {
pixhawk's avatar
pixhawk committed
592 593
        bool found = false;
        QTreeWidgetItem* parent = components->value(component);
lm's avatar
lm committed
594 595
        for (int i = 0; i < parent->childCount(); i++)
        {
pixhawk's avatar
pixhawk committed
596 597
            QTreeWidgetItem* child = parent->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
lm's avatar
lm committed
598 599
            if (key == parameterName)
            {
pixhawk's avatar
pixhawk committed
600
                //qDebug() << "UPDATED CHILD";
601 602
                parameterItem = child;
                parameterItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
603 604 605 606
                found = true;
            }
        }

lm's avatar
lm committed
607 608
        if (!found)
        {
609 610 611
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
612
            // CREATE PARAMETER ITEM
613
            parameterItem = new QTreeWidgetItem(plist);
614
            // CONFIGURE PARAMETER ITEM
615
            parameterItem->setData(1, Qt::DisplayRole, value);
616

lm's avatar
lm committed
617 618
            components->value(component)->addChild(parameterItem);
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
pixhawk's avatar
pixhawk committed
619
        }
620
        //tree->expandAll();
621
    }
622
    // Reset background color
623
    parameterItem->setBackground(0, Qt::NoBrush);
624
    parameterItem->setBackground(1, Qt::NoBrush);
lm's avatar
lm committed
625
    // Add tooltip
626 627 628 629
    QString tooltipFormat;
    if (paramDefault.contains(parameterName))
    {
        tooltipFormat = tr("Default: %1, %2");
LM's avatar
LM committed
630
        tooltipFormat = tooltipFormat.arg(paramDefault.value(parameterName, 0.0f)).arg(paramToolTips.value(parameterName, ""));
631 632 633 634 635 636 637
    }
    else
    {
        tooltipFormat = paramToolTips.value(parameterName, "");
    }
    parameterItem->setToolTip(0, tooltipFormat);
    parameterItem->setToolTip(1, tooltipFormat);
lm's avatar
lm committed
638

639
    //tree->update();
pixhawk's avatar
pixhawk committed
640
    if (changedValues.contains(component)) changedValues.value(component)->remove(parameterName);
pixhawk's avatar
pixhawk committed
641 642
}

643 644 645 646
/**
 * Send a request to deliver the list of onboard parameters
 * to the MAV.
 */
647 648
void QGCParamWidget::requestParameterList()
{
649
    if (!mav) return;
650 651 652 653 654 655 656
    // 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
657
    // Clear view and request param list
658
    clear();
659
    parameters.clear();
lm's avatar
lm committed
660
    received.clear();
lm's avatar
lm committed
661 662 663
    // Clear transmission state
    transmissionListMode = true;
    transmissionListSizeKnown.clear();
Lorenz Meier's avatar
Lorenz Meier committed
664 665
    foreach (int key, transmissionMissingPackets.keys())
    {
lm's avatar
lm committed
666 667 668
        transmissionMissingPackets.value(key)->clear();
    }
    transmissionActive = true;
lm's avatar
lm committed
669 670 671 672 673

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

    mav->requestParameters();
674 675
}

676
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column)
lm's avatar
lm committed
677
{
678
    if (current && column > 0) {
679
        QTreeWidgetItem* parent = current->parent();
680
        while (parent->parent() != NULL) {
681 682 683 684
            parent = parent->parent();
        }
        // Parent is now top-level component
        int key = components->key(parent);
685
        if (!changedValues.contains(key)) {
686
            changedValues.insert(key, new QMap<QString, QVariant>());
687
        }
688
        QMap<QString, QVariant>* map = changedValues.value(key, NULL);
689
        if (map) {
690
            QString str = current->data(0, Qt::DisplayRole).toString();
691
            QVariant value = current->data(1, Qt::DisplayRole);
lm's avatar
lm committed
692
            // Set parameter on changed list to be transmitted to MAV
693 694 695 696
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorOrange);
            statusLabel->setPalette(pal);
            statusLabel->setText(tr("Transmit pend. %1:%2: %3").arg(key).arg(str).arg(value.toFloat(), 5, 'f', 1, QChar(' ')));
697 698 699 700 701 702 703 704 705 706
            //qDebug() << "PARAM CHANGED: COMP:" << key << "KEY:" << str << "VALUE:" << value;
            // Changed values list
            if (map->contains(str)) map->remove(str);
            map->insert(str, value);

            // Check if the value was numerically changed
            if (!parameters.value(key)->contains(str) || parameters.value(key)->value(str, value.toDouble()-1) != value) {
                current->setBackground(0, QBrush(QColor(QGC::colorOrange)));
                current->setBackground(1, QBrush(QColor(QGC::colorOrange)));
            }
707

708 709 710
            switch (parameters.value(key)->value(str).type())
            {
            case QVariant::Int:
Lorenz Meier's avatar
Lorenz Meier committed
711 712 713 714
            {
                QVariant fixedValue(value.toInt());
                parameters.value(key)->insert(str, fixedValue);
            }
715 716
                break;
            case QVariant::UInt:
Lorenz Meier's avatar
Lorenz Meier committed
717 718 719 720
            {
                QVariant fixedValue(value.toUInt());
                parameters.value(key)->insert(str, fixedValue);
            }
721 722
                break;
            case QMetaType::Float:
Lorenz Meier's avatar
Lorenz Meier committed
723 724 725 726
            {
                QVariant fixedValue(value.toFloat());
                parameters.value(key)->insert(str, fixedValue);
            }
727
                break;
728 729 730 731 732 733
            case QMetaType::QChar:
            {
                QVariant fixedValue(QChar((unsigned char)value.toUInt()));
                parameters.value(key)->insert(str, fixedValue);
            }
                break;
734 735 736
            default:
                qCritical() << "ABORTED PARAM UPDATE, NO VALID QVARIANT TYPE";
                return;
737 738 739 740 741 742
            }
        }
    }
}

void QGCParamWidget::saveParameters()
743
{
744
    if (!mav) return;
745 746
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "./parameters.txt", tr("Parameter File (*.txt)"));
    QFile file(fileName);
747
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
748 749 750 751 752 753 754 755 756 757
        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
758
    QMap<int, QMap<QString, QVariant>*>::iterator i;
759
    for (i = parameters.begin(); i != parameters.end(); ++i) {
760 761
        // Iterate through the parameters of the component
        int compid = i.key();
762
        QMap<QString, QVariant>* comp = i.value();
763
        {
764 765 766
            QMap<QString, QVariant>::iterator j;
            for (j = comp->begin(); j != comp->end(); ++j)
            {
767
                QString paramValue("%1");
768 769 770 771 772
                QString paramType("%1");
                switch (j.value().type())
                {
                case QVariant::Int:
                    paramValue = paramValue.arg(j.value().toInt());
773
                    paramType = paramType.arg(MAV_PARAM_TYPE_INT32);
774 775 776
                    break;
                case QVariant::UInt:
                    paramValue = paramValue.arg(j.value().toUInt());
777
                    paramType = paramType.arg(MAV_PARAM_TYPE_UINT32);
778 779 780
                    break;
                case QMetaType::Float:
                    paramValue = paramValue.arg(j.value().toDouble(), 25, 'g', 12);
781
                    paramType = paramType.arg(MAV_PARAM_TYPE_REAL32);
782 783 784 785 786 787
                    break;
                default:
                    qCritical() << "ABORTED PARAM WRITE TO FILE, NO VALID QVARIANT TYPE" << j.value();
                    return;
                }
                in << mav->getUASID() << "\t" << compid << "\t" << j.key() << "\t" << paramValue << "\t" << paramType << "\n";
788 789 790 791 792 793 794 795 796
                in.flush();
            }
        }
    }
    file.close();
}

void QGCParamWidget::loadParameters()
{
797
    if (!mav) return;
798 799 800 801 802
    QString fileName = QFileDialog::getOpenFileName(this, tr("Load File"), ".", tr("Parameter file (*.txt)"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

Lorenz Meier's avatar
Lorenz Meier committed
803
    bool userWarned = false;
804 805

    QTextStream in(&file);
806
    while (!in.atEnd()) {
807
        QString line = in.readLine();
808
        if (!line.startsWith("#")) {
809
            QStringList wpParams = line.split("\t");
810
            if (wpParams.size() == 5) {
811
                // Only load parameters for right mav
Lorenz Meier's avatar
Lorenz Meier committed
812 813 814 815
                if (!userWarned && (mav->getUASID() != wpParams.at(0).toInt())) {
                    MainWindow::instance()->showCriticalMessage(tr("Parameter loading warning"), tr("The parameters from the file %1 have been saved from system %2, but the currently selected system has the ID %3. If this is unintentional, please click on <READ> to revert to the parameters that are currently onboard").arg(fileName).arg(wpParams.at(0).toInt()).arg(mav->getUASID()));
                    userWarned = true;
                }
pixhawk's avatar
pixhawk committed
816

Lorenz Meier's avatar
Lorenz Meier committed
817 818 819 820 821 822 823 824
                bool changed = false;
                int component = wpParams.at(1).toInt();
                QString parameterName = wpParams.at(2);
                if (!parameters.contains(component) ||
                        fabs((static_cast<float>(parameters.value(component)->value(parameterName, wpParams.at(3).toDouble()).toDouble())) - (wpParams.at(3).toDouble())) > 2.0f * FLT_EPSILON) {
                    changed = true;
                    qDebug() << "Changed" << parameterName << "VAL" << wpParams.at(3).toDouble();
                }
lm's avatar
lm committed
825

Lorenz Meier's avatar
Lorenz Meier committed
826
                // Set parameter value
lm's avatar
lm committed
827

Lorenz Meier's avatar
Lorenz Meier committed
828 829 830 831
                // Create changed values data structure if necessary
                if (changed && !changedValues.contains(wpParams.at(1).toInt())) {
                    changedValues.insert(wpParams.at(1).toInt(), new QMap<QString, QVariant>());
                }
pixhawk's avatar
pixhawk committed
832

Lorenz Meier's avatar
Lorenz Meier committed
833 834 835 836
                // Add to changed values
                if (changed && changedValues.value(wpParams.at(1).toInt())->contains(wpParams.at(2))) {
                    changedValues.value(wpParams.at(1).toInt())->remove(wpParams.at(2));
                }
pixhawk's avatar
pixhawk committed
837

Lorenz Meier's avatar
Lorenz Meier committed
838 839 840 841 842 843 844 845
                switch (wpParams.at(4).toUInt())
                {
                case (int)MAV_PARAM_TYPE_REAL32:
                    addParameter(wpParams.at(0).toInt(), wpParams.at(1).toInt(), wpParams.at(2), wpParams.at(3).toFloat());
                    if (changed) {
                        changedValues.value(wpParams.at(1).toInt())->insert(wpParams.at(2), wpParams.at(3).toFloat());
                        setParameter(wpParams.at(1).toInt(), wpParams.at(2), wpParams.at(3).toFloat());
                        qDebug() << "FLOAT PARAM CHANGED";
846
                    }
Lorenz Meier's avatar
Lorenz Meier committed
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
                    break;
                case (int)MAV_PARAM_TYPE_UINT32:
                    addParameter(wpParams.at(0).toInt(), wpParams.at(1).toInt(), wpParams.at(2), wpParams.at(3).toUInt());
                    if (changed) {
                        changedValues.value(wpParams.at(1).toInt())->insert(wpParams.at(2), wpParams.at(3).toUInt());
                        setParameter(wpParams.at(1).toInt(), wpParams.at(2), QVariant(wpParams.at(3).toUInt()));
                    }
                    break;
                case (int)MAV_PARAM_TYPE_INT32:
                    addParameter(wpParams.at(0).toInt(), wpParams.at(1).toInt(), wpParams.at(2), wpParams.at(3).toInt());
                    if (changed) {
                        changedValues.value(wpParams.at(1).toInt())->insert(wpParams.at(2), wpParams.at(3).toInt());
                        setParameter(wpParams.at(1).toInt(), wpParams.at(2), QVariant(wpParams.at(3).toInt()));
                    }
                    break;
                default:
                    qDebug() << "FAILED LOADING PARAM" << wpParams.at(2) << "NO KNOWN DATA TYPE";
864
                }
Lorenz Meier's avatar
Lorenz Meier committed
865 866 867 868 869 870

                //qDebug() << "MARKING COMP" << wpParams.at(1).toInt() << "PARAM" << wpParams.at(2) << "VALUE" << (float)wpParams.at(3).toDouble() << "AS CHANGED";

                // Mark in UI


lm's avatar
lm committed
871 872
            }
        }
873
    }
874 875
    file.close();

lm's avatar
lm committed
876 877
}

878 879 880 881 882 883 884 885 886
/**
 * 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)
{
887
    if (enabled) {
888
        retransmissionTimer.start(retransmissionTimeout);
889
    } else {
890 891 892 893 894 895
        retransmissionTimer.stop();
    }
}

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

lm's avatar
lm committed
899 900
        // Check for timeout
        // stop retransmission attempts on timeout
901
        if (QGC::groundTimeMilliseconds() > transmissionTimeout) {
lm's avatar
lm committed
902 903 904 905 906 907 908
            setRetransmissionGuardEnabled(false);
            transmissionActive = false;

            // Empty read retransmission list
            // Empty write retransmission list
            int missingReadCount = 0;
            QList<int> readKeys = transmissionMissingPackets.keys();
909
            foreach (int component, readKeys) {
lm's avatar
lm committed
910 911 912 913 914 915 916
                missingReadCount += transmissionMissingPackets.value(component)->count();
                transmissionMissingPackets.value(component)->clear();
            }

            // Empty write retransmission list
            int missingWriteCount = 0;
            QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
917
            foreach (int component, writeKeys) {
lm's avatar
lm committed
918 919 920 921 922 923 924 925
                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
926
        QMap<int, QMap<QString, QVariant>*>::iterator i;
927
        for (i = parameters.begin(); i != parameters.end(); ++i) {
928 929
            // Iterate through the parameters of the component
            int component = i.key();
lm's avatar
lm committed
930
            // Request n parameters from this component (at maximum)
931
            QList<int> * paramList = transmissionMissingPackets.value(component, NULL);
932
            if (paramList) {
933
                int count = 0;
934 935
                foreach (int id, *paramList) {
                    if (count < retransmissionBurstRequestSize) {
936
                        //qDebug() << __FILE__ << __LINE__ << "RETRANSMISSION GUARD REQUESTS RETRANSMISSION OF PARAM #" << id << "FROM COMPONENT #" << component;
937
                        emit requestParameter(component, id);
lm's avatar
lm committed
938
                        statusLabel->setText(tr("Requested retransmission of #%1").arg(id+1));
939
                        count++;
940
                    } else {
941 942 943 944 945
                        break;
                    }
                }
            }
        }
lm's avatar
lm committed
946 947 948 949 950

        // Re-request at maximum retransmissionBurstRequestSize parameters at once
        // to prevent write-request link flooding
        // Empty write retransmission list
        QList<int> writeKeys = transmissionMissingWriteAckPackets.keys();
951
        foreach (int component, writeKeys) {
lm's avatar
lm committed
952
            int count = 0;
953
            QMap <QString, QVariant>* missingParams = transmissionMissingWriteAckPackets.value(component);
954 955
            foreach (QString key, missingParams->keys()) {
                if (count < retransmissionBurstRequestSize) {
lm's avatar
lm committed
956
                    // Re-request write operation
957 958 959 960
                    QVariant value = missingParams->value(key);
                    switch (parameters.value(component)->value(key).type())
                    {
                    case QVariant::Int:
Lorenz Meier's avatar
Lorenz Meier committed
961 962 963 964
                    {
                        QVariant fixedValue(value.toInt());
                        emit parameterChanged(component, key, fixedValue);
                    }
965 966
                        break;
                    case QVariant::UInt:
Lorenz Meier's avatar
Lorenz Meier committed
967 968 969 970
                    {
                        QVariant fixedValue(value.toUInt());
                        emit parameterChanged(component, key, fixedValue);
                    }
971 972
                        break;
                    case QMetaType::Float:
Lorenz Meier's avatar
Lorenz Meier committed
973 974 975 976
                    {
                        QVariant fixedValue(value.toFloat());
                        emit parameterChanged(component, key, fixedValue);
                    }
977 978
                        break;
                    default:
979
                        //qCritical() << "ABORTED PARAM RETRANSMISSION, NO VALID QVARIANT TYPE";
980 981
                        return;
                    }
982
                    statusLabel->setText(tr("Requested rewrite of: %1: %2").arg(key).arg(missingParams->value(key).toDouble()));
lm's avatar
lm committed
983
                    count++;
984
                } else {
lm's avatar
lm committed
985 986 987 988
                    break;
                }
            }
        }
989
    } else {
990
        //qDebug() << __FILE__ << __LINE__ << "STOPPING RETRANSMISSION GUARD GRACEFULLY";
991 992 993 994
        setRetransmissionGuardEnabled(false);
    }
}

995

996 997 998 999 1000
/**
 * The .. signal is emitted
 */
void QGCParamWidget::requestParameterUpdate(int component, const QString& parameter)
{
1001
    if (mav) mav->requestParameter(component, parameter);
1002 1003 1004
}


1005 1006 1007 1008 1009
/**
 * @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
 */
1010
void QGCParamWidget::setParameter(int component, QString parameterName, QVariant value)
1011
{
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
    if (paramMin.contains(parameterName) && value.toDouble() < paramMin.value(parameterName))
    {
        statusLabel->setText(tr("REJ. %1 < min").arg(value.toDouble()));
        return;
    }
    if (paramMax.contains(parameterName) && value.toDouble() > paramMax.value(parameterName))
    {
        statusLabel->setText(tr("REJ. %1 > max").arg(value.toDouble()));
        return;
    }
1022 1023 1024

    switch (parameters.value(component)->value(parameterName).type())
    {
1025 1026 1027 1028 1029 1030 1031
    case QVariant::Char:
    {
        QVariant fixedValue(QChar((unsigned char)value.toInt()));
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
        break;
1032
    case QVariant::Int:
Lorenz Meier's avatar
Lorenz Meier committed
1033 1034 1035 1036 1037
    {
        QVariant fixedValue(value.toInt());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
1038 1039
        break;
    case QVariant::UInt:
Lorenz Meier's avatar
Lorenz Meier committed
1040 1041 1042 1043 1044
    {
        QVariant fixedValue(value.toUInt());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
1045 1046
        break;
    case QMetaType::Float:
Lorenz Meier's avatar
Lorenz Meier committed
1047 1048 1049 1050 1051
    {
        QVariant fixedValue(value.toFloat());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
1052 1053 1054 1055 1056 1057
        break;
    default:
        qCritical() << "ABORTED PARAM SEND, NO VALID QVARIANT TYPE";
        return;
    }

lm's avatar
lm committed
1058 1059
    // Wait for parameter to be written back
    // mark it therefore as missing
1060 1061 1062
    if (!transmissionMissingWriteAckPackets.contains(component))
    {
        transmissionMissingWriteAckPackets.insert(component, new QMap<QString, QVariant>());
lm's avatar
lm committed
1063 1064 1065 1066 1067 1068
    }

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

    // Set timeouts
Lorenz Meier's avatar
Lorenz Meier committed
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
    if (transmissionActive)
    {
        transmissionTimeout += rewriteTimeout;
    }
    else
    {
        quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + rewriteTimeout;
        if (newTransmissionTimeout > transmissionTimeout)
        {
            transmissionTimeout = newTransmissionTimeout;
        }
        transmissionActive = true;
lm's avatar
lm committed
1081
    }
Lorenz Meier's avatar
Lorenz Meier committed
1082

lm's avatar
lm committed
1083 1084
    // Enable guard / reset timeouts
    setRetransmissionGuardEnabled(true);
1085 1086
}

1087 1088 1089
/**
 * Set all parameter in the parameter tree on the MAV
 */
1090 1091
void QGCParamWidget::setParameters()
{
1092
    // Iterate through all components, through all parameters and emit them
1093
    int parametersSent = 0;
1094
    QMap<int, QMap<QString, QVariant>*>::iterator i;
1095
    for (i = changedValues.begin(); i != changedValues.end(); ++i) {
1096 1097
        // Iterate through the parameters of the component
        int compid = i.key();
1098
        QMap<QString, QVariant>* comp = i.value();
1099
        {
1100
            QMap<QString, QVariant>::iterator j;
1101
            for (j = comp->begin(); j != comp->end(); ++j) {
1102
                setParameter(compid, j.key(), j.value());
1103
                parametersSent++;
1104 1105 1106 1107
            }
        }
    }

lm's avatar
lm committed
1108
    // Change transmission status if necessary
1109
    if (parametersSent == 0) {
1110
        statusLabel->setText(tr("No transmission: No changed values."));
1111
    } else {
1112
        statusLabel->setText(tr("Transmitting %1 parameters.").arg(parametersSent));
lm's avatar
lm committed
1113
        // Set timeouts
Lorenz Meier's avatar
Lorenz Meier committed
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
        if (transmissionActive)
        {
            transmissionTimeout += parametersSent*rewriteTimeout;
        }
        else
        {
            transmissionActive = true;
            quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + parametersSent*rewriteTimeout;
            if (newTransmissionTimeout > transmissionTimeout) {
                transmissionTimeout = newTransmissionTimeout;
            }
lm's avatar
lm committed
1125 1126 1127
        }
        // Enable guard
        setRetransmissionGuardEnabled(true);
1128
    }
1129 1130
}

1131 1132 1133 1134
/**
 * Write the current onboard parameters from RAM into
 * permanent storage, e.g. EEPROM or harddisk
 */
1135 1136
void QGCParamWidget::writeParameters()
{
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
    int changedParamCount = 0;

    QMap<int, QMap<QString, QVariant>*>::iterator i;
    for (i = changedValues.begin(); i != changedValues.end(); ++i)
    {
        // Iterate through the parameters of the component
        QMap<QString, QVariant>* comp = i.value();
        {
            QMap<QString, QVariant>::iterator j;
            for (j = comp->begin(); j != comp->end(); ++j)
            {
                changedParamCount++;
            }
        }
    }

    if (changedParamCount > 0)
    {
        QMessageBox msgBox;
        msgBox.setText(tr("There are locally changed parameters. Please transmit them first (<TRANSMIT>) or update them with the onboard values (<REFRESH>) before storing onboard from RAM to ROM."));
        msgBox.exec();
    }
    else
    {
        if (!mav) return;
        mav->writeParametersToStorage();
    }
1164 1165 1166 1167
}

void QGCParamWidget::readParameters()
{
1168
    if (!mav) return;
1169
    mav->readParametersFromStorage();
1170 1171
}

1172 1173 1174
/**
 * Clear all data in the parameter widget
 */
pixhawk's avatar
pixhawk committed
1175 1176 1177
void QGCParamWidget::clear()
{
    tree->clear();
lm's avatar
lm committed
1178
    components->clear();
pixhawk's avatar
pixhawk committed
1179
}