WimaController.cc 31.1 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 50 51 52 53
    , _localPlanDataValid       (false)
    , _metaDataMap              (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
54
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
55
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
56 57
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
58
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
59
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
60 61 62 63 64
    , _areaInterface(&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings()
    , _defaultManager(_managerSettings, _areaInterface)
    , _snakeManager(_managerSettings, _areaInterface)
    , _currentManager(_defaultManager)
65
    , _uploadOverrideRequired   (false)
66
    , _measurementPathLength    (-1)
67 68
    , _arrivalPathLength        (-1)
    , _returnPathLength         (-1)
69 70
    , _phaseDistance            (-1)
    , _phaseDuration            (-1)
71 72
    , _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 131 132 133 134 135 136 137 138 139 140 141
    return &_areas;
}

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());
142 143
}

144
QVariantList WimaController::currentWaypointPath()
145
{
146
    return const_cast<QVariantList&>(_currentManager.currentWaypointsVariant());
147 148
}

149 150 151
//QStringList WimaController::loadNameFilters() const
//{
//    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
152

153 154 155 156
//    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
//               tr("All Files (*.*)");
//    return filters;
//}
157

158 159 160
//QStringList WimaController::saveNameFilters() const
//{
//    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
161

162 163 164
//    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
//    return filters;
//}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
165

166 167 168 169 170
bool WimaController::uploadOverrideRequired() const
{
    return _uploadOverrideRequired;
}

171 172 173 174 175 176 177 178 179 180
double WimaController::phaseDistance() const
{
    return _phaseDistance;
}

double WimaController::phaseDuration() const
{
    return _phaseDuration;
}

181 182 183 184 185
bool WimaController::vehicleHasLowBattery() const
{
    return _vehicleHasLowBattery;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
186 187 188 189 190 191 192 193 194 195
long WimaController::snakeConnectionStatus() const
{
    return _snakeConnectionStatus;
}

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

196 197 198
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
199
    _managerSettings.setMasterController(masterC);
200 201 202 203 204 205
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
206
    _managerSettings.setMissionController(missionC);
207 208 209
    emit missionControllerChanged();
}

210 211 212 213 214 215
/*!
 * \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
 */
216 217
void WimaController::setDataContainer(WimaDataContainer *container)
{
218 219
    if (container != nullptr) {
        if (_container != nullptr) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
220
           disconnect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
221 222
        }

223
        _container = container;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
224
        connect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
225 226 227 228 229

        emit dataContainerChanged();
    }
}

230 231 232 233 234 235 236 237 238
void WimaController::setUploadOverrideRequired(bool overrideRequired)
{
    if (_uploadOverrideRequired != overrideRequired) {
        _uploadOverrideRequired = overrideRequired;

        emit uploadOverrideRequiredChanged();
    }
}

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

244
void WimaController::previousPhase()
245 246 247
{        
    if ( !_currentManager.previous() ) {
        assert(false);
248
    }
249 250 251 252 253

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

void WimaController::resetPhase()
{
258 259
    if ( !_currentManager.reset() ) {
        assert(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
260
    }
261 262 263 264 265

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

268
bool WimaController::uploadToVehicle()
269
{
270
    auto &currentMissionItems = _defaultManager.currentMissionItems();
271
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
272
        && currentMissionItems.count() > 0) {
273 274 275 276 277 278 279 280 281
        setUploadOverrideRequired(true);
        return false;
    }

    return forceUploadToVehicle();
}

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

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

298 299 300
    // Copy mission items to _missionController.
    for (int i = 0; i < currentMissionItems.count(); i++){
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
301 302 303
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

304
    _masterController->sendToVehicle();
305 306

    return true;
307
}
308

309 310 311
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    _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;
329 330 331 332 333 334
//    bool retValue = _calcReturnPath(errorString);
//    if (retValue == false) {
//        qgcApp()->showMessage(errorString);
//        return false;
//    }
//    return true;
335 336
}

337
void WimaController::executeSmartRTL()
338
{
339
    _executeSmartRTL();
340 341
}

342
void WimaController::initSmartRTL()
343
{
344 345
    _srtlReason = UserRequest;
    _initSmartRTL();
346 347
}

348 349 350 351 352 353
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

354 355 356 357
//void WimaController::saveToFile(const QString& filename)
//{
//    QString file = filename;
//}
358

359 360 361 362
//bool WimaController::loadFromCurrent()
//{
//    return true;
//}
363

364 365 366 367 368
//bool WimaController::loadFromFile(const QString &filename)
//{
//    QString file = filename;
//    return true;
//}
369

370 371


372 373 374 375
//QJsonDocument WimaController::saveToJson(FileType fileType)
//{
//    if(fileType)
//    {
376

377 378 379
//    }
//    return QJsonDocument();
//}
380

381
bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
382 383 384
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
385
    QVector<QPointF> path2D;
386 387 388 389 390 391 392 393 394 395
    bool retVal = PolygonCalculus::shortestPath(
                                   toQPolygonF(toCartesian2D(_joinedArea.coordinateList(), /*origin*/ start)),
                                   /*start point*/ QPointF(0,0),
                                   /*destination*/ toCartesian2D(destination, start),
                                   /*shortest path*/ path2D);
    path.append(toGeo(path2D, /*origin*/ start));

    return  retVal;
}

396 397 398 399 400 401 402
/*!
 * \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
403
bool WimaController::_fetchContainerData()
404
{
405
    // fetch only if valid, return true on success
406

407
    // reset visual items
408 409
    _areas.clear();
    _defaultManager.clear();
410 411
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
412
    _snakeTileCenterPoints.clear();
413

414 415 416
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
417
    emit waypointPathChanged();
418
    emit currentWaypointPathChanged();
419
    emit snakeTilesChanged();
420
    emit snakeTileCenterPointsChanged();
421

422
    _localPlanDataValid = false;
423

424 425
    if (_container == nullptr) {
        qWarning("WimaController::fetchContainerData(): No container assigned!");
426
        assert(false);
427 428
        return false;
    }
429

430
    WimaPlanData planData = _container->pull();
431

432 433
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
434

435
    int areaCounter = 0;
436
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
437 438
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
439

440 441 442
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
443
            _areas.append(&_serviceArea);
444

445 446
            continue;
        }
447

448 449 450
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
451
            _areas.append(&_measurementArea);
452

453
            continue;
454
        }
455

456 457 458 459
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
460

461 462
            continue;
        }
463

464 465 466
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
467
            _areas.append(&_joinedArea);
468

469 470
            continue;
        }
471

472 473 474
        if (areaCounter >= numAreas)
            break;
    }
475

476 477
    if (areaCounter != numAreas) {
        assert(false);
478 479
        return false;
    }
480

481 482 483 484
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
        assert(false);
485
        return false;
486
    }
487

488 489
    for (auto item : tempMissionItems)
        _defaultManager.push_back(item.coordinate());
490

491 492 493
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
494

495 496
    if( !_defaultManager.update() ){
        assert(false);
497
        return false;
498
    }
499

Valentin Platzgummer's avatar
Valentin Platzgummer committed
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
    // 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;
521

Valentin Platzgummer's avatar
Valentin Platzgummer committed
522 523 524 525
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

526 527
    // Check if scenario is defined.
    if ( !_verifyScenarioDefinedWithErrorMessage() )
528
        assert(false);
529 530
        return false;

531
    {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
532 533 534 535 536
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
537 538
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
539
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
540 541 542 543 544 545 546 547 548 549 550
            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));
551
        }
552 553 554 555 556
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
557
        for ( unsigned int i=0; i < tiles.size(); ++i ) {
558 559 560 561
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
562
                Tile.path().append(QVertex);
563 564 565
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
566 567
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
568

569

570
    emit visualItemsChanged();
571
    emit missionItemsChanged();
572 573
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
574 575
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
576

577 578
    _localPlanDataValid = true;
    return true;
579 580
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
581
bool WimaController::_calcNextPhase()
582
{
583 584 585 586
    bool value;
    _currentManager.setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
587

588 589
    if ( !_currentManager.next() ) {
        assert(false);
590 591
        return false;
    }
592

593
    emit missionItemsChanged();
594
    emit currentMissionItemsChanged();
595 596
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
597 598

    return true;
599
}
600

601
void WimaController::_recalcCurrentPhase()
602
{
603 604 605
    if ( !_currentManager.update() ) {
        assert(false);
    }
606

607 608 609
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
610 611
    emit waypointPathChanged();
}
612 613

void WimaController::_updateOverlap()
614
{
615 616 617 618
    bool value;
    _currentManager.setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
619

620 621 622 623 624 625
    if ( !_currentManager.update() ) {
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
626
    emit currentWaypointPathChanged();
627
    emit waypointPathChanged();
628
}
629

630
void WimaController::_updateMaxWaypoints()
631
{
632 633 634 635
    bool value;
    _currentManager.setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
636

637 638 639
    if ( !_currentManager.update() ) {
        assert(false);
    }
640

641 642 643 644
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
645

646 647
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
648
void WimaController::_updateflightSpeed()
649
{
650 651
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble());
    _currentManager.update();
652

653 654 655 656
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
657 658
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
659
void WimaController::_updateArrivalReturnSpeed()
660
{
661 662
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble());
    _currentManager.update();
663

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
670
void WimaController::_updateAltitude()
671
{
672 673 674 675 676 677 678
    _managerSettings.setAltitude(_altitude.rawValue().toDouble());
    _currentManager.update();

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
679 680
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
681
void WimaController::_checkBatteryLevel()
682
{
683 684 685 686 687
    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();
688

Valentin Platzgummer's avatar
Valentin Platzgummer committed
689
    if (managerVehicle != nullptr && enabled == true) {
690 691 692 693 694 695 696 697
        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) {
698 699 700 701 702

                if (_missionController->remainingTime() <= minTime) {
                    _lowBatteryHandlingTriggered = true;
                }
                else {
703 704 705
                    _lowBatteryHandlingTriggered = true;
                    _srtlReason = BatteryLow;
                    _initSmartRTL();
706 707 708 709 710 711 712 713 714 715 716
                }
            }
        }
        else {
            _setVehicleHasLowBattery(false);
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
717 718 719 720
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
    static EventTicker snakeEventLoopTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
721
    static EventTicker rosBridgeTicker(EVENT_TIMER_INTERVAL, 1000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
722 723 724 725 726 727

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

    // Snake flight plan update necessary?
728 729 730 731
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
732

733
    if (rosBridgeTicker.ready()) {
734

Valentin Platzgummer's avatar
Valentin Platzgummer committed
735 736 737 738
        _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
        _pRosBridge->publish(_snakeOrigin, "/snake/origin");

        using namespace std::placeholders;
739 740
        auto callBack = std::bind(&WimaController::_progressFromJson,
                                  this,
Valentin Platzgummer's avatar
Valentin Platzgummer committed
741
                                  _1,
742
                                  std::ref(_nemoProgress));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
743
        _pRosBridge->subscribe("/nemo/progress", callBack);
744
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
745 746 747
}

void WimaController::_smartRTLCleanUp(bool flying)
748 749
{

750 751 752 753 754 755 756 757 758 759 760 761
//    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);
//        }
//    }
762 763
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
764
void WimaController::_enableDisableLowBatteryHandling(QVariant enable)
765 766
{
    if (enable.toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
767
        _eventTimer.start();
768
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
769
        _eventTimer.stop();
770 771 772
    }
}

773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
void WimaController::_setPhaseDistance(double distance)
{
    if (!qFuzzyCompare(distance, _phaseDistance)) {
        _phaseDistance = distance;

        emit phaseDistanceChanged();
    }
}

void WimaController::_setPhaseDuration(double duration)
{
    if (!qFuzzyCompare(duration, _phaseDuration)) {
        _phaseDuration = duration;

        emit phaseDurationChanged();
    }
}

791 792 793 794 795 796 797
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();
798 799 800 801 802 803 804 805 806
    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;
    }
807 808

   return true;
809 810 811 812 813 814 815 816 817 818 819
}

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

        emit vehicleHasLowBatteryChanged();
    }
}

820 821
void WimaController::_initSmartRTL()
{
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
//    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);
//    }
852 853 854
}

void WimaController::_executeSmartRTL()
855 856 857 858 859
{
    forceUploadToVehicle();
    masterController()->managerVehicle()->startMission();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
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);
    }
891
    return value;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
892 893
}

894 895
void WimaController::_snakeStoreWorkerResults()
{
896
    auto start = std::chrono::high_resolution_clock::now();
897 898
    _snakeManager.clear();

899
    const WorkerResult_t &r = _snakeWorker.getResult();
900 901 902 903 904 905 906 907 908
    _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);
909 910 911

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
912 913
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
914
        _snakeManager.push_back(r.waypoints[int(i)].value<QGeoCoordinate>());
915 916
    }

917
    _snakeManager.update();
918

919
    emit missionItemsChanged();
920 921 922
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
923 924 925 926

    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
927 928
}

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

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

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

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

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

void WimaController::_progressFromJson(JsonDocUPtr pDoc,
960
                                       QNemoProgress &progress)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
961
{
962 963 964 965 966 967 968
    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
969
}
970 971 972 973 974 975 976

template<>
QVariant getCoordinate<QVariant>(const SimpleMissionItem* item)
{
    return QVariant::fromValue(item->coordinate());
}