WimaController.cc 49.3 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 4 5 6
#include "ros_bridge/include/jsongenerator.h"
#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 8

#include "time.h"
9
#include "assert.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
10

11

12 13 14 15 16 17 18 19 20 21
const char* WimaController::wimaFileExtension           = "wima";
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";
22
const char* WimaController::flightSpeedName             = "FlightSpeed";
23
const char* WimaController::arrivalReturnSpeedName      = "ArrivalReturnSpeed";
24
const char* WimaController::altitudeName                = "Altitude";
25
const char* WimaController::reverseName                 = "Reverse";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
26 27
const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
28
const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
29 30 31 32 33
const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";

using namespace snake;
using namespace snake_geometry;
34

35
WimaController::WimaController(QObject *parent)
36 37 38 39 40 41 42 43 44 45 46
    : QObject                   (parent)
    , _container                (nullptr)
    , _joinedArea               (this)
    , _measurementArea          (this)
    , _serviceArea              (this)
    , _corridor                 (this)
    , _localPlanDataValid       (false)
    , _metaDataMap              (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
47
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
48
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
49 50
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
51
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
52
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
53
    , _reverse                  (settingsGroup, _metaDataMap[reverseName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
54
    , _endWaypointIndex         (0)
55 56
    , _startWaypointIndex       (0)
    , _uploadOverrideRequired   (false)
57
    , _measurementPathLength    (-1)
58 59
    , _arrivalPathLength        (-1)
    , _returnPathLength         (-1)
60 61
    , _phaseDistance            (-1)
    , _phaseDuration            (-1)
62 63
    , _phaseDistanceBuffer      (-1)
    , _phaseDurationBuffer      (-1)
64 65 66
    , _vehicleHasLowBattery         (false)
    , _lowBatteryHandlingTriggered  (false)
    , _executingSmartRTL            (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
67 68 69 70 71 72 73 74
    , _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])
75 76 77
{
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
78 79 80 81 82 83 84
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateNextWaypoint);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_recalcCurrentPhase);
    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);
    connect(&_reverse,                      &Fact::rawValueChanged, this, &WimaController::_reverseChangedHandler);
85

86
    // setup low battery handling
Valentin Platzgummer's avatar
Valentin Platzgummer committed
87 88
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
    _eventTimer.setInterval(EVENT_TIMER_INTERVAL);
89

90
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
91 92 93 94
    connect(enableLowBatteryHandling, &Fact::rawValueChanged, this, &WimaController::_enableDisableLowBatteryHandling);
    _enableDisableLowBatteryHandling(enableLowBatteryHandling->rawValue());

    // Snake Worker Thread.
95
    connect(&_snakeWorker, &SnakeWorker::resultReady, this, &WimaController::_snakeStoreWorkerResults);
96 97
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
98 99 100 101
QStringList WimaController::loadNameFilters() const
{
    QStringList filters;

102
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
Valentin Platzgummer's avatar
Valentin Platzgummer committed
103 104
               tr("All Files (*.*)");
    return filters;
105 106 107 108 109
}

QStringList WimaController::saveNameFilters() const
{
    QStringList filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
110

111 112
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
113 114
}

115 116 117 118 119
bool WimaController::uploadOverrideRequired() const
{
    return _uploadOverrideRequired;
}

120 121 122 123 124 125 126 127 128 129
double WimaController::phaseDistance() const
{
    return _phaseDistance;
}

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

130 131 132 133 134
bool WimaController::vehicleHasLowBattery() const
{
    return _vehicleHasLowBattery;
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
135 136 137 138 139 140 141 142 143 144
long WimaController::snakeConnectionStatus() const
{
    return _snakeConnectionStatus;
}

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

145 146 147 148 149 150 151 152 153 154 155 156
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

void WimaController::setMissionController(MissionController *missionC)
{
    _missionController = missionC;
    emit missionControllerChanged();
}

157 158 159 160 161 162
/*!
 * \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
 */
163 164
void WimaController::setDataContainer(WimaDataContainer *container)
{
165 166
    if (container != nullptr) {
        if (_container != nullptr) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
167
           disconnect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
168 169
        }

170
        _container = container;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
171
        connect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
172 173 174 175 176

        emit dataContainerChanged();
    }
}

177 178 179 180 181 182 183 184 185
void WimaController::setUploadOverrideRequired(bool overrideRequired)
{
    if (_uploadOverrideRequired != overrideRequired) {
        _uploadOverrideRequired = overrideRequired;

        emit uploadOverrideRequiredChanged();
    }
}

186 187
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
188
    _calcNextPhase();
189 190
}

191
void WimaController::previousPhase()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
{    
    bool reverseBool = _reverse.rawValue().toBool();
    if (!reverseBool){
        int startIndex = _nextPhaseStartWaypointIndex.rawValue().toInt();
        if (startIndex > 0) {
            _nextPhaseStartWaypointIndex.setRawValue(1+std::max(_startWaypointIndex
                                                              - _maxWaypointsPerPhase.rawValue().toInt()
                                                              + _overlapWaypoints.rawValue().toInt(), 0));
        }
    }
    else {
        int startIndex = _nextPhaseStartWaypointIndex.rawValue().toInt();
        if (startIndex <= _missionItems.count()) {
            _nextPhaseStartWaypointIndex.setRawValue(1+std::min(_startWaypointIndex
                                                              + _maxWaypointsPerPhase.rawValue().toInt()
                                                              - _overlapWaypoints.rawValue().toInt(), _missionItems.count()-1));
        }
209
    }
210 211 212 213
}

void WimaController::resetPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
214 215 216 217 218 219 220
    bool reverseBool = _reverse.rawValue().toBool();
    if (!reverseBool) {
        _nextPhaseStartWaypointIndex.setRawValue(int(1));
    }
    else {
        _nextPhaseStartWaypointIndex.setRawValue(_missionItems.count());
    }
221 222
}

223
bool WimaController::uploadToVehicle()
224
{
225 226
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
        && _currentMissionItems.count() > 0) {
227 228 229 230 231 232 233 234 235 236
        setUploadOverrideRequired(true);
        return false;
    }

    return forceUploadToVehicle();
}

bool WimaController::forceUploadToVehicle()
{
    setUploadOverrideRequired(false);
237
    if (_currentMissionItems.count() < 1)
238
        return false;
239 240 241 242 243 244 245

    _missionController->removeAll();
    // set homeposition of settingsItem
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
246
        return false;
247 248 249 250 251 252 253
    }
    settingsItem->setCoordinate(_takeoffLandPostion);

    // copy mission items from _currentMissionItems to _missionController
    for (int i = 0; i < _currentMissionItems.count(); i++){
        SimpleMissionItem *item = _currentMissionItems.value<SimpleMissionItem *>(i);
        _missionController->insertSimpleMissionItem(*item, visuals->count());
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
        if (item->command() == MAV_CMD_DO_CHANGE_SPEED) {
        }
    }

    for (int i = 0; i < _missionController->visualItems()->count(); i++){
        SimpleMissionItem *item =  _missionController->visualItems()->value<SimpleMissionItem*>(i);
        if (item == nullptr)
            continue;
        if (item->command() == MAV_CMD_DO_CHANGE_SPEED) {
        }
    }
    for (int i = 0; i < _missionController->visualItems()->count(); i++){
        SimpleMissionItem *item =  _missionController->visualItems()->value<SimpleMissionItem*>(i);
        if (item == nullptr)
            continue;
269 270
    }

271
    _masterController->sendToVehicle();
272 273

    return true;
274
}
275

276 277 278
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    _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;
    bool retValue = _calcReturnPath(errorString);
    if (retValue == false) {
        qgcApp()->showMessage(errorString);
        return false;
    }
    return true;
302 303
}

304
void WimaController::executeSmartRTL()
305
{
306
    _executeSmartRTL();
307 308
}

309
void WimaController::initSmartRTL()
310
{
311 312
    _srtlReason = UserRequest;
    _initSmartRTL();
313 314
}

315 316 317 318 319 320
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

321
void WimaController::saveToCurrent()
322
{
323

324 325
}

326 327
void WimaController::saveToFile(const QString& filename)
{
328
    QString file = filename;
329 330
}

331
bool WimaController::loadFromCurrent()
332
{
333
    return true;
334 335 336 337
}

bool WimaController::loadFromFile(const QString &filename)
{
338
    QString file = filename;
339
    return true;
340 341 342
}


343

344
QJsonDocument WimaController::saveToJson(FileType fileType)
345
{
346 347 348 349
    if(fileType)
    {

    }
350
    return QJsonDocument();
351 352
}

353
bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
354 355 356
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
357
    QVector<QPointF> path2D;
358 359 360 361 362 363 364 365 366 367
    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;
}

368
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVector<QGeoCoordinate> &coordinateList)
369 370 371 372
{
    return extractCoordinateList(missionItems, coordinateList, 0, missionItems.count()-1);
}

373
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVector<QGeoCoordinate> &coordinateList, int startIndex, int endIndex)
374 375 376 377 378 379 380 381 382 383 384 385
{
    if (   startIndex >= 0
        && startIndex < missionItems.count()
        && endIndex >= 0
        && endIndex < missionItems.count()) {
        if (startIndex > endIndex) {
            if (!extractCoordinateList(missionItems, coordinateList, startIndex, missionItems.count()-1))
                return false;
            if (!extractCoordinateList(missionItems, coordinateList, 0, endIndex))
                return false;
        } else {
            for (int i = startIndex; i <= endIndex; i++) {
386
                SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(i);
387 388 389 390 391 392 393 394 395 396 397 398 399 400

                if (mItem == nullptr) {
                    coordinateList.clear();
                    return false;
                }
                coordinateList.append(mItem->coordinate());
            }
        }
    } else
        return false;

    return true;
}

401
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList)
402 403 404 405
{
    return extractCoordinateList(missionItems, coordinateList, 0 , missionItems.count()-1);
}

406
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList, int startIndex, int endIndex)
407
{
408
    QVector<QGeoCoordinate> geoCoordintateList;
409 410 411

    bool retValue = extractCoordinateList(missionItems, geoCoordintateList, startIndex, endIndex);

412 413 414
    if (!retValue)
        return false;

415 416
    for (int i = 0; i < geoCoordintateList.size(); i++) {
        QGeoCoordinate vertex = geoCoordintateList[i];
Valentin Platzgummer's avatar
Valentin Platzgummer committed
417 418
        if (   (qFuzzyIsNull(vertex.latitude()) && qFuzzyIsNull(vertex.longitude()))
            || !vertex.isValid())
419 420 421
            geoCoordintateList.removeAt(i);
    }

422 423 424 425 426 427
    for (auto coordinate : geoCoordintateList)
        coordinateList.append(QVariant::fromValue(coordinate));

    return true;
}

428 429 430 431 432 433 434
/*!
 * \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
435
bool WimaController::_fetchContainerData()
436
{
437
    // fetch only if valid, return true on success
438

439 440
    // reset visual items
    _visualItems.clear();
441 442
    _missionItems.clearAndDeleteContents();
    _currentMissionItems.clearAndDeleteContents();
443 444
    _waypointPath.clear();
    _currentWaypointPath.clear();
445
    _snakeTiles.clearAndDeleteContents();
446
    _snakeTileCenterPoints.clear();
447

448 449 450 451
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
452
    emit snakeTilesChanged();
453
    emit snakeTileCenterPointsChanged();
454

455
    _localPlanDataValid = false;
456

457 458 459 460
    if (_container == nullptr) {
        qWarning("WimaController::fetchContainerData(): No container assigned!");
        return false;
    }
461

462
    WimaPlanData planData = _container->pull();
463

464 465
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
466

467 468 469 470
    int areaCounter = 0;
    int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
471

472 473 474 475
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
            _visualItems.append(&_serviceArea);
476

477 478
            continue;
        }
479

480 481 482 483
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
            _visualItems.append(&_measurementArea);
484

485
            continue;
486
        }
487

488 489 490 491
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
492

493 494
            continue;
        }
495

496 497 498 499
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
            _visualItems.append(&_joinedArea);
500

501 502
            continue;
        }
503

504 505 506
        if (areaCounter >= numAreas)
            break;
    }
507

508
    // extract mission items
509
    QList<MissionItem> tempMissionItems = planData.missionItems();
510 511
    if (tempMissionItems.size() < 1)
        return false;
512

513 514 515
    // create mission items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();
516

517 518
    // create SimpleMissionItem by using _missionController
    for ( int i = 0; i < tempMissionItems.size(); i++) {
519
        _missionController->insertSimpleMissionItem(tempMissionItems[i], missionControllerVisualItems->count());
520 521 522 523 524 525 526
    }
    // copy mission items from _missionController to _missionItems
    for ( int i = 1; i < missionControllerVisualItems->count(); i++) {
        SimpleMissionItem *visualItem     = qobject_cast<SimpleMissionItem *>((*missionControllerVisualItems)[i]);
        if (visualItem == nullptr) {
            qWarning("WimaController::fetchContainerData(): Nullptr at SimpleMissionItem!");
            return false;
527
        }
528 529 530 531 532
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
        _missionItems.append(visualItemCopy);
    }
    if (areaCounter != numAreas)
        return false;
533

Valentin Platzgummer's avatar
Valentin Platzgummer committed
534
    if (!_setTakeoffLandPosition())
535
        return false;
536

Valentin Platzgummer's avatar
Valentin Platzgummer committed
537
    _updateWaypointPath();
538

539
    // set _nextPhaseStartWaypointIndex to 1
Valentin Platzgummer's avatar
Valentin Platzgummer committed
540
    disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
541 542
    bool reverse = _reverse.rawValue().toBool();
    _nextPhaseStartWaypointIndex.setRawValue(reverse? _missionItems.count() : int(1));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
543
    connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
544

Valentin Platzgummer's avatar
Valentin Platzgummer committed
545
    if(!_calcNextPhase())
546
        return false;
547

Valentin Platzgummer's avatar
Valentin Platzgummer committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    // 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;
569

Valentin Platzgummer's avatar
Valentin Platzgummer committed
570 571 572 573
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
    // Check if scenario is defined.
    if ( !_verifyScenarioDefinedWithErrorMessage() )
        return false;

    // Get tiles.
    const auto &tiles = _scenario.getTiles();
    const auto &cps = _scenario.getTileCenterPoints();
    for ( int i=0; i < int(tiles.size()); ++i ) {
        const auto &tile = tiles[i];
        WimaAreaData *QTile = new WimaAreaData(this);
        for ( const auto &vertex : tile) {
            QGeoCoordinate QVertex(vertex[0], vertex[1], vertex[2]);
            QTile->append(QVertex);
        }
        const auto &centerPoint = cps[i];
        QGeoCoordinate QCenterPoint(centerPoint[0], centerPoint[1], centerPoint[2]);
        QTile->setCenter(QCenterPoint);
        _snakeTiles.append(QTile);
592
        _snakeTileCenterPoints.append(QVariant::fromValue(QCenterPoint));
593 594
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
595

596
    emit visualItemsChanged();
597
    emit missionItemsChanged();
598 599
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
600

601 602
    _localPlanDataValid = true;
    return true;
603 604
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
605
bool WimaController::_calcNextPhase()
606
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
607 608 609
    if (_missionItems.count() < 1) {
        _startWaypointIndex = 0;
        _endWaypointIndex = 0;
610
        return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
611
    }
612

613
    _currentMissionItems.clearAndDeleteContents();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
614

Valentin Platzgummer's avatar
Valentin Platzgummer committed
615 616 617 618
    bool reverse = _reverse.rawValue().toBool(); // Reverses the phase direction. Phases go from high to low waypoint numbers, if true.
    int startIndex = _nextPhaseStartWaypointIndex.rawValue().toInt()-1;
    if (!reverse) {
        if (startIndex > _missionItems.count()-1)
619 620 621
            return false;
    }
    else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
622
        if (startIndex < 0)
623
            return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
624 625
    }    
    _startWaypointIndex = startIndex;
626

Valentin Platzgummer's avatar
Valentin Platzgummer committed
627
    int maxWaypointsPerPhase = _maxWaypointsPerPhase.rawValue().toInt();
628
    // determine end waypoint index
Valentin Platzgummer's avatar
Valentin Platzgummer committed
629 630 631
    bool lastMissionPhaseReached = false;
    if (!reverse) {
        _endWaypointIndex = std::min(_startWaypointIndex + maxWaypointsPerPhase - 1, _missionItems.count()-1);
632
        if (_endWaypointIndex == _missionItems.count() - 1)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
633
            lastMissionPhaseReached = true;
634 635
    }
    else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
636
        _endWaypointIndex = std::max(_startWaypointIndex - maxWaypointsPerPhase + 1, 0);
637
        if (_endWaypointIndex == 0)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
638
            lastMissionPhaseReached = true;
639 640
    }

641

642
    // extract waypoints
643
    QVector<QGeoCoordinate> CSWpList; // list with potential waypoints (from _missionItems), for _currentMissionItems
644

Valentin Platzgummer's avatar
Valentin Platzgummer committed
645
    if (!reverse) {
646
        if (!extractCoordinateList(_missionItems, CSWpList, _startWaypointIndex, _endWaypointIndex)) {
647 648 649 650
            qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
            return false;
        }
    } else {
651
        if (!extractCoordinateList(_missionItems, CSWpList, _endWaypointIndex, _startWaypointIndex)) {
652 653 654 655 656
            qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
            return false;
        }

        // reverse path
657 658
        QVector<QGeoCoordinate> reversePath;
        for (QGeoCoordinate c : CSWpList)
659
            reversePath.prepend(c);
660 661
        CSWpList.clear();
        CSWpList.append(reversePath);
662
    }
663

664

665 666 667 668 669 670
    // calculate phase length
    _measurementPathLength = 0;
    for (int i = 0; i < CSWpList.size()-1; ++i)
        _measurementPathLength += CSWpList[i].distanceTo(CSWpList[i+1]);


671
    // set start waypoint index for next phase
Valentin Platzgummer's avatar
Valentin Platzgummer committed
672
    if (!lastMissionPhaseReached) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
673
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
674
        if (!reverse) {
675 676 677 678 679 680 681 682 683
            int untruncated = std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0);
            int truncated   = std::min(untruncated , _missionItems.count()-1);
            _nextPhaseStartWaypointIndex.setRawValue(truncated  + 1);
        }
        else {
            int untruncated = std::min(_endWaypointIndex - 1 + _overlapWaypoints.rawValue().toInt(), _missionItems.count()-1);
            int truncated   = std::max(untruncated , 0);
            _nextPhaseStartWaypointIndex.setRawValue(truncated  + 1);
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
684
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
685
    }
686 687

    // calculate path from home to first waypoint
688
    QVector<QGeoCoordinate> arrivalPath;
689 690 691 692
    if (!_takeoffLandPostion.isValid()){
        qWarning("WimaController::calcNextPhase(): _takeoffLandPostion not valid.");
        return false;
    }
693
    if ( !calcShortestPath(_takeoffLandPostion, CSWpList.first(), arrivalPath) ) {
694 695
        qWarning("WimaController::calcNextPhase(): Not able to calc path from home to first waypoint.");
        return false;
696
    }
697 698 699 700 701 702

    // calculate arrival path length
    _arrivalPathLength = 0;
    for (int i = 0; i < arrivalPath.size()-1; ++i)
        _arrivalPathLength += arrivalPath[i].distanceTo(arrivalPath[i+1]);

703
    arrivalPath.removeFirst();
704 705

    // calculate path from last waypoint to home
706 707
    QVector<QGeoCoordinate> returnPath;
    if ( !calcShortestPath(CSWpList.last(), _takeoffLandPostion, returnPath) ) {
708
        qWarning("WimaController::calcNextPhase(): Not able to calc path from home to last waypoint.");
709
        return false;
710 711 712 713 714 715 716
    }

    // calculate arrival path length
    _returnPathLength = 0;
    for (int i = 0; i < returnPath.size()-1; ++i)
        _returnPathLength += returnPath[i].distanceTo(returnPath[i+1]);

717 718
    returnPath.removeFirst();
    returnPath.removeLast();
719

720

721

722 723 724
    // create Mission Items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();
725 726 727 728

    // set homeposition of settingsItem
    MissionSettingsItem* settingsItem = missionControllerVisuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
729 730
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
731 732 733
    }
    settingsItem->setCoordinate(_takeoffLandPostion);

734
    // set takeoff position for first mission item (bug)
735
    missionController()->insertSimpleMissionItem(_takeoffLandPostion, 1);
736 737 738 739
    SimpleMissionItem *takeoffItem = missionControllerVisuals->value<SimpleMissionItem*>(1);
    if (takeoffItem == nullptr) {
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
740
    }
741
    takeoffItem->setCoordinate(_takeoffLandPostion);
742

743
    // create change speed item, after take off
744 745 746 747 748 749
    _missionController->insertSimpleMissionItem(_takeoffLandPostion, 2);
    SimpleMissionItem *speedItem = missionControllerVisuals->value<SimpleMissionItem*>(2);
    if (speedItem == nullptr) {
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
750
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED);// set coordinate must be after setCommand (setCommand sets coordinate to zero)
751
    speedItem->setCoordinate(_takeoffLandPostion);
752 753 754 755 756 757 758 759 760 761 762 763 764 765
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());

    // insert arrival path
    for (auto coordinate : arrivalPath)
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());

    // create change speed item, after arrival path
    int index = missionControllerVisuals->count();
    _missionController->insertSimpleMissionItem(CSWpList.first(), index);
    speedItem = missionControllerVisuals->value<SimpleMissionItem*>(index);
    if (speedItem == nullptr) {
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
766
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
767
    speedItem->setCoordinate(CSWpList.first());
768 769
    speedItem->missionItem().setParam2(_flightSpeed.rawValue().toDouble());

770 771 772 773 774 775 776 777 778 779 780 781
    // insert Circular Survey coordinates
    for (auto coordinate : CSWpList)
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());

    // create change speed item, after circular survey
    index = missionControllerVisuals->count();
    _missionController->insertSimpleMissionItem(CSWpList.last(), index);
    speedItem = missionControllerVisuals->value<SimpleMissionItem*>(index);
    if (speedItem == nullptr) {
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
782 783
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
    speedItem->setCoordinate(CSWpList.last());
784 785 786 787 788
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());

    // insert return path coordinates
    for (auto coordinate : returnPath)
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());
789 790

    // set land command for last mission item
791 792 793
    index = missionControllerVisuals->count();
    _missionController->insertSimpleMissionItem(_takeoffLandPostion, index);
    SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(index);
794
    if (landItem == nullptr) {
795 796
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
797
    }
798
    _missionController->setLandCommand(*landItem);
799

800
    // copy to _currentMissionItems
801 802 803
    for ( int i = 1; i < missionControllerVisuals->count(); i++) {
        SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
        if (visualItem == nullptr) {
804
            qWarning("WimaController::calcNextPhase(): Nullptr at SimpleMissionItem!");
805
            _currentMissionItems.clear();
806
            return false;
807
        }
808

809
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
810 811
        _currentMissionItems.append(visualItemCopy);
    }
812

813 814 815 816 817 818
    double dist = 0;
    double time = 0;
    if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
        qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
    _setPhaseDistance(dist);
    _setPhaseDuration(time);
819
    _missionController->removeAll(); // remove items from _missionController, will be added on upload
Valentin Platzgummer's avatar
Valentin Platzgummer committed
820
    _updateAltitude();
821

Valentin Platzgummer's avatar
Valentin Platzgummer committed
822
    _updateCurrentPath();
823
    emit currentMissionItemsChanged();
824 825

    return true;
826
}
827

Valentin Platzgummer's avatar
Valentin Platzgummer committed
828
void WimaController::_updateWaypointPath()
829 830
{
    _waypointPath.clear();
831
    extractCoordinateList(_missionItems, _waypointPath, 0, _missionItems.count()-1);
832

833 834
    emit waypointPathChanged();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
835
void WimaController::_updateCurrentPath()
836 837
{
    _currentWaypointPath.clear();
838
    extractCoordinateList(_currentMissionItems, _currentWaypointPath, 0, _currentMissionItems.count()-1);
839

840 841
    emit currentWaypointPathChanged();
}
842

Valentin Platzgummer's avatar
Valentin Platzgummer committed
843
void WimaController::_updateNextWaypoint()
844 845
{
    if (_endWaypointIndex < _missionItems.count()-2) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
846
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
847
        _nextPhaseStartWaypointIndex.setRawValue(1 + std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
848
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
849 850 851
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
852
void WimaController::_recalcCurrentPhase()
853
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
854
    disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
855
    _nextPhaseStartWaypointIndex.setRawValue(_startWaypointIndex + 1);    
Valentin Platzgummer's avatar
Valentin Platzgummer committed
856 857
    connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    _calcNextPhase();
858 859
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
860
bool WimaController::_setTakeoffLandPosition()
861 862 863 864 865 866
{
    _takeoffLandPostion.setAltitude(0);
    _takeoffLandPostion.setLongitude(_serviceArea.center().longitude());
    _takeoffLandPostion.setLatitude(_serviceArea.center().latitude());

    return true;
867 868
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
869
void WimaController::_updateflightSpeed()
870
{
871 872 873 874 875 876 877 878 879
    int speedItemCounter = 0;
    for (int i = 0; i < _currentMissionItems.count(); i++) {
        SimpleMissionItem *item = _currentMissionItems.value<SimpleMissionItem *>(i);
        if (item != nullptr && item->command() == MAV_CMD_DO_CHANGE_SPEED) {
            speedItemCounter++;
            if (speedItemCounter == 2) {
                item->missionItem().setParam2(_flightSpeed.rawValue().toDouble());
            }
        }
880
    }
881

882 883 884 885
    _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                      + (_arrivalPathLength + _returnPathLength)
                      / _arrivalReturnSpeed.rawValue().toDouble());

886 887 888 889
    if (speedItemCounter != 3)
        qWarning("WimaController::updateflightSpeed(): internal error.");
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
890
void WimaController::_updateArrivalReturnSpeed()
891 892 893 894 895 896 897 898 899 900
{
    int speedItemCounter = 0;
    for (int i = 0; i < _currentMissionItems.count(); i++) {
        SimpleMissionItem *item = _currentMissionItems.value<SimpleMissionItem *>(i);
        if (item != nullptr && item->command() == MAV_CMD_DO_CHANGE_SPEED) {
            speedItemCounter++;
            if (speedItemCounter != 2) {
                item->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());
            }
        }
901
    }
902

903 904 905 906
    _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                      + (_arrivalPathLength + _returnPathLength)
                      / _arrivalReturnSpeed.rawValue().toDouble());

907 908 909
    if (speedItemCounter != 3)
        qWarning("WimaController::updateArrivalReturnSpeed(): internal error.");

910 911
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
912
void WimaController::_updateAltitude()
913 914 915 916 917 918 919 920 921 922 923
{
    for (int i = 0; i < _currentMissionItems.count(); i++) {
        SimpleMissionItem *item = _currentMissionItems.value<SimpleMissionItem *>(i);
        if (item == nullptr) {
            qWarning("WimaController::updateAltitude(): nullptr");
            return;
        }
        item->altitude()->setRawValue(_altitude.rawValue().toDouble());
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
924
void WimaController::_checkBatteryLevel()
925
{
926 927 928 929 930
    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();
931

Valentin Platzgummer's avatar
Valentin Platzgummer committed
932
    if (managerVehicle != nullptr && enabled == true) {
933 934 935 936 937 938 939 940
        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) {
941 942 943 944 945

                if (_missionController->remainingTime() <= minTime) {
                    _lowBatteryHandlingTriggered = true;
                }
                else {
946 947 948
                    _lowBatteryHandlingTriggered = true;
                    _srtlReason = BatteryLow;
                    _initSmartRTL();
949 950 951 952 953 954 955 956 957 958 959
                }
            }
        }
        else {
            _setVehicleHasLowBattery(false);
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
960 961 962 963
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
    static EventTicker snakeEventLoopTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
964
    static EventTicker ros_bridgeTicker(EVENT_TIMER_INTERVAL, 1000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
965 966 967 968 969 970 971 972 973

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

    // Snake flight plan update necessary?
    if ( snakeEventLoopTicker.ready() ) {
        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {

974
            _snakeProgress.clear();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
975
            long n = _scenario.getTilesENU().size();
976
            _snakeProgress.reserve(n);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
977 978
            std::srand(time(NULL));
            for (long i=0; i<n; ++i){
979
                int r{rand()%200};
Valentin Platzgummer's avatar
Valentin Platzgummer committed
980 981
                if ( r > 100 )
                    r = 100;
982
                _snakeProgress.append(r);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
983 984 985
            }

            _snakeWorker.setScenario(_scenario);
986
            _snakeWorker.setProgress(_snakeProgress);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
987 988
            _snakeWorker.setLineDistance(_snakeLineDistance.rawValue().toDouble());
            _snakeWorker.setMinTransectLength(_snakeMinTransectLength.rawValue().toDouble());
989
            _setSnakeCalcInProgress(true);
990
            _snakeWorker.start();
991 992

            emit snakeProgressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
993 994
        }
    }
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

    if (ros_bridgeTicker.ready()) {
        using namespace ros_bridge;

        class Time time(1, 2);
        JsonGenerator<> gen;
        gen.set(time);
        rapidjson::Document doc(rapidjson::kObjectType);

        // Write to stdout
        cout << "Return value : " << gen.run(MessageType::Time , doc, doc.GetAllocator()) << std::endl;
        rapidjson::OStreamWrapper out(std::cout);
        rapidjson::Writer<rapidjson::OStreamWrapper> writer(out);
        doc.Accept(writer);
        std::cout << std::endl;
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1011 1012 1013
}

void WimaController::_smartRTLCleanUp(bool flying)
1014 1015 1016 1017 1018 1019
{

    if ( !flying) { // vehicle has landed
        if (_executingSmartRTL) {
            _executingSmartRTL = false;
            _loadCurrentMissionItemsFromBuffer();
1020 1021
            _setPhaseDistance(_phaseDistanceBuffer);
            _setPhaseDuration(_phaseDurationBuffer);
1022
            _showAllMissionItems.setRawValue(true);
1023 1024
            _missionController->removeAllFromVehicle();
            _missionController->removeAll();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1025
            disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1026 1027 1028 1029
        }
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1030
void WimaController::_enableDisableLowBatteryHandling(QVariant enable)
1031 1032
{
    if (enable.toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1033
        _eventTimer.start();
1034
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1035
        _eventTimer.stop();
1036 1037 1038
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1039
void WimaController::_reverseChangedHandler()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1040
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1041
    disconnect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1042
    _nextPhaseStartWaypointIndex.setRawValue(_endWaypointIndex+1);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1043
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1044

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1045
    _calcNextPhase();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1046 1047
}

1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
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();
    }
}

1066 1067 1068 1069 1070 1071 1072
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();
1073 1074 1075 1076 1077 1078 1079 1080 1081
    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;
    }
1082 1083

   return true;
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
}

bool WimaController::_calcReturnPath(QString &errorSring)
{
    // it is assumed that checkSmartRTLPreCondition() was called first and true was returned


    Vehicle *managerVehicle = masterController()->managerVehicle();

    QGeoCoordinate currentVehiclePosition = managerVehicle->coordinate();
    // check if vehicle inside _joinedArea, this statement is not inside checkSmartRTLPreCondition() because during checkSmartRTLPreCondition() vehicle is not paused yet
    if (!_joinedArea.containsCoordinate(currentVehiclePosition)) {
        errorSring.append(tr("Vehicle not inside joined area. Action not supported."));
        return false;
    }

    // calculate return path
1101
    QVector<QGeoCoordinate> returnPath;
1102 1103 1104 1105 1106 1107 1108 1109
    calcShortestPath(currentVehiclePosition, _takeoffLandPostion, returnPath);
    // successful?
    if (returnPath.isEmpty()) {
        errorSring.append(tr("Not able to calculate return path."));
        return false;
    }
    // qWarning() << "returnPath.size()" << returnPath.size();

1110
    _saveCurrentMissionItemsToBuffer();
1111 1112 1113
    _phaseDistanceBuffer = _phaseDistance;
    _phaseDurationBuffer = _phaseDuration;

1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142

    // create Mission Items
    removeFromVehicle();
    QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();

    // set homeposition of settingsItem
    MissionSettingsItem* settingsItem = missionControllerVisuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
        qWarning("WimaController: nullptr");
        return false;
    }
    settingsItem->setCoordinate(_takeoffLandPostion);

    // copy from returnPath to _missionController
    QGeoCoordinate speedItemCoordinate = returnPath.first();
    for (auto coordinate : returnPath) {
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());
    }
    //qWarning() << "missionControllerVisuals->count()" << missionControllerVisuals->count();

    // create speed item
    int speedItemIndex = 1;
    _missionController->insertSimpleMissionItem(speedItemCoordinate, speedItemIndex);
    SimpleMissionItem *speedItem = missionControllerVisuals->value<SimpleMissionItem*>(speedItemIndex);
    if (speedItem == nullptr) {
        qWarning("WimaController: nullptr");
        return false;
    }
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED);
1143 1144
    speedItem->setCoordinate(speedItemCoordinate);
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178

    // set second item command to ordinary waypoint (is takeoff)
    SimpleMissionItem *secondItem = missionControllerVisuals->value<SimpleMissionItem*>(2);
    if (secondItem == nullptr) {
        qWarning("WimaController: nullptr");
        return false;
    }
    secondItem->setCoordinate(speedItemCoordinate);
    secondItem->setCommand(MAV_CMD_NAV_WAYPOINT);


    // set land command for last mission item
    SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(missionControllerVisuals->count()-1);
    if (landItem == nullptr) {
        qWarning("WimaController: nullptr");
        return false;
    }
    _missionController->setLandCommand(*landItem);

    // copy to _currentMissionItems
    //qWarning() << "_currentMissionItems.count()" << _currentMissionItems.count();
    for ( int i = 1; i < missionControllerVisuals->count(); i++) {
        SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
        if (visualItem == nullptr) {
            qWarning("WimaController: Nullptr at SimpleMissionItem!");
            _currentMissionItems.clear();
            return false;
        }

        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
        _currentMissionItems.append(visualItemCopy);
    }
    //qWarning() << "_currentMissionItems.count()" << _currentMissionItems.count();

1179 1180 1181 1182 1183 1184
    double dist = 0;
    double time = 0;
    if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
        qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
    _setPhaseDistance(dist);
    _setPhaseDuration(time);
1185
    _missionController->removeAll(); // remove items from _missionController, will be added on upload
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1186
    _updateAltitude();
1187

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1188
    _updateCurrentPath();
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207
    emit currentMissionItemsChanged();


    //qWarning() << "_currentMissionItems.count()" << _currentMissionItems.count();
    _showAllMissionItems.setRawValue(false);
    managerVehicle->trajectoryPoints()->clear();

    return true;
}

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

        emit vehicleHasLowBatteryChanged();
    }
}

1208 1209 1210 1211 1212 1213 1214 1215
void WimaController::_initSmartRTL()
{
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

    if (_checkSmartRTLPreCondition(errorString) == true) {
        _masterController->managerVehicle()->pauseVehicle();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1216
        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
        if (_calcReturnPath(errorString)) {
            _executingSmartRTL = true;
            attemptCounter = 0;

            switch(_srtlReason) {
                case BatteryLow:
                    emit returnBatteryLowConfirmRequired();
                break;
                case UserRequest:
                    emit returnUserRequestConfirmRequired();
                break;
                default:
                    qWarning("\nWimaController::_initSmartRTL: default case reached!");
            }

            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);
    }
}

void WimaController::_executeSmartRTL()
1245 1246 1247 1248 1249
{
    forceUploadToVehicle();
    masterController()->managerVehicle()->startMission();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
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);
    }
1281
    return value;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1282 1283
}

1284 1285 1286 1287
void WimaController::_snakeStoreWorkerResults()
{
    _missionItems.clearAndDeleteContents();
    const WorkerResult_t &r = _snakeWorker.getResult();
1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
    _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);
    QVector<MissionItem> missionItems;
    missionItems.reserve(int(n));

1300 1301 1302 1303 1304 1305
    // Remove all items from mission controller.
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
1306
    unsigned  long endIdx = r.returnPathIdx.first();
1307
    QVector<QGeoCoordinate> waypoints;
1308 1309
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
        QGeoCoordinate wp{r.waypoints[int(i)].value<QGeoCoordinate>()};
1310
        waypoints.append(wp);
1311 1312 1313
    }

    // create SimpleMissionItem by using _missionController
1314 1315 1316 1317 1318 1319 1320
    long insertIdx = missionControllerVisualItems->count();
    for (auto wp : waypoints)
        _missionController->insertSimpleMissionItem(wp, insertIdx++);
    SimpleMissionItem *takeOffItem = missionControllerVisualItems->value<SimpleMissionItem*>(1);
    if (takeOffItem == nullptr) {
        qWarning("WimaController::_snakeStoreWorkerResults(): Nullptr at SimpleMissionItem!");
        return;
1321
    }
1322 1323 1324
    takeOffItem->setCommand(MAV_CMD_NAV_WAYPOINT);
    takeOffItem->setCoordinate(waypoints[0]);

1325
    // copy mission items from _missionController to _missionItems
1326
    cout.precision(10);
1327 1328 1329
    for ( int i = 1; i < missionControllerVisualItems->count(); i++) {
        SimpleMissionItem *visualItem     = qobject_cast<SimpleMissionItem *>((*missionControllerVisualItems)[i]);
        if (visualItem == nullptr) {
1330
            qWarning("WimaController::_snakeStoreWorkerResults(): Nullptr at SimpleMissionItem!");
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
            return;
        }
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
        _missionItems.append(visualItemCopy);
    }

    _updateWaypointPath();

    // set _nextPhaseStartWaypointIndex to 1
    disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    bool reverse = _reverse.rawValue().toBool();
    _nextPhaseStartWaypointIndex.setRawValue(reverse? _missionItems.count() : int(1));
    connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);

1345 1346
    _calcNextPhase();
    emit missionItemsChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1347 1348
}

1349 1350 1351 1352 1353 1354
void WimaController::_loadCurrentMissionItemsFromBuffer()
{
    _currentMissionItems.clear();
    int numItems = _missionItemsBuffer.count();
    for (int i = 0; i < numItems; i++)
        _currentMissionItems.append(_missionItemsBuffer.removeAt(0));
1355

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1356
    _updateCurrentPath();
1357 1358 1359 1360 1361 1362
}


void WimaController::_saveCurrentMissionItemsToBuffer()
{
    _missionItemsBuffer.clear();
1363 1364
    int numCurrentMissionItems = _currentMissionItems.count();
    for (int i = 0; i < numCurrentMissionItems; i++)
1365 1366 1367
        _missionItemsBuffer.append(_currentMissionItems.removeAt(0));
}

1368 1369