WimaController.cc 30.1 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "WimaController.h"
2

Valentin Platzgummer's avatar
Valentin Platzgummer committed
3
#include "utilities.h"
4

5
#include "Snake/QNemoProgress.h"
6
#include "Snake/QNemoHeartbeat.h"
7

8
#include "QVector3D"
9
#include <QScopedPointer>
10

11 12
#include <memory>

13 14 15 16
#define SMART_RTL_MAX_ATTEMPTS 3 // times
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
#define EVENT_TIMER_INTERVAL 50 // ms

17

18

19 20 21 22 23 24 25 26 27
const char* WimaController::areaItemsName               = "AreaItems";
const char* WimaController::missionItemsName            = "MissionItems";
const char* WimaController::settingsGroup               = "WimaController";
const char* WimaController::enableWimaControllerName    = "EnableWimaController";
const char* WimaController::overlapWaypointsName        = "OverlapWaypoints";
const char* WimaController::maxWaypointsPerPhaseName    = "MaxWaypointsPerPhase";
const char* WimaController::startWaypointIndexName      = "StartWaypointIndex";
const char* WimaController::showAllMissionItemsName     = "ShowAllMissionItems";
const char* WimaController::showCurrentMissionItemsName = "ShowCurrentMissionItems";
28
const char* WimaController::flightSpeedName             = "FlightSpeed";
29
const char* WimaController::arrivalReturnSpeedName      = "ArrivalReturnSpeed";
30
const char* WimaController::altitudeName                = "Altitude";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
31 32
const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
33
const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
34 35 36
const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";

Valentin Platzgummer's avatar
Valentin Platzgummer committed
37 38 39
WimaController::StatusMap WimaController::_nemoStatusMap{
    std::make_pair<int, QString>(0, "No Heartbeat"),
    std::make_pair<int, QString>(1, "Connected"),
40 41
    std::make_pair<int, QString>(-1, "Timeout"),
    std::make_pair<int, QString>(-2, "Error")};
42

Valentin Platzgummer's avatar
Valentin Platzgummer committed
43
using namespace snake;
44

45
WimaController::WimaController(QObject *parent)
46
    : QObject                   (parent)
47 48 49 50
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
51
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
52 53 54 55
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
56 57
    , _rtlManager               (_managerSettings, _areaInterface)
    , _currentManager           (&_defaultManager)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
58
    , _managerList              {&_defaultManager, &_snakeManager, &_rtlManager}
59 60 61
    , _metaDataMap              (
          FactMetaData::createMapFromJsonFile(
              QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
62 63 64
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
65
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
66
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
67 68
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
69
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
70
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
71 72 73 74 75
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
76
    , _lowBatteryHandlingTriggered  (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
77
    , _measurementPathLength    (-1)
78
    , _snakeCalcInProgress      (false)
79
    , _nemoHeartbeat            (0 /*status: not connected*/)
80
    , _fallbackStatus           (0 /*status: not connected*/)
81 82 83
    , _batteryLevelTicker       (EVENT_TIMER_INTERVAL, 1000 /*ms*/)
    , _snakeTicker              (EVENT_TIMER_INTERVAL, 200 /*ms*/)
    , _nemoTimeoutTicker        (EVENT_TIMER_INTERVAL, 5000 /*ms*/)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
84
    , _topicServiceSetupDone    (false)
85
{
86

87
    // Set up facts.
88 89
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
90 91
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
92
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
93 94 95
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
96

Valentin Platzgummer's avatar
Valentin Platzgummer committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110
    // Init waypoint managers.
    bool value;
    size_t overlap  = _overlapWaypoints.rawValue().toUInt(&value);
    Q_ASSERT(value);
    size_t N        = _maxWaypointsPerPhase.rawValue().toUInt(&value);
    Q_ASSERT(value);
    size_t startIdx = _nextPhaseStartWaypointIndex.rawValue().toUInt(&value);
    Q_ASSERT(value);
    (void)value;
    for (auto manager : _managerList){
        manager->setOverlap(overlap);
        manager->setN(N);
        manager->setStartIndex(startIdx);
    }
111

112
    // Periodic.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
113
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
114 115
    //_eventTimer.setInterval(EVENT_TIMER_INTERVAL);
    _eventTimer.start(EVENT_TIMER_INTERVAL);
116

Valentin Platzgummer's avatar
Valentin Platzgummer committed
117
    // Snake Worker Thread.
118
    connect(&_snakeDataManager, &SnakeDataManager::finished, this, &WimaController::_snakeStoreWorkerResults);
119
    connect(this, &WimaController::nemoProgressChanged, this, &WimaController::_initStartSnakeWorker);
120
    connect(this, &QObject::destroyed, &this->_snakeDataManager, &SnakeDataManager::quit);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
121

122
    // Snake.
123 124 125 126 127 128 129 130 131 132
    auto configRosBridge = [this]{
        if ( this->_enableSnake.rawValue().toBool() ){
            this->_snakeDataManager.enableRosBridge();
        } else {
            this->_snakeDataManager.disableRosBride();
        }
    };
    connect(&_enableSnake, &Fact::rawValueChanged, configRosBridge);
    configRosBridge();

133 134
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_switchSnakeManager);
    _switchSnakeManager(_enableSnake.rawValue());
135 136
}

137 138 139 140 141 142 143 144 145
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

MissionController *WimaController::missionController() {
    return _missionController;
}

QmlObjectListModel *WimaController::visualItems() {
146 147 148 149
    return &_areas;
}

QmlObjectListModel *WimaController::missionItems() {
150
    return const_cast<QmlObjectListModel*>(&_currentManager->missionItems());
151 152 153
}

QmlObjectListModel *WimaController::currentMissionItems() {
154
    return const_cast<QmlObjectListModel*>(&_currentManager->currentMissionItems());
155 156 157 158
}

QVariantList WimaController::waypointPath()
{
159
    return const_cast<QVariantList&>(_currentManager->waypointsVariant());
160 161
}

162
QVariantList WimaController::currentWaypointPath()
163
{
164
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
165 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
Fact *WimaController::enableWimaController() {
    return &_enableWimaController;
}

Fact *WimaController::overlapWaypoints() {
    return &_overlapWaypoints;
}

Fact *WimaController::maxWaypointsPerPhase() {
    return &_maxWaypointsPerPhase;
}

Fact *WimaController::startWaypointIndex() {
    return &_nextPhaseStartWaypointIndex;
}

Fact *WimaController::showAllMissionItems() {
    return &_showAllMissionItems;
}

Fact *WimaController::showCurrentMissionItems() {
    return &_showCurrentMissionItems;
}

Fact *WimaController::flightSpeed() {
    return &_flightSpeed;
}

Fact *WimaController::arrivalReturnSpeed() {
    return &_arrivalReturnSpeed;
}

Fact *WimaController::altitude() {
    return &_altitude;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
203 204
QmlObjectListModel *WimaController::snakeTiles()
{
205
    static QmlObjectListModel list;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
206
    qWarning() << "using snake tile dummy";
207
    return &list;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
208 209 210 211 212 213 214 215
}

QVariantList WimaController::snakeTileCenterPoints()
{
    qWarning() << "using snakeTileCenterPoints dummy";
    return QVariantList();
}

216 217 218 219
QVector<int> WimaController::nemoProgress()
{
    qWarning() << "using nemoProgress dummy";
    return QVector<int>();
220 221
}

222 223
double WimaController::phaseDistance() const
{
224
    return 0.0;
225 226 227 228
}

double WimaController::phaseDuration() const
{
229
    return 0.0;
230 231
}

232 233
int WimaController::nemoStatus() const
{
234 235
    qWarning() << "using nemoStatus dummy";
    return 0;
236 237 238
}

QString WimaController::nemoStatusString() const
Valentin Platzgummer's avatar
Valentin Platzgummer committed
239
{
240
    return _nemoStatusMap.at(nemoStatus());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
241 242 243 244
}

bool WimaController::snakeCalcInProgress() const
{
245 246
    qWarning() << "using snakeCalcInProgress dummy";
    return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
247 248
}

249 250 251
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
252
    _managerSettings.setMasterController(masterC);
253 254 255 256 257 258
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
259
    _managerSettings.setMissionController(missionC);
260 261 262
    emit missionControllerChanged();
}

263 264
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
265
    _calcNextPhase();
266 267
}

268
void WimaController::previousPhase()
269
{        
270 271
    if ( !_currentManager->previous() ) {
        Q_ASSERT(false);
272
    }
273 274 275 276 277

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
278 279 280 281
}

void WimaController::resetPhase()
{
282 283
    if ( !_currentManager->reset() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
284
    }
285 286 287 288 289

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
290 291
}

292 293
void WimaController::requestSmartRTL()
{
294 295 296 297 298
    QString errorString("Smart RTL requested. ");
    if ( !_checkSmartRTLPreCondition(errorString) ){
        qgcApp()->showMessage(errorString);
        return;
    }
299 300 301 302
    emit smartRTLRequestConfirm();
}

bool WimaController::upload()
303
{
304
    auto &currentMissionItems = _defaultManager.currentMissionItems();
305
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
306
        && currentMissionItems.count() > 0) {
307
        emit forceUploadConfirm();
308 309 310
        return false;
    }

311
    return forceUpload();
312 313
}

314
bool WimaController::forceUpload()
315
{
316 317
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (currentMissionItems.count() < 1)
318
        return false;
319 320

    _missionController->removeAll();
321
    // Set homeposition of settingsItem.
322 323 324
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
325
        Q_ASSERT(false);
326
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
327
        return false;
328
    }
329
    settingsItem->setCoordinate(_managerSettings.homePosition());
330

331
    // Copy mission items to _missionController.
332
    for (int i = 1; i < currentMissionItems.count(); i++){
333
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
334 335 336
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

337
    _masterController->sendToVehicle();
338 339

    return true;
340
}
341

342 343 344
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
345 346 347
    _missionController->removeAll();
}

348
void WimaController::executeSmartRTL()
349
{
350 351
    forceUpload();
    masterController()->managerVehicle()->startMission();
352 353
}

354
void WimaController::initSmartRTL()
355
{
356
    _initSmartRTL();
357 358
}

359 360 361 362 363 364
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
365 366 367
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
                                       QVector<QGeoCoordinate> &path)
368 369 370
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
371 372 373 374 375
    QPolygonF polygon2D;
    toCartesianList(_joinedArea.coordinateList(), /*origin*/ start, polygon2D);
    QPointF start2D(0,0);
    QPointF end2D;
    toCartesian(destination, start, end2D);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
376
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
377 378 379

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
380 381 382 383

    return  retVal;
}

384
bool WimaController::setWimaPlanData(const WimaPlanData &planData)
385
{
386
    // reset visual items
387 388
    _areas.clear();
    _defaultManager.clear();
389
    _snakeManager.clear();
390 391
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
392
    _snakeTileCenterPoints.clear();
393

394 395 396
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
397
    emit waypointPathChanged();
398
    emit currentWaypointPathChanged();
399
    emit snakeTilesChanged();
400
    emit snakeTileCenterPointsChanged();
401

402
    _localPlanDataValid = false;
403

404 405
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
406

407
    int areaCounter = 0;
408
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
409 410
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
411

412 413 414
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
415
            _areas.append(&_serviceArea);
416

417 418
            continue;
        }
419

420 421 422
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
423
            _areas.append(&_measurementArea);
424

425
            continue;
426
        }
427

428 429 430 431
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
432

433 434
            continue;
        }
435

436 437 438
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
439
            _areas.append(&_joinedArea);
440

441 442
            continue;
        }
443

444 445 446
        if (areaCounter >= numAreas)
            break;
    }
447

448
    if (areaCounter != numAreas) {
449
        Q_ASSERT(false);
450 451
        return false;
    }
452

453 454
    emit visualItemsChanged();

455 456 457
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
458
        qWarning("WimaController: Mission items from WimaPlaner empty!");
459
        return false;
460
    }
461

462
    for (auto item : tempMissionItems) {
463
        _defaultManager.push_back(item.coordinate());
464
    }
465

466 467 468
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
469

470
    if( !_defaultManager.reset() ){
471
        Q_ASSERT(false);
472
        return false;
473
    }
474

475 476 477 478 479 480 481
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



Valentin Platzgummer's avatar
Valentin Platzgummer committed
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
    // Initialize _scenario.
    Area mArea;
    for (auto variant : _measurementArea.path()){
        QGeoCoordinate c{variant.value<QGeoCoordinate>()};
        mArea.geoPolygon.push_back(GeoPoint2D{c.latitude(), c.longitude()});
    }
    mArea.type = AreaType::MeasurementArea;

    Area sArea;
    for (auto variant : _serviceArea.path()){
        QGeoCoordinate c{variant.value<QGeoCoordinate>()};
        sArea.geoPolygon.push_back(GeoPoint2D{c.latitude(), c.longitude()});
    }
    sArea.type = AreaType::ServiceArea;

    Area corridor;
    for (auto variant : _corridor.path()){
        QGeoCoordinate c{variant.value<QGeoCoordinate>()};
        corridor.geoPolygon.push_back(GeoPoint2D{c.latitude(), c.longitude()});
    }
    corridor.type = AreaType::Corridor;
503

Valentin Platzgummer's avatar
Valentin Platzgummer committed
504 505 506 507
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

508
    // Check if scenario is defined.
509 510 511
    if ( !_scenario.defined(_snakeTileWidth.rawValue().toUInt(),
                            _snakeTileHeight.rawValue().toUInt(),
                            _snakeMinTileArea.rawValue().toUInt()) ) {
512
        Q_ASSERT(false);
513
        return false;
514
    }
515

516
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
517 518 519 520 521
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
522 523
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
524
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
525 526 527 528 529 530 531 532 533 534 535
            const auto &tile = tiles[i];
            SnakeTile Tile;
            for ( const auto &vertex : tile) {
                QGeoCoordinate QVertex(vertex[0], vertex[1], vertex[2]);
                Tile.append(QVertex);
            }
            const auto &centerPoint = cps[i];
            QGeoCoordinate QCenterPoint(centerPoint[0], centerPoint[1], centerPoint[2]);
            Tile.setCenter(QCenterPoint);
            _snakeTiles.polygons().append(Tile);
            _snakeTileCenterPoints.append(QVariant::fromValue(QCenterPoint));
536
        }
537 538 539 540 541
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
542
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
543 544 545 546
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
547
                Tile.path().append(QVertex);
548 549 550
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
551
    }
552 553
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
554

555
    if ( _enableSnake.rawValue().toBool()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
         && _pRosBridge->isRunning()
         && _pRosBridge->connected()
         && _topicServiceSetupDone
         && _snakeTilesLocal.polygons().size() > 0
         )
    {
        using namespace ros_bridge::messages;
        // Publish snake origin.
        JsonDocUPtr jOrigin(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
        bool ret = geographic_msgs::geo_point::toJson(
                     _snakeOrigin, *jOrigin, jOrigin->GetAllocator());
        Q_ASSERT(ret);
        _pRosBridge->publish(std::move(jOrigin), "/snake/origin");
        // Publish snake tiles.
        JsonDocUPtr jSnakeTiles(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
        ret = jsk_recognition_msgs::polygon_array::toJson(
                     _snakeTilesLocal, *jSnakeTiles, jSnakeTiles->GetAllocator());
        Q_ASSERT(ret);
        _pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");

576 577
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
578

579 580 581
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
582

583 584 585 586 587 588 589 590 591 592 593
WimaController *WimaController::thisPointer()
{
    return this;
}

bool WimaController::_calcNextPhase()
{
    if ( !_currentManager->next() ) {
        Q_ASSERT(false);
        return false;
    }
594

595
    emit missionItemsChanged();
596 597
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
598
    emit waypointPathChanged();
599

600
    return true;
601 602
}

603
bool WimaController::_setStartIndex()
604
{
605
    bool value;
606
    _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
607 608 609 610 611
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
612 613
        return false;
    }
614

615
    emit missionItemsChanged();
616
    emit currentMissionItemsChanged();
617 618
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
619 620

    return true;
621
}
622

623
void WimaController::_recalcCurrentPhase()
624
{
625 626
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
627
    }
628

629 630 631
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
632 633
    emit waypointPathChanged();
}
634 635

void WimaController::_updateOverlap()
636
{
637
    bool value;
638
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
639 640
    Q_ASSERT(value);
    (void)value;
641

642
    if ( !_currentManager->update() ) {
643 644 645 646 647
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
648
    emit currentWaypointPathChanged();
649
    emit waypointPathChanged();
650
}
651

652
void WimaController::_updateMaxWaypoints()
653
{
654
    bool value;
655
    _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
656 657
    Q_ASSERT(value);
    (void)value;
658

659 660
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
661
    }
662

663 664 665 666
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
667 668
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
669
void WimaController::_updateflightSpeed()
670
{
671
    bool value;
672
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
673 674 675 676 677 678
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
679

680 681 682 683
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
684 685
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
686
void WimaController::_updateArrivalReturnSpeed()
687
{
688
    bool value;
689
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble(&value));
690 691 692 693 694 695
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
696

697 698 699 700
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
701 702
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
703
void WimaController::_updateAltitude()
704
{
705
    bool value;
706
    _managerSettings.setAltitude(_altitude.rawValue().toDouble(&value));
707 708 709 710 711 712
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
713 714 715 716 717

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
718 719
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
720
void WimaController::_checkBatteryLevel()
721
{
722 723 724 725 726
    Vehicle *managerVehicle     = masterController()->managerVehicle();
    WimaSettings* wimaSettings  = qgcApp()->toolbox()->settingsManager()->wimaSettings();
    int batteryThreshold        = wimaSettings->lowBatteryThreshold()->rawValue().toInt();
    bool enabled                = _enableWimaController.rawValue().toBool();
    unsigned int minTime        = wimaSettings->minimalRemainingMissionTime()->rawValue().toUInt();
727

Valentin Platzgummer's avatar
Valentin Platzgummer committed
728
    if (managerVehicle != nullptr && enabled == true) {
729 730 731 732 733 734
        Fact *battery1percentRemaining = managerVehicle->battery1FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);
        Fact *battery2percentRemaining = managerVehicle->battery2FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);


        if (battery1percentRemaining->rawValue().toDouble() < batteryThreshold
                && battery2percentRemaining->rawValue().toDouble() < batteryThreshold) {
735 736 737
            if (!_lowBatteryHandlingTriggered) {                
                _lowBatteryHandlingTriggered = true;
                if ( !(_missionController->remainingTime() <= minTime) ) {
738
                    requestSmartRTL();
739 740 741 742 743 744 745 746 747 748
                }
            }
        }
        else {
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
749 750 751
void WimaController::_eventTimerHandler()
{
    // Battery level check necessary?
752
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
753
    if ( enableLowBatteryHandling->rawValue().toBool() && _batteryLevelTicker.ready() )
Valentin Platzgummer's avatar
Valentin Platzgummer committed
754 755 756
        _checkBatteryLevel();

    // Snake flight plan update necessary?
757 758 759 760
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
761

762
    if ( _nemoTimeoutTicker.ready() && _enableSnake.rawValue().toBool() ) {
763 764 765 766
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
767 768 769
}

void WimaController::_smartRTLCleanUp(bool flying)
770
{
771 772 773 774 775
    if ( !flying) { // vehicle has landed
        _switchWaypointManager(_defaultManager);
        _missionController->removeAllFromVehicle();
        _missionController->removeAll();
        disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
776 777 778
    }
}

779 780
void WimaController::_setPhaseDistance(double distance)
{
781 782 783
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
784

785 786
//        emit phaseDistanceChanged();
//    }
787 788 789 790
}

void WimaController::_setPhaseDuration(double duration)
{
791 792 793
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
794

795 796
//        emit phaseDurationChanged();
//    }
797 798
}

799
bool WimaController::_checkSmartRTLPreCondition(QString &errorString)
800 801
{
    if (!_localPlanDataValid) {
802 803
        errorString.append(tr("No WiMA data available. Please define at least a measurement and a service area."));
        return false;
804
    }
805

806
    return _rtlManager.checkPrecondition(errorString);
807 808
}

809
void WimaController::_switchWaypointManager(WaypointManager::ManagerBase &manager)
810
{
811 812 813
    if (_currentManager != &manager) {
        _currentManager = &manager;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
        disconnect(&_overlapWaypoints,             &Fact::rawValueChanged,
                   this, &WimaController::_updateOverlap);
        disconnect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged,
                   this, &WimaController::_updateMaxWaypoints);
        disconnect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged,
                   this, &WimaController::_setStartIndex);

        _maxWaypointsPerPhase.setRawValue(_currentManager->N());
        _overlapWaypoints.setRawValue(_currentManager->overlap());
        _nextPhaseStartWaypointIndex.setRawValue(_currentManager->startIndex());

        connect(&_overlapWaypoints,             &Fact::rawValueChanged,
                this, &WimaController::_updateOverlap);
        connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged,
                this, &WimaController::_updateMaxWaypoints);
        connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged,
                this, &WimaController::_setStartIndex);
831

832 833 834 835
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
836

837
        qWarning() << "WimaController::_switchWaypointManager: statistics update missing.";
838 839 840
    }
}

841 842
void WimaController::_initSmartRTL()
{
843 844 845 846
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

847
    if ( _checkSmartRTLPreCondition(errorString) ) {
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
        _masterController->managerVehicle()->pauseVehicle();
        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
        if ( _rtlManager.update() ) { // Calculate return path.
            _switchWaypointManager(_rtlManager);
            attemptCounter = 0;
            emit smartRTLPathConfirm();
            return;
        }
    } else if (attemptCounter > SMART_RTL_MAX_ATTEMPTS) {
        errorString.append(tr("Smart RTL: No success after maximum number of attempts."));
        qgcApp()->showMessage(errorString);
        attemptCounter = 0;
    } else {
        _smartRTLTimer.singleShot(SMART_RTL_ATTEMPT_INTERVAL, this, &WimaController::_initSmartRTL);
    }
863 864
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
865 866 867 868 869 870 871 872
void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

873 874
void WimaController::_snakeStoreWorkerResults()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
875
    _setSnakeCalcInProgress(false);
876
    auto start = std::chrono::high_resolution_clock::now();
877 878
    _snakeManager.clear();

879
    const auto &r = _snakeDataManager.getResult();
880
    if (!r.success) {
881
        //qgcApp()->showMessage(r.errorMessage);
882 883 884 885 886
        return;
    }

    // create Mission items from r.waypoints
    long n = r.waypoints.size() - r.returnPathIdx.size() - r.arrivalPathIdx.size() + 2;
887
    if (n < 1) {
888 889
        return;
    }
890 891 892

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
893 894
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
895
        _snakeManager.push_back(r.waypoints[int(i)].value<QGeoCoordinate>());
896
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
897

898
    _snakeManager.update();
899

900
    emit missionItemsChanged();
901 902 903
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
904 905 906 907

    auto end = std::chrono::high_resolution_clock::now();
    double duration = std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();
    qWarning() << "WimaController::_snakeStoreWorkerResults execution time: " << duration << " ms.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
908 909
}

910 911 912 913 914 915
void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

    // Stop worker thread if running.
916 917
    if ( _snakeDataManager.isRunning() ) {
        _snakeDataManager.quit();
918 919 920
    }

    // Initialize _snakeWorker.
921
    _snakeDataManager.setProgress(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
922
                _nemoProgress.progress());
923
    _snakeDataManager.setLineDistance(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
924
                _snakeLineDistance.rawValue().toDouble());
925
    _snakeDataManager.setMinTransectLength(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
926
                _snakeMinTransectLength.rawValue().toDouble());
927
    _snakeDataManager.setTileHeight(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
928
                _snakeTileHeight.rawValue().toDouble());
929
    _snakeDataManager.setTileWidth(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
930
                _snakeTileWidth.rawValue().toDouble());
931
    _snakeDataManager.setMinTileArea(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
932
                _snakeMinTileArea.rawValue().toDouble());
933 934 935
    _setSnakeCalcInProgress(true);

    // Start worker thread.
936
    _snakeDataManager.start();
937 938
}

939 940 941 942 943 944 945 946
void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}
947 948