WimaController.cc 34.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";

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)
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*/)
92 93 94 95 96 97 98
{    

    // ROS Bridge.
    WimaSettings* wimaSettings  = qgcApp()->toolbox()->settingsManager()->wimaSettings();
    auto connectionStringFact       = wimaSettings->rosbridgeConnectionString();
    auto setConnectionString = [connectionStringFact, this]{
        auto connectionString = connectionStringFact->rawValue().toString();
99 100
        if ( ros_bridge::isValidConnectionString(connectionString.toLocal8Bit().data()) ){
            this->_pRosBridge.reset(new ros_bridge::ROSBridge(connectionString.toLocal8Bit().data()));
101 102
        } else {
            qgcApp()->showMessage("ROS Bridge connection string invalid: " + connectionString);
103
            this->_pRosBridge.reset(new ros_bridge::ROSBridge("localhost:9090"));
104 105 106 107 108
        }
    };
    setConnectionString();
    connect(wimaSettings->rosbridgeConnectionString(), &SettingsFact::rawValueChanged, setConnectionString);

109
    // Set up facts.
110 111
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
112 113
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
114
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
115 116 117
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132
    // 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);
    }
133

134
    // Periodic.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
135
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
136 137
    //_eventTimer.setInterval(EVENT_TIMER_INTERVAL);
    _eventTimer.start(EVENT_TIMER_INTERVAL);
138

Valentin Platzgummer's avatar
Valentin Platzgummer committed
139
    // Snake Worker Thread.
140 141 142
    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
143

144
    // Snake.
145 146 147 148
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_initStartSnakeWorker);
    _initStartSnakeWorker();
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_switchSnakeManager);
    _switchSnakeManager(_enableSnake.rawValue());
149 150
}

151 152 153 154 155 156 157 158 159
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

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

QmlObjectListModel *WimaController::visualItems() {
160 161 162 163
    return &_areas;
}

QmlObjectListModel *WimaController::missionItems() {
164
    return const_cast<QmlObjectListModel*>(&_currentManager->missionItems());
165 166 167
}

QmlObjectListModel *WimaController::currentMissionItems() {
168
    return const_cast<QmlObjectListModel*>(&_currentManager->currentMissionItems());
169 170 171 172
}

QVariantList WimaController::waypointPath()
{
173
    return const_cast<QVariantList&>(_currentManager->waypointsVariant());
174 175
}

176
QVariantList WimaController::currentWaypointPath()
177
{
178
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
179 180
}

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
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;
}

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

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

double WimaController::phaseDuration() const
{
231
    return 0.0;
232 233
}

234 235 236 237 238 239
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

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

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

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

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

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

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

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

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

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

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

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

311
    return forceUpload();
312 313
}

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

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

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

337
    _masterController->sendToVehicle();
338 339

    return true;
340
}
341

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

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

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

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

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 554 555 556 557 558 559 560 561
    if ( _enableSnake.rawValue().toBool()
            && _pRosBridge->isRunning()
            && _pRosBridge->connected() ){
        if ( _snakeTilesLocal.polygons().size() > 0 ){
            _pRosBridge->publish(_snakeOrigin, "/snake/origin");
            _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
        }
    }

562 563 564
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
565

566 567 568 569 570 571 572 573 574 575 576
WimaController *WimaController::thisPointer()
{
    return this;
}

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

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

583
    return true;
584 585
}

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

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
595 596
        return false;
    }
597

598
    emit missionItemsChanged();
599
    emit currentMissionItemsChanged();
600 601
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
602 603

    return true;
604
}
605

606
void WimaController::_recalcCurrentPhase()
607
{
608 609
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
610
    }
611

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

void WimaController::_updateOverlap()
619
{
620
    bool value;
621
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
622 623
    Q_ASSERT(value);
    (void)value;
624

625
    if ( !_currentManager->update() ) {
626 627 628 629 630
        assert(false);
    }

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

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

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

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

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

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

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

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

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

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

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

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
696 697 698 699 700

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
701 702
}

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

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


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

    }
}

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

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

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

751
    if ( _snakeTicker.ready() ) {
752
        static bool setupDone = false;
753
        if ( _enableSnake.rawValue().toBool() ) {
754 755 756 757 758
            if ( !_pRosBridge->isRunning()) {
                _pRosBridge->start();
            } else if ( _pRosBridge->isRunning() && _pRosBridge->connected() && !setupDone) {
                _pRosBridge->reset();
                _pRosBridge->start();
759
                _setupTopicService();
760
                setupDone = true;
761
            } else if ( _pRosBridge->isRunning() && !_pRosBridge->connected() ){
762
                setupDone = false;
763
            }
764
        } else  if ( _pRosBridge->isRunning() ) {
765
                _pRosBridge->reset();
766
                setupDone = false;
767
        }
768
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
769 770 771
}

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

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

787 788
//        emit phaseDistanceChanged();
//    }
789 790 791 792
}

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

797 798
//        emit phaseDurationChanged();
//    }
799 800
}

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

808
    return _rtlManager.checkPrecondition(errorString);
809 810
}

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

816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
        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);
833

834 835 836 837
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
838

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

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

849
    if ( _checkSmartRTLPreCondition(errorString) ) {
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
        _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);
    }
865 866
}

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

875 876
void WimaController::_snakeStoreWorkerResults()
{
877
    _setSnakeCalcInProgress(false);
878
    auto start = std::chrono::high_resolution_clock::now();
879 880
    _snakeManager.clear();

881
    const WorkerResult_t &r = _snakeWorker.getResult();
882
    if (!r.success) {
883
        //qgcApp()->showMessage(r.errorMessage);
884 885 886 887 888
        return;
    }

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

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
895 896
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
897
        _snakeManager.push_back(r.waypoints[int(i)].value<QGeoCoordinate>());
898 899
    }

900
    _snakeManager.update();
901

902
    emit missionItemsChanged();
903 904 905
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
906 907 908 909

    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
910 911
}

912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
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();
}

933 934 935 936 937 938 939 940
void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}
941 942 943

void WimaController::_setupTopicService()
{
944
    using namespace ros_bridge::messages;
945
    if ( _snakeTilesLocal.polygons().size() > 0){
946 947 948 949 950 951 952 953 954 955
        // Publish snake origin.
        JsonDocUPtr jOrigin(rapidjson::kObjectType);
        Q_ASSERT(geographic_msgs::geo_point::toJson(
                     _snakeOrigin, *jOrigin, jOrigin->GetAllocator()));
        _pRosBridge->publish(std::move(jOrigin), "/snake/origin");
        // Publish snake tiles.
        JsonDocUPtr jSnakeTiles(rapidjson::kObjectType);
        Q_ASSERT(jsk_recognition_msgs::polygon_array::toJson(
                     _snakeTilesLocal, *jSnakeTiles, jSnakeTiles->GetAllocator()));
        _pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");
956
    }
957
    // Subscribe nemo progress.
958 959 960
    _pRosBridge->subscribe("/nemo/progress",
                           /* callback */ [this](JsonDocUPtr pDoc){
        int requiredSize = this->_snakeTilesLocal.polygons().size();
961 962 963
        auto& progress_msg = this->_nemoProgress;
        if ( !nemo_msgs::progress::fromJson(*pDoc, progress_msg)
             || progress_msg.progress().size() != requiredSize ) { // Some error occured.
964
            // Set progress to default.
965 966
            progress_msg.progress().fill(0, requiredSize);
            // Publish origin and tiles again, if valid.
967
            if ( this->_snakeTilesLocal.polygons().size() > 0){
968 969 970 971 972 973 974 975 976 977
                // Publish snake origin.
                JsonDocUPtr jOrigin(rapidjson::kObjectType);
                Q_ASSERT(geographic_msgs::geo_point::toJson(
                             this->_snakeOrigin, *jOrigin, jOrigin->GetAllocator()));
                this->_pRosBridge->publish(std::move(jOrigin), "/snake/origin");
                // Publish snake tiles.
                JsonDocUPtr jSnakeTiles(rapidjson::kObjectType);
                Q_ASSERT(jsk_recognition_msgs::polygon_array::toJson(
                             this->_snakeTilesLocal, *jSnakeTiles, jSnakeTiles->GetAllocator()));
                this->_pRosBridge->publish(std::move(jSnakeTiles), "/snake/tiles");
978
            }
979 980 981 982 983 984
        }

        emit WimaController::nemoProgressChanged();
    });
    _pRosBridge->subscribe("/nemo/heartbeat",
                           /* callback */ [this](JsonDocUPtr pDoc){
985 986 987
        auto &heartbeat_msg = this->_nemoHeartbeat;
        if ( !nemo_msgs::heartbeat(*pDoc, heartbeat_msg) ) {
            if ( heartbeat_msg.status() == this->_fallbackStatus )
988
                return;
989
            heartbeat_msg.setStatus(this->_fallbackStatus);
990 991 992 993 994 995 996 997 998
        }

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

    _pRosBridge->advertiseService("/snake/get_origin", "snake_msgs/GetOrigin",
999 1000 1001 1002
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
        JsonDocUPtr pDoc(std::make_unique(rapidjson::kObjectType));
        rapidjson::Value jOrigin(rapidjson::kObjectType);
1003
        if ( this->_snakeTilesLocal.polygons().size() > 0){
1004 1005
            geometry_msg::geo_point::toJson(
                        this->_snakeOrigin, jOrigin, pDoc->GetAllocator());
1006
        } else {
1007 1008
            geometry_msg::geo_point::polygon_array::toJson(
                        ::GeoPoint3D(0,0,0), jOrigin, pDoc->GetAllocator());
1009
        }
1010 1011
        pDoc->AddMember("origin", jOrigin, pDoc->GetAllocator());
        return pDoc;
1012 1013 1014
    });

    _pRosBridge->advertiseService("/snake/get_tiles", "snake_msgs/GetTiles",
1015 1016 1017 1018 1019 1020 1021 1022
                                  [this](JsonDocUPtr) -> JsonDocUPtr
    {
        JsonDocUPtr pDoc(std::make_unique(rapidjson::kObjectType));
        rapidjson::Value jSnakeTiles(rapidjson::kObjectType);
        jsk_recognition_msgs::polygon_array::toJson(
                    this->_snakeTilesLocal, jSnakeTiles, pDoc->GetAllocator());
        pDoc->AddMember("tiles", jSnakeTiles, pDoc->GetAllocator());
        return pDoc;
1023 1024
    });
}