SimpleMissionItem.cc 36.7 KB
Newer Older
1 2 3 4 5 6 7 8
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
9 10


11 12 13
#include <QStringList>
#include <QDebug>

14
#include "SimpleMissionItem.h"
15 16 17
#include "FirmwarePluginManager.h"
#include "QGCApplication.h"
#include "JsonHelper.h"
18 19
#include "MissionCommandTree.h"
#include "MissionCommandUIInfo.h"
20
#include "QGroundControlQmlGlobal.h"
21
#include "SettingsManager.h"
22

Don Gagne's avatar
Don Gagne committed
23 24 25 26 27 28
FactMetaData* SimpleMissionItem::_altitudeMetaData =        NULL;
FactMetaData* SimpleMissionItem::_commandMetaData =         NULL;
FactMetaData* SimpleMissionItem::_defaultParamMetaData =    NULL;
FactMetaData* SimpleMissionItem::_frameMetaData =           NULL;
FactMetaData* SimpleMissionItem::_latitudeMetaData =        NULL;
FactMetaData* SimpleMissionItem::_longitudeMetaData =       NULL;
29

30 31 32 33
const char* SimpleMissionItem::_jsonAltitudeModeKey =           "AltitudeMode";
const char* SimpleMissionItem::_jsonAltitudeKey =               "Altitude";
const char* SimpleMissionItem::_jsonAMSLAltAboveTerrainKey =    "AMSLAltAboveTerrain";

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
struct EnumInfo_s {
    const char *    label;
    MAV_FRAME       frame;
};

static const struct EnumInfo_s _rgMavFrameInfo[] = {
{ "MAV_FRAME_GLOBAL",                   MAV_FRAME_GLOBAL },
{ "MAV_FRAME_LOCAL_NED",                MAV_FRAME_LOCAL_NED },
{ "MAV_FRAME_MISSION",                  MAV_FRAME_MISSION },
{ "MAV_FRAME_GLOBAL_RELATIVE_ALT",      MAV_FRAME_GLOBAL_RELATIVE_ALT },
{ "MAV_FRAME_LOCAL_ENU",                MAV_FRAME_LOCAL_ENU },
{ "MAV_FRAME_GLOBAL_INT",               MAV_FRAME_GLOBAL_INT },
{ "MAV_FRAME_GLOBAL_RELATIVE_ALT_INT",  MAV_FRAME_GLOBAL_RELATIVE_ALT_INT },
{ "MAV_FRAME_LOCAL_OFFSET_NED",         MAV_FRAME_LOCAL_OFFSET_NED },
{ "MAV_FRAME_BODY_NED",                 MAV_FRAME_BODY_NED },
{ "MAV_FRAME_BODY_OFFSET_NED",          MAV_FRAME_BODY_OFFSET_NED },
{ "MAV_FRAME_GLOBAL_TERRAIN_ALT",       MAV_FRAME_GLOBAL_TERRAIN_ALT },
{ "MAV_FRAME_GLOBAL_TERRAIN_ALT_INT",   MAV_FRAME_GLOBAL_TERRAIN_ALT_INT },
};
53 54

SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent)
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    : VisualMissionItem                 (vehicle, parent)
    , _rawEdit                          (false)
    , _dirty                            (false)
    , _ignoreDirtyChangeSignals         (false)
    , _speedSection                     (NULL)
    , _cameraSection                    (NULL)
    , _commandTree                      (qgcApp()->toolbox()->missionCommandTree())
    , _supportedCommandFact             (0, "Command:",             FactMetaData::valueTypeUint32)
    , _altitudeMode                     (AltitudeRelative)
    , _altitudeFact                     (0, "Altitude",             FactMetaData::valueTypeDouble)
    , _amslAltAboveTerrainFact          (0, "Alt above terrain",    FactMetaData::valueTypeDouble)
    , _param1MetaData                   (FactMetaData::valueTypeDouble)
    , _param2MetaData                   (FactMetaData::valueTypeDouble)
    , _param3MetaData                   (FactMetaData::valueTypeDouble)
    , _param4MetaData                   (FactMetaData::valueTypeDouble)
    , _param5MetaData                   (FactMetaData::valueTypeDouble)
    , _param6MetaData                   (FactMetaData::valueTypeDouble)
    , _param7MetaData                   (FactMetaData::valueTypeDouble)
    , _syncingHeadingDegreesAndParam4   (false)
74
{
Don Gagne's avatar
Don Gagne committed
75
    _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");
76

77 78
    _setupMetaData();
    _connectSignals();
79
    _updateOptionalSections();
80 81

    setDefaultsForCommand();
82
    _rebuildFacts();
83

DonLakeFlyer's avatar
DonLakeFlyer committed
84
    connect(&_missionItem, &MissionItem::specifiedFlightSpeedChanged, this, &SimpleMissionItem::specifiedFlightSpeedChanged);
85

86
    connect(this, &SimpleMissionItem::sequenceNumberChanged,        this, &SimpleMissionItem::lastSequenceNumberChanged);
87
    connect(this, &SimpleMissionItem::cameraSectionChanged,         this, &SimpleMissionItem::_setDirty);
88
    connect(this, &SimpleMissionItem::cameraSectionChanged,         this, &SimpleMissionItem::_updateLastSequenceNumber);
89 90
}

91
SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, bool editMode, const MissionItem& missionItem, QObject* parent)
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    : VisualMissionItem         (vehicle, parent)
    , _missionItem              (missionItem)
    , _rawEdit                  (false)
    , _dirty                    (false)
    , _ignoreDirtyChangeSignals (false)
    , _speedSection             (NULL)
    , _cameraSection            (NULL)
    , _commandTree              (qgcApp()->toolbox()->missionCommandTree())
    , _supportedCommandFact     (0,         "Command:",             FactMetaData::valueTypeUint32)
    , _altitudeMode             (missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL)
    , _altitudeFact             (0,         "Altitude",             FactMetaData::valueTypeDouble)
    , _amslAltAboveTerrainFact  (0,         "Alt above terrain",    FactMetaData::valueTypeDouble)
    , _param1MetaData           (FactMetaData::valueTypeDouble)
    , _param2MetaData           (FactMetaData::valueTypeDouble)
    , _param3MetaData           (FactMetaData::valueTypeDouble)
    , _param4MetaData           (FactMetaData::valueTypeDouble)
    , _param5MetaData           (FactMetaData::valueTypeDouble)
    , _param6MetaData           (FactMetaData::valueTypeDouble)
    , _param7MetaData           (FactMetaData::valueTypeDouble)
111
    , _syncingHeadingDegreesAndParam4           (false)
112
{
Don Gagne's avatar
Don Gagne committed
113 114
    _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");

115
    _isCurrentItem = missionItem.isCurrentItem();
116 117
    _altitudeFact.setRawValue(specifiesCoordinate() ? _missionItem._param7Fact.rawValue() : qQNaN());
    _amslAltAboveTerrainFact.setRawValue(qQNaN());
118

119 120 121 122
    // In !editMode we skip some of the intialization to save memory
    if (editMode) {
        _setupMetaData();
    }
123
    _connectSignals();
124
    _updateOptionalSections();
125 126 127
    if (editMode) {
        _rebuildFacts();
    }
128 129 130

    // Signal coordinate changed to kick off terrain query
    emit coordinateChanged(coordinate());
131 132 133
}

SimpleMissionItem::SimpleMissionItem(const SimpleMissionItem& other, QObject* parent)
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
    : VisualMissionItem         (other, parent)
    , _missionItem              (other._vehicle)
    , _rawEdit                  (false)
    , _dirty                    (false)
    , _ignoreDirtyChangeSignals (false)
    , _speedSection             (NULL)
    , _cameraSection            (NULL)
    , _commandTree              (qgcApp()->toolbox()->missionCommandTree())
    , _supportedCommandFact     (0,         "Command:",             FactMetaData::valueTypeUint32)
    , _altitudeMode             (other._altitudeMode)
    , _altitudeFact             (0,         "Altitude",             FactMetaData::valueTypeDouble)
    , _amslAltAboveTerrainFact  (qQNaN(),   "Alt above terrain",    FactMetaData::valueTypeDouble)
    , _param1MetaData           (FactMetaData::valueTypeDouble)
    , _param2MetaData           (FactMetaData::valueTypeDouble)
    , _param3MetaData           (FactMetaData::valueTypeDouble)
    , _param4MetaData           (FactMetaData::valueTypeDouble)
150
    , _syncingHeadingDegreesAndParam4           (false)
151
{
Don Gagne's avatar
Don Gagne committed
152 153
    _editorQml = QStringLiteral("qrc:/qml/SimpleItemEditor.qml");

154 155 156
    _altitudeFact.setRawValue(other._altitudeFact.rawValue());
    _amslAltAboveTerrainFact.setRawValue(other._amslAltAboveTerrainFact.rawValue());

157 158
    _setupMetaData();
    _connectSignals();
159
    _updateOptionalSections();
160

161
    _rebuildFacts();
162 163
}

164 165 166
void SimpleMissionItem::_connectSignals(void)
{
    // Connect to change signals to track dirty state
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
    connect(&_missionItem._param1Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param2Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param3Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param4Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param5Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param6Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._param7Fact,  &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._frameFact,   &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem._commandFact, &Fact::valueChanged,                    this, &SimpleMissionItem::_setDirty);
    connect(&_missionItem,              &MissionItem::sequenceNumberChanged,    this, &SimpleMissionItem::_setDirty);
    connect(this,                       &SimpleMissionItem::altitudeModeChanged,this, &SimpleMissionItem::_setDirty);

    // These changes may need to trigger terrain queries
    connect(&_altitudeFact,             &Fact::valueChanged,                    this, &SimpleMissionItem::_altitudeChanged);
    connect(this,                       &SimpleMissionItem::altitudeModeChanged,this, &SimpleMissionItem::_altitudeModeChanged);

    connect(this,                       &SimpleMissionItem::terrainAltitudeChanged,this, &SimpleMissionItem::_terrainAltChanged);
184 185

    // These are coordinate parameters, they must emit coordinateChanged signal
186 187 188
    connect(&_missionItem._param5Fact,  &Fact::valueChanged, this, &SimpleMissionItem::_sendCoordinateChanged);
    connect(&_missionItem._param6Fact,  &Fact::valueChanged, this, &SimpleMissionItem::_sendCoordinateChanged);
    connect(&_missionItem._param7Fact,  &Fact::valueChanged, this, &SimpleMissionItem::_sendCoordinateChanged);
189 190 191 192 193 194 195 196 197 198

    // The following changes may also change friendlyEditAllowed
    connect(&_missionItem._autoContinueFact,    &Fact::valueChanged, this, &SimpleMissionItem::_sendFriendlyEditAllowedChanged);
    connect(&_missionItem._commandFact,         &Fact::valueChanged, this, &SimpleMissionItem::_sendFriendlyEditAllowedChanged);
    connect(&_missionItem._frameFact,           &Fact::valueChanged, this, &SimpleMissionItem::_sendFriendlyEditAllowedChanged);

    // A command change triggers a number of other changes as well.
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::setDefaultsForCommand);
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::commandNameChanged);
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::commandDescriptionChanged);
199
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::abbreviationChanged);
200
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::specifiesCoordinateChanged);
201
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::specifiesAltitudeOnlyChanged);
202 203 204
    connect(&_missionItem._commandFact, &Fact::valueChanged, this, &SimpleMissionItem::isStandaloneCoordinateChanged);

    // Whenever these properties change the ui model changes as well
205 206
    connect(this, &SimpleMissionItem::commandChanged,       this, &SimpleMissionItem::_rebuildFacts);
    connect(this, &SimpleMissionItem::rawEditChanged,       this, &SimpleMissionItem::_rebuildFacts);
207 208 209

    // These fact signals must alway signal out through SimpleMissionItem signals
    connect(&_missionItem._commandFact,     &Fact::valueChanged, this, &SimpleMissionItem::_sendCommandChanged);
210

Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
211
    // Sequence number is kept in mission iteem, so we need to propagate signal up as well
212
    connect(&_missionItem, &MissionItem::sequenceNumberChanged, this, &SimpleMissionItem::sequenceNumberChanged);
213 214 215 216 217 218 219 220 221
}

void SimpleMissionItem::_setupMetaData(void)
{
    QStringList enumStrings;
    QVariantList enumValues;

    if (!_altitudeMetaData) {
        _altitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
222
        _altitudeMetaData->setRawUnits("m");
223
        _altitudeMetaData->setRawIncrement(1);
224 225 226 227
        _altitudeMetaData->setDecimalPlaces(2);

        enumStrings.clear();
        enumValues.clear();
228 229 230 231
        MissionCommandTree* commandTree = qgcApp()->toolbox()->missionCommandTree();
        foreach (const MAV_CMD command, commandTree->allCommandIds()) {
            enumStrings.append(commandTree->rawName(command));
            enumValues.append(QVariant((int)command));
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
        }
        _commandMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
        _commandMetaData->setEnumInfo(enumStrings, enumValues);

        _defaultParamMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
        _defaultParamMetaData->setDecimalPlaces(7);

        enumStrings.clear();
        enumValues.clear();
        for (size_t i=0; i<sizeof(_rgMavFrameInfo)/sizeof(_rgMavFrameInfo[0]); i++) {
            const struct EnumInfo_s* mavFrameInfo = &_rgMavFrameInfo[i];

            enumStrings.append(mavFrameInfo->label);
            enumValues.append(QVariant(mavFrameInfo->frame));
        }
        _frameMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
        _frameMetaData->setEnumInfo(enumStrings, enumValues);

        _latitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
        _latitudeMetaData->setRawUnits("deg");
        _latitudeMetaData->setDecimalPlaces(7);

        _longitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
        _longitudeMetaData->setRawUnits("deg");
        _longitudeMetaData->setDecimalPlaces(7);

    }

    _missionItem._commandFact.setMetaData(_commandMetaData);
    _missionItem._frameFact.setMetaData(_frameMetaData);
262
    _altitudeFact.setMetaData(_altitudeMetaData);
263
    _amslAltAboveTerrainFact.setMetaData(_altitudeMetaData);
264 265 266 267 268 269
}

SimpleMissionItem::~SimpleMissionItem()
{    
}

270
void SimpleMissionItem::save(QJsonArray&  missionItems)
271
{
272 273 274 275 276 277 278 279
    QList<MissionItem*> items;

    appendMissionItems(items, this);

    for (int i=0; i<items.count(); i++) {
        MissionItem* item = items[i];
        QJsonObject saveObject;
        item->save(saveObject);
280 281 282 283 284 285 286 287
        if (i == 0) {
            // This is the main simple item, save the alt/terrain data
            if (specifiesCoordinate()) {
                saveObject[_jsonAltitudeModeKey] =          _altitudeMode;
                saveObject[_jsonAltitudeKey] =              _altitudeFact.rawValue().toDouble();
                saveObject[_jsonAMSLAltAboveTerrainKey] =   _amslAltAboveTerrainFact.rawValue().toDouble();
            }
        }
288 289 290
        missionItems.append(saveObject);
        item->deleteLater();
    }
291 292 293 294
}

bool SimpleMissionItem::load(QTextStream &loadStream)
{
295 296
    bool success;
    if ((success = _missionItem.load(loadStream))) {
297 298 299 300 301
        if (specifiesCoordinate()) {
            _altitudeMode = _missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL;
            _altitudeFact.setRawValue(_missionItem._param7Fact.rawValue());
            _amslAltAboveTerrainFact.setRawValue(qQNaN());
        }
302 303 304
        _updateOptionalSections();
    }
    return success;
305 306
}

Don Gagne's avatar
Don Gagne committed
307
bool SimpleMissionItem::load(const QJsonObject& json, int sequenceNumber, QString& errorString)
308
{
309 310
    if (!_missionItem.load(json, sequenceNumber, errorString)) {
        return false;
311
    }
312 313 314 315 316 317 318 319 320 321 322 323

    if (specifiesCoordinate()) {
        if (json.contains(_jsonAltitudeModeKey) || json.contains(_jsonAltitudeKey) || json.contains(_jsonAMSLAltAboveTerrainKey)) {
            QList<JsonHelper::KeyValidateInfo> keyInfoList = {
                { _jsonAltitudeModeKey,         QJsonValue::Double, true },
                { _jsonAltitudeKey,             QJsonValue::Double, true },
                { _jsonAMSLAltAboveTerrainKey,  QJsonValue::Double, true },
            };
            if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
                return false;
            }

DonLakeFlyer's avatar
DonLakeFlyer committed
324
            _altitudeMode = (AltitudeMode)(int)json[_jsonAltitudeModeKey].toDouble();
325 326 327 328 329 330 331 332 333 334 335 336
            _altitudeFact.setRawValue(JsonHelper::possibleNaNJsonValue(json[_jsonAltitudeKey]));
            _amslAltAboveTerrainFact.setRawValue(JsonHelper::possibleNaNJsonValue(json[_jsonAltitudeKey]));
        } else {
            _altitudeMode = _missionItem.relativeAltitude() ? AltitudeRelative : AltitudeAMSL;
            _altitudeFact.setRawValue(_missionItem._param7Fact.rawValue());
            _amslAltAboveTerrainFact.setRawValue(qQNaN());
        }
    }

    _updateOptionalSections();

    return true;
337 338 339 340
}

bool SimpleMissionItem::isStandaloneCoordinate(void) const
{
341 342 343
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo) {
        return uiInfo->isStandaloneCoordinate();
344 345 346 347 348 349 350
    } else {
        return false;
    }
}

bool SimpleMissionItem::specifiesCoordinate(void) const
{
351 352 353
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo) {
        return uiInfo->specifiesCoordinate();
354 355 356 357 358
    } else {
        return false;
    }
}

359 360 361 362 363 364 365 366 367 368
bool SimpleMissionItem::specifiesAltitudeOnly(void) const
{
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo) {
        return uiInfo->specifiesAltitudeOnly();
    } else {
        return false;
    }
}

369 370
QString SimpleMissionItem::commandDescription(void) const
{
371 372 373
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo) {
        return uiInfo->description();
374 375 376 377 378 379 380 381
    } else {
        qWarning() << "Should not ask for command description on unknown command";
        return commandName();
    }
}

QString SimpleMissionItem::commandName(void) const
{
382 383 384
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo) {
        return uiInfo->friendlyName();
385 386
    } else {
        qWarning() << "Request for command name on unknown command";
387
        return tr("Unknown: %1").arg(command());
388 389 390
    }
}

391 392 393
QString SimpleMissionItem::abbreviation() const
{
    if (homePosition())
394
        return tr("H");
395 396

    switch(command()) {
397
    case MAV_CMD_NAV_TAKEOFF:
398
        return tr("Takeoff");
399
    case MAV_CMD_NAV_LAND:
400
        return tr("Land");
401
    case MAV_CMD_NAV_VTOL_TAKEOFF:
402
        return tr("VTOL Takeoff");
403
    case MAV_CMD_NAV_VTOL_LAND:
404
        return tr("VTOL Land");
405
    case MAV_CMD_DO_SET_ROI:
406 407 408
        return tr("ROI");
    default:
        return QString();
409 410 411
    }
}

412
void SimpleMissionItem::_rebuildTextFieldFacts(void)
413
{
414
    _textFieldFacts.clear();
415 416
    
    if (rawEdit()) {
417
        _missionItem._param1Fact._setName("Param1");
418
        _missionItem._param1Fact.setMetaData(_defaultParamMetaData);
419
        _textFieldFacts.append(&_missionItem._param1Fact);
420
        _missionItem._param2Fact._setName("Param2");
421
        _missionItem._param2Fact.setMetaData(_defaultParamMetaData);
422
        _textFieldFacts.append(&_missionItem._param2Fact);
423
        _missionItem._param3Fact._setName("Param3");
424
        _missionItem._param3Fact.setMetaData(_defaultParamMetaData);
425
        _textFieldFacts.append(&_missionItem._param3Fact);
426
        _missionItem._param4Fact._setName("Param4");
427
        _missionItem._param4Fact.setMetaData(_defaultParamMetaData);
428
        _textFieldFacts.append(&_missionItem._param4Fact);
429
        _missionItem._param5Fact._setName("Lat/X");
430
        _missionItem._param5Fact.setMetaData(_defaultParamMetaData);
431
        _textFieldFacts.append(&_missionItem._param5Fact);
432
        _missionItem._param6Fact._setName("Lon/Y");
433
        _missionItem._param6Fact.setMetaData(_defaultParamMetaData);
434
        _textFieldFacts.append(&_missionItem._param6Fact);
435
        _missionItem._param7Fact._setName("Alt/Z");
436
        _missionItem._param7Fact.setMetaData(_defaultParamMetaData);
437
        _textFieldFacts.append(&_missionItem._param7Fact);
438
    } else {
439 440
        _ignoreDirtyChangeSignals = true;

441 442 443 444 445 446 447 448 449 450
        MAV_CMD command;
        if (_homePositionSpecialCase) {
            command = MAV_CMD_NAV_LAST;
        } else {
            command = _missionItem.command();
        }

        Fact*           rgParamFacts[7] =       { &_missionItem._param1Fact, &_missionItem._param2Fact, &_missionItem._param3Fact, &_missionItem._param4Fact, &_missionItem._param5Fact, &_missionItem._param6Fact, &_missionItem._param7Fact };
        FactMetaData*   rgParamMetaData[7] =    { &_param1MetaData, &_param2MetaData, &_param3MetaData, &_param4MetaData, &_param5MetaData, &_param6MetaData, &_param7MetaData };

451 452
        const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command);

453
        for (int i=1; i<=7; i++) {
454 455
            bool showUI;
            const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI);
456

457
            if (showUI && paramInfo && paramInfo->enumStrings().count() == 0 && !paramInfo->nanUnchanged()) {
458 459 460 461 462 463 464
                Fact*               paramFact =     rgParamFacts[i-1];
                FactMetaData*       paramMetaData = rgParamMetaData[i-1];

                paramFact->_setName(paramInfo->label());
                paramMetaData->setDecimalPlaces(paramInfo->decimalPlaces());
                paramMetaData->setRawUnits(paramInfo->units());
                paramFact->setMetaData(paramMetaData);
465
                _textFieldFacts.append(paramFact);
466 467 468
            }
        }

469
        _ignoreDirtyChangeSignals = false;
470 471 472
    }
}

473
void SimpleMissionItem::_rebuildNaNFacts(void)
474
{
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
    _nanFacts.clear();

    if (!rawEdit()) {
        _ignoreDirtyChangeSignals = true;

        MAV_CMD command;
        if (_homePositionSpecialCase) {
            command = MAV_CMD_NAV_LAST;
        } else {
            command = _missionItem.command();
        }

        Fact*           rgParamFacts[7] =       { &_missionItem._param1Fact, &_missionItem._param2Fact, &_missionItem._param3Fact, &_missionItem._param4Fact, &_missionItem._param5Fact, &_missionItem._param6Fact, &_missionItem._param7Fact };
        FactMetaData*   rgParamMetaData[7] =    { &_param1MetaData, &_param2MetaData, &_param3MetaData, &_param4MetaData, &_param5MetaData, &_param6MetaData, &_param7MetaData };

        const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command);

        for (int i=1; i<=7; i++) {
493 494
            bool showUI;
            const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI);
495

496
            if (showUI && paramInfo && paramInfo->nanUnchanged()) {
497 498 499 500 501 502
                // Show hide Heading field on waypoint based on vehicle yaw to next waypoint setting. This needs to come from the actual vehicle if it exists
                // and not _vehicle which is always offline.
                Vehicle* firmwareVehicle = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle();
                if (!firmwareVehicle) {
                    firmwareVehicle = _vehicle;
                }
503
                bool hideWaypointHeading = (command == MAV_CMD_NAV_WAYPOINT || command == MAV_CMD_NAV_TAKEOFF) && (i == 4) && firmwareVehicle->firmwarePlugin()->vehicleYawsToNextWaypointInMission(firmwareVehicle);
504 505 506
                if (hideWaypointHeading) {
                    continue;
                }
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
                Fact*               paramFact =     rgParamFacts[i-1];
                FactMetaData*       paramMetaData = rgParamMetaData[i-1];

                paramFact->_setName(paramInfo->label());
                paramMetaData->setDecimalPlaces(paramInfo->decimalPlaces());
                paramMetaData->setRawUnits(paramInfo->units());
                paramFact->setMetaData(paramMetaData);
                _nanFacts.append(paramFact);
            }
        }

        _ignoreDirtyChangeSignals = false;
    }
}

523
bool SimpleMissionItem::specifiesAltitude(void) const
524
{
525
    return specifiesCoordinate() || specifiesAltitudeOnly();
526 527
}

528
void SimpleMissionItem::_rebuildComboBoxFacts(void)
529
{
530
    _comboboxFacts.clear();
531 532

    if (rawEdit()) {
533 534
        _comboboxFacts.append(&_missionItem._commandFact);
        _comboboxFacts.append(&_missionItem._frameFact);
535 536 537 538 539 540 541 542 543 544 545 546
    } else {
        Fact*           rgParamFacts[7] =       { &_missionItem._param1Fact, &_missionItem._param2Fact, &_missionItem._param3Fact, &_missionItem._param4Fact, &_missionItem._param5Fact, &_missionItem._param6Fact, &_missionItem._param7Fact };
        FactMetaData*   rgParamMetaData[7] =    { &_param1MetaData, &_param2MetaData, &_param3MetaData, &_param4MetaData, &_param5MetaData, &_param6MetaData, &_param7MetaData };

        MAV_CMD command;
        if (_homePositionSpecialCase) {
            command = MAV_CMD_NAV_LAST;
        } else {
            command = (MAV_CMD)this->command();
        }

        for (int i=1; i<=7; i++) {
547 548
            bool showUI;
            const MissionCmdParamInfo* paramInfo = _commandTree->getUIInfo(_vehicle, command)->getParamInfo(i, showUI);
549

550
            if (showUI && paramInfo && paramInfo->enumStrings().count() != 0) {
551 552 553 554 555 556 557 558
                Fact*               paramFact =     rgParamFacts[i-1];
                FactMetaData*       paramMetaData = rgParamMetaData[i-1];

                paramFact->_setName(paramInfo->label());
                paramMetaData->setDecimalPlaces(paramInfo->decimalPlaces());
                paramMetaData->setEnumInfo(paramInfo->enumStrings(), paramInfo->enumValues());
                paramMetaData->setRawUnits(paramInfo->units());
                paramFact->setMetaData(paramMetaData);
559
                _comboboxFacts.append(paramFact);
560 561 562
            }
        }
    }
563
}
564

565 566 567 568 569
void SimpleMissionItem::_rebuildFacts(void)
{
    _rebuildTextFieldFacts();
    _rebuildNaNFacts();
    _rebuildComboBoxFacts();
570 571 572 573
}

bool SimpleMissionItem::friendlyEditAllowed(void) const
{
574 575
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, (MAV_CMD)command());
    if (uiInfo && uiInfo->friendlyEdit()) {
576 577 578 579
        if (!_missionItem.autoContinue()) {
            return false;
        }

580
        if (specifiesCoordinate() || specifiesAltitudeOnly()) {
581 582 583 584 585 586 587 588 589 590
            MAV_FRAME frame = _missionItem.frame();
            switch (frame) {
            case MAV_FRAME_GLOBAL:
            case MAV_FRAME_GLOBAL_RELATIVE_ALT:
                return true;
                break;

            default:
                return false;
            }
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
        }

        return true;
    }

    return false;
}

bool SimpleMissionItem::rawEdit(void) const
{
    return _rawEdit || !friendlyEditAllowed();
}

void SimpleMissionItem::setRawEdit(bool rawEdit)
{
    if (this->rawEdit() != rawEdit) {
        _rawEdit = rawEdit;
        emit rawEditChanged(this->rawEdit());
    }
}

void SimpleMissionItem::setDirty(bool dirty)
{
614
    if (!_homePositionSpecialCase || (_dirty != dirty)) {
615
        _dirty = dirty;
616 617 618 619
        if (!dirty) {
            _cameraSection->setDirty(false);
            _speedSection->setDirty(false);
        }
620
        emit dirtyChanged(dirty);
621 622 623
    }
}

624
void SimpleMissionItem::_setDirty(void)
625
{
626 627 628
    if (!_ignoreDirtyChangeSignals) {
        setDirty(true);
    }
629 630 631 632 633 634 635
}

void SimpleMissionItem::_sendCoordinateChanged(void)
{
    emit coordinateChanged(coordinate());
}

636
void SimpleMissionItem::_altitudeModeChanged(void)
637
{
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
    switch (_altitudeMode) {
    case AltitudeAboveTerrain:
        // Terrain altitudes are AMSL
        _missionItem.setFrame(MAV_FRAME_GLOBAL);
        // Clear any old values
        _missionItem._param7Fact.setRawValue(qQNaN());
        _amslAltAboveTerrainFact.setRawValue(qQNaN());
        _altitudeChanged();
        break;
    case AltitudeAMSL:
        _missionItem.setFrame(MAV_FRAME_GLOBAL);
        break;
    case AltitudeRelative:
        _missionItem.setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT);
        break;
653
    }
654
    emit coordinateHasRelativeAltitudeChanged(_altitudeMode == AltitudeRelative);
655 656
}

657
void SimpleMissionItem::_altitudeChanged(void)
658
{
659 660 661 662 663
    if (!specifiesAltitude()) {
        return;
    }

    if (_altitudeMode == AltitudeAboveTerrain) {
664
        _amslAltAboveTerrainFact.setRawValue(qQNaN());
665 666 667
        _terrainAltChanged();
    } else {
        _missionItem._param7Fact.setRawValue(_altitudeFact.rawValue());
668 669 670
    }
}

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
void SimpleMissionItem::_terrainAltChanged(void)
{
    if (!specifiesAltitude() || _altitudeMode != AltitudeAboveTerrain) {
        // We don't need terrain data
        return;
    }

    if (!qIsNaN(_amslAltAboveTerrainFact.rawValue().toDouble())) {
        // We already have terrain values set. Don't do it again to prevent dirty bit changing.
        return;
    }

    if (qIsNaN(terrainAltitude())) {
        // Set NaNs to signal we are waiting on terrain data
        _missionItem._param7Fact.setRawValue(qQNaN());
        _amslAltAboveTerrainFact.setRawValue(qQNaN());
    } else {
        double aboveTerrain = terrainAltitude() + _altitudeFact.rawValue().toDouble();
        _missionItem._param7Fact.setRawValue(aboveTerrain);
        _amslAltAboveTerrainFact.setRawValue(aboveTerrain);
    }
}

bool SimpleMissionItem::readyForSave(void) const
{
    return !specifiesAltitude() || !qIsNaN(_missionItem._param7Fact.rawValue().toDouble());
}

699 700 701
void SimpleMissionItem::setDefaultsForCommand(void)
{
    // We set these global defaults first, then if there are param defaults they will get reset
702 703 704 705 706
    _altitudeMode = AltitudeRelative;
    double defaultAlt = qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble();
    _altitudeFact.setRawValue(defaultAlt);
    _missionItem._param7Fact.setRawValue(defaultAlt);
    _amslAltAboveTerrainFact.setRawValue(qQNaN());
707 708

    MAV_CMD command = (MAV_CMD)this->command();
709 710 711
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command);
    if (uiInfo) {
        for (int i=1; i<=7; i++) {
712 713
            bool showUI;
            const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI);
714 715 716 717
            if (paramInfo) {
                Fact* rgParamFacts[7] = { &_missionItem._param1Fact, &_missionItem._param2Fact, &_missionItem._param3Fact, &_missionItem._param4Fact, &_missionItem._param5Fact, &_missionItem._param6Fact, &_missionItem._param7Fact };
                rgParamFacts[paramInfo->param()-1]->setRawValue(paramInfo->defaultValue());
            }
718 719 720
        }
    }

721 722
    switch (command) {
    case MAV_CMD_NAV_WAYPOINT:
723 724 725
        // We default all acceptance radius to 0. This allows flight controller to be in control of
        // accept radius.
        _missionItem.setParam2(0);
726 727 728
        break;

    case MAV_CMD_NAV_LAND:
Don Gagne's avatar
Don Gagne committed
729
    case MAV_CMD_NAV_VTOL_LAND:
730 731
        _missionItem.setParam7(0);
        break;
Donald Gagne's avatar
Donald Gagne committed
732 733
    default:
        break;
734 735 736
    }

    _missionItem.setAutoContinue(true);
737
    _missionItem.setFrame((specifiesCoordinate() || specifiesAltitudeOnly()) ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_MISSION);
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
    setRawEdit(false);
}

void SimpleMissionItem::_sendCommandChanged(void)
{
    emit commandChanged(command());
}

void SimpleMissionItem::_sendFriendlyEditAllowedChanged(void)
{
    emit friendlyEditAllowedChanged(friendlyEditAllowed());
}

QString SimpleMissionItem::category(void) const
{
753
    return _commandTree->getUIInfo(_vehicle, (MAV_CMD)command())->category();
754 755
}

756
void SimpleMissionItem::setCommand(int command)
757 758 759
{
    if ((MAV_CMD)command != _missionItem.command()) {
        _missionItem.setCommand((MAV_CMD)command);
760
        _updateOptionalSections();
761 762 763 764 765 766 767 768 769
    }
}

void SimpleMissionItem::setCoordinate(const QGeoCoordinate& coordinate)
{
    if (_missionItem.coordinate() != coordinate) {
        _missionItem.setCoordinate(coordinate);
    }
}
770 771 772

void SimpleMissionItem::setSequenceNumber(int sequenceNumber)
{
773 774 775 776 777 778
    if (_missionItem.sequenceNumber() != sequenceNumber) {
        _missionItem.setSequenceNumber(sequenceNumber);
        emit sequenceNumberChanged(sequenceNumber);
        // This is too likely to ignore
        emit abbreviationChanged();
    }
779
}
780

DonLakeFlyer's avatar
DonLakeFlyer committed
781
double SimpleMissionItem::specifiedFlightSpeed(void)
782
{
783 784 785 786 787
    if (_speedSection->specifyFlightSpeed()) {
        return _speedSection->flightSpeed()->rawValue().toDouble();
    } else {
        return missionItem().specifiedFlightSpeed();
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
788 789 790 791 792
}

double SimpleMissionItem::specifiedGimbalYaw(void)
{
    return _cameraSection->available() ? _cameraSection->specifiedGimbalYaw() : missionItem().specifiedGimbalYaw();
793
}
794

795 796 797 798 799
double SimpleMissionItem::specifiedGimbalPitch(void)
{
    return _cameraSection->available() ? _cameraSection->specifiedGimbalPitch() : missionItem().specifiedGimbalPitch();
}

800
bool SimpleMissionItem::scanForSections(QmlObjectListModel* visualItems, int scanIndex, Vehicle* vehicle)
801
{
802
    bool sectionFound = false;
803

804
    Q_UNUSED(vehicle);
805 806

    if (_cameraSection->available()) {
807 808 809 810
        sectionFound |= _cameraSection->scanForSection(visualItems, scanIndex);
    }
    if (_speedSection->available()) {
        sectionFound |= _speedSection->scanForSection(visualItems, scanIndex);
811
    }
812 813

    return sectionFound;
814 815
}

816
void SimpleMissionItem::_updateOptionalSections(void)
817
{
818
    // Remove previous sections
819 820 821 822
    if (_cameraSection) {
        _cameraSection->deleteLater();
        _cameraSection = NULL;
    }
823 824 825 826
    if (_speedSection) {
        _speedSection->deleteLater();
        _speedSection = NULL;
    }
827

828 829 830 831 832
    // Add new sections

    _cameraSection = new CameraSection(_vehicle, this);
    _speedSection = new SpeedSection(_vehicle, this);
    if ((MAV_CMD)command() == MAV_CMD_NAV_WAYPOINT) {
833
        _cameraSection->setAvailable(true);
834
        _speedSection->setAvailable(true);
835 836
    }

837 838 839 840 841 842
    connect(_cameraSection, &CameraSection::dirtyChanged,                   this, &SimpleMissionItem::_sectionDirtyChanged);
    connect(_cameraSection, &CameraSection::itemCountChanged,               this, &SimpleMissionItem::_updateLastSequenceNumber);
    connect(_cameraSection, &CameraSection::availableChanged,               this, &SimpleMissionItem::specifiedGimbalYawChanged);
    connect(_cameraSection, &CameraSection::availableChanged,               this, &SimpleMissionItem::specifiedGimbalPitchChanged);
    connect(_cameraSection, &CameraSection::specifiedGimbalPitchChanged,    this, &SimpleMissionItem::specifiedGimbalPitchChanged);
    connect(_cameraSection, &CameraSection::specifiedGimbalYawChanged,      this, &SimpleMissionItem::specifiedGimbalYawChanged);
843

844 845 846
    connect(_speedSection,  &SpeedSection::dirtyChanged,                this, &SimpleMissionItem::_sectionDirtyChanged);
    connect(_speedSection,  &SpeedSection::itemCountChanged,            this, &SimpleMissionItem::_updateLastSequenceNumber);
    connect(_speedSection,  &SpeedSection::specifiedFlightSpeedChanged, this, &SimpleMissionItem::specifiedFlightSpeedChanged);
847

848
    emit cameraSectionChanged(_cameraSection);
849
    emit speedSectionChanged(_speedSection);
850
    emit lastSequenceNumberChanged(lastSequenceNumber());
851 852 853 854
}

int SimpleMissionItem::lastSequenceNumber(void) const
{
855
    return sequenceNumber() + (_cameraSection ? _cameraSection->itemCount() : 0) + (_speedSection ? _speedSection->itemCount() : 0);
856 857 858 859 860 861 862
}

void SimpleMissionItem::_updateLastSequenceNumber(void)
{
    emit lastSequenceNumberChanged(lastSequenceNumber());
}

863
void SimpleMissionItem::_sectionDirtyChanged(bool dirty)
864 865 866 867 868 869 870 871 872 873 874 875 876
{
    if (dirty) {
        setDirty(true);
    }
}

void SimpleMissionItem::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent)
{
    int seqNum = sequenceNumber();

    items.append(new MissionItem(missionItem(), missionItemParent));
    seqNum++;

877 878
    _cameraSection->appendSectionItems(items, missionItemParent, seqNum);
    _speedSection->appendSectionItems(items, missionItemParent, seqNum);
879
}
DonLakeFlyer's avatar
DonLakeFlyer committed
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897

void SimpleMissionItem::applyNewAltitude(double newAltitude)
{
    MAV_CMD command = (MAV_CMD)this->command();
    const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command);

    if (uiInfo->specifiesCoordinate() || uiInfo->specifiesAltitudeOnly()) {
        switch ((MAV_CMD)this->command()) {
        case MAV_CMD_NAV_LAND:
        case MAV_CMD_NAV_VTOL_LAND:
            // Leave alone
            break;
        default:
            _missionItem.setParam7(newAltitude);
            break;
        }
    }
}
898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914

void SimpleMissionItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus)
{
    // If user has not already set speed/gimbal, set defaults from previous items.
    VisualMissionItem::setMissionFlightStatus(missionFlightStatus);
    if (_speedSection->available() && !_speedSection->specifyFlightSpeed() && !qFuzzyCompare(_speedSection->flightSpeed()->rawValue().toDouble(), missionFlightStatus.vehicleSpeed)) {
        _speedSection->flightSpeed()->setRawValue(missionFlightStatus.vehicleSpeed);
    }
    if (_cameraSection->available() && !_cameraSection->specifyGimbal()) {
        if (!qIsNaN(missionFlightStatus.gimbalYaw) && !qFuzzyCompare(_cameraSection->gimbalYaw()->rawValue().toDouble(), missionFlightStatus.gimbalYaw)) {
            _cameraSection->gimbalYaw()->setRawValue(missionFlightStatus.gimbalYaw);
        }
        if (!qIsNaN(missionFlightStatus.gimbalPitch) && !qFuzzyCompare(_cameraSection->gimbalPitch()->rawValue().toDouble(), missionFlightStatus.gimbalPitch)) {
            _cameraSection->gimbalPitch()->setRawValue(missionFlightStatus.gimbalPitch);
        }
    }
}
915 916 917 918 919 920 921 922

void SimpleMissionItem::setAltitudeMode(AltitudeMode altitudeMode)
{
    if (altitudeMode != _altitudeMode) {
        _altitudeMode = altitudeMode;
        emit altitudeModeChanged();
    }
}