WimaController.cc 32.2 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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
13
#include "time.h"
14
#include "assert.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
15

16
#include "QVector3D"
17
#include <QScopedPointer>
18

19

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

using namespace snake;
using namespace snake_geometry;
41

42
WimaController::WimaController(QObject *parent)
43 44
    : QObject                   (parent)
    , _container                (nullptr)
45 46 47 48
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
49
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
50 51 52 53 54
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
    , _currentManager           (_defaultManager)
55 56 57 58
    , _metaDataMap              (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
59
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
60
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
61 62
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
63
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
64
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
65
    , _uploadOverrideRequired   (false)
66
    , _measurementPathLength    (-1)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
67 68 69 70 71 72
//    , _arrivalPathLength        (-1)
//    , _returnPathLength         (-1)
//    , _phaseDistance            (-1)
//    , _phaseDuration            (-1)
//    , _phaseDistanceBuffer      (-1)
//    , _phaseDurationBuffer      (-1)
73 74 75
    , _vehicleHasLowBattery         (false)
    , _lowBatteryHandlingTriggered  (false)
    , _executingSmartRTL            (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
76 77 78 79 80 81 82 83
    , _snakeConnectionStatus    (SnakeConnectionStatus::Connected) // TODO: implement automatic connection
    , _snakeCalcInProgress      (false)
    , _scenarioDefinedBool      (false)
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
84
    , _pRosBridge               (new ROSBridge::ROSBridge())
85
{
86
    // Set up facts.
87 88
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
89 90
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
91 92 93 94
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);
95 96 97 98

    _defaultManager.setOverlap(_overlapWaypoints.rawValue().toUInt());
    _defaultManager.setN(_maxWaypointsPerPhase.rawValue().toUInt());
    _defaultManager.setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt());
99

100
    // setup low battery handling
Valentin Platzgummer's avatar
Valentin Platzgummer committed
101 102
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
    _eventTimer.setInterval(EVENT_TIMER_INTERVAL);
103

104
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
105 106 107 108
    connect(enableLowBatteryHandling, &Fact::rawValueChanged, this, &WimaController::_enableDisableLowBatteryHandling);
    _enableDisableLowBatteryHandling(enableLowBatteryHandling->rawValue());

    // Snake Worker Thread.
109 110 111
    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
112 113 114

    // Start, stop RosBridge.
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_startStopRosBridge);
115
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_initStartSnakeWorker);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
116
    _startStopRosBridge();
117 118
}

119 120 121 122 123 124 125 126 127
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

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

QmlObjectListModel *WimaController::visualItems() {
128 129 130
    return &_areas;
}

131 132 133 134
WimaDataContainer *WimaController::dataContainer() {
    return _container;
}

135 136 137 138 139 140 141 142 143 144 145
QmlObjectListModel *WimaController::missionItems() {
    return const_cast<QmlObjectListModel*>(&_currentManager.missionItems());
}

QmlObjectListModel *WimaController::currentMissionItems() {
    return const_cast<QmlObjectListModel*>(&_currentManager.currentMissionItems());
}

QVariantList WimaController::waypointPath()
{
    return const_cast<QVariantList&>(_currentManager.waypointsVariant());
146 147
}

148
QVariantList WimaController::currentWaypointPath()
149
{
150
    return const_cast<QVariantList&>(_currentManager.currentWaypointsVariant());
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 188
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;
}

189 190 191
//QStringList WimaController::loadNameFilters() const
//{
//    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
192

193 194 195 196
//    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
//               tr("All Files (*.*)");
//    return filters;
//}
197

198 199 200
//QStringList WimaController::saveNameFilters() const
//{
//    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
201

202 203 204
//    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
//    return filters;
//}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
205

206 207 208 209 210
bool WimaController::uploadOverrideRequired() const
{
    return _uploadOverrideRequired;
}

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

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

221 222 223 224 225
bool WimaController::vehicleHasLowBattery() const
{
    return _vehicleHasLowBattery;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
226 227 228 229 230 231 232 233 234 235
long WimaController::snakeConnectionStatus() const
{
    return _snakeConnectionStatus;
}

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

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

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

250 251 252 253 254 255
/*!
 * \fn void WimaController::setDataContainer(WimaDataContainer *container)
 * Sets the pointer to the \c WimaDataContainer, which is meant to exchange data between the \c WimaController and the \c WimaPlaner.
 *
 * \sa WimaPlaner, WimaDataContainer, WimaPlanData
 */
256 257
void WimaController::setDataContainer(WimaDataContainer *container)
{
258 259
    if (container != nullptr) {
        if (_container != nullptr) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
260
           disconnect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
261 262
        }

263
        _container = container;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
264
        connect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
265 266 267 268 269

        emit dataContainerChanged();
    }
}

270 271 272 273 274 275 276 277 278
void WimaController::setUploadOverrideRequired(bool overrideRequired)
{
    if (_uploadOverrideRequired != overrideRequired) {
        _uploadOverrideRequired = overrideRequired;

        emit uploadOverrideRequiredChanged();
    }
}

279 280
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
281
    _calcNextPhase();
282 283
}

284
void WimaController::previousPhase()
285 286 287
{        
    if ( !_currentManager.previous() ) {
        assert(false);
288
    }
289 290 291 292 293

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
294 295 296 297
}

void WimaController::resetPhase()
{
298 299
    if ( !_currentManager.reset() ) {
        assert(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
300
    }
301 302 303 304 305

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
306 307
}

308
bool WimaController::uploadToVehicle()
309
{
310
    auto &currentMissionItems = _defaultManager.currentMissionItems();
311
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
312
        && currentMissionItems.count() > 0) {
313 314 315 316 317 318 319 320 321
        setUploadOverrideRequired(true);
        return false;
    }

    return forceUploadToVehicle();
}

bool WimaController::forceUploadToVehicle()
{
322
    auto &currentMissionItems = _defaultManager.currentMissionItems();
323
    setUploadOverrideRequired(false);
324
    if (currentMissionItems.count() < 1)
325
        return false;
326 327 328 329 330 331

    _missionController->removeAll();
    // set homeposition of settingsItem
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
332
        assert(false);
333
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
334
        return false;
335
    }
336
    settingsItem->setCoordinate(_managerSettings.homePosition());
337

338 339 340
    // Copy mission items to _missionController.
    for (int i = 0; i < currentMissionItems.count(); i++){
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
341 342 343
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

344
    _masterController->sendToVehicle();
345 346

    return true;
347
}
348

349 350 351
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    _missionController->removeAll();
}

bool WimaController::checkSmartRTLPreCondition()
{
    QString errorString;
    bool retValue = _checkSmartRTLPreCondition(errorString);
    if (retValue == false) {
        qgcApp()->showMessage(errorString);
        return false;
    }
    return true;
}

bool WimaController::calcReturnPath()
{
    QString errorString;
369 370 371 372 373
//    bool retValue = _calcReturnPath(errorString);
//    if (retValue == false) {
//        qgcApp()->showMessage(errorString);
//        return false;
//    }
374
    return true;
375 376
}

377
void WimaController::executeSmartRTL()
378
{
379
    _executeSmartRTL();
380 381
}

382
void WimaController::initSmartRTL()
383
{
384 385
    _srtlReason = UserRequest;
    _initSmartRTL();
386 387
}

388 389 390 391 392 393
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

394 395 396 397
//void WimaController::saveToFile(const QString& filename)
//{
//    QString file = filename;
//}
398

399 400 401 402
//bool WimaController::loadFromCurrent()
//{
//    return true;
//}
403

404 405 406 407 408
//bool WimaController::loadFromFile(const QString &filename)
//{
//    QString file = filename;
//    return true;
//}
409

410 411


412 413 414 415
//QJsonDocument WimaController::saveToJson(FileType fileType)
//{
//    if(fileType)
//    {
416

417 418 419
//    }
//    return QJsonDocument();
//}
420

421
bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
422 423 424
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
425 426 427 428 429
    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
430
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
431 432 433

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
434 435 436 437

    return  retVal;
}

438 439 440 441 442 443 444
/*!
 * \fn void WimaController::containerDataValidChanged(bool valid)
 * Pulls plan data generated by \c WimaPlaner from the \c _container if the data is valid (\a valid equals true).
 * Is connected to the dataValidChanged() signal of the \c WimaDataContainer.
 *
 * \sa WimaDataContainer, WimaPlaner, WimaPlanData
 */
Valentin Platzgummer's avatar
Valentin Platzgummer committed
445
bool WimaController::_fetchContainerData()
446
{
447
    // fetch only if valid, return true on success
448

449
    // reset visual items
450 451
    _areas.clear();
    _defaultManager.clear();
452 453
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
454
    _snakeTileCenterPoints.clear();
455

456 457 458
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
459
    emit waypointPathChanged();
460
    emit currentWaypointPathChanged();
461
    emit snakeTilesChanged();
462
    emit snakeTileCenterPointsChanged();
463

464
    _localPlanDataValid = false;
465

466 467
    if (_container == nullptr) {
        qWarning("WimaController::fetchContainerData(): No container assigned!");
468
        assert(false);
469 470
        return false;
    }
471

472
    WimaPlanData planData = _container->pull();
473

474 475
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
476

477
    int areaCounter = 0;
478
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
479 480
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
481

482 483 484
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
485
            _areas.append(&_serviceArea);
486

487 488
            continue;
        }
489

490 491 492
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
493
            _areas.append(&_measurementArea);
494

495
            continue;
496
        }
497

498 499 500 501
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
502

503 504
            continue;
        }
505

506 507 508
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
509
            _areas.append(&_joinedArea);
510

511 512
            continue;
        }
513

514 515 516
        if (areaCounter >= numAreas)
            break;
    }
517

518 519
    if (areaCounter != numAreas) {
        assert(false);
520 521
        return false;
    }
522

523 524 525
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
526
        qWarning("WimaController: Mission items from WimaPlaner empty!");
527
        return false;
528
    }
529

530
    for (auto item : tempMissionItems) {
531
        _defaultManager.push_back(item.coordinate());
532
    }
533

534 535 536
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
537

538
    if( !_defaultManager.reset() ){
539
        assert(false);
540
        return false;
541
    }
542

Valentin Platzgummer's avatar
Valentin Platzgummer committed
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
    // 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;
564

Valentin Platzgummer's avatar
Valentin Platzgummer committed
565 566 567 568
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

569 570
    // Check if scenario is defined.
    if ( !_verifyScenarioDefinedWithErrorMessage() )
571
        assert(false);
572 573
        return false;

574
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
575 576 577 578 579
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
580 581
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
582
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
583 584 585 586 587 588 589 590 591 592 593
            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));
594
        }
595 596 597 598 599
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
600
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
601 602 603 604
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
605
                Tile.path().append(QVertex);
606 607 608
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
609 610
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
611

612

613
    emit visualItemsChanged();
614
    emit missionItemsChanged();
615 616
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
617 618
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
619

620 621
    _localPlanDataValid = true;
    return true;
622 623
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
624
bool WimaController::_calcNextPhase()
625
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
626 627 628 629
    qDebug() << "WimaController::_calcNextPhase: "
             << "overlap=" << _currentManager.overlap()
             << "N=" << _currentManager.N()
             << "startIndex=" << _currentManager.startIndex();
630 631 632 633 634 635 636
//    bool value;
//    _currentManager.setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value));
//    Q_ASSERT(value);
//    (void)value;
//    qDebug() << "overlap=" << _currentManager.overlap()
//             << "N=" << _currentManager.N()
//             << "startIndex=" << _currentManager.startIndex();
637

638 639
    if ( !_currentManager.next() ) {
        assert(false);
640 641
        return false;
    }
642

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

    return true;
649
}
650

651
void WimaController::_recalcCurrentPhase()
652
{
653 654 655
    if ( !_currentManager.update() ) {
        assert(false);
    }
656

657 658 659
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
660 661
    emit waypointPathChanged();
}
662 663

void WimaController::_updateOverlap()
664
{
665 666 667 668
    bool value;
    _currentManager.setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
669

670 671 672 673 674 675
    if ( !_currentManager.update() ) {
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
676
    emit currentWaypointPathChanged();
677
    emit waypointPathChanged();
678
}
679

680
void WimaController::_updateMaxWaypoints()
681
{
682 683 684 685
    bool value;
    _currentManager.setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
686

687 688 689
    if ( !_currentManager.update() ) {
        assert(false);
    }
690

691 692 693 694
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
695

696 697
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
698
void WimaController::_updateflightSpeed()
699
{
700 701
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble());
    _currentManager.update();
702

703 704 705 706
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
707 708
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
709
void WimaController::_updateArrivalReturnSpeed()
710
{
711 712
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble());
    _currentManager.update();
713

714 715 716 717
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
718 719
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
720
void WimaController::_updateAltitude()
721
{
722 723 724 725 726 727 728
    _managerSettings.setAltitude(_altitude.rawValue().toDouble());
    _currentManager.update();

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
729 730
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
731
void WimaController::_checkBatteryLevel()
732
{
733 734 735 736 737
    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();
738

Valentin Platzgummer's avatar
Valentin Platzgummer committed
739
    if (managerVehicle != nullptr && enabled == true) {
740 741 742 743 744 745 746 747
        Fact *battery1percentRemaining = managerVehicle->battery1FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);
        Fact *battery2percentRemaining = managerVehicle->battery2FactGroup()->getFact(VehicleBatteryFactGroup::_percentRemainingFactName);


        if (battery1percentRemaining->rawValue().toDouble() < batteryThreshold
                && battery2percentRemaining->rawValue().toDouble() < batteryThreshold) {
            _setVehicleHasLowBattery(true);
            if (!_lowBatteryHandlingTriggered) {
748 749 750 751 752

                if (_missionController->remainingTime() <= minTime) {
                    _lowBatteryHandlingTriggered = true;
                }
                else {
753 754 755
                    _lowBatteryHandlingTriggered = true;
                    _srtlReason = BatteryLow;
                    _initSmartRTL();
756 757 758 759 760 761 762 763 764 765 766
                }
            }
        }
        else {
            _setVehicleHasLowBattery(false);
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
767 768 769 770
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
    static EventTicker snakeEventLoopTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
771
    static EventTicker rosBridgeTicker(EVENT_TIMER_INTERVAL, 1000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
772 773 774 775 776 777

    // Battery level check necessary?
    if ( batteryLevelTicker.ready() )
        _checkBatteryLevel();

    // Snake flight plan update necessary?
778 779 780 781
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
782

783
    if (rosBridgeTicker.ready()) {
784

Valentin Platzgummer's avatar
Valentin Platzgummer committed
785 786 787 788
        _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
        _pRosBridge->publish(_snakeOrigin, "/snake/origin");

        using namespace std::placeholders;
789 790
        auto callBack = std::bind(&WimaController::_progressFromJson,
                                  this,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
791
                                  _1,
792
                                  std::ref(_nemoProgress));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
793
        _pRosBridge->subscribe("/nemo/progress", callBack);
794
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
795 796 797
}

void WimaController::_smartRTLCleanUp(bool flying)
798
{
799
    (void)flying;
800

801 802 803 804 805 806 807 808 809 810 811 812
//    if ( !flying) { // vehicle has landed
//        if (_executingSmartRTL) {
//            _executingSmartRTL = false;
//            _loadCurrentMissionItemsFromBuffer();
//            _setPhaseDistance(_phaseDistanceBuffer);
//            _setPhaseDuration(_phaseDurationBuffer);
//            _showAllMissionItems.setRawValue(true);
//            _missionController->removeAllFromVehicle();
//            _missionController->removeAll();
//            disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
//        }
//    }
813 814
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
815
void WimaController::_enableDisableLowBatteryHandling(QVariant enable)
816 817
{
    if (enable.toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
818
        _eventTimer.start();
819
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
820
        _eventTimer.stop();
821 822 823
    }
}

824 825
void WimaController::_setPhaseDistance(double distance)
{
826 827 828
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
829

830 831
//        emit phaseDistanceChanged();
//    }
832 833 834 835
}

void WimaController::_setPhaseDuration(double duration)
{
836 837 838
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
839

840 841
//        emit phaseDurationChanged();
//    }
842 843
}

844 845 846 847 848 849 850
bool WimaController::_checkSmartRTLPreCondition(QString &errorString)
{
    if (!_localPlanDataValid) {
        errorString.append(tr("No WiMA data available. Please define at least a measurement and a service area."));
        return false;
    }
    Vehicle *managerVehicle = masterController()->managerVehicle();
851 852 853 854 855 856 857 858 859
    if (!managerVehicle->flying()) {
         errorString.append(tr("Vehicle is not flying. Smart RTL not available."));
         return false;
    }

    if (!_joinedArea.containsCoordinate(managerVehicle->coordinate())) {
         errorString.append(tr("Vehicle not inside save area. Smart RTL not available."));
         return false;
    }
860 861

   return true;
862 863 864 865 866 867 868 869 870 871 872
}

void WimaController::_setVehicleHasLowBattery(bool batteryLow)
{
    if (_vehicleHasLowBattery != batteryLow) {
        _vehicleHasLowBattery = batteryLow;

        emit vehicleHasLowBatteryChanged();
    }
}

873 874
void WimaController::_initSmartRTL()
{
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904
//    QString errorString;
//    static int attemptCounter = 0;
//    attemptCounter++;

//    if (_checkSmartRTLPreCondition(errorString) == true) {
//        _masterController->managerVehicle()->pauseVehicle();
//        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
//        if (_calcReturnPath(errorString)) {
//            _executingSmartRTL = true;
//            attemptCounter = 0;

//            switch(_srtlReason) {
//                case BatteryLow:
//                    emit returnBatteryLowConfirmRequired();
//                break;
//                case UserRequest:
//                    emit returnUserRequestConfirmRequired();
//                break;
//            }

//            return;
//        }
//    }
//    if (attemptCounter > SMART_RTL_MAX_ATTEMPTS) { // try 3 times, somtimes vehicle is outside joined area
//        errorString.append(tr("Smart RTL: No success after maximum number of attempts."));
//        qgcApp()->showMessage(errorString);
//        attemptCounter = 0;
//    } else {
//        _smartRTLAttemptTimer.singleShot(SMART_RTL_ATTEMPT_INTERVAL, this, &WimaController::_initSmartRTL);
//    }
905 906 907
}

void WimaController::_executeSmartRTL()
908 909 910 911 912
{
    forceUploadToVehicle();
    masterController()->managerVehicle()->startMission();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
void WimaController::_setSnakeConnectionStatus(WimaController::SnakeConnectionStatus status)
{
    if (_snakeConnectionStatus != status) {
        _snakeConnectionStatus = status;
        emit snakeConnectionStatusChanged();
    }
}

void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

bool WimaController::_verifyScenarioDefined()
{
    _scenarioDefinedBool = _scenario.defined(_snakeTileWidth.rawValue().toDouble(), _snakeTileHeight.rawValue().toDouble(), _snakeMinTileArea.rawValue().toDouble());
    return _scenarioDefinedBool;
}

bool WimaController::_verifyScenarioDefinedWithErrorMessage()
{
    bool value = _verifyScenarioDefined();
    if (!value){
        QString errorString;
        for (auto c : _scenario.errorString)
            errorString.push_back(c);
        qgcApp()->showMessage(errorString);
    }
944
    return value;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
945 946
}

947 948
void WimaController::_snakeStoreWorkerResults()
{
949
    auto start = std::chrono::high_resolution_clock::now();
950 951
    _snakeManager.clear();

952
    const WorkerResult_t &r = _snakeWorker.getResult();
953 954 955 956 957 958 959 960 961
    _setSnakeCalcInProgress(false);
    if (!r.success) {
        qgcApp()->showMessage(r.errorMessage);
        return;
    }

    // create Mission items from r.waypoints
    long n = r.waypoints.size() - r.returnPathIdx.size() - r.arrivalPathIdx.size() + 2;
    assert(n >= 1);
962 963 964

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
965 966
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
967
        _snakeManager.push_back(r.waypoints[int(i)].value<QGeoCoordinate>());
968 969
    }

970
    _snakeManager.update();
971

972
    emit missionItemsChanged();
973 974 975
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
976 977 978 979

    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
980 981
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
982 983
void WimaController::_startStopRosBridge()
{
984
    if ( _enableSnake.rawValue().toBool() ) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
985 986
        _pRosBridge->start();
    } else {
987
        _pRosBridge->reset();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
988 989 990
    }
}

991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
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();
}

void WimaController::_progressFromJson(JsonDocUPtr pDoc,
1013
                                       QNemoProgress &progress)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1014
{
1015 1016 1017 1018 1019 1020 1021
    int requiredSize = _snakeTilesLocal.polygons().size();
    if ( !_pRosBridge->casePacker()->unpack(pDoc, progress)
         || progress.progress().size() != requiredSize ) {
        progress.progress().fill(0, requiredSize);
    }

    emit WimaController::nemoProgressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1022
}
1023