GeoFenceController.cc 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/****************************************************************************
 *
 *   (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.
 *
 ****************************************************************************/


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

#include "GeoFenceController.h"
#include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h"
#include "QGCApplication.h"
19 20
#include "ParameterManager.h"
#include "JsonHelper.h"
21
#include "QGCQGeoCoordinate.h"
22
#include "AppSettings.h"
DonLakeFlyer's avatar
DonLakeFlyer committed
23
#include "PlanMasterController.h"
Don Gagne's avatar
Don Gagne committed
24 25

#ifndef __mobile__
26
#include "MainWindow.h"
27
#include "QGCQFileDialog.h"
Don Gagne's avatar
Don Gagne committed
28
#endif
29 30

#include <QJsonDocument>
31
#include <QJsonArray>
32 33 34

QGC_LOGGING_CATEGORY(GeoFenceControllerLog, "GeoFenceControllerLog")

35 36
const char* GeoFenceController::_jsonFileTypeValue =    "GeoFence";
const char* GeoFenceController::_jsonBreachReturnKey =  "breachReturn";
37

38 39 40
GeoFenceController::GeoFenceController(PlanMasterController* masterController, QObject* parent)
    : PlanElementController(masterController, parent)
    , _geoFenceManager(_managerVehicle->geoFenceManager())
41
    , _dirty(false)
42
    , _mapPolygon(this)
DonLakeFlyer's avatar
DonLakeFlyer committed
43
    , _itemsRequested(false)
44
{
45 46
    connect(_mapPolygon.qmlPathModel(), &QmlObjectListModel::countChanged, this, &GeoFenceController::_updateContainsItems);
    connect(_mapPolygon.qmlPathModel(), &QmlObjectListModel::dirtyChanged, this, &GeoFenceController::_polygonDirtyChanged);
47 48

    managerVehicleChanged(_managerVehicle);
49 50 51 52 53 54 55 56 57 58 59 60
}

GeoFenceController::~GeoFenceController()
{

}

void GeoFenceController::start(bool editMode)
{
    qCDebug(GeoFenceControllerLog) << "start editMode" << editMode;

    PlanElementController::start(editMode);
61 62 63 64 65
    _init();
}

void GeoFenceController::_init(void)
{
66

67 68 69 70
}

void GeoFenceController::setBreachReturnPoint(const QGeoCoordinate& breachReturnPoint)
{
71 72 73
    if (_breachReturnPoint != breachReturnPoint) {
        _breachReturnPoint = breachReturnPoint;
        setDirty(true);
74 75 76 77
        emit breachReturnPointChanged(breachReturnPoint);
    }
}

78
void GeoFenceController::_signalAll(void)
79
{
80 81
    emit breachReturnSupportedChanged(breachReturnSupported());
    emit breachReturnPointChanged(breachReturnPoint());
82
    emit circleEnabledChanged(circleEnabled());
83
    emit circleRadiusFactChanged(circleRadiusFact());
84
    emit polygonEnabledChanged(polygonEnabled());
85
    emit polygonSupportedChanged(polygonSupported());
86
    emit dirtyChanged(dirty());
87 88
}

89
void GeoFenceController::managerVehicleChanged(Vehicle* managerVehicle)
90
{
91 92 93 94 95
    if (_managerVehicle) {
        _geoFenceManager->disconnect(this);
        _managerVehicle = NULL;
        _geoFenceManager = NULL;
    }
96

97 98 99 100 101 102 103 104 105 106 107 108
    _managerVehicle = managerVehicle;
    if (!_managerVehicle) {
        qWarning() << "GeoFenceController::managerVehicleChanged managerVehicle=NULL";
        return;
    }

    _geoFenceManager = _managerVehicle->geoFenceManager();
    connect(_geoFenceManager, &GeoFenceManager::breachReturnSupportedChanged,   this, &GeoFenceController::breachReturnSupportedChanged);
    connect(_geoFenceManager, &GeoFenceManager::circleEnabledChanged,           this, &GeoFenceController::circleEnabledChanged);
    connect(_geoFenceManager, &GeoFenceManager::circleRadiusFactChanged,        this, &GeoFenceController::circleRadiusFactChanged);
    connect(_geoFenceManager, &GeoFenceManager::polygonEnabledChanged,          this, &GeoFenceController::polygonEnabledChanged);
    connect(_geoFenceManager, &GeoFenceManager::polygonSupportedChanged,        this, &GeoFenceController::polygonSupportedChanged);
DonLakeFlyer's avatar
DonLakeFlyer committed
109 110 111
    connect(_geoFenceManager, &GeoFenceManager::loadComplete,                   this, &GeoFenceController::_managerLoadComplete);
    connect(_geoFenceManager, &GeoFenceManager::sendComplete,                   this, &GeoFenceController::_managerSendComplete);
    connect(_geoFenceManager, &GeoFenceManager::removeAllComplete,              this, &GeoFenceController::_managerRemoveAllComplete);
112 113
    connect(_geoFenceManager, &GeoFenceManager::inProgressChanged,              this, &GeoFenceController::syncInProgressChanged);

Don Gagne's avatar
Don Gagne committed
114
    _signalAll();
115
}
Don Gagne's avatar
Don Gagne committed
116

117
bool GeoFenceController::load(const QJsonObject& json, QString& errorString)
118
{
119 120
    QString errorStr;
    QString errorMessage = tr("GeoFence: %1");
121

122 123 124
    if (json.contains(_jsonBreachReturnKey) &&
            !JsonHelper::loadGeoCoordinate(json[_jsonBreachReturnKey], false /* altitudeRequired */, _breachReturnPoint, errorStr)) {
        errorString = errorMessage.arg(errorStr);
125 126 127
        return false;
    }

128 129
    if (!_mapPolygon.loadFromJson(json, true, errorStr)) {
        errorString = errorMessage.arg(errorStr);
130
        return false;
131
    }
132
    _mapPolygon.setDirty(false);
133
    setDirty(false);
134

135
    _signalAll();
136 137

    return true;
138 139
}

140
void  GeoFenceController::save(QJsonObject& json)
141
{
142
    json[JsonHelper::jsonVersionKey] = 1;
143

144
    if (_breachReturnPoint.isValid()) {
145 146
        QJsonValue jsonBreachReturn;
        JsonHelper::saveGeoCoordinate(_breachReturnPoint, false /* writeAltitude */, jsonBreachReturn);
147
        json[_jsonBreachReturnKey] = jsonBreachReturn;
148 149
    }

150
    _mapPolygon.saveToJson(json);
151 152
}

153 154
void GeoFenceController::removeAll(void)
{
155
    setBreachReturnPoint(QGeoCoordinate());
156
    _mapPolygon.clear();
157 158
}

DonLakeFlyer's avatar
DonLakeFlyer committed
159 160 161 162 163 164 165 166 167 168 169
void GeoFenceController::removeAllFromVehicle(void)
{
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::removeAllFromVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::removeAllFromVehicle called while syncInProgress";
    } else {
        _geoFenceManager->removeAll();
    }
}

170 171
void GeoFenceController::loadFromVehicle(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
172 173 174 175
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::loadFromVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::loadFromVehicle called while syncInProgress";
176
    } else {
DonLakeFlyer's avatar
DonLakeFlyer committed
177 178
        _itemsRequested = true;
        _geoFenceManager->loadFromVehicle();
179 180 181 182 183
    }
}

void GeoFenceController::sendToVehicle(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
184 185 186 187 188 189 190 191 192
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle called while syncInProgress";
    } else {
        _geoFenceManager->sendToVehicle(_breachReturnPoint, _mapPolygon.pathModel());
        _mapPolygon.setDirty(false);
        setDirty(false);
    }
193 194 195 196
}

bool GeoFenceController::syncInProgress(void) const
{
197
    return _geoFenceManager->inProgress();
198 199 200 201
}

bool GeoFenceController::dirty(void) const
{
202
    return _dirty;
203 204 205 206 207 208 209 210
}


void GeoFenceController::setDirty(bool dirty)
{
    if (dirty != _dirty) {
        _dirty = dirty;
        if (!dirty) {
211
            _mapPolygon.setDirty(dirty);
212 213 214 215 216 217 218 219 220 221 222
        }
        emit dirtyChanged(dirty);
    }
}

void GeoFenceController::_polygonDirtyChanged(bool dirty)
{
    if (dirty) {
        setDirty(true);
    }
}
223

224 225
bool GeoFenceController::breachReturnSupported(void) const
{
226
    return _geoFenceManager->breachReturnSupported();
227 228
}

229
bool GeoFenceController::circleEnabled(void) const
230
{
231
    return _geoFenceManager->circleEnabled();
232 233
}

234 235
Fact* GeoFenceController::circleRadiusFact(void) const
{
236
    return _geoFenceManager->circleRadiusFact();
237 238 239 240
}

bool GeoFenceController::polygonSupported(void) const
{
241
    return _geoFenceManager->polygonSupported();
242 243
}

244
bool GeoFenceController::polygonEnabled(void) const
245
{
246
    return _geoFenceManager->polygonEnabled();
247 248
}

249
QVariantList GeoFenceController::params(void) const
250
{
251
    return _geoFenceManager->params();
252 253 254 255
}

QStringList GeoFenceController::paramLabels(void) const
{
256
    return _geoFenceManager->paramLabels();
257 258 259 260 261 262 263
}

void GeoFenceController::_setDirty(void)
{
    setDirty(true);
}

264
void GeoFenceController::_setPolygonFromManager(const QList<QGeoCoordinate>& polygon)
265
{
266 267 268 269 270
    _mapPolygon.clear();
    for (int i=0; i<polygon.count(); i++) {
        _mapPolygon.appendVertex(polygon[i]);
    }
    _mapPolygon.setDirty(false);
271 272 273 274 275 276
}

void GeoFenceController::_setReturnPointFromManager(QGeoCoordinate breachReturnPoint)
{
    _breachReturnPoint = breachReturnPoint;
    emit breachReturnPointChanged(_breachReturnPoint);
277 278
}

DonLakeFlyer's avatar
DonLakeFlyer committed
279
void GeoFenceController::_managerLoadComplete(const QGeoCoordinate& breachReturn, const QList<QGeoCoordinate>& polygon)
280
{
DonLakeFlyer's avatar
DonLakeFlyer committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
    // Fly view always reloads on _loadComplete
    // Plan view only reloads on _loadComplete if specifically requested
    if (!_editMode || _itemsRequested) {
        _setReturnPointFromManager(breachReturn);
        _setPolygonFromManager(polygon);
        setDirty(false);
        _signalAll();
        emit loadComplete();
    }
    _itemsRequested = false;
}

void GeoFenceController::_managerSendComplete(void)
{
    // Fly view always reloads on manager sendComplete
    if (!_editMode) {
        showPlanFromManagerVehicle();
    }
}

void GeoFenceController::_managerRemoveAllComplete(void)
{
    // Remove all from vehicle so we always update
    showPlanFromManagerVehicle();
305
}
306

307 308 309 310 311 312 313 314 315 316
bool GeoFenceController::containsItems(void) const
{
    return _mapPolygon.count() > 2;
}

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

DonLakeFlyer's avatar
DonLakeFlyer committed
317
bool GeoFenceController::showPlanFromManagerVehicle(void)
318
{
DonLakeFlyer's avatar
DonLakeFlyer committed
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
    qCDebug(GeoFenceControllerLog) << "showPlanFromManagerVehicle";
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::showPlanFromManagerVehicle called while offline";
        return true;    // stops further propogation of showPlanFromManagerVehicle due to error
    } else {
        _itemsRequested = true;
        if (!_managerVehicle->initialPlanRequestComplete()) {
            // The vehicle hasn't completed initial load, we can just wait for newMissionItemsAvailable to be signalled automatically
            qCDebug(GeoFenceControllerLog) << "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(GeoFenceControllerLog) << "showPlanFromManagerVehicle: syncInProgress wait for signal";
            return true;
        } else {
            // Fake a _loadComplete with the current items
            qCDebug(GeoFenceControllerLog) << "showPlanFromManagerVehicle: sync complete simulate signal";
            _itemsRequested = true;
            _managerLoadComplete(_geoFenceManager->breachReturnPoint(), _geoFenceManager->polygon());
            return false;
        }
    }
341
}