WimaController.cc 33.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
#include <memory>

19 20 21 22
#define SMART_RTL_MAX_ATTEMPTS 3 // times
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
#define EVENT_TIMER_INTERVAL 50 // ms

23

24

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

43 44 45 46
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")};
47

Valentin Platzgummer's avatar
Valentin Platzgummer committed
48 49
using namespace snake;
using namespace snake_geometry;
50

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

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

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

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

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

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

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

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

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

QmlObjectListModel *WimaController::visualItems() {
158 159 160 161
    return &_areas;
}

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

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

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

174
QVariantList WimaController::currentWaypointPath()
175
{
176
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
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 203 204 205 206 207 208 209 210 211 212 213 214
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;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

309
    return forceUpload();
310 311
}

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

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

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

335
    _masterController->sendToVehicle();
336 337

    return true;
338
}
339

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

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

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

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

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

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

    return  retVal;
}

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

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

398
    _localPlanDataValid = false;
399

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

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

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

413 414
            continue;
        }
415

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

421
            continue;
422
        }
423

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

429 430
            continue;
        }
431

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

437 438
            continue;
        }
439

440 441 442
        if (areaCounter >= numAreas)
            break;
    }
443

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

449 450
    emit visualItemsChanged();

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

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

462 463 464
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
465

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

471 472 473 474 475 476 477
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



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

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

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

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

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

551 552 553 554 555 556 557 558 559
    if ( _enableSnake.rawValue().toBool()
            && _pRosBridge->isRunning()
            && _pRosBridge->connected() ){
        if ( _snakeTilesLocal.polygons().size() > 0 ){
            _pRosBridge->publish(_snakeOrigin, "/snake/origin");
            _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
        }
    }

560 561 562
    _localPlanDataValid = true;
    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
563

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

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

576
    emit missionItemsChanged();
577 578
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
579
    emit waypointPathChanged();
580

581
    return true;
582 583
}

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

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
593 594
        return false;
    }
595

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

    return true;
602
}
603

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
699 700
}

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

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


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

    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

898
    _snakeManager.update();
899

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

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

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

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

void WimaController::_setupTopicService()
{
942 943 944 945
    if ( _snakeTilesLocal.polygons().size() > 0){
        _pRosBridge->publish(_snakeOrigin, "/snake/origin");
        _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
    }
946 947 948 949 950
    _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)
951 952
             || progress.progress().size() != requiredSize ) { // Some error occured.
            // Set progress to default.
953
            progress.progress().fill(0, requiredSize);
954 955 956 957 958
            // Publish origin and tiles again.
            if ( this->_snakeTilesLocal.polygons().size() > 0){
                this->_pRosBridge->publish(this->_snakeOrigin, "/snake/origin");
                this->_pRosBridge->publish(this->_snakeTilesLocal, "/snake/tiles");
            }
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
        }

        emit WimaController::nemoProgressChanged();
    });
    _pRosBridge->subscribe("/nemo/heartbeat",
                           /* callback */ [this](JsonDocUPtr pDoc){
        if ( !this->_pRosBridge->casePacker()->unpack(pDoc, this->_nemoHeartbeat) ) {
            if ( this->_nemoHeartbeat.status() == this->_fallbackStatus )
                return;
            this->_nemoHeartbeat.setStatus(this->_fallbackStatus);
        }

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

    _pRosBridge->advertiseService("/snake/get_origin", "snake_msgs/GetOrigin",
978 979 980
                                  [this](JsonDocUPtr) -> JsonDocUPtr {
        JsonDocUPtr pDoc;
        if ( this->_snakeTilesLocal.polygons().size() > 0){
981
            pDoc = this->_pRosBridge->casePacker()->pack(this->_snakeOrigin, "");
982
        } else {
983
            pDoc = this->_pRosBridge->casePacker()->pack(::GeoPoint3D(0,0,0), "");
984
        }
985
        this->_pRosBridge->casePacker()->removeTag(pDoc);
986 987 988 989 990 991 992 993 994
        rapidjson::Document value(rapidjson::kObjectType);
        JsonDocUPtr         pReturn(new rapidjson::Document(rapidjson::kObjectType));
        value.CopyFrom(*pDoc, pReturn->GetAllocator());
        pReturn->AddMember("origin", value, pReturn->GetAllocator());

        return pReturn;
    });

    _pRosBridge->advertiseService("/snake/get_tiles", "snake_msgs/GetTiles",
995
                                  [this](JsonDocUPtr) -> JsonDocUPtr {
996 997
        JsonDocUPtr pDoc = this->_pRosBridge->casePacker()->pack(this->_snakeTilesLocal, "");
        this->_pRosBridge->casePacker()->removeTag(pDoc);
998 999 1000 1001 1002 1003 1004
        rapidjson::Document value(rapidjson::kObjectType);
        JsonDocUPtr         pReturn(new rapidjson::Document(rapidjson::kObjectType));
        value.CopyFrom(*pDoc, pReturn->GetAllocator());
        pReturn->AddMember("tiles", value, pReturn->GetAllocator());
        return pReturn;
    });
}