GeoFenceController.cc 10.9 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());
Don Gagne's avatar
Don Gagne committed
87 88
    emit paramsChanged(params());
    emit paramLabelsChanged(paramLabels());
89 90
}

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

99 100 101 102 103 104 105 106 107 108 109 110
    _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
111 112 113
    connect(_geoFenceManager, &GeoFenceManager::loadComplete,                   this, &GeoFenceController::_managerLoadComplete);
    connect(_geoFenceManager, &GeoFenceManager::sendComplete,                   this, &GeoFenceController::_managerSendComplete);
    connect(_geoFenceManager, &GeoFenceManager::removeAllComplete,              this, &GeoFenceController::_managerRemoveAllComplete);
114 115
    connect(_geoFenceManager, &GeoFenceManager::inProgressChanged,              this, &GeoFenceController::syncInProgressChanged);

Don Gagne's avatar
Don Gagne committed
116
    _signalAll();
117
}
Don Gagne's avatar
Don Gagne committed
118

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

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

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

137
    _signalAll();
138 139

    return true;
140 141
}

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

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

152
    _mapPolygon.saveToJson(json);
153 154
}

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

DonLakeFlyer's avatar
DonLakeFlyer committed
161 162 163 164 165 166 167 168 169 170 171
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();
    }
}

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

void GeoFenceController::sendToVehicle(void)
{
DonLakeFlyer's avatar
DonLakeFlyer committed
186 187 188 189 190
    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
191
        qCDebug(GeoFenceControllerLog) << "GeoFenceController::sendToVehicle";
DonLakeFlyer's avatar
DonLakeFlyer committed
192 193 194 195
        _geoFenceManager->sendToVehicle(_breachReturnPoint, _mapPolygon.pathModel());
        _mapPolygon.setDirty(false);
        setDirty(false);
    }
196 197 198 199
}

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

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


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

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

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

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

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

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

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

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

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

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

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

void GeoFenceController::_setReturnPointFromManager(QGeoCoordinate breachReturnPoint)
{
    _breachReturnPoint = breachReturnPoint;
    emit breachReturnPointChanged(_breachReturnPoint);
280 281
}

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

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

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

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

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

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