WimaController.cc 35.6 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 6 7
#include "ros_bridge/rapidjson/include/rapidjson/document.h"
#include "ros_bridge/rapidjson/include/rapidjson/writer.h"
#include "ros_bridge/rapidjson/include/rapidjson/ostreamwrapper.h"
8 9 10 11
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
#include "ros_bridge/include/messages/nemo_msgs/progress.h"
#include "ros_bridge/include/messages/nemo_msgs/heartbeat.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
12

13
#include "Snake/QNemoProgress.h"
14
#include "Snake/QNemoHeartbeat.h"
15

16
#include "QVector3D"
17
#include <QScopedPointer>
18

19 20
#include <memory>

21 22 23 24
#define SMART_RTL_MAX_ATTEMPTS 3 // times
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
#define EVENT_TIMER_INTERVAL 50 // ms

25

26

27 28 29 30 31 32 33 34 35
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";
36
const char* WimaController::flightSpeedName             = "FlightSpeed";
37
const char* WimaController::arrivalReturnSpeedName      = "ArrivalReturnSpeed";
38
const char* WimaController::altitudeName                = "Altitude";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
39 40
const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
41
const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
42 43 44
const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";

Valentin Platzgummer's avatar
Valentin Platzgummer committed
45 46 47 48
WimaController::StatusMap WimaController::_nemoStatusMap{
    std::make_pair<int, QString>(0, "No Heartbeat"),
    std::make_pair<int, QString>(1, "Connected"),
    std::make_pair<int, QString>(-1, "Timeout")};
49

Valentin Platzgummer's avatar
Valentin Platzgummer committed
50 51
using namespace snake;
using namespace snake_geometry;
52

53
WimaController::WimaController(QObject *parent)
54
    : QObject                   (parent)
55 56 57 58
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
59
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
60 61 62 63
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
64 65
    , _rtlManager               (_managerSettings, _areaInterface)
    , _currentManager           (&_defaultManager)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
66
    , _managerList              {&_defaultManager, &_snakeManager, &_rtlManager}
67 68 69
    , _metaDataMap              (
          FactMetaData::createMapFromJsonFile(
              QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
70 71 72
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
73
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
74
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
75 76
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
77
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
78
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
79 80 81 82 83
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
84
    , _lowBatteryHandlingTriggered  (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
85
    , _measurementPathLength    (-1)
86
    , _snakeCalcInProgress      (false)
87
    , _nemoHeartbeat            (0 /*status: not connected*/)
88
    , _fallbackStatus           (0 /*status: not connected*/)
89 90 91
    , _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
92
    , _topicServiceSetupDone    (false)
93
{
94

95
    // Set up facts.
96 97
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
98 99
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
100
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
101 102 103
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
104

Valentin Platzgummer's avatar
Valentin Platzgummer committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118
    // 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);
    }
119

120
    // Periodic.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
121
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
122 123
    //_eventTimer.setInterval(EVENT_TIMER_INTERVAL);
    _eventTimer.start(EVENT_TIMER_INTERVAL);
124

Valentin Platzgummer's avatar
Valentin Platzgummer committed
125
    // Snake Worker Thread.
126
    connect(&_snakeDataManager, &SnakeDataManager::finished, this, &WimaController::_snakeStoreWorkerResults);
127
    connect(this, &WimaController::nemoProgressChanged, this, &WimaController::_initStartSnakeWorker);
128
    connect(this, &QObject::destroyed, &this->_snakeDataManager, &SnakeWorker::quit);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
129

130
    // Snake.
131 132 133 134
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_initStartSnakeWorker);
    _initStartSnakeWorker();
    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 205 206 207 208 209 210 211 212 213 214
QmlObjectListModel *WimaController::snakeTiles()
{
    qWarning() << "using snake tile dummy";
    return QmlObjectListModel();
}

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

215 216 217 218 219 220 221
QVector<int> WimaController::nemoProgress() {
    if ( _nemoProgress.progress().size() == _snakeTileCenterPoints.size() )
        return _nemoProgress.progress();
    else
        return QVector<int>(_snakeTileCenterPoints.size(), 0);
}

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 234 235 236 237
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

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

bool WimaController::snakeCalcInProgress() const
{
    return _snakeCalcInProgress;
}

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

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

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

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

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
276 277 278 279
}

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

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
288 289
}

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

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

309
    return forceUpload();
310 311
}

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

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

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

335
    _masterController->sendToVehicle();
336 337

    return true;
338
}
339

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

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

352
void WimaController::initSmartRTL()
353
{
354
    _initSmartRTL();
355 356
}

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
363 364 365
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
                                       QVector<QGeoCoordinate> &path)
366 367 368
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
369 370 371 372 373
    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
374
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
375 376 377

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

    return  retVal;
}

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

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

400
    _localPlanDataValid = false;
401

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

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

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

415 416
            continue;
        }
417

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

423
            continue;
424
        }
425

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

431 432
            continue;
        }
433

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

439 440
            continue;
        }
441

442 443 444
        if (areaCounter >= numAreas)
            break;
    }
445

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

451 452
    emit visualItemsChanged();

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

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

464 465 466
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
467

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

473 474 475 476 477 478 479
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



Valentin Platzgummer's avatar
Valentin Platzgummer committed
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
    // 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;
501

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

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

514
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
515 516 517 518 519
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
520 521
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
522
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
523 524 525 526 527 528 529 530 531 532 533
            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));
534
        }
535 536 537 538 539
    }

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

553
    if ( _enableSnake.rawValue().toBool()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
         && _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");

574 575
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
576

577 578 579
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
580

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

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

593
    emit missionItemsChanged();
594 595
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
596
    emit waypointPathChanged();
597

598
    return true;
599 600
}

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

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
610 611
        return false;
    }
612

613
    emit missionItemsChanged();
614
    emit currentMissionItemsChanged();
615 616
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
617 618

    return true;
619
}
620

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

627 628 629
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
630 631
    emit waypointPathChanged();
}
632 633

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

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

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
646
    emit currentWaypointPathChanged();
647
    emit waypointPathChanged();
648
}
649

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

657 658
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
659
    }
660

661 662 663 664
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
665 666
}

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

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

678 679 680 681
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
682 683
}

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

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

695 696 697 698
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
699 700
}

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

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

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
716 717
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
718
void WimaController::_checkBatteryLevel()
719
{
720 721 722 723 724
    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();
725

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


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

    }
}

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

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

760
    if ( _nemoTimeoutTicker.ready() && _enableSnake.rawValue().toBool() ) {
761 762 763 764 765
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }

766 767
    if ( _snakeTicker.ready() ) {
        if ( _enableSnake.rawValue().toBool() ) {
768
            if ( !_pRosBridge->isRunning()) {
769
                _pRosBridge->start();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
770 771 772 773 774 775 776 777
            } else if ( _pRosBridge->isRunning()
                        && _pRosBridge->connected()
                        && !_topicServiceSetupDone) {
                if ( _doTopicServiceSetup() )
                    _topicServiceSetupDone = true;
            } else if ( _pRosBridge->isRunning()
                        && !_pRosBridge->connected()
                        && _topicServiceSetupDone){
778 779
                _pRosBridge->reset();
                _pRosBridge->start();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
780
                _topicServiceSetupDone = false;
781
            }
782
        } else  if ( _pRosBridge->isRunning() ) {
783
                _pRosBridge->reset();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
784
                _topicServiceSetupDone = false;
785
        }
786
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
787 788 789
}

void WimaController::_smartRTLCleanUp(bool flying)
790
{
791 792 793 794 795
    if ( !flying) { // vehicle has landed
        _switchWaypointManager(_defaultManager);
        _missionController->removeAllFromVehicle();
        _missionController->removeAll();
        disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
796 797 798
    }
}

799 800
void WimaController::_setPhaseDistance(double distance)
{
801 802 803
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
804

805 806
//        emit phaseDistanceChanged();
//    }
807 808 809 810
}

void WimaController::_setPhaseDuration(double duration)
{
811 812 813
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
814

815 816
//        emit phaseDurationChanged();
//    }
817 818
}

819
bool WimaController::_checkSmartRTLPreCondition(QString &errorString)
820 821
{
    if (!_localPlanDataValid) {
822 823
        errorString.append(tr("No WiMA data available. Please define at least a measurement and a service area."));
        return false;
824
    }
825

826
    return _rtlManager.checkPrecondition(errorString);
827 828
}

829
void WimaController::_switchWaypointManager(WaypointManager::ManagerBase &manager)
830
{
831 832 833
    if (_currentManager != &manager) {
        _currentManager = &manager;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
        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);
851

852 853 854 855
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
856

857
        qWarning() << "WimaController::_switchWaypointManager: statistics update missing.";
858 859 860
    }
}

861 862
void WimaController::_initSmartRTL()
{
863 864 865 866
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

867
    if ( _checkSmartRTLPreCondition(errorString) ) {
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
        _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);
    }
883 884
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
885 886 887 888 889 890 891 892
void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

893 894
void WimaController::_snakeStoreWorkerResults()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
895
    _setSnakeCalcInProgress(false);
896
    auto start = std::chrono::high_resolution_clock::now();
897 898
    _snakeManager.clear();

899
    const auto &r = _snakeDataManager.getResult();
900
    if (!r.success) {
901
        //qgcApp()->showMessage(r.errorMessage);
902 903 904 905 906
        return;
    }

    // create Mission items from r.waypoints
    long n = r.waypoints.size() - r.returnPathIdx.size() - r.arrivalPathIdx.size() + 2;
907
    if (n < 1) {
908 909
        return;
    }
910 911 912

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

918
    _snakeManager.update();
919

920
    emit missionItemsChanged();
921 922 923
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
924 925 926 927

    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
928 929
}

930 931 932 933 934 935
void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

    // Stop worker thread if running.
936 937
    if ( _snakeDataManager.isRunning() ) {
        _snakeDataManager.quit();
938 939 940
    }

    // Initialize _snakeWorker.
941
    _snakeDataManager.setProgress(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
942
                _nemoProgress.progress());
943
    _snakeDataManager.setLineDistance(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
944
                _snakeLineDistance.rawValue().toDouble());
945
    _snakeDataManager.setMinTransectLength(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
946
                _snakeMinTransectLength.rawValue().toDouble());
947
    _snakeDataManager.setTileHeight(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
948
                _snakeTileHeight.rawValue().toDouble());
949
    _snakeDataManager.setTileWidth(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
950
                _snakeTileWidth.rawValue().toDouble());
951
    _snakeDataManager.setMinTileArea(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
952
                _snakeMinTileArea.rawValue().toDouble());
953 954 955
    _setSnakeCalcInProgress(true);

    // Start worker thread.
956
    _snakeDataManager.start();
957 958
}

959 960 961 962 963 964 965 966
void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}
967

Valentin Platzgummer's avatar
Valentin Platzgummer committed
968
bool WimaController::_doTopicServiceSetup()
969
{
970
    using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996

    if ( _snakeTilesLocal.polygons().size() == 0)
        return false;

    // Publish snake origin.
    _pRosBridge->advertiseTopic("/snake/origin",
                                geographic_msgs::geo_point::messageType().c_str());
    JsonDocUPtr jOrigin(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
    bool ret = geographic_msgs::geo_point::toJson(
                _snakeOrigin, *jOrigin.get(), jOrigin->GetAllocator());
    Q_ASSERT(ret);
    (void)ret;
    _pRosBridge->publish(std::move(jOrigin), "/snake/origin");


    // Publish snake tiles.
    _pRosBridge->advertiseTopic("/snake/tiles",
                                jsk_recognition_msgs::polygon_array::messageType().c_str());
    JsonDocUPtr jSnakeTiles(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
    ret = jsk_recognition_msgs::polygon_array::toJson(
                _snakeTilesLocal, *jSnakeTiles, jSnakeTiles->GetAllocator());
    Q_ASSERT(ret);
    (void)ret;
    _pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");


997
    // Subscribe nemo progress.
998 999 1000
    _pRosBridge->subscribe("/nemo/progress",
                           /* callback */ [this](JsonDocUPtr pDoc){
        int requiredSize = this->_snakeTilesLocal.polygons().size();
1001 1002 1003
        auto& progress_msg = this->_nemoProgress;
        if ( !nemo_msgs::progress::fromJson(*pDoc, progress_msg)
             || progress_msg.progress().size() != requiredSize ) { // Some error occured.
1004
            // Set progress to default.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
            progress_msg.progress().fill(0, requiredSize);            

            // Publish snake origin.
            JsonDocUPtr jOrigin(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
            bool ret = geographic_msgs::geo_point::toJson(
                         this->_snakeOrigin, *jOrigin, jOrigin->GetAllocator());
            Q_ASSERT(ret);
            (void)ret;
            this->_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(
                         this->_snakeTilesLocal, *jSnakeTiles, jSnakeTiles->GetAllocator());
            Q_ASSERT(ret);
            (void)ret;
            this->_pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");
1022 1023 1024 1025
        }

        emit WimaController::nemoProgressChanged();
    });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1026 1027 1028


    // Subscribe /nemo/heartbeat.
1029 1030
    _pRosBridge->subscribe("/nemo/heartbeat",
                           /* callback */ [this](JsonDocUPtr pDoc){
1031
        auto &heartbeat_msg = this->_nemoHeartbeat;
1032
        if ( !nemo_msgs::heartbeat::fromJson(*pDoc, heartbeat_msg) ) {
1033
            if ( heartbeat_msg.status() == this->_fallbackStatus )
1034
                return;
1035
            heartbeat_msg.setStatus(this->_fallbackStatus);
1036 1037 1038 1039 1040 1041 1042 1043
        }

        this->_nemoTimeoutTicker.reset();
        this->_fallbackStatus = -1; /*Timeout*/
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    });

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1044 1045

    // Advertise /snake/get_origin.
1046
    _pRosBridge->advertiseService("/snake/get_origin", "snake_msgs/GetOrigin",
1047 1048
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
1049
        using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1050

1051
        JsonDocUPtr pDoc(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1052
        ::GeoPoint3D origin = this->_snakeOrigin;
1053
        rapidjson::Value jOrigin(rapidjson::kObjectType);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1054 1055 1056 1057
        bool ret = geographic_msgs::geo_point::toJson(
                    origin, jOrigin, pDoc->GetAllocator());
        Q_ASSERT(ret);
        (void)ret;
1058 1059
        pDoc->AddMember("origin", jOrigin, pDoc->GetAllocator());
        return pDoc;
1060 1061
    });

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1062 1063

    // Advertise /snake/get_tiles.
1064
    _pRosBridge->advertiseService("/snake/get_tiles", "snake_msgs/GetTiles",
1065 1066
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
1067
        JsonDocUPtr pDoc(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
1068
        rapidjson::Value jSnakeTiles(rapidjson::kObjectType);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1069
        bool ret = jsk_recognition_msgs::polygon_array::toJson(
1070
                    this->_snakeTilesLocal, jSnakeTiles, pDoc->GetAllocator());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1071 1072
        Q_ASSERT(ret);
        (void)ret;
1073 1074
        pDoc->AddMember("tiles", jSnakeTiles, pDoc->GetAllocator());
        return pDoc;
1075
    });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1076 1077

    return true;
1078
}