MissionSettingsItem.cc 16.7 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
const char* MissionSettingsItem::_plannedHomePositionAltitudeName = "PlannedHomePositionAltitude";
const char* MissionSettingsItem::_missionFlightSpeedName =          "FlightSpeed";
const char* MissionSettingsItem::_missionEndActionName =            "MissionEndAction";
29

30
QMap<QString, FactMetaData*> MissionSettingsItem::_metaDataMap;
31

32
MissionSettingsItem::MissionSettingsItem(Vehicle* vehicle, QObject* parent)
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
    : ComplexMissionItem(vehicle, parent)
    , _specifyMissionFlightSpeed(false)
    , _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 */);
    }

    _plannedHomePositionAltitudeFact.setMetaData    (_metaDataMap[_plannedHomePositionAltitudeName]);
    _missionFlightSpeedFact.setMetaData             (_metaDataMap[_missionFlightSpeedName]);
    _missionEndActionFact.setMetaData               (_metaDataMap[_missionEndActionName]);

    _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);

61 62
    connect(this,               &MissionSettingsItem::specifyMissionFlightSpeedChanged, this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
    connect(&_cameraSection,    &CameraSection::missionItemCountChanged,                this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
63

64 65
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_updateAltitudeInCoordinate);
66

67 68
    connect(&_missionFlightSpeedFact,           &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
    connect(&_missionEndActionFact,             &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
69

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

72 73 74
    connect(&_missionFlightSpeedFact,   &Fact::valueChanged,                        this, &MissionSettingsItem::specifiedFlightSpeedChanged);
    connect(&_cameraSection,            &CameraSection::specifyGimbalChanged,       this, &MissionSettingsItem::specifiedGimbalYawChanged);
    connect(&_cameraSection,            &CameraSection::specifiedGimbalYawChanged,  this, &MissionSettingsItem::specifiedGimbalYawChanged);
75 76 77
}


78
void MissionSettingsItem::setSpecifyMissionFlightSpeed(bool specifyMissionFlightSpeed)
79 80 81 82 83 84 85
{
    if (specifyMissionFlightSpeed != _specifyMissionFlightSpeed) {
        _specifyMissionFlightSpeed = specifyMissionFlightSpeed;
        emit specifyMissionFlightSpeedChanged(specifyMissionFlightSpeed);
    }
}

86
int MissionSettingsItem::lastSequenceNumber(void) const
87
{
88
    int lastSequenceNumber = _sequenceNumber;
89 90 91 92

    if (_specifyMissionFlightSpeed) {
        lastSequenceNumber++;
    }
93
    lastSequenceNumber += _cameraSection.missionItemCount();
94 95 96 97

    return lastSequenceNumber;
}

98
void MissionSettingsItem::setDirty(bool dirty)
99 100 101 102 103 104 105
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(_dirty);
    }
}

106
void MissionSettingsItem::save(QJsonArray&  missionItems)
107
{
108 109 110
    QList<MissionItem*> items;

    appendMissionItems(items, this);
111 112

    // First item show be planned home position, we are not reponsible for save/load
113
    // Remaining items we just output as is
114 115
    for (int i=1; i<items.count(); i++) {
        MissionItem* item = items[i];
116 117 118
        QJsonObject saveObject;
        item->save(saveObject);
        missionItems.append(saveObject);
119
        item->deleteLater();
120 121 122
    }
}

123
void MissionSettingsItem::setSequenceNumber(int sequenceNumber)
124 125 126 127 128 129 130 131
{
    if (_sequenceNumber != sequenceNumber) {
        _sequenceNumber = sequenceNumber;
        emit sequenceNumberChanged(sequenceNumber);
        emit lastSequenceNumberChanged(lastSequenceNumber());
    }
}

132
bool MissionSettingsItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString)
133 134 135 136 137 138 139 140
{
    Q_UNUSED(complexObject);
    Q_UNUSED(sequenceNumber);
    Q_UNUSED(errorString);

    return true;
}

141
double MissionSettingsItem::greatestDistanceTo(const QGeoCoordinate &other) const
142 143 144 145 146
{
    Q_UNUSED(other);
    return 0;
}

147
bool MissionSettingsItem::specifiesCoordinate(void) const
148 149 150 151
{
    return false;
}

152
void MissionSettingsItem::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent)
153 154 155
{
    int seqNum = _sequenceNumber;

156
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
157 158 159 160 161 162 163 164 165

    // 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
166 167
                                        coordinate().latitude(),
                                        coordinate().longitude(),
168 169 170
                                        _plannedHomePositionAltitudeFact.rawValue().toDouble(),
                                        true,                   // autoContinue
                                        false,                  // isCurrentItem
171 172
                                        missionItemParent);
    items.append(item);
173 174

    if (_specifyMissionFlightSpeed) {
175
        qCDebug(MissionSettingsComplexItemLog) << "Appending MAV_CMD_DO_CHANGE_SPEED";
176 177 178 179 180 181 182 183 184 185
        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
186 187
                                            missionItemParent);
        items.append(item);
188 189
    }

190
    _cameraSection.appendMissionItems(items, missionItemParent, seqNum);
191 192
}

193
bool MissionSettingsItem::addMissionEndAction(QList<MissionItem*>& items, int seqNum, QObject* missionItemParent)
194 195 196
{
    MissionItem* item = NULL;

197
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
198 199 200 201 202 203 204 205 206 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

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

257
bool MissionSettingsItem::scanForMissionSettings(QmlObjectListModel* visualItems, int scanIndex, Vehicle* vehicle)
258 259
{
    bool foundSpeed = false;
260 261 262
    bool foundCameraSection = false;
    bool stopLooking = false;

263
    qCDebug(MissionSettingsComplexItemLog) << "MissionSettingsItem::scanForMissionSettings count:scanIndex" << visualItems->count() << scanIndex;
264

265
    MissionSettingsItem* settingsItem = visualItems->value<MissionSettingsItem*>(scanIndex);
266
    if (!settingsItem) {
267
        qWarning() << "Item specified by scanIndex not MissionSettingsItem";
268
        return false;
269 270 271 272
    }

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

273 274 275
    scanIndex++;
    while (!stopLooking && visualItems->count() > 1) {
        SimpleMissionItem* item = visualItems->value<SimpleMissionItem*>(scanIndex);
276
        if (!item) {
277 278
            // We hit a complex item, there can be no more possible mission settings
            break;
279 280 281
        }
        MissionItem& missionItem = item->missionItem();

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

284
        // See MissionSettingsItem::getMissionItems for specs on what compomises a known mission setting
285

286
        switch ((MAV_CMD)item->command()) {
287 288 289 290
        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) {
291 292
                        stopLooking = true;
                        break;
293 294 295
                    }
                } else {
                    if (missionItem.param1() != 0) {
296 297
                        stopLooking = true;
                        break;
298 299 300 301 302
                    }
                }
                foundSpeed = true;
                settingsItem->setSpecifyMissionFlightSpeed(true);
                settingsItem->missionFlightSpeed()->setRawValue(missionItem.param2());
303
                visualItems->removeAt(scanIndex)->deleteLater();
304
                qCDebug(MissionSettingsComplexItemLog) << "Scan: Found MAV_CMD_DO_CHANGE_SPEED";
305
                continue;
306
            }
307
            stopLooking = true;
308 309
            break;

310 311 312 313
        default:
            if (!foundCameraSection) {
                if (settingsItem->_cameraSection.scanForCameraSection(visualItems, scanIndex)) {
                    foundCameraSection = true;
314
                    qCDebug(MissionSettingsComplexItemLog) << "Scan: Found Camera Section";
315 316
                    continue;
                }
317
            }
318
            stopLooking = true;
319 320 321
            break;
        }
    }
322

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
    // 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;
        }
    }

352
    return foundSpeed || foundCameraSection;
353 354
}

355
double MissionSettingsItem::complexDistance(void) const
356 357 358 359
{
    return 0;
}

360
void MissionSettingsItem::_setDirty(void)
361 362 363 364
{
    setDirty(true);
}

365
void MissionSettingsItem::setCoordinate(const QGeoCoordinate& coordinate)
366
{
367 368 369 370
    if (_plannedHomePositionCoordinate != coordinate) {
        _plannedHomePositionCoordinate = coordinate;
        emit coordinateChanged(coordinate);
        emit exitCoordinateChanged(coordinate);
371 372 373 374
        _plannedHomePositionAltitudeFact.setRawValue(coordinate.altitude());
    }
}

375
void MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber(void)
376 377 378 379 380
{
    emit lastSequenceNumberChanged(lastSequenceNumber());
    setDirty(true);
}

381
void MissionSettingsItem::_cameraSectionDirtyChanged(bool dirty)
382 383 384 385 386
{
    if (dirty) {
        setDirty(true);
    }
}
DonLakeFlyer's avatar
DonLakeFlyer committed
387

388
double MissionSettingsItem::specifiedFlightSpeed(void)
DonLakeFlyer's avatar
DonLakeFlyer committed
389 390 391 392
{
    return _specifyMissionFlightSpeed ? _missionFlightSpeedFact.rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN();
}

393
double MissionSettingsItem::specifiedGimbalYaw(void)
DonLakeFlyer's avatar
DonLakeFlyer committed
394 395 396
{
    return _cameraSection.specifyGimbal() ? _cameraSection.gimbalYaw()->rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN();
}
397 398 399 400 401 402 403 404 405 406 407 408

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

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

        _plannedHomePositionCoordinate.setAltitude(newAltitude);
        emit coordinateChanged(_plannedHomePositionCoordinate);
        emit exitCoordinateChanged(_plannedHomePositionCoordinate);
    }
}