WimaController.cc 35.4 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;
}

203 204 205 206 207 208 209
QVector<int> WimaController::nemoProgress() {
    if ( _nemoProgress.progress().size() == _snakeTileCenterPoints.size() )
        return _nemoProgress.progress();
    else
        return QVector<int>(_snakeTileCenterPoints.size(), 0);
}

210 211
double WimaController::phaseDistance() const
{
212
    return 0.0;
213 214 215 216
}

double WimaController::phaseDuration() const
{
217
    return 0.0;
218 219
}

220 221 222 223 224 225
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

QString WimaController::nemoStatusString() const
Valentin Platzgummer's avatar
Valentin Platzgummer committed
226
{
227
    return _nemoStatusMap.at(_nemoHeartbeat.status());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
228 229 230 231 232 233 234
}

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

235 236 237
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
238
    _managerSettings.setMasterController(masterC);
239 240 241 242 243 244
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
245
    _managerSettings.setMissionController(missionC);
246 247 248
    emit missionControllerChanged();
}

249 250
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
251
    _calcNextPhase();
252 253
}

254
void WimaController::previousPhase()
255
{        
256 257
    if ( !_currentManager->previous() ) {
        Q_ASSERT(false);
258
    }
259 260 261 262 263

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
264 265 266 267
}

void WimaController::resetPhase()
{
268 269
    if ( !_currentManager->reset() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
270
    }
271 272 273 274 275

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

278 279
void WimaController::requestSmartRTL()
{
280 281 282 283 284
    QString errorString("Smart RTL requested. ");
    if ( !_checkSmartRTLPreCondition(errorString) ){
        qgcApp()->showMessage(errorString);
        return;
    }
285 286 287 288
    emit smartRTLRequestConfirm();
}

bool WimaController::upload()
289
{
290
    auto &currentMissionItems = _defaultManager.currentMissionItems();
291
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
292
        && currentMissionItems.count() > 0) {
293
        emit forceUploadConfirm();
294 295 296
        return false;
    }

297
    return forceUpload();
298 299
}

300
bool WimaController::forceUpload()
301
{
302 303
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (currentMissionItems.count() < 1)
304
        return false;
305 306

    _missionController->removeAll();
307
    // Set homeposition of settingsItem.
308 309 310
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
311
        Q_ASSERT(false);
312
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
313
        return false;
314
    }
315
    settingsItem->setCoordinate(_managerSettings.homePosition());
316

317
    // Copy mission items to _missionController.
318
    for (int i = 1; i < currentMissionItems.count(); i++){
319
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
320 321 322
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

323
    _masterController->sendToVehicle();
324 325

    return true;
326
}
327

328 329 330
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
331 332 333
    _missionController->removeAll();
}

334
void WimaController::executeSmartRTL()
335
{
336 337
    forceUpload();
    masterController()->managerVehicle()->startMission();
338 339
}

340
void WimaController::initSmartRTL()
341
{
342
    _initSmartRTL();
343 344
}

345 346 347 348 349 350
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
351 352 353
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
                                       QVector<QGeoCoordinate> &path)
354 355 356
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
357 358 359 360 361
    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
362
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
363 364 365

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
366 367 368 369

    return  retVal;
}

370
bool WimaController::setWimaPlanData(const WimaPlanData &planData)
371
{
372
    // reset visual items
373 374
    _areas.clear();
    _defaultManager.clear();
375
    _snakeManager.clear();
376 377
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
378
    _snakeTileCenterPoints.clear();
379

380 381 382
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
383
    emit waypointPathChanged();
384
    emit currentWaypointPathChanged();
385
    emit snakeTilesChanged();
386
    emit snakeTileCenterPointsChanged();
387

388
    _localPlanDataValid = false;
389

390 391
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
392

393
    int areaCounter = 0;
394
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
395 396
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
397

398 399 400
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
401
            _areas.append(&_serviceArea);
402

403 404
            continue;
        }
405

406 407 408
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
409
            _areas.append(&_measurementArea);
410

411
            continue;
412
        }
413

414 415 416 417
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
418

419 420
            continue;
        }
421

422 423 424
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
425
            _areas.append(&_joinedArea);
426

427 428
            continue;
        }
429

430 431 432
        if (areaCounter >= numAreas)
            break;
    }
433

434
    if (areaCounter != numAreas) {
435
        Q_ASSERT(false);
436 437
        return false;
    }
438

439 440
    emit visualItemsChanged();

441 442 443
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
444
        qWarning("WimaController: Mission items from WimaPlaner empty!");
445
        return false;
446
    }
447

448
    for (auto item : tempMissionItems) {
449
        _defaultManager.push_back(item.coordinate());
450
    }
451

452 453 454
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
455

456
    if( !_defaultManager.reset() ){
457
        Q_ASSERT(false);
458
        return false;
459
    }
460

461 462 463 464 465 466 467
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



Valentin Platzgummer's avatar
Valentin Platzgummer committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
    // 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;
489

Valentin Platzgummer's avatar
Valentin Platzgummer committed
490 491 492 493
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

494
    // Check if scenario is defined.
495 496 497
    if ( !_scenario.defined(_snakeTileWidth.rawValue().toUInt(),
                            _snakeTileHeight.rawValue().toUInt(),
                            _snakeMinTileArea.rawValue().toUInt()) ) {
498
        Q_ASSERT(false);
499
        return false;
500
    }
501

502
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
503 504 505 506 507
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
508 509
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
510
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
511 512 513 514 515 516 517 518 519 520 521
            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));
522
        }
523 524 525 526 527
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
528
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
529 530 531 532
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
533
                Tile.path().append(QVertex);
534 535 536
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
537
    }
538 539
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
540

541
    if ( _enableSnake.rawValue().toBool()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
         && _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");

562 563
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
564

565 566 567
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
568

569 570 571 572 573 574 575 576 577 578 579
WimaController *WimaController::thisPointer()
{
    return this;
}

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

581
    emit missionItemsChanged();
582 583
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
584
    emit waypointPathChanged();
585

586
    return true;
587 588
}

589
bool WimaController::_setStartIndex()
590
{
591
    bool value;
592
    _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
593 594 595 596 597
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
598 599
        return false;
    }
600

601
    emit missionItemsChanged();
602
    emit currentMissionItemsChanged();
603 604
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
605 606

    return true;
607
}
608

609
void WimaController::_recalcCurrentPhase()
610
{
611 612
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
613
    }
614

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

void WimaController::_updateOverlap()
622
{
623
    bool value;
624
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
625 626
    Q_ASSERT(value);
    (void)value;
627

628
    if ( !_currentManager->update() ) {
629 630 631 632 633
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
634
    emit currentWaypointPathChanged();
635
    emit waypointPathChanged();
636
}
637

638
void WimaController::_updateMaxWaypoints()
639
{
640
    bool value;
641
    _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
642 643
    Q_ASSERT(value);
    (void)value;
644

645 646
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
647
    }
648

649 650 651 652
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
653 654
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
655
void WimaController::_updateflightSpeed()
656
{
657
    bool value;
658
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
659 660 661 662 663 664
    Q_ASSERT(value);
    (void)value;

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

666 667 668 669
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
670 671
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
672
void WimaController::_updateArrivalReturnSpeed()
673
{
674
    bool value;
675
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble(&value));
676 677 678 679 680 681
    Q_ASSERT(value);
    (void)value;

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

683 684 685 686
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
687 688
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
689
void WimaController::_updateAltitude()
690
{
691
    bool value;
692
    _managerSettings.setAltitude(_altitude.rawValue().toDouble(&value));
693 694 695 696 697 698
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
699 700 701 702 703

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
704 705
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
706
void WimaController::_checkBatteryLevel()
707
{
708 709 710 711 712
    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();
713

Valentin Platzgummer's avatar
Valentin Platzgummer committed
714
    if (managerVehicle != nullptr && enabled == true) {
715 716 717 718 719 720
        Fact *battery1percentRemaining = managerVehicle->battery1FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);
        Fact *battery2percentRemaining = managerVehicle->battery2FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);


        if (battery1percentRemaining->rawValue().toDouble() < batteryThreshold
                && battery2percentRemaining->rawValue().toDouble() < batteryThreshold) {
721 722 723
            if (!_lowBatteryHandlingTriggered) {                
                _lowBatteryHandlingTriggered = true;
                if ( !(_missionController->remainingTime() <= minTime) ) {
724
                    requestSmartRTL();
725 726 727 728 729 730 731 732 733 734
                }
            }
        }
        else {
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
735 736 737
void WimaController::_eventTimerHandler()
{
    // Battery level check necessary?
738
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
739
    if ( enableLowBatteryHandling->rawValue().toBool() && _batteryLevelTicker.ready() )
Valentin Platzgummer's avatar
Valentin Platzgummer committed
740 741 742
        _checkBatteryLevel();

    // Snake flight plan update necessary?
743 744 745 746
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
747

748
    if ( _nemoTimeoutTicker.ready() && _enableSnake.rawValue().toBool() ) {
749 750 751 752 753
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }

754 755
    if ( _snakeTicker.ready() ) {
        if ( _enableSnake.rawValue().toBool() ) {
756
            if ( !_pRosBridge->isRunning()) {
757
                _pRosBridge->start();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
758 759 760 761 762 763 764 765
            } else if ( _pRosBridge->isRunning()
                        && _pRosBridge->connected()
                        && !_topicServiceSetupDone) {
                if ( _doTopicServiceSetup() )
                    _topicServiceSetupDone = true;
            } else if ( _pRosBridge->isRunning()
                        && !_pRosBridge->connected()
                        && _topicServiceSetupDone){
766 767
                _pRosBridge->reset();
                _pRosBridge->start();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
768
                _topicServiceSetupDone = false;
769
            }
770
        } else  if ( _pRosBridge->isRunning() ) {
771
                _pRosBridge->reset();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
772
                _topicServiceSetupDone = false;
773
        }
774
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
775 776 777
}

void WimaController::_smartRTLCleanUp(bool flying)
778
{
779 780 781 782 783
    if ( !flying) { // vehicle has landed
        _switchWaypointManager(_defaultManager);
        _missionController->removeAllFromVehicle();
        _missionController->removeAll();
        disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
784 785 786
    }
}

787 788
void WimaController::_setPhaseDistance(double distance)
{
789 790 791
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
792

793 794
//        emit phaseDistanceChanged();
//    }
795 796 797 798
}

void WimaController::_setPhaseDuration(double duration)
{
799 800 801
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
802

803 804
//        emit phaseDurationChanged();
//    }
805 806
}

807
bool WimaController::_checkSmartRTLPreCondition(QString &errorString)
808 809
{
    if (!_localPlanDataValid) {
810 811
        errorString.append(tr("No WiMA data available. Please define at least a measurement and a service area."));
        return false;
812
    }
813

814
    return _rtlManager.checkPrecondition(errorString);
815 816
}

817
void WimaController::_switchWaypointManager(WaypointManager::ManagerBase &manager)
818
{
819 820 821
    if (_currentManager != &manager) {
        _currentManager = &manager;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
        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);
839

840 841 842 843
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
844

845
        qWarning() << "WimaController::_switchWaypointManager: statistics update missing.";
846 847 848
    }
}

849 850
void WimaController::_initSmartRTL()
{
851 852 853 854
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

855
    if ( _checkSmartRTLPreCondition(errorString) ) {
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870
        _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);
    }
871 872
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
873 874 875 876 877 878 879 880
void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

881 882
void WimaController::_snakeStoreWorkerResults()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
883
    _setSnakeCalcInProgress(false);
884
    auto start = std::chrono::high_resolution_clock::now();
885 886
    _snakeManager.clear();

887
    const auto &r = _snakeDataManager.getResult();
888
    if (!r.success) {
889
        //qgcApp()->showMessage(r.errorMessage);
890 891 892 893 894
        return;
    }

    // create Mission items from r.waypoints
    long n = r.waypoints.size() - r.returnPathIdx.size() - r.arrivalPathIdx.size() + 2;
895
    if (n < 1) {
896 897
        return;
    }
898 899 900

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

906
    _snakeManager.update();
907

908
    emit missionItemsChanged();
909 910 911
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
912 913 914 915

    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
916 917
}

918 919 920 921 922 923
void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

    // Stop worker thread if running.
924 925
    if ( _snakeDataManager.isRunning() ) {
        _snakeDataManager.quit();
926 927 928
    }

    // Initialize _snakeWorker.
929
    _snakeDataManager.setProgress(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
930
                _nemoProgress.progress());
931
    _snakeDataManager.setLineDistance(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
932
                _snakeLineDistance.rawValue().toDouble());
933
    _snakeDataManager.setMinTransectLength(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
934
                _snakeMinTransectLength.rawValue().toDouble());
935
    _snakeDataManager.setTileHeight(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
936
                _snakeTileHeight.rawValue().toDouble());
937
    _snakeDataManager.setTileWidth(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
938
                _snakeTileWidth.rawValue().toDouble());
939
    _snakeDataManager.setMinTileArea(
Valentin Platzgummer's avatar
Valentin Platzgummer committed
940
                _snakeMinTileArea.rawValue().toDouble());
941 942 943
    _setSnakeCalcInProgress(true);

    // Start worker thread.
944
    _snakeDataManager.start();
945 946
}

947 948 949 950 951 952 953 954
void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}
955

Valentin Platzgummer's avatar
Valentin Platzgummer committed
956
bool WimaController::_doTopicServiceSetup()
957
{
958
    using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984

    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");


985
    // Subscribe nemo progress.
986 987 988
    _pRosBridge->subscribe("/nemo/progress",
                           /* callback */ [this](JsonDocUPtr pDoc){
        int requiredSize = this->_snakeTilesLocal.polygons().size();
989 990 991
        auto& progress_msg = this->_nemoProgress;
        if ( !nemo_msgs::progress::fromJson(*pDoc, progress_msg)
             || progress_msg.progress().size() != requiredSize ) { // Some error occured.
992
            // Set progress to default.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
            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");
1010 1011 1012 1013
        }

        emit WimaController::nemoProgressChanged();
    });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1014 1015 1016


    // Subscribe /nemo/heartbeat.
1017 1018
    _pRosBridge->subscribe("/nemo/heartbeat",
                           /* callback */ [this](JsonDocUPtr pDoc){
1019
        auto &heartbeat_msg = this->_nemoHeartbeat;
1020
        if ( !nemo_msgs::heartbeat::fromJson(*pDoc, heartbeat_msg) ) {
1021
            if ( heartbeat_msg.status() == this->_fallbackStatus )
1022
                return;
1023
            heartbeat_msg.setStatus(this->_fallbackStatus);
1024 1025 1026 1027 1028 1029 1030 1031
        }

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1032 1033

    // Advertise /snake/get_origin.
1034
    _pRosBridge->advertiseService("/snake/get_origin", "snake_msgs/GetOrigin",
1035 1036
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
1037
        using namespace ros_bridge::messages;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1038

1039
        JsonDocUPtr pDoc(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1040
        ::GeoPoint3D origin = this->_snakeOrigin;
1041
        rapidjson::Value jOrigin(rapidjson::kObjectType);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1042 1043 1044 1045
        bool ret = geographic_msgs::geo_point::toJson(
                    origin, jOrigin, pDoc->GetAllocator());
        Q_ASSERT(ret);
        (void)ret;
1046 1047
        pDoc->AddMember("origin", jOrigin, pDoc->GetAllocator());
        return pDoc;
1048 1049
    });

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1050 1051

    // Advertise /snake/get_tiles.
1052
    _pRosBridge->advertiseService("/snake/get_tiles", "snake_msgs/GetTiles",
1053 1054
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
1055
        JsonDocUPtr pDoc(std::make_unique<rapidjson::Document>(rapidjson::kObjectType));
1056
        rapidjson::Value jSnakeTiles(rapidjson::kObjectType);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1057
        bool ret = jsk_recognition_msgs::polygon_array::toJson(
1058
                    this->_snakeTilesLocal, jSnakeTiles, pDoc->GetAllocator());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1059 1060
        Q_ASSERT(ret);
        (void)ret;
1061 1062
        pDoc->AddMember("tiles", jSnakeTiles, pDoc->GetAllocator());
        return pDoc;
1063
    });
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1064 1065

    return true;
1066
}