WimaController.cc 32.4 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "WimaController.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
2
#include "utilities.h"
3
#include "ros_bridge/include/JsonMethodes.h"
4 5 6
#include "ros_bridge/rapidjson/include/rapidjson/document.h"
#include "ros_bridge/rapidjson/include/rapidjson/writer.h"
#include "ros_bridge/rapidjson/include/rapidjson/ostreamwrapper.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
7
#include "ros_bridge/include/CasePacker.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
8

9 10 11
#include "Snake/QtROSJsonFactory.h"
#include "Snake/QtROSTypeFactory.h"
#include "Snake/QNemoProgress.h"
12
#include "Snake/QNemoHeartbeat.h"
13

14
#include "QVector3D"
15
#include <QScopedPointer>
16

17

18

19
// const char* WimaController::wimaFileExtension           = "wima";
20 21 22 23 24 25 26 27 28
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";
29
const char* WimaController::flightSpeedName             = "FlightSpeed";
30
const char* WimaController::arrivalReturnSpeedName      = "ArrivalReturnSpeed";
31
const char* WimaController::altitudeName                = "Altitude";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
32 33
const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
34
const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
35 36 37
const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";

38 39 40 41
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")};
42

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

46
WimaController::WimaController(QObject *parent)
47
    : QObject                   (parent)
48 49 50 51
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
52
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
53 54 55 56
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
57 58
    , _rtlManager               (_managerSettings, _areaInterface)
    , _currentManager           (&_defaultManager)
59 60 61
    , _managerList              {&_defaultManager, &_snakeManager, &_rtlManager}
    , _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])
71 72
    , _measurementPathLength    (-1)
    , _lowBatteryHandlingTriggered  (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
73 74 75 76 77 78
    , _snakeCalcInProgress      (false)
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
79
    , _nemoHeartbeat            (0 /*status: not connected*/)
80 81
    , _fallbackStatus           (0 /*status: not connected*/)
    , _bridgeSetupDone          (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
82
    , _pRosBridge               (new ROSBridge::ROSBridge())
83
{
84
    // Set up facts.
85 86
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
87 88
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
89
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
90 91 92
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107
    // 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);
    }
108

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
114
    // Snake Worker Thread.
115 116 117
    connect(&_snakeWorker, &SnakeWorker::finished, this, &WimaController::_snakeStoreWorkerResults);
    connect(this, &WimaController::nemoProgressChanged, this, &WimaController::_initStartSnakeWorker);
    connect(this, &QObject::destroyed, &this->_snakeWorker, &SnakeWorker::quit);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
118

119
    // Snake.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
120 121
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_startStopRosBridge);
    _startStopRosBridge();
122 123 124 125
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_initStartSnakeWorker);
    _initStartSnakeWorker();
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_switchSnakeManager);
    _switchSnakeManager(_enableSnake.rawValue());
126 127
}

128 129 130 131 132 133 134 135 136
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

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

QmlObjectListModel *WimaController::visualItems() {
137 138 139 140
    return &_areas;
}

QmlObjectListModel *WimaController::missionItems() {
141
    return const_cast<QmlObjectListModel*>(&_currentManager->missionItems());
142 143 144
}

QmlObjectListModel *WimaController::currentMissionItems() {
145
    return const_cast<QmlObjectListModel*>(&_currentManager->currentMissionItems());
146 147 148 149
}

QVariantList WimaController::waypointPath()
{
150
    return const_cast<QVariantList&>(_currentManager->waypointsVariant());
151 152
}

153
QVariantList WimaController::currentWaypointPath()
154
{
155
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
156 157
}

158 159 160 161 162 163 164 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
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;
}

194 195 196 197 198 199 200
QVector<int> WimaController::nemoProgress() {
    if ( _nemoProgress.progress().size() == _snakeTileCenterPoints.size() )
        return _nemoProgress.progress();
    else
        return QVector<int>(_snakeTileCenterPoints.size(), 0);
}

201 202
double WimaController::phaseDistance() const
{
203
    return 0.0;
204 205 206 207
}

double WimaController::phaseDuration() const
{
208
    return 0.0;
209 210
}

211 212 213 214 215 216
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

QString WimaController::nemoStatusString() const
Valentin Platzgummer's avatar
Valentin Platzgummer committed
217
{
218
    return _nemoStatusMap.at(_nemoHeartbeat.status());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
219 220 221 222 223 224 225
}

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

226 227 228
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
229
    _managerSettings.setMasterController(masterC);
230 231 232 233 234 235
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
236
    _managerSettings.setMissionController(missionC);
237 238 239
    emit missionControllerChanged();
}

240 241
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
242
    _calcNextPhase();
243 244
}

245
void WimaController::previousPhase()
246
{        
247 248
    if ( !_currentManager->previous() ) {
        Q_ASSERT(false);
249
    }
250 251 252 253 254

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
255 256 257 258
}

void WimaController::resetPhase()
{
259 260
    if ( !_currentManager->reset() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
261
    }
262 263 264 265 266

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
267 268
}

269 270
void WimaController::requestSmartRTL()
{
271 272 273 274 275
    QString errorString("Smart RTL requested. ");
    if ( !_checkSmartRTLPreCondition(errorString) ){
        qgcApp()->showMessage(errorString);
        return;
    }
276 277 278 279
    emit smartRTLRequestConfirm();
}

bool WimaController::upload()
280
{
281
    auto &currentMissionItems = _defaultManager.currentMissionItems();
282
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
283
        && currentMissionItems.count() > 0) {
284
        emit forceUploadConfirm();
285 286 287
        return false;
    }

288
    return forceUpload();
289 290
}

291
bool WimaController::forceUpload()
292
{
293 294
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (currentMissionItems.count() < 1)
295
        return false;
296 297

    _missionController->removeAll();
298
    // Set homeposition of settingsItem.
299 300 301
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
302
        Q_ASSERT(false);
303
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
304
        return false;
305
    }
306
    settingsItem->setCoordinate(_managerSettings.homePosition());
307

308
    // Copy mission items to _missionController.
309
    for (int i = 1; i < currentMissionItems.count(); i++){
310
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
311 312 313
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

314
    _masterController->sendToVehicle();
315 316

    return true;
317
}
318

319 320 321
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
322 323 324
    _missionController->removeAll();
}

325
void WimaController::executeSmartRTL()
326
{
327 328
    forceUpload();
    masterController()->managerVehicle()->startMission();
329 330
}

331
void WimaController::initSmartRTL()
332
{
333
    _initSmartRTL();
334 335
}

336 337 338 339 340 341
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

342
bool WimaController::_calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
343 344 345
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
346 347 348 349 350
    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
351
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
352 353 354

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
355 356 357 358

    return  retVal;
}

359
bool WimaController::setWimaPlanData(const WimaPlanData &planData)
360
{
361
    // reset visual items
362 363
    _areas.clear();
    _defaultManager.clear();
364 365
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
366
    _snakeTileCenterPoints.clear();
367

368 369 370
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
371
    emit waypointPathChanged();
372
    emit currentWaypointPathChanged();
373
    emit snakeTilesChanged();
374
    emit snakeTileCenterPointsChanged();
375

376
    _localPlanDataValid = false;
377

378 379
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
380

381
    int areaCounter = 0;
382
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
383 384
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
385

386 387 388
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
389
            _areas.append(&_serviceArea);
390

391 392
            continue;
        }
393

394 395 396
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
397
            _areas.append(&_measurementArea);
398

399
            continue;
400
        }
401

402 403 404 405
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
406

407 408
            continue;
        }
409

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

415 416
            continue;
        }
417

418 419 420
        if (areaCounter >= numAreas)
            break;
    }
421

422
    if (areaCounter != numAreas) {
423
        Q_ASSERT(false);
424 425
        return false;
    }
426

427 428
    emit visualItemsChanged();

429 430 431
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
432
        qWarning("WimaController: Mission items from WimaPlaner empty!");
433
        return false;
434
    }
435

436
    for (auto item : tempMissionItems) {
437
        _defaultManager.push_back(item.coordinate());
438
    }
439

440 441 442
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
443

444
    if( !_defaultManager.reset() ){
445
        Q_ASSERT(false);
446
        return false;
447
    }
448

449 450 451 452 453 454 455
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



Valentin Platzgummer's avatar
Valentin Platzgummer committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    // 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;
477

Valentin Platzgummer's avatar
Valentin Platzgummer committed
478 479 480 481
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

482
    // Check if scenario is defined.
483 484
    if ( !_isScenarioDefinedErrorMessage() ) {
        Q_ASSERT(false);
485
        return false;
486
    }
487

488
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
489 490 491 492 493
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
494 495
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
496
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
497 498 499 500 501 502 503 504 505 506 507
            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));
508
        }
509 510 511 512 513
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
514
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
515 516 517 518
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
519
                Tile.path().append(QVertex);
520 521 522
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
523
    }
524 525
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
526

527 528 529
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
530

531 532 533 534 535 536 537 538 539 540 541
WimaController *WimaController::thisPointer()
{
    return this;
}

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

543
    emit missionItemsChanged();
544 545
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
546
    emit waypointPathChanged();
547

548
    return true;
549 550
}

551
bool WimaController::_setStartIndex()
552
{
553
    bool value;
554
    _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
555 556 557 558 559
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
560 561
        return false;
    }
562

563
    emit missionItemsChanged();
564
    emit currentMissionItemsChanged();
565 566
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
567 568

    return true;
569
}
570

571
void WimaController::_recalcCurrentPhase()
572
{
573 574
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
575
    }
576

577 578 579
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
580 581
    emit waypointPathChanged();
}
582 583

void WimaController::_updateOverlap()
584
{
585
    bool value;
586
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
587 588
    Q_ASSERT(value);
    (void)value;
589

590
    if ( !_currentManager->update() ) {
591 592 593 594 595
        assert(false);
    }

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

600
void WimaController::_updateMaxWaypoints()
601
{
602
    bool value;
603
    _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
604 605
    Q_ASSERT(value);
    (void)value;
606

607 608
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
609
    }
610

611 612 613 614
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
615 616
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
617
void WimaController::_updateflightSpeed()
618
{
619
    bool value;
620
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
621 622 623 624 625 626
    Q_ASSERT(value);
    (void)value;

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
634
void WimaController::_updateArrivalReturnSpeed()
635
{
636
    bool value;
637
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble(&value));
638 639 640 641 642 643
    Q_ASSERT(value);
    (void)value;

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
651
void WimaController::_updateAltitude()
652
{
653
    bool value;
654
    _managerSettings.setAltitude(_altitude.rawValue().toDouble(&value));
655 656 657 658 659 660
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
661 662 663 664 665

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
666 667
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
668
void WimaController::_checkBatteryLevel()
669
{
670 671 672 673 674
    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();
675

Valentin Platzgummer's avatar
Valentin Platzgummer committed
676
    if (managerVehicle != nullptr && enabled == true) {
677 678 679 680 681 682
        Fact *battery1percentRemaining = managerVehicle->battery1FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);
        Fact *battery2percentRemaining = managerVehicle->battery2FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);


        if (battery1percentRemaining->rawValue().toDouble() < batteryThreshold
                && battery2percentRemaining->rawValue().toDouble() < batteryThreshold) {
683 684 685
            if (!_lowBatteryHandlingTriggered) {                
                _lowBatteryHandlingTriggered = true;
                if ( !(_missionController->remainingTime() <= minTime) ) {
686
                    requestSmartRTL();
687 688 689 690 691 692 693 694 695 696
                }
            }
        }
        else {
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
697 698 699
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
700
    static EventTicker snakeTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
701
    static EventTicker nemoStatusTicker(EVENT_TIMER_INTERVAL, 5000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
702 703

    // Battery level check necessary?
704 705
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
    if ( enableLowBatteryHandling->rawValue().toBool() && batteryLevelTicker.ready() )
Valentin Platzgummer's avatar
Valentin Platzgummer committed
706 707 708
        _checkBatteryLevel();

    // Snake flight plan update necessary?
709 710 711 712
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
713

714 715 716 717 718 719
    if ( nemoStatusTicker.ready() ) {
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }

720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
    if ( snakeTicker.ready() ) {
        if ( _enableSnake.rawValue().toBool()
             && _pRosBridge->connected() ) {
            if ( !_bridgeSetupDone ) {
                qWarning() << "ROS Bridge setup. ";
                auto start = std::chrono::high_resolution_clock::now();
                _pRosBridge->start();
                auto end = std::chrono::high_resolution_clock::now();
                qWarning() << "_pRosBridge->start() time: "
                           << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                           << " ms";
                start = std::chrono::high_resolution_clock::now();
                _pRosBridge->publish(_snakeOrigin, "/snake/origin");
                end = std::chrono::high_resolution_clock::now();
                qWarning() << "_pRosBridge->publish(_snakeOrigin, \"/snake/origin\") time: "
                           << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                           << " ms";
                start = std::chrono::high_resolution_clock::now();
                _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
                end = std::chrono::high_resolution_clock::now();
                qWarning() << "_pRosBridge->publish(_snakeTilesLocal, \"/snake/tiles\") time: "
                           << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                           << " ms";


                start = std::chrono::high_resolution_clock::now();
                _pRosBridge->subscribe("/nemo/progress",
                                       /* callback */ [this](JsonDocUPtr pDoc){
                    int requiredSize = this->_snakeTilesLocal.polygons().size();
                    auto& progress = this->_nemoProgress;
                    if ( !this->_pRosBridge->casePacker()->unpack(pDoc, progress)
                         || progress.progress().size() != requiredSize ) {
                        progress.progress().fill(0, requiredSize);
                    }

                    emit WimaController::nemoProgressChanged();
                });
                end = std::chrono::high_resolution_clock::now();
                qWarning() << "_pRosBridge->subscribe(\"/nemo/progress\" time: "
                           << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                           << " ms";

                start = std::chrono::high_resolution_clock::now();
                _pRosBridge->subscribe("/nemo/heartbeat",
                                       /* callback */ [this, &nemoStatusTicker](JsonDocUPtr pDoc){
                    if ( !this->_pRosBridge->casePacker()->unpack(pDoc, this->_nemoHeartbeat) ) {
                        if ( this->_nemoHeartbeat.status() == this->_fallbackStatus )
                            return;
                        this->_nemoHeartbeat.setStatus(this->_fallbackStatus);
                    }

                    nemoStatusTicker.reset();
                    this->_fallbackStatus = -1; /*Timeout*/
                    emit WimaController::nemoStatusChanged();
                    emit WimaController::nemoStatusStringChanged();
                });
                end = std::chrono::high_resolution_clock::now();
                qWarning() << "_pRosBridge->subscribe(\"/nemo/heartbeat\" time: "
                           << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                           << " ms";
                _bridgeSetupDone = true;
            }
        } else if ( _bridgeSetupDone) {
            _pRosBridge->reset();
            _bridgeSetupDone = false;
        }
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;

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()
{
895
    _setSnakeCalcInProgress(false);
896
    auto start = std::chrono::high_resolution_clock::now();
897 898
    _snakeManager.clear();

899
    const WorkerResult_t &r = _snakeWorker.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 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
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
930 931
void WimaController::_startStopRosBridge()
{
932
    if ( _enableSnake.rawValue().toBool() ) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
933 934
        _pRosBridge->start();
    } else {
935
        _pRosBridge->reset();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
936 937 938
    }
}

939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

    // Stop worker thread if running.
    if ( _snakeWorker.isRunning() ) {
        _snakeWorker.quit();
    }

    // Initialize _snakeWorker.
    _snakeWorker.setScenario(_scenario);
    _snakeWorker.setProgress(_nemoProgress.progress());
    _snakeWorker.setLineDistance(_snakeLineDistance.rawValue().toDouble());
    _snakeWorker.setMinTransectLength(_snakeMinTransectLength.rawValue().toDouble());
    _setSnakeCalcInProgress(true);

    // Start worker thread.
    _snakeWorker.start();
}

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