WimaController.cc 52.2 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "WimaController.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
2
#include "utilities.h"
3
#include "ros_bridge/include/messages.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 8

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

11 12
#include "QVector3D"

13

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

using namespace snake;
using namespace snake_geometry;
36

37
WimaController::WimaController(QObject *parent)
38 39 40 41 42 43 44 45 46 47 48
    : 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])
49
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
50
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
51 52
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
53
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
54
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
55
    , _reverse                  (settingsGroup, _metaDataMap[reverseName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
56
    , _endWaypointIndex         (0)
57 58
    , _startWaypointIndex       (0)
    , _uploadOverrideRequired   (false)
59
    , _measurementPathLength    (-1)
60 61
    , _arrivalPathLength        (-1)
    , _returnPathLength         (-1)
62 63
    , _phaseDistance            (-1)
    , _phaseDuration            (-1)
64 65
    , _phaseDistanceBuffer      (-1)
    , _phaseDurationBuffer      (-1)
66 67 68
    , _vehicleHasLowBattery         (false)
    , _lowBatteryHandlingTriggered  (false)
    , _executingSmartRTL            (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
69 70 71 72 73 74 75 76
    , _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])
77 78 79
{
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
80 81 82 83 84 85 86
    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);
87

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

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

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

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

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

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

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

117 118 119 120 121
bool WimaController::uploadOverrideRequired() const
{
    return _uploadOverrideRequired;
}

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

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

132 133 134 135 136
bool WimaController::vehicleHasLowBattery() const
{
    return _vehicleHasLowBattery;
}

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

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

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

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

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

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

        emit dataContainerChanged();
    }
}

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

        emit uploadOverrideRequiredChanged();
    }
}

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

193
void WimaController::previousPhase()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
{    
    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));
        }
211
    }
212 213 214 215
}

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

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

    return forceUploadToVehicle();
}

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

    _missionController->removeAll();
    // set homeposition of settingsItem
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
248
        return false;
249 250 251 252 253 254 255
    }
    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());
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
        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;
271 272
    }

273
    _masterController->sendToVehicle();
274 275

    return true;
276
}
277

278 279 280
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
    _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;
304 305
}

306
void WimaController::executeSmartRTL()
307
{
308
    _executeSmartRTL();
309 310
}

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

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

323
void WimaController::saveToCurrent()
324
{
325

326 327
}

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

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

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


345

346
QJsonDocument WimaController::saveToJson(FileType fileType)
347
{
348 349 350 351
    if(fileType)
    {

    }
352
    return QJsonDocument();
353 354
}

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

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

375
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVector<QGeoCoordinate> &coordinateList, int startIndex, int endIndex)
376 377 378 379 380 381 382 383 384 385 386 387
{
    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++) {
388
                SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(i);
389 390 391 392 393 394 395 396 397 398 399 400 401 402

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

    return true;
}

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

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

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

414 415 416
    if (!retValue)
        return false;

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

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

    return true;
}

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

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

450 451 452 453
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
454
    emit snakeTilesChanged();
455
    emit snakeTileCenterPointsChanged();
456

457
    _localPlanDataValid = false;
458

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

464
    WimaPlanData planData = _container->pull();
465

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

469 470 471 472
    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];
473

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

479 480
            continue;
        }
481

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

487
            continue;
488
        }
489

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

495 496
            continue;
        }
497

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

503 504
            continue;
        }
505

506 507 508
        if (areaCounter >= numAreas)
            break;
    }
509

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

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

519 520
    // create SimpleMissionItem by using _missionController
    for ( int i = 0; i < tempMissionItems.size(); i++) {
521
        _missionController->insertSimpleMissionItem(tempMissionItems[i], missionControllerVisualItems->count());
522 523 524 525 526 527 528
    }
    // 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;
529
        }
530 531 532 533 534
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
        _missionItems.append(visualItemCopy);
    }
    if (areaCounter != numAreas)
        return false;
535

Valentin Platzgummer's avatar
Valentin Platzgummer committed
536
    if (!_setTakeoffLandPosition())
537
        return false;
538

Valentin Platzgummer's avatar
Valentin Platzgummer committed
539
    _updateWaypointPath();
540

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
547
    if(!_calcNextPhase())
548
        return false;
549

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

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

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593
    // 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);
594
        _snakeTileCenterPoints.append(QVariant::fromValue(QCenterPoint));
595 596
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
597

598
    emit visualItemsChanged();
599
    emit missionItemsChanged();
600 601
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
602

603 604
    _localPlanDataValid = true;
    return true;
605 606
}

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

615
    _currentMissionItems.clearAndDeleteContents();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
616

Valentin Platzgummer's avatar
Valentin Platzgummer committed
617 618 619 620
    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)
621 622 623
            return false;
    }
    else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
624
        if (startIndex < 0)
625
            return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
626 627
    }    
    _startWaypointIndex = startIndex;
628

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

643

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

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

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

666

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


673
    // set start waypoint index for next phase
Valentin Platzgummer's avatar
Valentin Platzgummer committed
674
    if (!lastMissionPhaseReached) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
675
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
676
        if (!reverse) {
677 678 679 680 681 682 683 684 685
            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
686
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
687
    }
688 689

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

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

705
    arrivalPath.removeFirst();
706 707

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

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

719 720
    returnPath.removeFirst();
    returnPath.removeLast();
721

722

723

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

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

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

745
    // create change speed item, after take off
746 747 748 749 750 751
    _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
752
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED);// set coordinate must be after setCommand (setCommand sets coordinate to zero)
753
    speedItem->setCoordinate(_takeoffLandPostion);
754 755 756 757 758 759 760 761 762 763 764 765 766 767
    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
768
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
769
    speedItem->setCoordinate(CSWpList.first());
770 771
    speedItem->missionItem().setParam2(_flightSpeed.rawValue().toDouble());

772 773 774 775 776 777 778 779 780 781 782 783
    // 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
784 785
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
    speedItem->setCoordinate(CSWpList.last());
786 787 788 789 790
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());

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

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

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

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
824
    _updateCurrentPath();
825
    emit currentMissionItemsChanged();
826 827

    return true;
828
}
829

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

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

842 843
    emit currentWaypointPathChanged();
}
844

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

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

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

    return true;
869 870
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
871
void WimaController::_updateflightSpeed()
872
{
873 874 875 876 877 878 879 880 881
    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());
            }
        }
882
    }
883

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
892
void WimaController::_updateArrivalReturnSpeed()
893 894 895 896 897 898 899 900 901 902
{
    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());
            }
        }
903
    }
904

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

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

912 913
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
914
void WimaController::_updateAltitude()
915 916 917 918 919 920 921 922 923 924 925
{
    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
926
void WimaController::_checkBatteryLevel()
927
{
928 929 930 931 932
    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();
933

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

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

    }
}

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

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

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

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

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

            emit snakeProgressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
995 996
        }
    }
997

998
    if (rosBridgeTicker.ready()) {
999 1000
        using namespace ros_bridge;

1001 1002

        // Time
1003 1004 1005
        class Time time(1, 2);
        rapidjson::Document doc(rapidjson::kObjectType);
        // Write to stdout
1006 1007
        cout << "Time" << endl;
        cout << "Return value : " << time.toJson(doc, doc.GetAllocator()) << std::endl;
1008 1009 1010
        rapidjson::OStreamWrapper out(std::cout);
        rapidjson::Writer<rapidjson::OStreamWrapper> writer(out);
        doc.Accept(writer);
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
        std::cout << std::endl << std::endl;


        // Header
        Header header(1, &time, "/map");
        // Write to stdout
        cout << "Header" << endl;
        rapidjson::Document doc2(rapidjson::kObjectType);
        cout << "Return value : " << header.toJson(doc2, doc2.GetAllocator()) << std::endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer2(out);
        doc2.Accept(writer2);
        std::cout << std::endl << std::endl;


        // Point
        QVector3D p1(1, 2, 3);
        typedef Point32<QVector3D> PointWrapper;
        PointWrapper pw1(&p1);
        // Write to stdout
        cout << "Point" << endl;
        rapidjson::Document doc3(rapidjson::kObjectType);
        cout << "Return value : " << pw1.toJson(doc3, doc3.GetAllocator()) << std::endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer3(out);
        doc3.Accept(writer3);
        std::cout << std::endl << std::endl;


        // Polygon
        QVector3D p2(4, 5, 6);
        QVector3D p3(7, 8, 9);
        PointWrapper pw2(&p2);
        PointWrapper pw3(&p3);
        QVector<PointWrapper> pointList;
        pointList.append(pw1);
        pointList.append(pw2);
        pointList.append(pw3);
        typedef Polygon<PointWrapper, QVector> Poly;
        Poly polyW(&pointList);
        // Write to stdout
        cout << "Polygon" << endl;
        rapidjson::Document doc4(rapidjson::kObjectType);
        cout << "Return value : " << polyW.toJson(doc4, doc4.GetAllocator()) << std::endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer4(out);
        doc4.Accept(writer4);
        std::cout << std::endl << std::endl;


        // PolygonStamped
        typedef PolygonStamped<PointWrapper, Poly> PolyWrapper;
        PolyWrapper polyStamped(&header, &polyW);
        // Write to stdout
        cout << "PolygonStamped" << endl;
        rapidjson::Document doc5(rapidjson::kObjectType);
        cout << "Return value : " << polyStamped.toJson(doc5, doc5.GetAllocator()) << std::endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer5(out);
        doc5.Accept(writer5);
        std::cout << std::endl << std::endl;


        // PolygonArray
        // Define second Polygon
        QVector3D p4(10, 11, 12);
        QVector3D p5(13, 14, 15);
        QVector3D p6(16, 17, 18);
        PointWrapper pw4(&p4);
        PointWrapper pw5(&p5);
        PointWrapper pw6(&p6);
        QVector<PointWrapper> pointList2{pw4, pw5, pw6};
        Poly polyW2(&pointList2);
        typedef PolygonArray<PointWrapper, Poly, QVector> PolyArray;
        QVector<Poly*> polygons{&polyW, &polyW2};
        PolyArray polyArray{};
        // Write to stdout
        cout << "PolygonStamped" << endl;
        rapidjson::Document doc5(rapidjson::kObjectType);
        cout << "Return value : " << polyStamped.toJson(doc5, doc5.GetAllocator()) << std::endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer5(out);
        doc5.Accept(writer5);
        std::cout << std::endl << std::endl;
1090
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1091 1092 1093
}

void WimaController::_smartRTLCleanUp(bool flying)
1094 1095 1096 1097 1098 1099
{

    if ( !flying) { // vehicle has landed
        if (_executingSmartRTL) {
            _executingSmartRTL = false;
            _loadCurrentMissionItemsFromBuffer();
1100 1101
            _setPhaseDistance(_phaseDistanceBuffer);
            _setPhaseDuration(_phaseDurationBuffer);
1102
            _showAllMissionItems.setRawValue(true);
1103 1104
            _missionController->removeAllFromVehicle();
            _missionController->removeAll();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1105
            disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1106 1107 1108 1109
        }
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1110
void WimaController::_enableDisableLowBatteryHandling(QVariant enable)
1111 1112
{
    if (enable.toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1113
        _eventTimer.start();
1114
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1115
        _eventTimer.stop();
1116 1117 1118
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1119
void WimaController::_reverseChangedHandler()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1120
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1121
    disconnect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1122
    _nextPhaseStartWaypointIndex.setRawValue(_endWaypointIndex+1);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1123
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1124

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1125
    _calcNextPhase();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1126 1127
}

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
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();
    }
}

1146 1147 1148 1149 1150 1151 1152
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();
1153 1154 1155 1156 1157 1158 1159 1160 1161
    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;
    }
1162 1163

   return true;
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
}

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
1181
    QVector<QGeoCoordinate> returnPath;
1182 1183 1184 1185 1186 1187 1188 1189
    calcShortestPath(currentVehiclePosition, _takeoffLandPostion, returnPath);
    // successful?
    if (returnPath.isEmpty()) {
        errorSring.append(tr("Not able to calculate return path."));
        return false;
    }
    // qWarning() << "returnPath.size()" << returnPath.size();

1190
    _saveCurrentMissionItemsToBuffer();
1191 1192 1193
    _phaseDistanceBuffer = _phaseDistance;
    _phaseDurationBuffer = _phaseDuration;

1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222

    // 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);
1223 1224
    speedItem->setCoordinate(speedItemCoordinate);
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258

    // 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();

1259 1260 1261 1262 1263 1264
    double dist = 0;
    double time = 0;
    if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
        qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
    _setPhaseDistance(dist);
    _setPhaseDuration(time);
1265
    _missionController->removeAll(); // remove items from _missionController, will be added on upload
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1266
    _updateAltitude();
1267

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1268
    _updateCurrentPath();
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
    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();
    }
}

1288 1289 1290 1291 1292 1293 1294 1295
void WimaController::_initSmartRTL()
{
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

    if (_checkSmartRTLPreCondition(errorString) == true) {
        _masterController->managerVehicle()->pauseVehicle();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1296
        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324
        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()
1325 1326 1327 1328 1329
{
    forceUploadToVehicle();
    masterController()->managerVehicle()->startMission();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
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);
    }
1361
    return value;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1362 1363
}

1364 1365 1366 1367
void WimaController::_snakeStoreWorkerResults()
{
    _missionItems.clearAndDeleteContents();
    const WorkerResult_t &r = _snakeWorker.getResult();
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
    _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));

1380 1381 1382 1383 1384 1385
    // Remove all items from mission controller.
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
1386
    unsigned  long endIdx = r.returnPathIdx.first();
1387
    QVector<QGeoCoordinate> waypoints;
1388 1389
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
        QGeoCoordinate wp{r.waypoints[int(i)].value<QGeoCoordinate>()};
1390
        waypoints.append(wp);
1391 1392 1393
    }

    // create SimpleMissionItem by using _missionController
1394 1395 1396 1397 1398 1399 1400
    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;
1401
    }
1402 1403 1404
    takeOffItem->setCommand(MAV_CMD_NAV_WAYPOINT);
    takeOffItem->setCoordinate(waypoints[0]);

1405
    // copy mission items from _missionController to _missionItems
1406
    cout.precision(10);
1407 1408 1409
    for ( int i = 1; i < missionControllerVisualItems->count(); i++) {
        SimpleMissionItem *visualItem     = qobject_cast<SimpleMissionItem *>((*missionControllerVisualItems)[i]);
        if (visualItem == nullptr) {
1410
            qWarning("WimaController::_snakeStoreWorkerResults(): Nullptr at SimpleMissionItem!");
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
            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);

1425 1426
    _calcNextPhase();
    emit missionItemsChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1427 1428
}

1429 1430 1431 1432 1433 1434
void WimaController::_loadCurrentMissionItemsFromBuffer()
{
    _currentMissionItems.clear();
    int numItems = _missionItemsBuffer.count();
    for (int i = 0; i < numItems; i++)
        _currentMissionItems.append(_missionItemsBuffer.removeAt(0));
1435

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1436
    _updateCurrentPath();
1437 1438 1439 1440 1441 1442
}


void WimaController::_saveCurrentMissionItemsToBuffer()
{
    _missionItemsBuffer.clear();
1443 1444
    int numCurrentMissionItems = _currentMissionItems.count();
    for (int i = 0; i < numCurrentMissionItems; i++)
1445 1446 1447
        _missionItemsBuffer.append(_currentMissionItems.removeAt(0));
}

1448 1449