MissionSettingsItem.cc 18 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (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.
 *
 ****************************************************************************/

10
#include "MissionSettingsItem.h"
11 12 13 14 15 16 17
#include "JsonHelper.h"
#include "MissionController.h"
#include "QGCGeo.h"
#include "QGroundControlQmlGlobal.h"
#include "SimpleMissionItem.h"
#include "SettingsManager.h"
#include "AppSettings.h"
18
#include "MissionCommandUIInfo.h"
19 20 21 22 23

#include <QPolygonF>

QGC_LOGGING_CATEGORY(MissionSettingsComplexItemLog, "MissionSettingsComplexItemLog")

24
const char* MissionSettingsItem::jsonComplexItemTypeValue = "MissionSettings";
25

DonLakeFlyer's avatar
DonLakeFlyer committed
26 27 28 29 30
const char* MissionSettingsItem::_missionNameName =                 "MissionName";
const char* MissionSettingsItem::_missionFullPathName =             "MissionFullPath";
const char* MissionSettingsItem::_plannedHomePositionAltitudeName = "PlannedHomePositionAltitude";
const char* MissionSettingsItem::_missionFlightSpeedName =          "FlightSpeed";
const char* MissionSettingsItem::_missionEndActionName =            "MissionEndAction";
31

32
QMap<QString, FactMetaData*> MissionSettingsItem::_metaDataMap;
33

34
MissionSettingsItem::MissionSettingsItem(Vehicle* vehicle, QObject* parent)
35
    : ComplexMissionItem(vehicle, parent)
DonLakeFlyer's avatar
DonLakeFlyer committed
36
    , _existingMission(false)
37
    , _specifyMissionFlightSpeed(false)
DonLakeFlyer's avatar
DonLakeFlyer committed
38 39
    , _missionNameFact                  (0, _missionNameName,                   FactMetaData::valueTypeString)
    , _missionFullPathFact              (0, _missionFullPathName,               FactMetaData::valueTypeString)
40 41 42 43 44 45 46 47 48 49 50 51
    , _plannedHomePositionAltitudeFact  (0, _plannedHomePositionAltitudeName,   FactMetaData::valueTypeDouble)
    , _missionFlightSpeedFact           (0, _missionFlightSpeedName,            FactMetaData::valueTypeDouble)
    , _missionEndActionFact             (0, _missionEndActionName,              FactMetaData::valueTypeUint32)
    , _sequenceNumber(0)
    , _dirty(false)
{
    _editorQml = "qrc:/qml/MissionSettingsEditor.qml";

    if (_metaDataMap.isEmpty()) {
        _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/MissionSettings.FactMetaData.json"), NULL /* metaDataParent */);
    }

DonLakeFlyer's avatar
DonLakeFlyer committed
52
    _missionNameFact.setMetaData                    (_metaDataMap[_missionNameName]);
53 54 55 56
    _plannedHomePositionAltitudeFact.setMetaData    (_metaDataMap[_plannedHomePositionAltitudeName]);
    _missionFlightSpeedFact.setMetaData             (_metaDataMap[_missionFlightSpeedName]);
    _missionEndActionFact.setMetaData               (_metaDataMap[_missionEndActionName]);

DonLakeFlyer's avatar
DonLakeFlyer committed
57
    _missionNameFact.setRawValue                    (_missionNameFact.rawDefaultValue());
58 59 60 61 62 63 64 65 66 67
    _plannedHomePositionAltitudeFact.setRawValue    (_plannedHomePositionAltitudeFact.rawDefaultValue());
    _missionEndActionFact.setRawValue               (_missionEndActionFact.rawDefaultValue());

    // FIXME: Flight speed default value correctly based firmware parameter if online
    AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings();
    Fact* speedFact = vehicle->multiRotor() ? appSettings->offlineEditingHoverSpeed() : appSettings->offlineEditingCruiseSpeed();
    _missionFlightSpeedFact.setRawValue(speedFact->rawValue().toDouble());

    setHomePositionSpecialCase(true);

DonLakeFlyer's avatar
DonLakeFlyer committed
68 69
    connect(&_missionNameFact,  &Fact::valueChanged, this, &MissionSettingsItem::_missionNameChanged);

70 71
    connect(this,               &MissionSettingsItem::specifyMissionFlightSpeedChanged,  this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
    connect(&_cameraSection,    &CameraSection::missionItemCountChanged,                        this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
72

73 74
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_updateAltitudeInCoordinate);
75

76 77
    connect(&_missionFlightSpeedFact,           &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
    connect(&_missionEndActionFact,             &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
78

79
    connect(&_cameraSection, &CameraSection::dirtyChanged, this, &MissionSettingsItem::_cameraSectionDirtyChanged);
DonLakeFlyer's avatar
DonLakeFlyer committed
80

81 82 83
    connect(&_missionFlightSpeedFact,   &Fact::valueChanged,                        this, &MissionSettingsItem::specifiedFlightSpeedChanged);
    connect(&_cameraSection,            &CameraSection::specifyGimbalChanged,       this, &MissionSettingsItem::specifiedGimbalYawChanged);
    connect(&_cameraSection,            &CameraSection::specifiedGimbalYawChanged,  this, &MissionSettingsItem::specifiedGimbalYawChanged);
84 85 86
}


87
void MissionSettingsItem::setSpecifyMissionFlightSpeed(bool specifyMissionFlightSpeed)
88 89 90 91 92 93 94
{
    if (specifyMissionFlightSpeed != _specifyMissionFlightSpeed) {
        _specifyMissionFlightSpeed = specifyMissionFlightSpeed;
        emit specifyMissionFlightSpeedChanged(specifyMissionFlightSpeed);
    }
}

95
int MissionSettingsItem::lastSequenceNumber(void) const
96
{
97
    int lastSequenceNumber = _sequenceNumber;
98 99 100 101

    if (_specifyMissionFlightSpeed) {
        lastSequenceNumber++;
    }
102
    lastSequenceNumber += _cameraSection.missionItemCount();
103 104 105 106

    return lastSequenceNumber;
}

107
void MissionSettingsItem::setDirty(bool dirty)
108 109 110 111 112 113 114
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(_dirty);
    }
}

115
void MissionSettingsItem::save(QJsonArray&  missionItems)
116
{
117 118 119
    QList<MissionItem*> items;

    appendMissionItems(items, this);
120 121

    // First item show be planned home position, we are not reponsible for save/load
122
    // Remaining items we just output as is
123 124
    for (int i=1; i<items.count(); i++) {
        MissionItem* item = items[i];
125 126 127
        QJsonObject saveObject;
        item->save(saveObject);
        missionItems.append(saveObject);
128
        item->deleteLater();
129 130 131
    }
}

132
void MissionSettingsItem::setSequenceNumber(int sequenceNumber)
133 134 135 136 137 138 139 140
{
    if (_sequenceNumber != sequenceNumber) {
        _sequenceNumber = sequenceNumber;
        emit sequenceNumberChanged(sequenceNumber);
        emit lastSequenceNumberChanged(lastSequenceNumber());
    }
}

141
bool MissionSettingsItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString)
142 143 144 145 146 147 148 149
{
    Q_UNUSED(complexObject);
    Q_UNUSED(sequenceNumber);
    Q_UNUSED(errorString);

    return true;
}

150
double MissionSettingsItem::greatestDistanceTo(const QGeoCoordinate &other) const
151 152 153 154 155
{
    Q_UNUSED(other);
    return 0;
}

156
bool MissionSettingsItem::specifiesCoordinate(void) const
157 158 159 160
{
    return false;
}

161
void MissionSettingsItem::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent)
162 163 164
{
    int seqNum = _sequenceNumber;

165
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
166 167 168 169 170 171 172 173 174

    // Planned home position
    MissionItem* item = new MissionItem(seqNum++,
                                        MAV_CMD_NAV_WAYPOINT,
                                        MAV_FRAME_GLOBAL,
                                        0,                      // Hold time
                                        0,                      // Acceptance radius
                                        0,                      // Not sure?
                                        0,                      // Yaw
175 176
                                        coordinate().latitude(),
                                        coordinate().longitude(),
177 178 179
                                        _plannedHomePositionAltitudeFact.rawValue().toDouble(),
                                        true,                   // autoContinue
                                        false,                  // isCurrentItem
180 181
                                        missionItemParent);
    items.append(item);
182 183

    if (_specifyMissionFlightSpeed) {
184
        qCDebug(MissionSettingsComplexItemLog) << "Appending MAV_CMD_DO_CHANGE_SPEED";
185 186 187 188 189 190 191 192 193 194
        MissionItem* item = new MissionItem(seqNum++,
                                            MAV_CMD_DO_CHANGE_SPEED,
                                            MAV_FRAME_MISSION,
                                            _vehicle->multiRotor() ? 1 /* groundspeed */ : 0 /* airspeed */,    // Change airspeed or groundspeed
                                            _missionFlightSpeedFact.rawValue().toDouble(),
                                            -1,                                                                 // No throttle change
                                            0,                                                                  // Absolute speed change
                                            0, 0, 0,                                                            // param 5-7 not used
                                            true,                                                               // autoContinue
                                            false,                                                              // isCurrentItem
195 196
                                            missionItemParent);
        items.append(item);
197 198
    }

199
    _cameraSection.appendMissionItems(items, missionItemParent, seqNum);
200 201
}

202
bool MissionSettingsItem::addMissionEndAction(QList<MissionItem*>& items, int seqNum, QObject* missionItemParent)
203 204 205
{
    MissionItem* item = NULL;

206
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
207 208 209 210 211 212 213 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 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265

    // Find last waypoint coordinate information so we have a lat/lon/alt to use
    QGeoCoordinate  lastWaypointCoord;
    MAV_FRAME       lastWaypointFrame;

    bool found = false;
    for (int i=items.count()-1; i>0; i--) {
        MissionItem* missionItem = items[i];

        const MissionCommandUIInfo* uiInfo = qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_vehicle, (MAV_CMD)missionItem->command());
        if (uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate()) {
            lastWaypointCoord = missionItem->coordinate();
            lastWaypointFrame = missionItem->frame();
            found = true;
            break;
        }
    }
    if (!found) {
        return false;
    }

    switch(_missionEndActionFact.rawValue().toInt()) {
    case MissionEndLoiter:
        qCDebug(MissionSettingsComplexItemLog) << "Appending end action Loiter seqNum" << seqNum;
        item = new MissionItem(seqNum,
                               MAV_CMD_NAV_LOITER_UNLIM,
                               lastWaypointFrame,
                               0, 0,                        // param 1-2 unused
                               0,                           // use default loiter radius
                               0,                           // param 4 not used
                               lastWaypointCoord.latitude(),
                               lastWaypointCoord.longitude(),
                               lastWaypointCoord.altitude(),
                               true,                        // autoContinue
                               false,                       // isCurrentItem
                               missionItemParent);
        break;
    case MissionEndRTL:
        qCDebug(MissionSettingsComplexItemLog) << "Appending end action RTL seqNum" << seqNum;
        item = new MissionItem(seqNum,
                               MAV_CMD_NAV_RETURN_TO_LAUNCH,
                               MAV_FRAME_MISSION,
                               0, 0, 0, 0, 0, 0, 0,        // param 1-7 not used
                               true,                       // autoContinue
                               false,                      // isCurrentItem
                               missionItemParent);
        break;
    default:
        break;
    }

    if (item) {
        items.append(item);
        return true;
    } else {
        return false;
    }
}

266
bool MissionSettingsItem::scanForMissionSettings(QmlObjectListModel* visualItems, int scanIndex, Vehicle* vehicle)
267 268
{
    bool foundSpeed = false;
269 270 271
    bool foundCameraSection = false;
    bool stopLooking = false;

272
    qCDebug(MissionSettingsComplexItemLog) << "MissionSettingsItem::scanForMissionSettings count:scanIndex" << visualItems->count() << scanIndex;
273

274
    MissionSettingsItem* settingsItem = visualItems->value<MissionSettingsItem*>(scanIndex);
275
    if (!settingsItem) {
276
        qWarning() << "Item specified by scanIndex not MissionSettingsItem";
277
        return false;
278 279 280 281
    }

    // Scan through the initial mission items for possible mission settings

282 283 284
    scanIndex++;
    while (!stopLooking && visualItems->count() > 1) {
        SimpleMissionItem* item = visualItems->value<SimpleMissionItem*>(scanIndex);
285
        if (!item) {
286 287
            // We hit a complex item, there can be no more possible mission settings
            break;
288 289 290
        }
        MissionItem& missionItem = item->missionItem();

291
        qCDebug(MissionSettingsComplexItemLog) << item->command() << missionItem.param1() << missionItem.param2() << missionItem.param3() << missionItem.param4() << missionItem.param5() << missionItem.param6() << missionItem.param7() ;
292

293
        // See MissionSettingsItem::getMissionItems for specs on what compomises a known mission setting
294

295
        switch ((MAV_CMD)item->command()) {
296 297 298 299
        case MAV_CMD_DO_CHANGE_SPEED:
            if (!foundSpeed && missionItem.param3() == -1 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) {
                if (vehicle->multiRotor()) {
                    if (missionItem.param1() != 1) {
300 301
                        stopLooking = true;
                        break;
302 303 304
                    }
                } else {
                    if (missionItem.param1() != 0) {
305 306
                        stopLooking = true;
                        break;
307 308 309 310 311
                    }
                }
                foundSpeed = true;
                settingsItem->setSpecifyMissionFlightSpeed(true);
                settingsItem->missionFlightSpeed()->setRawValue(missionItem.param2());
312
                visualItems->removeAt(scanIndex)->deleteLater();
313
                qCDebug(MissionSettingsComplexItemLog) << "Scan: Found MAV_CMD_DO_CHANGE_SPEED";
314
                continue;
315
            }
316
            stopLooking = true;
317 318
            break;

319 320 321 322
        default:
            if (!foundCameraSection) {
                if (settingsItem->_cameraSection.scanForCameraSection(visualItems, scanIndex)) {
                    foundCameraSection = true;
323
                    qCDebug(MissionSettingsComplexItemLog) << "Scan: Found Camera Section";
324 325
                    continue;
                }
326
            }
327
            stopLooking = true;
328 329 330
            break;
        }
    }
331

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    // Look at the end of the mission for end actions

    int lastIndex = visualItems->count() - 1;
    SimpleMissionItem* item = visualItems->value<SimpleMissionItem*>(lastIndex);
    if (item) {
        MissionItem& missionItem = item->missionItem();

        switch ((MAV_CMD)item->command()) {
        case MAV_CMD_NAV_LOITER_UNLIM:
            if (missionItem.param1() == 0 && missionItem.param2() == 0 && missionItem.param3() == 0 && missionItem.param4() == 0) {
                qCDebug(MissionSettingsComplexItemLog) << "Scan: Found end action Loiter";
                settingsItem->missionEndAction()->setRawValue(MissionEndLoiter);
                visualItems->removeAt(lastIndex)->deleteLater();
            }
            break;

        case MAV_CMD_NAV_RETURN_TO_LAUNCH:
            if (missionItem.param1() == 0 && missionItem.param2() == 0 && missionItem.param3() == 0 && missionItem.param4() == 0 && missionItem.param5() == 0 && missionItem.param6() == 0 && missionItem.param7() == 0) {
                qCDebug(MissionSettingsComplexItemLog) << "Scan: Found end action RTL";
                settingsItem->missionEndAction()->setRawValue(MissionEndRTL);
                visualItems->removeAt(lastIndex)->deleteLater();
            }
            break;

        default:
            break;
        }
    }

361
    return foundSpeed || foundCameraSection;
362 363
}

364
double MissionSettingsItem::complexDistance(void) const
365 366 367 368
{
    return 0;
}

369
void MissionSettingsItem::_setDirty(void)
370 371 372 373
{
    setDirty(true);
}

374
void MissionSettingsItem::setCoordinate(const QGeoCoordinate& coordinate)
375
{
376 377 378 379
    if (_plannedHomePositionCoordinate != coordinate) {
        _plannedHomePositionCoordinate = coordinate;
        emit coordinateChanged(coordinate);
        emit exitCoordinateChanged(coordinate);
380 381 382 383
        _plannedHomePositionAltitudeFact.setRawValue(coordinate.altitude());
    }
}

384
void MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber(void)
385 386 387 388 389
{
    emit lastSequenceNumberChanged(lastSequenceNumber());
    setDirty(true);
}

390
void MissionSettingsItem::_cameraSectionDirtyChanged(bool dirty)
391 392 393 394 395
{
    if (dirty) {
        setDirty(true);
    }
}
DonLakeFlyer's avatar
DonLakeFlyer committed
396

397
double MissionSettingsItem::specifiedFlightSpeed(void)
DonLakeFlyer's avatar
DonLakeFlyer committed
398 399 400 401
{
    return _specifyMissionFlightSpeed ? _missionFlightSpeedFact.rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN();
}

402
double MissionSettingsItem::specifiedGimbalYaw(void)
DonLakeFlyer's avatar
DonLakeFlyer committed
403 404 405
{
    return _cameraSection.specifyGimbal() ? _cameraSection.gimbalYaw()->rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN();
}
406 407 408 409 410 411 412 413 414 415 416 417

void MissionSettingsItem::_updateAltitudeInCoordinate(QVariant value)
{
    double newAltitude = value.toDouble();

    if (!qFuzzyCompare(_plannedHomePositionCoordinate.altitude(), newAltitude)) {

        _plannedHomePositionCoordinate.setAltitude(newAltitude);
        emit coordinateChanged(_plannedHomePositionCoordinate);
        emit exitCoordinateChanged(_plannedHomePositionCoordinate);
    }
}
DonLakeFlyer's avatar
DonLakeFlyer committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437

void MissionSettingsItem::_missionNameChanged(QVariant value)
{
    QString missionDir = qgcApp()->toolbox()->settingsManager()->appSettings()->missionSavePath();
    QString missionName = value.toString();

    if (missionName.isEmpty()) {
        _missionFullPathFact.setRawValue(QString());
    } else {
        _missionFullPathFact.setRawValue(missionDir + "/" + missionName);
    }
}

void MissionSettingsItem::setExistingMission(bool existingMission)
{
    if (existingMission != _existingMission) {
        _existingMission = existingMission;
        emit existingMissionChanged(existingMission );
    }
}