RallyPointController.cc 11.3 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/


/// @file
///     @author Don Gagne <don@thegagnes.com>

#include "RallyPointController.h"
#include "RallyPoint.h"
#include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h"
#include "QGCApplication.h"
#include "ParameterManager.h"
#include "JsonHelper.h"
#include "SimpleMissionItem.h"
23
#include "QGroundControlQmlGlobal.h"
24
#include "SettingsManager.h"
25
#include "AppSettings.h"
DonLakeFlyer's avatar
DonLakeFlyer committed
26
#include "PlanMasterController.h"
27 28 29 30 31 32 33 34 35

#include <QJsonDocument>
#include <QJsonArray>

QGC_LOGGING_CATEGORY(RallyPointControllerLog, "RallyPointControllerLog")

const char* RallyPointController::_jsonFileTypeValue =  "RallyPoints";
const char* RallyPointController::_jsonPointsKey =      "points";

36
RallyPointController::RallyPointController(PlanMasterController* masterController, QObject* parent)
37 38 39
    : PlanElementController (masterController, parent)
    , _managerVehicle               (masterController->managerVehicle())
    , _rallyPointManager    (masterController->managerVehicle()->rallyPointManager())
40
{
41
    connect(&_points, &QmlObjectListModel::countChanged, this, &RallyPointController::_updateContainsItems);
42 43 44 45 46 47 48
}

RallyPointController::~RallyPointController()
{

}

49 50 51 52 53 54 55 56 57 58 59
void RallyPointController::start(bool flyView)
{
    qCDebug(GeoFenceControllerLog) << "start flyView" << flyView;

    _managerVehicleChanged(_masterController->managerVehicle());
    connect(_masterController, &PlanMasterController::managerVehicleChanged, this, &RallyPointController::_managerVehicleChanged);

    PlanElementController::start(flyView);
}

void RallyPointController::_managerVehicleChanged(Vehicle* managerVehicle)
60
{
61 62
    if (_managerVehicle) {
        _rallyPointManager->disconnect(this);
63
        _managerVehicle->disconnect(this);
64 65
        _managerVehicle = nullptr;
        _rallyPointManager = nullptr;
66
    }
67

68 69 70 71 72 73 74
    _managerVehicle = managerVehicle;
    if (!_managerVehicle) {
        qWarning() << "RallyPointController::managerVehicleChanged managerVehicle=NULL";
        return;
    }

    _rallyPointManager = _managerVehicle->rallyPointManager();
DonLakeFlyer's avatar
DonLakeFlyer committed
75 76 77
    connect(_rallyPointManager, &RallyPointManager::loadComplete,       this, &RallyPointController::_managerLoadComplete);
    connect(_rallyPointManager, &RallyPointManager::sendComplete,       this, &RallyPointController::_managerSendComplete);
    connect(_rallyPointManager, &RallyPointManager::removeAllComplete,  this, &RallyPointController::_managerRemoveAllComplete);
78
    connect(_rallyPointManager, &RallyPointManager::inProgressChanged,  this, &RallyPointController::syncInProgressChanged);
79

80
    //-- RallyPointController::supported() tests both the capability bit AND the protocol version.
81
    connect(_managerVehicle,    &Vehicle::capabilityBitsChanged,        this, &RallyPointController::supportedChanged);
82
    connect(_managerVehicle,    &Vehicle::requestProtocolVersion,       this, &RallyPointController::supportedChanged);
83 84

    emit supportedChanged(supported());
85 86
}

87
bool RallyPointController::load(const QJsonObject& json, QString& errorString)
88
{
DonLakeFlyer's avatar
DonLakeFlyer committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
    removeAll();

    errorString.clear();

    if (json.contains(JsonHelper::jsonVersionKey) && json[JsonHelper::jsonVersionKey].toInt() == 1) {
        // We just ignore old version 1 data
        return true;
    }

    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
        { JsonHelper::jsonVersionKey,   QJsonValue::Double, true },
        { _jsonPointsKey,               QJsonValue::Array,  true },
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
        return false;
    }

106 107
    QString errorStr;
    QString errorMessage = tr("Rally: %1");
108

DonLakeFlyer's avatar
DonLakeFlyer committed
109 110
    if (json[JsonHelper::jsonVersionKey].toInt() != _jsonCurrentVersion) {
        errorString = tr("Rally Points supports version %1").arg(_jsonCurrentVersion);
111 112 113 114
        return false;
    }

    QList<QGeoCoordinate> rgPoints;
115 116
    if (!JsonHelper::loadGeoCoordinateArray(json[_jsonPointsKey], true /* altitudeRequired */, rgPoints, errorStr)) {
        errorString = errorMessage.arg(errorStr);
117
        return false;
118
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
119

120 121 122 123 124 125
    QObjectList pointList;
    for (int i=0; i<rgPoints.count(); i++) {
        pointList.append(new RallyPoint(rgPoints[i], this));
    }
    _points.swapObjectList(pointList);

126
    setDirty(false);
127
    _setFirstPointCurrent();
128 129

    return true;
130 131
}

132
void RallyPointController::save(QJsonObject& json)
133
{
DonLakeFlyer's avatar
DonLakeFlyer committed
134
    json[JsonHelper::jsonVersionKey] = _jsonCurrentVersion;
135

136 137 138 139 140
    QJsonArray rgPoints;
    QJsonValue jsonPoint;
    for (int i=0; i<_points.count(); i++) {
        JsonHelper::saveGeoCoordinate(qobject_cast<RallyPoint*>(_points[i])->coordinate(), true /* writeAltitude */, jsonPoint);
        rgPoints.append(jsonPoint);
141
    }
142
    json[_jsonPointsKey] = QJsonValue(rgPoints);
143 144 145 146 147 148
}

void RallyPointController::removeAll(void)
{
    _points.clearAndDeleteContents();
    setDirty(true);
149
    setCurrentRallyPoint(nullptr);
150 151
}

DonLakeFlyer's avatar
DonLakeFlyer committed
152 153 154 155 156 157 158 159 160 161 162
void RallyPointController::removeAllFromVehicle(void)
{
    if (_masterController->offline()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::removeAllFromVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::removeAllFromVehicle called while syncInProgress";
    } else {
        _rallyPointManager->removeAll();
    }
}

163 164
void RallyPointController::loadFromVehicle(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
165 166 167 168
    if (_masterController->offline()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::loadFromVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::loadFromVehicle called while syncInProgress";
169
    } else {
DonLakeFlyer's avatar
DonLakeFlyer committed
170 171
        _itemsRequested = true;
        _rallyPointManager->loadFromVehicle();
172 173 174 175 176
    }
}

void RallyPointController::sendToVehicle(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
177 178 179 180 181
    if (_masterController->offline()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::sendToVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::sendToVehicle called while syncInProgress";
    } else {
DonLakeFlyer's avatar
DonLakeFlyer committed
182
        qCDebug(RallyPointControllerLog) << "RallyPointController::sendToVehicle";
183 184 185 186 187
        setDirty(false);
        QList<QGeoCoordinate> rgPoints;
        for (int i=0; i<_points.count(); i++) {
            rgPoints.append(qobject_cast<RallyPoint*>(_points[i])->coordinate());
        }
188
        _rallyPointManager->sendToVehicle(rgPoints);
189 190 191 192 193
    }
}

bool RallyPointController::syncInProgress(void) const
{
194
    return _rallyPointManager->inProgress();
195 196 197 198 199 200 201 202 203 204 205 206
}

void RallyPointController::setDirty(bool dirty)
{
    if (dirty != _dirty) {
        _dirty = dirty;
        emit dirtyChanged(dirty);
    }
}

QString RallyPointController::editorQml(void) const
{
207
    return _rallyPointManager->editorQml();
208 209
}

Don Gagne's avatar
Don Gagne committed
210
void RallyPointController::_managerLoadComplete(void)
211
{
DonLakeFlyer's avatar
DonLakeFlyer committed
212
    // Fly view always reloads on _loadComplete
213 214 215 216
    // Plan view only reloads if:
    //  - Load was specifically requested
    //  - There is no current Plan
    if (_flyView || _itemsRequested || isEmpty()) {
DonLakeFlyer's avatar
DonLakeFlyer committed
217 218
        _points.clearAndDeleteContents();
        QObjectList pointList;
Don Gagne's avatar
Don Gagne committed
219 220
        for (int i=0; i<_rallyPointManager->points().count(); i++) {
            pointList.append(new RallyPoint(_rallyPointManager->points()[i], this));
DonLakeFlyer's avatar
DonLakeFlyer committed
221 222 223 224 225 226 227 228 229
        }
        _points.swapObjectList(pointList);
        setDirty(false);
        _setFirstPointCurrent();
        emit loadComplete();
    }
    _itemsRequested = false;
}

230
void RallyPointController::_managerSendComplete(bool error)
DonLakeFlyer's avatar
DonLakeFlyer committed
231 232
{
    // Fly view always reloads after send
Don Gagne's avatar
Don Gagne committed
233
    if (!error && _flyView) {
DonLakeFlyer's avatar
DonLakeFlyer committed
234
        showPlanFromManagerVehicle();
235
    }
DonLakeFlyer's avatar
DonLakeFlyer committed
236 237
}

238
void RallyPointController::_managerRemoveAllComplete(bool error)
DonLakeFlyer's avatar
DonLakeFlyer committed
239
{
240 241 242 243
    if (!error) {
        // Remove all from vehicle so we always update
        showPlanFromManagerVehicle();
    }
244 245 246 247 248 249 250 251
}

void RallyPointController::addPoint(QGeoCoordinate point)
{
    double defaultAlt;
    if (_points.count()) {
        defaultAlt = qobject_cast<RallyPoint*>(_points[_points.count() - 1])->coordinate().altitude();
    } else {
252 253 254 255 256 257
        if(_masterController->controllerVehicle()->fixedWing()) {
            defaultAlt = qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue().toDouble();
        }
        else {
            defaultAlt = RallyPoint::getDefaultFactAltitude();
        }
258 259 260 261 262 263 264 265
    }
    point.setAltitude(defaultAlt);
    RallyPoint* newPoint = new RallyPoint(point, this);
    _points.append(newPoint);
    setCurrentRallyPoint(newPoint);
    setDirty(true);
}

266
bool RallyPointController::supported(void) const
267
{
268
    return (_managerVehicle->capabilityBits() & MAV_PROTOCOL_CAPABILITY_MISSION_RALLY) && (_managerVehicle->maxProtoVersion() >= 200);
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
}

void RallyPointController::removePoint(QObject* rallyPoint)
{
    int foundIndex = 0;
    for (foundIndex=0; foundIndex<_points.count(); foundIndex++) {
        if (_points[foundIndex] == rallyPoint) {
            _points.removeOne(rallyPoint);
            rallyPoint->deleteLater();
        }
    }

    if (_points.count()) {
        int newIndex = qMin(foundIndex, _points.count() - 1);
        newIndex = qMax(newIndex, 0);
        setCurrentRallyPoint(_points[newIndex]);
    } else {
286
        setCurrentRallyPoint(nullptr);
287 288 289 290 291 292 293 294 295 296 297 298 299
    }
}

void RallyPointController::setCurrentRallyPoint(QObject* rallyPoint)
{
    if (_currentRallyPoint != rallyPoint) {
        _currentRallyPoint = rallyPoint;
        emit currentRallyPointChanged(rallyPoint);
    }
}

void RallyPointController::_setFirstPointCurrent(void)
{
300
    setCurrentRallyPoint(_points.count() ? _points[0] : nullptr);
301
}
302 303 304 305 306 307 308 309 310 311 312

bool RallyPointController::containsItems(void) const
{
    return _points.count() > 0;
}

void RallyPointController::_updateContainsItems(void)
{
    emit containsItemsChanged(containsItems());
}

DonLakeFlyer's avatar
DonLakeFlyer committed
313
bool RallyPointController::showPlanFromManagerVehicle (void)
314
{
315
    qCDebug(RallyPointControllerLog) << "showPlanFromManagerVehicle _flyView" << _flyView;
DonLakeFlyer's avatar
DonLakeFlyer committed
316 317
    if (_masterController->offline()) {
        qCWarning(RallyPointControllerLog) << "RallyPointController::showPlanFromManagerVehicle called while offline";
Patrick José Pereira's avatar
Patrick José Pereira committed
318
        return true;    // stops further propagation of showPlanFromManagerVehicle due to error
DonLakeFlyer's avatar
DonLakeFlyer committed
319 320
    } else {
        if (!_managerVehicle->initialPlanRequestComplete()) {
DonLakeFlyer's avatar
DonLakeFlyer committed
321
            // The vehicle hasn't completed initial load, we can just wait for loadComplete to be signalled automatically
DonLakeFlyer's avatar
DonLakeFlyer committed
322 323 324 325 326 327 328
            qCDebug(RallyPointControllerLog) << "showPlanFromManagerVehicle: !initialPlanRequestComplete, wait for signal";
            return true;
        } else if (syncInProgress()) {
            // If the sync is already in progress, _loadComplete will be called automatically when it is done. So no need to do anything.
            qCDebug(RallyPointControllerLog) << "showPlanFromManagerVehicle: syncInProgress wait for signal";
            return true;
        } else {
329
            qCDebug(RallyPointControllerLog) << "showPlanFromManagerVehicle: sync complete";
DonLakeFlyer's avatar
DonLakeFlyer committed
330
            _itemsRequested = true;
Don Gagne's avatar
Don Gagne committed
331
            _managerLoadComplete();
DonLakeFlyer's avatar
DonLakeFlyer committed
332 333 334
            return false;
        }
    }
335
}
336 337 338 339 340

bool RallyPointController::isEmpty(void) const
{
    return _points.count() == 0;
}