GeoFenceController.cc 10.8 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
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle called while offline";
    } else if (syncInProgress()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle called while syncInProgress";
    } else {
DonLakeFlyer's avatar
DonLakeFlyer committed
189
        qCDebug(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle";
DonLakeFlyer's avatar
DonLakeFlyer committed
190 191 192 193
        _geoFenceManager->sendToVehicle(_breachReturnPoint, _mapPolygon.pathModel());
        _mapPolygon.setDirty(false);
        setDirty(false);
    }
194 195 196 197
}

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

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


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

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

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

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

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

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

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

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

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

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

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

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

DonLakeFlyer's avatar
DonLakeFlyer committed
280
void GeoFenceController::_managerLoadComplete(const QGeoCoordinate& breachReturn, const QList<QGeoCoordinate>& polygon)
281
{
DonLakeFlyer's avatar
DonLakeFlyer committed
282 283 284 285 286 287 288 289 290 291 292 293
    // 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;
}

294
void GeoFenceController::_managerSendComplete(bool error)
DonLakeFlyer's avatar
DonLakeFlyer committed
295 296
{
    // Fly view always reloads on manager sendComplete
297
    if (!error && !_editMode) {
DonLakeFlyer's avatar
DonLakeFlyer committed
298 299 300 301
        showPlanFromManagerVehicle();
    }
}

302
void GeoFenceController::_managerRemoveAllComplete(bool error)
DonLakeFlyer's avatar
DonLakeFlyer committed
303
{
304 305 306 307
    if (!error) {
        // Remove all from vehicle so we always update
        showPlanFromManagerVehicle();
    }
308
}
309

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

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

DonLakeFlyer's avatar
DonLakeFlyer committed
320
bool GeoFenceController::showPlanFromManagerVehicle(void)
321
{
DonLakeFlyer's avatar
DonLakeFlyer committed
322
    qCDebug(GeoFenceControllerLog) << "showPlanFromManagerVehicle" << _editMode;
DonLakeFlyer's avatar
DonLakeFlyer committed
323 324
    if (_masterController->offline()) {
        qCWarning(GeoFenceControllerLog) << "GeoFenceController::showPlanFromManagerVehicle called while offline";
Patrick José Pereira's avatar
Patrick José Pereira committed
325
        return true;    // stops further propagation of showPlanFromManagerVehicle due to error
DonLakeFlyer's avatar
DonLakeFlyer committed
326 327 328
    } else {
        _itemsRequested = true;
        if (!_managerVehicle->initialPlanRequestComplete()) {
DonLakeFlyer's avatar
DonLakeFlyer committed
329
            // The vehicle hasn't completed initial load, we can just wait for loadComplete to be signalled automatically
DonLakeFlyer's avatar
DonLakeFlyer committed
330 331 332 333 334 335 336 337 338 339 340 341 342 343
            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;
        }
    }
344
}