WimaController.cc 30 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")};

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

45
WimaController::WimaController(QObject *parent)
46
    : QObject                   (parent)
47 48 49 50
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
51
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
52 53 54 55
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
56 57
    , _rtlManager               (_managerSettings, _areaInterface)
    , _currentManager           (&_defaultManager)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
58
    , _managerList              {&_defaultManager, &_snakeManager, &_rtlManager}
59 60 61 62
    , _metaDataMap              (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
63
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
64
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
65 66
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
67
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
68
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
69 70
    , _measurementPathLength    (-1)
    , _lowBatteryHandlingTriggered  (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
71 72 73 74 75 76
    , _snakeCalcInProgress      (false)
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
77
    , _nemoHeartbeat            (0 /*status: not connected*/)
78 79
    , _fallbackStatus           (0 /*status: not connected*/)
    , _bridgeSetupDone          (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
80
    , _pRosBridge               (new ROSBridge::ROSBridge())
81
{
82
    // Set up facts.
83 84
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
85 86
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
87
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
88 89 90
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
91

Valentin Platzgummer's avatar
Valentin Platzgummer committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105
    // 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);
    }
106

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
112
    // Snake Worker Thread.
113 114 115
    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
116

117
    // Snake.
118 119
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_switchSnakeManager);
    _switchSnakeManager(_enableSnake.rawValue());
120 121
}

122 123 124 125 126 127 128 129 130
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

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

QmlObjectListModel *WimaController::visualItems() {
131 132 133 134
    return &_areas;
}

QmlObjectListModel *WimaController::missionItems() {
135
    return const_cast<QmlObjectListModel*>(&_currentManager->missionItems());
136 137 138
}

QmlObjectListModel *WimaController::currentMissionItems() {
139
    return const_cast<QmlObjectListModel*>(&_currentManager->currentMissionItems());
140 141 142 143
}

QVariantList WimaController::waypointPath()
{
144
    return const_cast<QVariantList&>(_currentManager->waypointsVariant());
145 146
}

147
QVariantList WimaController::currentWaypointPath()
148
{
149
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
150 151
}

152 153 154 155 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
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;
}

188 189 190 191 192 193 194
QVector<int> WimaController::nemoProgress() {
    if ( _nemoProgress.progress().size() == _snakeTileCenterPoints.size() )
        return _nemoProgress.progress();
    else
        return QVector<int>(_snakeTileCenterPoints.size(), 0);
}

195 196
double WimaController::phaseDistance() const
{
197
    return 0.0;
198 199 200 201
}

double WimaController::phaseDuration() const
{
202
    return 0.0;
203 204
}

205 206 207 208 209 210
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

QString WimaController::nemoStatusString() const
Valentin Platzgummer's avatar
Valentin Platzgummer committed
211
{
212
    return _nemoStatusMap.at(_nemoHeartbeat.status());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
213 214 215 216 217 218 219
}

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

220 221 222
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
223
    _managerSettings.setMasterController(masterC);
224 225 226 227 228 229
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
230
    _managerSettings.setMissionController(missionC);
231 232 233
    emit missionControllerChanged();
}

234 235
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
236
    _calcNextPhase();
237 238
}

239
void WimaController::previousPhase()
240
{        
241 242
    if ( !_currentManager->previous() ) {
        Q_ASSERT(false);
243
    }
244 245 246 247 248

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
249 250 251 252
}

void WimaController::resetPhase()
{
253 254
    if ( !_currentManager->reset() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
255
    }
256 257 258 259 260

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
261 262
}

263 264
void WimaController::requestSmartRTL()
{
265 266 267 268 269
    QString errorString("Smart RTL requested. ");
    if ( !_checkSmartRTLPreCondition(errorString) ){
        qgcApp()->showMessage(errorString);
        return;
    }
270 271 272 273
    emit smartRTLRequestConfirm();
}

bool WimaController::upload()
274
{
275
    auto &currentMissionItems = _defaultManager.currentMissionItems();
276
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
277
        && currentMissionItems.count() > 0) {
278
        emit forceUploadConfirm();
279 280 281
        return false;
    }

282
    return forceUpload();
283 284
}

285
bool WimaController::forceUpload()
286
{
287 288
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (currentMissionItems.count() < 1)
289
        return false;
290 291

    _missionController->removeAll();
292
    // Set homeposition of settingsItem.
293 294 295
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
296
        Q_ASSERT(false);
297
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
298
        return false;
299
    }
300
    settingsItem->setCoordinate(_managerSettings.homePosition());
301

302
    // Copy mission items to _missionController.
303
    for (int i = 1; i < currentMissionItems.count(); i++){
304
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
305 306 307
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

308
    _masterController->sendToVehicle();
309 310

    return true;
311
}
312

313 314 315
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
316 317 318
    _missionController->removeAll();
}

319
void WimaController::executeSmartRTL()
320
{
321 322
    forceUpload();
    masterController()->managerVehicle()->startMission();
323 324
}

325
void WimaController::initSmartRTL()
326
{
327
    _initSmartRTL();
328 329
}

330 331 332 333 334 335
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

336
bool WimaController::_calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
337 338 339
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
340 341 342 343 344
    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
345
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
346 347 348

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
349 350 351 352

    return  retVal;
}

353
bool WimaController::setWimaPlanData(const WimaPlanData &planData)
354
{
355
    // fetch only if valid, return true on success
356

357
    // reset visual items
358 359
    _areas.clear();
    _defaultManager.clear();
360 361
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
362
    _snakeTileCenterPoints.clear();
363

364 365 366
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
367
    emit waypointPathChanged();
368
    emit currentWaypointPathChanged();
369
    emit snakeTilesChanged();
370
    emit snakeTileCenterPointsChanged();
371

372
    _localPlanDataValid = false;
373

374 375
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
376

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

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

387 388
            continue;
        }
389

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

395
            continue;
396
        }
397

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

403 404
            continue;
        }
405

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

411 412
            continue;
        }
413

414 415 416
        if (areaCounter >= numAreas)
            break;
    }
417

418
    if (areaCounter != numAreas) {
419
        Q_ASSERT(false);
420 421
        return false;
    }
422

423 424
    emit visualItemsChanged();

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

432
    for (auto item : tempMissionItems) {
433
        _defaultManager.push_back(item.coordinate());
434
    }
435

436 437 438
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
439

440
    if( !_defaultManager.reset() ){
441
        Q_ASSERT(false);
442
        return false;
443
    }
444

445 446 447 448 449
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
450
    _localPlanDataValid = true;
451

Valentin Platzgummer's avatar
Valentin Platzgummer committed
452
    _initStartSnakeWorker();
453 454 455

    return true;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
456

457 458 459 460 461 462 463 464 465 466 467
WimaController *WimaController::thisPointer()
{
    return this;
}

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

469
    emit missionItemsChanged();
470 471
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
472
    emit waypointPathChanged();
473

474
    return true;
475 476
}

477
bool WimaController::_setStartIndex()
478
{
479
    bool value;
480
    _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
481 482 483 484 485
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
486 487
        return false;
    }
488

489
    emit missionItemsChanged();
490
    emit currentMissionItemsChanged();
491 492
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
493 494

    return true;
495
}
496

497
void WimaController::_recalcCurrentPhase()
498
{
499 500
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
501
    }
502

503 504 505
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
506 507
    emit waypointPathChanged();
}
508 509

void WimaController::_updateOverlap()
510
{
511
    bool value;
512
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
513 514
    Q_ASSERT(value);
    (void)value;
515

516
    if ( !_currentManager->update() ) {
517 518 519 520 521
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
522
    emit currentWaypointPathChanged();
523
    emit waypointPathChanged();
524
}
525

526
void WimaController::_updateMaxWaypoints()
527
{
528
    bool value;
529
    _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
530 531
    Q_ASSERT(value);
    (void)value;
532

533 534
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
535
    }
536

537 538 539 540
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
541 542
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
543
void WimaController::_updateflightSpeed()
544
{
545
    bool value;
546
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
547 548 549 550 551 552
    Q_ASSERT(value);
    (void)value;

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

554 555 556 557
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
558 559
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
560
void WimaController::_updateArrivalReturnSpeed()
561
{
562
    bool value;
563
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble(&value));
564 565 566 567 568 569
    Q_ASSERT(value);
    (void)value;

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

571 572 573 574
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
575 576
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
577
void WimaController::_updateAltitude()
578
{
579
    bool value;
580
    _managerSettings.setAltitude(_altitude.rawValue().toDouble(&value));
581 582 583 584 585 586
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
587 588 589 590 591

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
592 593
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
594
void WimaController::_checkBatteryLevel()
595
{
596 597 598 599 600
    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();
601

Valentin Platzgummer's avatar
Valentin Platzgummer committed
602
    if (managerVehicle != nullptr && enabled == true) {
603 604 605 606 607 608
        Fact *battery1percentRemaining = managerVehicle->battery1FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);
        Fact *battery2percentRemaining = managerVehicle->battery2FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);


        if (battery1percentRemaining->rawValue().toDouble() < batteryThreshold
                && battery2percentRemaining->rawValue().toDouble() < batteryThreshold) {
609 610 611
            if (!_lowBatteryHandlingTriggered) {                
                _lowBatteryHandlingTriggered = true;
                if ( !(_missionController->remainingTime() <= minTime) ) {
612
                    requestSmartRTL();
613 614 615 616 617 618 619 620 621 622
                }
            }
        }
        else {
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
623 624 625
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
626
    static EventTicker snakeTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
627
    static EventTicker nemoStatusTicker(EVENT_TIMER_INTERVAL, 5000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
628 629

    // Battery level check necessary?
630 631
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
    if ( enableLowBatteryHandling->rawValue().toBool() && batteryLevelTicker.ready() )
Valentin Platzgummer's avatar
Valentin Platzgummer committed
632 633 634
        _checkBatteryLevel();

    // Snake flight plan update necessary?
635 636 637 638
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
639

640 641 642 643 644 645
    if ( nemoStatusTicker.ready() ) {
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
    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;
        }
712
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
713 714 715
}

void WimaController::_smartRTLCleanUp(bool flying)
716
{
717 718 719 720 721
    if ( !flying) { // vehicle has landed
        _switchWaypointManager(_defaultManager);
        _missionController->removeAllFromVehicle();
        _missionController->removeAll();
        disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
722 723 724
    }
}

725 726
void WimaController::_setPhaseDistance(double distance)
{
727 728 729
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
730

731 732
//        emit phaseDistanceChanged();
//    }
733 734 735 736
}

void WimaController::_setPhaseDuration(double duration)
{
737 738 739
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
740

741 742
//        emit phaseDurationChanged();
//    }
743 744
}

745
bool WimaController::_checkSmartRTLPreCondition(QString &errorString)
746 747
{
    if (!_localPlanDataValid) {
748 749
        errorString.append(tr("No WiMA data available. Please define at least a measurement and a service area."));
        return false;
750
    }
751

752
    return _rtlManager.checkPrecondition(errorString);
753 754
}

755
void WimaController::_switchWaypointManager(WaypointManager::ManagerBase &manager)
756
{
757 758 759
    if (_currentManager != &manager) {
        _currentManager = &manager;

Valentin Platzgummer's avatar
Valentin Platzgummer committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
        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);
777

778 779 780 781
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
782

783
        qWarning() << "WimaController::_switchWaypointManager: statistics update missing.";
784 785 786
    }
}

787 788
void WimaController::_initSmartRTL()
{
789 790 791 792
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

793
    if ( _checkSmartRTLPreCondition(errorString) ) {
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
        _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);
    }
809 810
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
811 812 813 814 815 816 817 818
void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

819 820
void WimaController::_snakeStoreWorkerResults()
{
821
    auto start = std::chrono::high_resolution_clock::now();
822 823
    _snakeManager.clear();

Valentin Platzgummer's avatar
Valentin Platzgummer committed
824
    const WorkerResult_t &r = _snakeWorker.result();
825 826
    _setSnakeCalcInProgress(false);
    if (!r.success) {
827
        //qgcApp()->showMessage(r.errorMessage);
828 829 830 831 832
        return;
    }

    // create Mission items from r.waypoints
    long n = r.waypoints.size() - r.returnPathIdx.size() - r.arrivalPathIdx.size() + 2;
833
    if (n < 1) {
834 835
        return;
    }
836 837 838

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
839 840
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
841
        _snakeManager.push_back(r.waypoints[int(i)]);
842 843
    }

844
    _snakeManager.update();
845

846
    emit missionItemsChanged();
847 848 849
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
850 851 852 853

    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
854 855
}

856 857 858 859 860 861 862 863 864 865 866
void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

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

    // Initialize _snakeWorker.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
867 868 869
    _snakeWorker.setMeasurementArea(_measurementArea.coordinateList());
    _snakeWorker.setServiceArea(_serviceArea.coordinateList());
    _snakeWorker.setCorridor(_corridor.coordinateList());
870 871 872
    _snakeWorker.setProgress(_nemoProgress.progress());
    _snakeWorker.setLineDistance(_snakeLineDistance.rawValue().toDouble());
    _snakeWorker.setMinTransectLength(_snakeMinTransectLength.rawValue().toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
873 874 875
    _snakeWorker.setTileHeight(_snakeTileHeight.rawValue().toDouble());
    _snakeWorker.setTileWidth(_snakeTileWidth.rawValue().toDouble());
    _snakeWorker.setMinTileArea(_snakeMinTileArea.rawValue().toDouble());
876 877 878 879 880 881
    _setSnakeCalcInProgress(true);

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

882 883 884 885 886 887 888 889
void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}