QGCParamWidget.cc 28.8 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 autoPilotName(uas->getAutopilotTypeName());
    QString sysTypeName(uas->getSystemTypeName());
    loadParamMetaInfoCSV(autoPilotName, sysTypeName);
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."));
tstellanova's avatar
tstellanova committed
90
    connect(refreshButton, SIGNAL(clicked()), this, SLOT(requestParameterListUpdate()));
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(loadParametersFromFile()));
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(saveParametersToFile()));
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(receivedParameterUpdate(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
tstellanova's avatar
tstellanova committed
153 154 155
    if (uas) {
        requestParameterListUpdate();
    }
pixhawk's avatar
pixhawk committed
156 157
}

tstellanova's avatar
tstellanova committed
158

159 160 161 162 163 164 165 166 167 168 169 170
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();
}

171 172 173



174
void QGCParamWidget::loadParamMetaInfoCSV(const QString& autopilot, const QString& airframe)
lm's avatar
lm committed
175
{
176 177 178 179
    Q_UNUSED(airframe);

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

LM's avatar
LM committed
180
    QDir appDir = QApplication::applicationDirPath();
181
    appDir.cd("files");
182
    QString fileName = QString("%1/%2/parameter_tooltips/tooltips.txt").arg(appDir.canonicalPath()).arg(autopilot.toLower());
LM's avatar
LM committed
183
    QFile paramMetaFile(fileName);
lm's avatar
lm committed
184

185 186 187
    qDebug() << "AUTOPILOT:" << autopilot;
    qDebug() << "FILENAME: " << fileName;

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


    QTextStream in(&paramMetaFile);
197 198
    paramDataModel->loadParamMetaInfoFromStream(in);
    paramMetaFile.close();
lm's avatar
lm committed
199 200 201

}

202 203 204 205
/**
 * @return The MAV of this widget. Unless the MAV object has been destroyed, this
 *         pointer is never zero.
 */
pixhawk's avatar
pixhawk committed
206 207 208 209 210 211 212 213 214 215 216
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
 */
217
void QGCParamWidget::addComponent(int uas, int component, QString componentName)
pixhawk's avatar
pixhawk committed
218
{
219
    Q_UNUSED(uas);
220
    if (components->contains(component)) {
pixhawk's avatar
pixhawk committed
221
        // Update existing
222 223 224
        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);
225
    } else {
pixhawk's avatar
pixhawk committed
226
        // Add new
lm's avatar
lm committed
227
        QStringList list(QString("%1 (#%2)").arg(componentName).arg(component));
pixhawk's avatar
pixhawk committed
228
        QTreeWidgetItem* comp = new QTreeWidgetItem(list);
lm's avatar
lm committed
229
        comp->setFirstColumnSpanned(true);
pixhawk's avatar
pixhawk committed
230 231 232
        components->insert(component, comp);
        // Create grouping and update maps
        paramGroups.insert(component, new QMap<QString, QTreeWidgetItem*>());
pixhawk's avatar
pixhawk committed
233 234 235
        tree->addTopLevelItem(comp);
        tree->update();
    }
236 237 238

    paramDataModel->addComponent(component);

pixhawk's avatar
pixhawk committed
239 240
}

lm's avatar
lm committed
241 242 243 244 245
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
246
void QGCParamWidget::receivedParameterUpdate(int uas, int component, int paramCount, int paramId, QString parameterName, QVariant value)
lm's avatar
lm committed
247
{
248
    receivedParameterUpdate(uas, component, parameterName, value);
lm's avatar
lm committed
249

lm's avatar
lm committed
250
    // Missing packets list has to be instantiated for all components
251
    if (!transmissionMissingPackets.contains(component)) {
lm's avatar
lm committed
252 253 254
        transmissionMissingPackets.insert(component, new QList<int>());
    }

lm's avatar
lm committed
255
    // List mode is different from single parameter transfers
256
    if (transmissionListMode) {
lm's avatar
lm committed
257 258
        // Only accept the list size once on the first packet from
        // each component
lm's avatar
lm committed
259 260
        if (!transmissionListSizeKnown.contains(component))
        {
lm's avatar
lm committed
261 262
            // Mark list size as known
            transmissionListSizeKnown.insert(component, true);
lm's avatar
lm committed
263

264
            // Mark all parameters as missing
lm's avatar
lm committed
265 266 267 268
            for (int i = 0; i < paramCount; ++i)
            {
                if (!transmissionMissingPackets.value(component)->contains(i))
                {
lm's avatar
lm committed
269 270 271
                    transmissionMissingPackets.value(component)->append(i);
                }
            }
272

lm's avatar
lm committed
273 274 275
            // 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
276
            quint64 thisTransmissionTimeout = QGC::groundTimeMilliseconds() + ((paramCount)*retransmissionTimeout);
lm's avatar
lm committed
277 278
            if (thisTransmissionTimeout > transmissionTimeout)
            {
lm's avatar
lm committed
279 280
                transmissionTimeout = thisTransmissionTimeout;
            }
lm's avatar
lm committed
281
        }
lm's avatar
lm committed
282 283 284 285

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

lm's avatar
lm committed
288
    // Mark this parameter as received in read list
lm's avatar
lm committed
289 290 291 292
    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
293 294
    bool justWritten = false;
    bool writeMismatch = false;
295
    //bool lastWritten = false;
lm's avatar
lm committed
296
    // Mark this parameter as received in write ACK list
297
    QMap<QString, QVariant>* map = transmissionMissingWriteAckPackets.value(component);
lm's avatar
lm committed
298 299
    if (map && map->contains(parameterName))
    {
lm's avatar
lm committed
300
        justWritten = true;
301
        QVariant newval = map->value(parameterName);
lm's avatar
lm committed
302 303
        if (map->value(parameterName) != value)
        {
lm's avatar
lm committed
304 305 306 307 308
            writeMismatch = true;
        }
        map->remove(parameterName);
    }

309
    int missCount = 0;
lm's avatar
lm committed
310 311
    foreach (int key, transmissionMissingPackets.keys())
    {
312 313 314
        missCount +=  transmissionMissingPackets.value(key)->count();
    }

lm's avatar
lm committed
315
    int missWriteCount = 0;
lm's avatar
lm committed
316 317
    foreach (int key, transmissionMissingWriteAckPackets.keys())
    {
lm's avatar
lm committed
318 319 320
        missWriteCount += transmissionMissingWriteAckPackets.value(key)->count();
    }

lm's avatar
lm committed
321 322
    if (justWritten && !writeMismatch && missWriteCount == 0)
    {
lm's avatar
lm committed
323 324 325 326 327
        // 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
328 329
    } else if (justWritten && !writeMismatch)
    {
330
        statusLabel->setText(tr("SUCCESS: Wrote %2 (#%1/%4): %3").arg(paramId+1).arg(parameterName).arg(value.toDouble()).arg(paramCount));
lm's avatar
lm committed
331 332 333
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorGreen);
        statusLabel->setPalette(pal);
lm's avatar
lm committed
334 335
    } else if (justWritten && writeMismatch)
    {
lm's avatar
lm committed
336
        // Mismatch, tell user
lm's avatar
lm committed
337 338 339
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorRed);
        statusLabel->setPalette(pal);
340
        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
341 342 343 344 345
    }
    else
    {
        if (missCount > 0)
        {
lm's avatar
lm committed
346 347 348
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorOrange);
            statusLabel->setPalette(pal);
lm's avatar
lm committed
349 350 351
        }
        else
        {
lm's avatar
lm committed
352 353 354 355
            QPalette pal = statusLabel->palette();
            pal.setColor(backgroundRole(), QGC::colorGreen);
            statusLabel->setPalette(pal);
        }
356 357
        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));
358 359 360 361 362 363 364 365 366 367 368 369
        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
370
    }
lm's avatar
lm committed
371 372

    // Check if last parameter was received
lm's avatar
lm committed
373 374
    if (missCount == 0 && missWriteCount == 0)
    {
lm's avatar
lm committed
375 376 377
        this->transmissionActive = false;
        this->transmissionListMode = false;
        transmissionListSizeKnown.clear();
lm's avatar
lm committed
378 379
        foreach (int key, transmissionMissingPackets.keys())
        {
lm's avatar
lm committed
380 381
            transmissionMissingPackets.value(key)->clear();
        }
382 383 384

        // Expand visual tree
        tree->expandItem(tree->topLevelItem(0));
lm's avatar
lm committed
385 386 387
    }
}

388 389 390 391 392
/**
 * @param uas System which has the component
 * @param component id of the component
 * @param parameterName human friendly name of the parameter
 */
393
void QGCParamWidget::receivedParameterUpdate(int uas, int component, QString parameterName, QVariant value)
pixhawk's avatar
pixhawk committed
394
{
395
    qDebug() << "PARAM WIDGET GOT PARAM:" << parameterName;
396
    Q_UNUSED(uas);
397
    // Reference to item in tree
398
    QTreeWidgetItem* parameterItem = NULL;
pixhawk's avatar
pixhawk committed
399 400

    // Get component
lm's avatar
lm committed
401 402
    if (!components->contains(component))
    {
Lorenz Meier's avatar
Lorenz Meier committed
403 404 405 406 407 408 409 410 411 412 413 414 415
        //        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;
        //        }
416
        QString componentName = tr("Component #%1").arg(component);
417
        addComponent(uas, component, componentName);
pixhawk's avatar
pixhawk committed
418
    }
419

420 421
    // Replace value in map

422
    paramDataModel->setOnboardParameter(component,parameterName,value);
423 424


pixhawk's avatar
pixhawk committed
425 426
    QString splitToken = "_";
    // Check if auto-grouping can work
lm's avatar
lm committed
427 428
    if (parameterName.contains(splitToken))
    {
pixhawk's avatar
pixhawk committed
429 430
        QString parent = parameterName.section(splitToken, 0, 0, QString::SectionSkipEmpty);
        QMap<QString, QTreeWidgetItem*>* compParamGroups = paramGroups.value(component);
431 432
        if (!compParamGroups->contains(parent))
        {
pixhawk's avatar
pixhawk committed
433 434 435 436 437 438
            // Insert group item
            QStringList glist;
            glist.append(parent);
            QTreeWidgetItem* item = new QTreeWidgetItem(glist);
            compParamGroups->insert(parent, item);
            components->value(component)->addChild(item);
439
        }
440 441 442 443

        // Append child to group
        bool found = false;
        QTreeWidgetItem* parentItem = compParamGroups->value(parent);
444
        for (int i = 0; i < parentItem->childCount(); i++) {
445 446
            QTreeWidgetItem* child = parentItem->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
447 448
            if (key == parameterName)
            {
449 450
                //qDebug() << "UPDATED CHILD";
                parameterItem = child;
451 452 453 454 455 456 457 458
                if (value.type() == QVariant::Char)
                {
                    parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
                }
                else
                {
                    parameterItem->setData(1, Qt::DisplayRole, value);
                }
459 460 461 462
                found = true;
            }
        }

lm's avatar
lm committed
463 464
        if (!found)
        {
465 466 467
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
468
            // CREATE PARAMETER ITEM
469
            parameterItem = new QTreeWidgetItem(plist);
470
            // CONFIGURE PARAMETER ITEM
471 472 473 474 475 476 477 478
            if (value.type() == QVariant::Char)
            {
                parameterItem->setData(1, Qt::DisplayRole, value.toUInt());
            }
            else
            {
                parameterItem->setData(1, Qt::DisplayRole, value);
            }
479 480

            compParamGroups->value(parent)->addChild(parameterItem);
lm's avatar
lm committed
481
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
482
        }
lm's avatar
lm committed
483 484 485
    }
    else
    {
pixhawk's avatar
pixhawk committed
486 487
        bool found = false;
        QTreeWidgetItem* parent = components->value(component);
lm's avatar
lm committed
488 489
        for (int i = 0; i < parent->childCount(); i++)
        {
pixhawk's avatar
pixhawk committed
490 491
            QTreeWidgetItem* child = parent->child(i);
            QString key = child->data(0, Qt::DisplayRole).toString();
lm's avatar
lm committed
492 493
            if (key == parameterName)
            {
pixhawk's avatar
pixhawk committed
494
                //qDebug() << "UPDATED CHILD";
495 496
                parameterItem = child;
                parameterItem->setData(1, Qt::DisplayRole, value);
pixhawk's avatar
pixhawk committed
497 498 499 500
                found = true;
            }
        }

lm's avatar
lm committed
501 502
        if (!found)
        {
503 504 505
            // Insert parameter into map
            QStringList plist;
            plist.append(parameterName);
506
            // CREATE PARAMETER ITEM
507
            parameterItem = new QTreeWidgetItem(plist);
508
            // CONFIGURE PARAMETER ITEM
509
            parameterItem->setData(1, Qt::DisplayRole, value);
510

lm's avatar
lm committed
511 512
            components->value(component)->addChild(parameterItem);
            parameterItem->setFlags(parameterItem->flags() | Qt::ItemIsEditable);
pixhawk's avatar
pixhawk committed
513
        }
514
        //tree->expandAll();
515
    }
516
    // Reset background color
517
    parameterItem->setBackground(0, Qt::NoBrush);
518
    parameterItem->setBackground(1, Qt::NoBrush);
lm's avatar
lm committed
519
    // Add tooltip
520
    QString paramDesc = paramDataModel->getParamDescription(parameterName);
521
    QString tooltipFormat;
522
    if (paramDataModel->isParamDefaultKnown(parameterName)) {
523
        tooltipFormat = tr("Default: %1, %2");
524 525
        double paramDefValue = paramDataModel->getParamDefault(parameterName);
        tooltipFormat = tooltipFormat.arg(paramDefValue).arg(paramDesc);
526
    }
527 528
    else {
        tooltipFormat = paramDesc;
529 530 531
    }
    parameterItem->setToolTip(0, tooltipFormat);
    parameterItem->setToolTip(1, tooltipFormat);
lm's avatar
lm committed
532

533 534
    paramDataModel->handleParameterUpdate(component,parameterName,value);

pixhawk's avatar
pixhawk committed
535 536
}

lm's avatar
lm committed
537

538

539
void QGCParamWidget::parameterItemChanged(QTreeWidgetItem* current, int column)
lm's avatar
lm committed
540
{
541
    if (current && column > 0) {
542
        QTreeWidgetItem* parent = current->parent();
543
        while (parent->parent() != NULL) {
544 545 546
            parent = parent->parent();
        }
        // Parent is now top-level component
547
        int componentId = components->key(parent);
548

549 550 551 552 553 554 555 556 557
        QString key = current->data(0, Qt::DisplayRole).toString();
        QVariant value = current->data(1, Qt::DisplayRole);
        // Set parameter on changed list to be transmitted to MAV
        QPalette pal = statusLabel->palette();
        pal.setColor(backgroundRole(), QGC::colorOrange);
        statusLabel->setPalette(pal);
        statusLabel->setText(tr("Transmit pend. %1:%2: %3").arg(componentId).arg(key).arg(value.toFloat(), 5, 'f', 1, QChar(' ')));
        //qDebug() << "PARAM CHANGED: COMP:" << key << "KEY:" << str << "VALUE:" << value;
        // Changed values list
558

559
        bool changed = paramDataModel->addPendingIfParameterChanged(componentId,key,value);
560

561 562 563 564 565 566 567
        // If the value was numerically changed, display it differently
        if (changed) {
            current->setBackground(0, QBrush(QColor(QGC::colorOrange)));
            current->setBackground(1, QBrush(QColor(QGC::colorOrange)));

            //TODO this seems incorrect-- we're pre-updating the onboard value before we've received confirmation
            paramDataModel->setOnboardParameterWithType(componentId,key,value);
568
        }
569

570 571 572
    }
}

573 574


575
void QGCParamWidget::saveParametersToFile()
576
{
577
    if (!mav) return;
578 579
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), "./parameters.txt", tr("Parameter File (*.txt)"));
    QFile file(fileName);
580
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
581 582 583
        return;
    }

584 585
    QTextStream outstream(&file);
    paramDataModel->writeOnboardParametersToStream(outstream,mav->getUASName());
586 587 588
    file.close();
}

589 590

void QGCParamWidget::loadParametersFromFile()
591
{
592
    if (!mav) return;
593 594 595 596 597 598
    QString fileName = QFileDialog::getOpenFileName(this, tr("Load File"), ".", tr("Parameter file (*.txt)"));
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QTextStream in(&file);
599
    paramDataModel->readUpdateParametersFromStream(in);
600 601
    file.close();

lm's avatar
lm committed
602 603
}

tstellanova's avatar
tstellanova committed
604
void QGCParamWidget::setParameterStatusMsg(const QString& msg)
605
{
tstellanova's avatar
tstellanova committed
606 607 608 609 610 611 612 613 614
    statusLabel->setText(msg);
}



void QGCParamWidget::requestParameterListUpdate()
{
    if (!mav) {
        return;
615
    }
tstellanova's avatar
tstellanova committed
616 617 618 619 620 621 622 623 624 625 626 627

    // 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

    // Clear view and request param list
    clear();

    requestParameterList();
628 629
}

tstellanova's avatar
tstellanova committed
630 631 632 633
/**
 * The .. signal is emitted
 */
void QGCParamWidget::requestParameterUpdate(int component, const QString& parameter)
634
{
tstellanova's avatar
tstellanova committed
635 636
    if (mav) mav->requestParameter(component, parameter);
}
lm's avatar
lm committed
637 638


tstellanova's avatar
tstellanova committed
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655

/**
 * Set all parameter in the parameter tree on the MAV
 */
void QGCParamWidget::setParameters()
{
    // Iterate through all components, through all parameters and emit them
    int parametersSent = 0;
    QMap<int, QMap<QString, QVariant>*> changedValues = paramDataModel->getPendingParameters();
    QMap<int, QMap<QString, QVariant>*>::iterator i;
    for (i = changedValues.begin(); i != changedValues.end(); ++i) {
        // Iterate through the parameters of the component
        int compid = i.key();
        QMap<QString, QVariant>* comp = i.value();
        {
            QMap<QString, QVariant>::iterator j;
            for (j = comp->begin(); j != comp->end(); ++j) {
tstellanova's avatar
tstellanova committed
656
                //TODO mavlink command for "set parameter list" ?
tstellanova's avatar
tstellanova committed
657 658
                setParameter(compid, j.key(), j.value());
                parametersSent++;
659 660
            }
        }
tstellanova's avatar
tstellanova committed
661
    }
lm's avatar
lm committed
662

tstellanova's avatar
tstellanova committed
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
    // Change transmission status if necessary
    if (parametersSent == 0) {
        statusLabel->setText(tr("No transmission: No changed values."));
    } else {
        statusLabel->setText(tr("Transmitting %1 parameters.").arg(parametersSent));
        // Set timeouts
        if (transmissionActive)
        {
            transmissionTimeout += parametersSent*rewriteTimeout;
        }
        else
        {
            transmissionActive = true;
            quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + parametersSent*rewriteTimeout;
            if (newTransmissionTimeout > transmissionTimeout) {
                transmissionTimeout = newTransmissionTimeout;
lm's avatar
lm committed
679 680
            }
        }
tstellanova's avatar
tstellanova committed
681 682
        // Enable guard
        setRetransmissionGuardEnabled(true);
683 684 685
    }
}

686
/**
tstellanova's avatar
tstellanova committed
687 688
 * Write the current onboard parameters from RAM into
 * permanent storage, e.g. EEPROM or harddisk
689
 */
tstellanova's avatar
tstellanova committed
690
void QGCParamWidget::writeParameters()
691
{
tstellanova's avatar
tstellanova committed
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
    int changedParamCount = 0;

    QMap<int, QMap<QString, QVariant>*>::iterator i;
    QMap<int, QMap<QString, QVariant>*> changedValues = paramDataModel->getPendingParameters();

    for (i = changedValues.begin(); i != changedValues.end() , (0 == changedParamCount);  ++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++;
            break;//it only takes one changed param to warrant warning the user
        }
    }
708

tstellanova's avatar
tstellanova committed
709 710 711 712 713 714 715 716 717 718 719 720
    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();
    }
}
721

722 723 724 725 726
/**
 * @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
 */
727
void QGCParamWidget::setParameter(int component, QString parameterName, QVariant value)
728
{
729 730 731 732
    if (parameterName.isEmpty()) {
        return;
    }

733
    double dblValue = value.toDouble();
734 735

    if (paramDataModel->isValueLessThanParamMin(parameterName,dblValue)) {
736
        statusLabel->setText(tr("REJ. %1, %2 < min").arg(parameterName).arg(dblValue));
737 738
        return;
    }
739
    if (paramDataModel->isValueGreaterThanParamMax(parameterName,dblValue)) {
740
        statusLabel->setText(tr("REJ. %1, %2 > max").arg(parameterName).arg(dblValue));
741 742
        return;
    }
743 744 745 746
    QVariant onboardVal;
    paramDataModel->getOnboardParameterValue(component,parameterName,onboardVal);
    if (onboardVal == value) {
        statusLabel->setText(tr("REJ. %1 already %2").arg(parameterName).arg(dblValue));
747 748
        return;
    }
749

750 751 752
    //int paramType = (int)onboardParameters.value(component)->value(parameterName).type();
    int paramType = (int)value.type();
    switch (paramType)
753
    {
754 755 756 757 758 759 760
    case QVariant::Char:
    {
        QVariant fixedValue(QChar((unsigned char)value.toInt()));
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
        break;
761
    case QVariant::Int:
Lorenz Meier's avatar
Lorenz Meier committed
762 763 764 765 766
    {
        QVariant fixedValue(value.toInt());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
767 768
        break;
    case QVariant::UInt:
Lorenz Meier's avatar
Lorenz Meier committed
769 770 771 772 773
    {
        QVariant fixedValue(value.toUInt());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
774 775
        break;
    case QMetaType::Float:
Lorenz Meier's avatar
Lorenz Meier committed
776 777 778 779 780
    {
        QVariant fixedValue(value.toFloat());
        emit parameterChanged(component, parameterName, fixedValue);
        //qDebug() << "PARAM WIDGET SENT:" << fixedValue;
    }
781 782 783 784 785 786
        break;
    default:
        qCritical() << "ABORTED PARAM SEND, NO VALID QVARIANT TYPE";
        return;
    }

lm's avatar
lm committed
787 788
    // Wait for parameter to be written back
    // mark it therefore as missing
789 790 791
    if (!transmissionMissingWriteAckPackets.contains(component))
    {
        transmissionMissingWriteAckPackets.insert(component, new QMap<QString, QVariant>());
lm's avatar
lm committed
792 793 794 795 796 797
    }

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

    // Set timeouts
Lorenz Meier's avatar
Lorenz Meier committed
798 799 800 801 802 803 804 805 806 807 808 809
    if (transmissionActive)
    {
        transmissionTimeout += rewriteTimeout;
    }
    else
    {
        quint64 newTransmissionTimeout = QGC::groundTimeMilliseconds() + rewriteTimeout;
        if (newTransmissionTimeout > transmissionTimeout)
        {
            transmissionTimeout = newTransmissionTimeout;
        }
        transmissionActive = true;
lm's avatar
lm committed
810
    }
Lorenz Meier's avatar
Lorenz Meier committed
811

lm's avatar
lm committed
812 813
    // Enable guard / reset timeouts
    setRetransmissionGuardEnabled(true);
814 815
}

816 817
void QGCParamWidget::readParameters()
{
818
    if (!mav) return;
819
    mav->readParametersFromStorage();
820 821
}

822 823 824
/**
 * Clear all data in the parameter widget
 */
pixhawk's avatar
pixhawk committed
825 826 827
void QGCParamWidget::clear()
{
    tree->clear();
lm's avatar
lm committed
828
    components->clear();
pixhawk's avatar
pixhawk committed
829
}