MissionSettingsItem.cc 12.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
const char* MissionSettingsItem::_plannedHomePositionAltitudeName = "PlannedHomePositionAltitude";
const char* MissionSettingsItem::_missionEndActionName =            "MissionEndAction";
28

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

31
MissionSettingsItem::MissionSettingsItem(Vehicle* vehicle, QObject* parent)
32 33 34
    : ComplexMissionItem(vehicle, parent)
    , _plannedHomePositionAltitudeFact  (0, _plannedHomePositionAltitudeName,   FactMetaData::valueTypeDouble)
    , _missionEndActionFact             (0, _missionEndActionName,              FactMetaData::valueTypeUint32)
35 36
    , _cameraSection(vehicle)
    , _speedSection(vehicle)
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
    , _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]);
    _missionEndActionFact.setMetaData               (_metaDataMap[_missionEndActionName]);

    _plannedHomePositionAltitudeFact.setRawValue    (_plannedHomePositionAltitudeFact.rawDefaultValue());
    _missionEndActionFact.setRawValue               (_missionEndActionFact.rawDefaultValue());

    setHomePositionSpecialCase(true);

54
    connect(this,               &MissionSettingsItem::specifyMissionFlightSpeedChanged, this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
55 56
    connect(&_cameraSection,    &CameraSection::itemCountChanged,                       this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
    connect(&_speedSection,     &CameraSection::itemCountChanged,                       this, &MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber);
57

58 59
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
    connect(&_plannedHomePositionAltitudeFact,  &Fact::valueChanged, this, &MissionSettingsItem::_updateAltitudeInCoordinate);
60

61
    connect(&_missionEndActionFact,             &Fact::valueChanged, this, &MissionSettingsItem::_setDirty);
62

63 64
    connect(&_cameraSection,    &CameraSection::dirtyChanged,   this, &MissionSettingsItem::_sectionDirtyChanged);
    connect(&_speedSection,     &SpeedSection::dirtyChanged,    this, &MissionSettingsItem::_sectionDirtyChanged);
DonLakeFlyer's avatar
DonLakeFlyer committed
65

66 67
    connect(&_cameraSection,            &CameraSection::specifyGimbalChanged,       this, &MissionSettingsItem::specifiedGimbalYawChanged);
    connect(&_cameraSection,            &CameraSection::specifiedGimbalYawChanged,  this, &MissionSettingsItem::specifiedGimbalYawChanged);
68 69 70
}


71
int MissionSettingsItem::lastSequenceNumber(void) const
72
{
73
    int lastSequenceNumber = _sequenceNumber;
74

75 76
    lastSequenceNumber += _cameraSection.itemCount();
    lastSequenceNumber += _speedSection.itemCount();
77 78 79 80

    return lastSequenceNumber;
}

81
void MissionSettingsItem::setDirty(bool dirty)
82 83 84 85 86 87 88
{
    if (_dirty != dirty) {
        _dirty = dirty;
        emit dirtyChanged(_dirty);
    }
}

89
void MissionSettingsItem::save(QJsonArray&  missionItems)
90
{
91 92 93
    QList<MissionItem*> items;

    appendMissionItems(items, this);
94 95

    // First item show be planned home position, we are not reponsible for save/load
96
    // Remaining items we just output as is
97 98
    for (int i=1; i<items.count(); i++) {
        MissionItem* item = items[i];
99 100 101
        QJsonObject saveObject;
        item->save(saveObject);
        missionItems.append(saveObject);
102
        item->deleteLater();
103 104 105
    }
}

106
void MissionSettingsItem::setSequenceNumber(int sequenceNumber)
107 108 109 110 111 112 113 114
{
    if (_sequenceNumber != sequenceNumber) {
        _sequenceNumber = sequenceNumber;
        emit sequenceNumberChanged(sequenceNumber);
        emit lastSequenceNumberChanged(lastSequenceNumber());
    }
}

115
bool MissionSettingsItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString)
116 117 118 119 120 121 122 123
{
    Q_UNUSED(complexObject);
    Q_UNUSED(sequenceNumber);
    Q_UNUSED(errorString);

    return true;
}

124
double MissionSettingsItem::greatestDistanceTo(const QGeoCoordinate &other) const
125 126 127 128 129
{
    Q_UNUSED(other);
    return 0;
}

130
bool MissionSettingsItem::specifiesCoordinate(void) const
131
{
DonLakeFlyer's avatar
DonLakeFlyer committed
132
    return true;
133 134
}

135
void MissionSettingsItem::appendMissionItems(QList<MissionItem*>& items, QObject* missionItemParent)
136 137 138
{
    int seqNum = _sequenceNumber;

139
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
140 141 142 143 144 145 146 147 148

    // 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
149 150
                                        coordinate().latitude(),
                                        coordinate().longitude(),
151 152 153
                                        _plannedHomePositionAltitudeFact.rawValue().toDouble(),
                                        true,                   // autoContinue
                                        false,                  // isCurrentItem
154 155
                                        missionItemParent);
    items.append(item);
156

157 158
    _cameraSection.appendSectionItems(items, missionItemParent, seqNum);
    _speedSection.appendSectionItems(items, missionItemParent, seqNum);
159 160
}

161
bool MissionSettingsItem::addMissionEndAction(QList<MissionItem*>& items, int seqNum, QObject* missionItemParent)
162 163 164
{
    MissionItem* item = NULL;

165
    // IMPORTANT NOTE: If anything changes here you must also change MissionSettingsItem::scanForMissionSettings
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 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

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

225
bool MissionSettingsItem::scanForMissionSettings(QmlObjectListModel* visualItems, int scanIndex)
226
{
227
    bool foundSpeedSection = false;
228 229
    bool foundCameraSection = false;

230
    qCDebug(MissionSettingsComplexItemLog) << "MissionSettingsItem::scanForMissionSettings count:scanIndex" << visualItems->count() << scanIndex;
231

232
    MissionSettingsItem* settingsItem = visualItems->value<MissionSettingsItem*>(scanIndex);
233
    if (!settingsItem) {
234
        qWarning() << "Item specified by scanIndex not MissionSettingsItem";
235
        return false;
236 237 238 239
    }

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

240
    scanIndex++;
241 242
    foundCameraSection = settingsItem->_cameraSection.scanForSection(visualItems, scanIndex);
    foundSpeedSection = settingsItem->_speedSection.scanForSection(visualItems, scanIndex);
243

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    // 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;
        }
    }

273
    return foundSpeedSection || foundCameraSection;
274 275
}

276
double MissionSettingsItem::complexDistance(void) const
277 278 279 280
{
    return 0;
}

281
void MissionSettingsItem::_setDirty(void)
282 283 284 285
{
    setDirty(true);
}

286
void MissionSettingsItem::setCoordinate(const QGeoCoordinate& coordinate)
287
{
288 289 290 291
    if (_plannedHomePositionCoordinate != coordinate) {
        _plannedHomePositionCoordinate = coordinate;
        emit coordinateChanged(coordinate);
        emit exitCoordinateChanged(coordinate);
292 293 294 295
        _plannedHomePositionAltitudeFact.setRawValue(coordinate.altitude());
    }
}

296
void MissionSettingsItem::_setDirtyAndUpdateLastSequenceNumber(void)
297 298 299 300 301
{
    emit lastSequenceNumberChanged(lastSequenceNumber());
    setDirty(true);
}

302
void MissionSettingsItem::_sectionDirtyChanged(bool dirty)
303 304 305 306 307
{
    if (dirty) {
        setDirty(true);
    }
}
DonLakeFlyer's avatar
DonLakeFlyer committed
308

309
double MissionSettingsItem::specifiedGimbalYaw(void)
DonLakeFlyer's avatar
DonLakeFlyer committed
310 311 312
{
    return _cameraSection.specifyGimbal() ? _cameraSection.gimbalYaw()->rawValue().toDouble() : std::numeric_limits<double>::quiet_NaN();
}
313 314 315 316 317 318 319 320 321 322 323 324

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

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

        _plannedHomePositionCoordinate.setAltitude(newAltitude);
        emit coordinateChanged(_plannedHomePositionCoordinate);
        emit exitCoordinateChanged(_plannedHomePositionCoordinate);
    }
}
325 326 327 328 329 330 331 332 333

double MissionSettingsItem::specifiedFlightSpeed(void)
{
    if (_speedSection.specifyFlightSpeed()) {
        return _speedSection.flightSpeed()->rawValue().toDouble();
    } else {
        return std::numeric_limits<double>::quiet_NaN();
    }
}