WimaController.cc 54.5 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 9
#include "QtROSJsonFactory.h"

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

13
#include "QVector3D"
14
#include <QScopedPointer>
15

16

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

using namespace snake;
using namespace snake_geometry;
39

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

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

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

    // Snake Worker Thread.
100
    connect(&_snakeWorker, &SnakeWorker::resultReady, this, &WimaController::_snakeStoreWorkerResults);
101 102
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
103 104 105 106
QStringList WimaController::loadNameFilters() const
{
    QStringList filters;

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

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

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

120 121 122 123 124
bool WimaController::uploadOverrideRequired() const
{
    return _uploadOverrideRequired;
}

125 126 127 128 129 130 131 132 133 134
double WimaController::phaseDistance() const
{
    return _phaseDistance;
}

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

135 136 137 138 139
bool WimaController::vehicleHasLowBattery() const
{
    return _vehicleHasLowBattery;
}

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

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

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

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

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

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

        emit dataContainerChanged();
    }
}

182 183 184 185 186 187 188 189 190
void WimaController::setUploadOverrideRequired(bool overrideRequired)
{
    if (_uploadOverrideRequired != overrideRequired) {
        _uploadOverrideRequired = overrideRequired;

        emit uploadOverrideRequiredChanged();
    }
}

191 192
void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
193
    _calcNextPhase();
194 195
}

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

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

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

    return forceUploadToVehicle();
}

bool WimaController::forceUploadToVehicle()
{
    setUploadOverrideRequired(false);
242
    if (_currentMissionItems.count() < 1)
243
        return false;
244 245 246 247 248 249 250

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

276
    _masterController->sendToVehicle();
277 278

    return true;
279
}
280

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

309
void WimaController::executeSmartRTL()
310
{
311
    _executeSmartRTL();
312 313
}

314
void WimaController::initSmartRTL()
315
{
316 317
    _srtlReason = UserRequest;
    _initSmartRTL();
318 319
}

320 321 322 323 324 325
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

326
void WimaController::saveToCurrent()
327
{
328

329 330
}

331 332
void WimaController::saveToFile(const QString& filename)
{
333
    QString file = filename;
334 335
}

336
bool WimaController::loadFromCurrent()
337
{
338
    return true;
339 340 341 342
}

bool WimaController::loadFromFile(const QString &filename)
{
343
    QString file = filename;
344
    return true;
345 346 347
}


348

349
QJsonDocument WimaController::saveToJson(FileType fileType)
350
{
351 352 353 354
    if(fileType)
    {

    }
355
    return QJsonDocument();
356 357
}

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

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

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

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

    return true;
}

406
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList)
407 408 409 410
{
    return extractCoordinateList(missionItems, coordinateList, 0 , missionItems.count()-1);
}

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

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

417 418 419
    if (!retValue)
        return false;

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

427 428 429 430 431 432
    for (auto coordinate : geoCoordintateList)
        coordinateList.append(QVariant::fromValue(coordinate));

    return true;
}

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

444 445
    // reset visual items
    _visualItems.clear();
446 447
    _missionItems.clearAndDeleteContents();
    _currentMissionItems.clearAndDeleteContents();
448 449
    _waypointPath.clear();
    _currentWaypointPath.clear();
450 451
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
452
    _snakeTileCenterPoints.clear();
453

454 455 456 457
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
458
    emit snakeTilesChanged();
459
    emit snakeTileCenterPointsChanged();
460

461
    _localPlanDataValid = false;
462

463 464 465 466
    if (_container == nullptr) {
        qWarning("WimaController::fetchContainerData(): No container assigned!");
        return false;
    }
467

468
    WimaPlanData planData = _container->pull();
469

470 471
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
472

473 474 475 476
    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];
477

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

483 484
            continue;
        }
485

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

491
            continue;
492
        }
493

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

499 500
            continue;
        }
501

502 503 504 505
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
            _visualItems.append(&_joinedArea);
506

507 508
            continue;
        }
509

510 511 512
        if (areaCounter >= numAreas)
            break;
    }
513

514
    // extract mission items
515
    QList<MissionItem> tempMissionItems = planData.missionItems();
516 517
    if (tempMissionItems.size() < 1)
        return false;
518

519 520 521
    // create mission items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();
522

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
540
    if (!_setTakeoffLandPosition())
541
        return false;
542

Valentin Platzgummer's avatar
Valentin Platzgummer committed
543
    _updateWaypointPath();
544

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
551
    if(!_calcNextPhase())
552
        return false;
553

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
576 577 578 579
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

580 581 582 583
    // Check if scenario is defined.
    if ( !_verifyScenarioDefinedWithErrorMessage() )
        return false;

584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
    {
        // 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];
            SnakeTile Tile;
            for ( const auto &vertex : tile) {
                QGeoCoordinate QVertex(vertex[0], vertex[1], vertex[2]);
                Tile.append(QVertex);
            }
            const auto &centerPoint = cps[i];
            QGeoCoordinate QCenterPoint(centerPoint[0], centerPoint[1], centerPoint[2]);
            Tile.setCenter(QCenterPoint);
            _snakeTiles.polygons().append(Tile);
            _snakeTileCenterPoints.append(QVariant::fromValue(QCenterPoint));
600
        }
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
        for ( int i=0; i < int(tiles.size()); ++i ) {
            const auto &tile = tiles[i];
            Polygon2D Tile;
            for ( const auto &vertex : tile.outer()) {
                QPointF QVertex(vertex.get<0>(), vertex.get<1>());
                Tile.append(QVertex);
            }
            _snakeTilesLocal.polygons().append(Tile);
        }

        QtROSJsonFactory factory;
        QScopedPointer<rapidjson::Document> doc(factory.create(_snakeTilesLocal));

        auto &temp = _scenario.getOrigin();
        ::GeoPoint3D origin(temp[0], temp[1], temp[2]);
        QScopedPointer<rapidjson::Document> doc2(factory.create(origin));

        cout << "Origin" << endl;
        rapidjson::OStreamWrapper out(std::cout);
        rapidjson::Writer<rapidjson::OStreamWrapper> writer(out);
        doc2->Accept(writer);
        std::cout << std::endl << std::endl;

        cout << "Snake Tiles" << endl;
        rapidjson::Writer<rapidjson::OStreamWrapper> writer2(out);
        doc->Accept(writer2);
        std::cout << std::endl << std::endl;


635 636
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
637

638
    emit visualItemsChanged();
639
    emit missionItemsChanged();
640 641
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
642

643 644
    _localPlanDataValid = true;
    return true;
645 646
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
647
bool WimaController::_calcNextPhase()
648
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
649 650 651
    if (_missionItems.count() < 1) {
        _startWaypointIndex = 0;
        _endWaypointIndex = 0;
652
        return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
653
    }
654

655
    _currentMissionItems.clearAndDeleteContents();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
656

Valentin Platzgummer's avatar
Valentin Platzgummer committed
657 658 659 660
    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)
661 662 663
            return false;
    }
    else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
664
        if (startIndex < 0)
665
            return false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
666 667
    }    
    _startWaypointIndex = startIndex;
668

Valentin Platzgummer's avatar
Valentin Platzgummer committed
669
    int maxWaypointsPerPhase = _maxWaypointsPerPhase.rawValue().toInt();
670
    // determine end waypoint index
Valentin Platzgummer's avatar
Valentin Platzgummer committed
671 672 673
    bool lastMissionPhaseReached = false;
    if (!reverse) {
        _endWaypointIndex = std::min(_startWaypointIndex + maxWaypointsPerPhase - 1, _missionItems.count()-1);
674
        if (_endWaypointIndex == _missionItems.count() - 1)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
675
            lastMissionPhaseReached = true;
676 677
    }
    else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
678
        _endWaypointIndex = std::max(_startWaypointIndex - maxWaypointsPerPhase + 1, 0);
679
        if (_endWaypointIndex == 0)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
680
            lastMissionPhaseReached = true;
681 682
    }

683

684
    // extract waypoints
685
    QVector<QGeoCoordinate> CSWpList; // list with potential waypoints (from _missionItems), for _currentMissionItems
686

Valentin Platzgummer's avatar
Valentin Platzgummer committed
687
    if (!reverse) {
688
        if (!extractCoordinateList(_missionItems, CSWpList, _startWaypointIndex, _endWaypointIndex)) {
689 690 691 692
            qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
            return false;
        }
    } else {
693
        if (!extractCoordinateList(_missionItems, CSWpList, _endWaypointIndex, _startWaypointIndex)) {
694 695 696 697 698
            qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
            return false;
        }

        // reverse path
699 700
        QVector<QGeoCoordinate> reversePath;
        for (QGeoCoordinate c : CSWpList)
701
            reversePath.prepend(c);
702 703
        CSWpList.clear();
        CSWpList.append(reversePath);
704
    }
705

706

707 708 709 710 711 712
    // calculate phase length
    _measurementPathLength = 0;
    for (int i = 0; i < CSWpList.size()-1; ++i)
        _measurementPathLength += CSWpList[i].distanceTo(CSWpList[i+1]);


713
    // set start waypoint index for next phase
Valentin Platzgummer's avatar
Valentin Platzgummer committed
714
    if (!lastMissionPhaseReached) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
715
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
716
        if (!reverse) {
717 718 719 720 721 722 723 724 725
            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
726
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
727
    }
728 729

    // calculate path from home to first waypoint
730
    QVector<QGeoCoordinate> arrivalPath;
731 732 733 734
    if (!_takeoffLandPostion.isValid()){
        qWarning("WimaController::calcNextPhase(): _takeoffLandPostion not valid.");
        return false;
    }
735
    if ( !calcShortestPath(_takeoffLandPostion, CSWpList.first(), arrivalPath) ) {
736 737
        qWarning("WimaController::calcNextPhase(): Not able to calc path from home to first waypoint.");
        return false;
738
    }
739 740 741 742 743 744

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

745
    arrivalPath.removeFirst();
746 747

    // calculate path from last waypoint to home
748 749
    QVector<QGeoCoordinate> returnPath;
    if ( !calcShortestPath(CSWpList.last(), _takeoffLandPostion, returnPath) ) {
750
        qWarning("WimaController::calcNextPhase(): Not able to calc path from home to last waypoint.");
751
        return false;
752 753 754 755 756 757 758
    }

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

759 760
    returnPath.removeFirst();
    returnPath.removeLast();
761

762

763

764 765 766
    // create Mission Items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();
767 768 769 770

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

776
    // set takeoff position for first mission item (bug)
777
    missionController()->insertSimpleMissionItem(_takeoffLandPostion, 1);
778 779 780 781
    SimpleMissionItem *takeoffItem = missionControllerVisuals->value<SimpleMissionItem*>(1);
    if (takeoffItem == nullptr) {
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
782
    }
783
    takeoffItem->setCoordinate(_takeoffLandPostion);
784

785
    // create change speed item, after take off
786 787 788 789 790 791
    _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
792
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED);// set coordinate must be after setCommand (setCommand sets coordinate to zero)
793
    speedItem->setCoordinate(_takeoffLandPostion);
794 795 796 797 798 799 800 801 802 803 804 805 806 807
    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
808
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
809
    speedItem->setCoordinate(CSWpList.first());
810 811
    speedItem->missionItem().setParam2(_flightSpeed.rawValue().toDouble());

812 813 814 815 816 817 818 819 820 821 822 823
    // 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
824 825
    speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
    speedItem->setCoordinate(CSWpList.last());
826 827 828 829 830
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());

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

    // set land command for last mission item
833 834 835
    index = missionControllerVisuals->count();
    _missionController->insertSimpleMissionItem(_takeoffLandPostion, index);
    SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(index);
836
    if (landItem == nullptr) {
837 838
        qWarning("WimaController::calcNextPhase(): nullptr");
        return false;
839
    }
840
    _missionController->setLandCommand(*landItem);
841

842
    // copy to _currentMissionItems
843 844 845
    for ( int i = 1; i < missionControllerVisuals->count(); i++) {
        SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
        if (visualItem == nullptr) {
846
            qWarning("WimaController::calcNextPhase(): Nullptr at SimpleMissionItem!");
847
            _currentMissionItems.clear();
848
            return false;
849
        }
850

851
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
852 853
        _currentMissionItems.append(visualItemCopy);
    }
854

855 856 857 858 859 860
    double dist = 0;
    double time = 0;
    if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
        qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
    _setPhaseDistance(dist);
    _setPhaseDuration(time);
861
    _missionController->removeAll(); // remove items from _missionController, will be added on upload
Valentin Platzgummer's avatar
Valentin Platzgummer committed
862
    _updateAltitude();
863

Valentin Platzgummer's avatar
Valentin Platzgummer committed
864
    _updateCurrentPath();
865
    emit currentMissionItemsChanged();
866 867

    return true;
868
}
869

Valentin Platzgummer's avatar
Valentin Platzgummer committed
870
void WimaController::_updateWaypointPath()
871 872
{
    _waypointPath.clear();
873
    extractCoordinateList(_missionItems, _waypointPath, 0, _missionItems.count()-1);
874

875 876
    emit waypointPathChanged();
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
877
void WimaController::_updateCurrentPath()
878 879
{
    _currentWaypointPath.clear();
880
    extractCoordinateList(_currentMissionItems, _currentWaypointPath, 0, _currentMissionItems.count()-1);
881

882 883
    emit currentWaypointPathChanged();
}
884

Valentin Platzgummer's avatar
Valentin Platzgummer committed
885
void WimaController::_updateNextWaypoint()
886 887
{
    if (_endWaypointIndex < _missionItems.count()-2) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
888
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
889
        _nextPhaseStartWaypointIndex.setRawValue(1 + std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0));
Valentin Platzgummer's avatar
Valentin Platzgummer committed
890
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
891 892 893
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
894
void WimaController::_recalcCurrentPhase()
895
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
896
    disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
897
    _nextPhaseStartWaypointIndex.setRawValue(_startWaypointIndex + 1);    
Valentin Platzgummer's avatar
Valentin Platzgummer committed
898 899
    connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    _calcNextPhase();
900 901
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
902
bool WimaController::_setTakeoffLandPosition()
903 904 905 906 907 908
{
    _takeoffLandPostion.setAltitude(0);
    _takeoffLandPostion.setLongitude(_serviceArea.center().longitude());
    _takeoffLandPostion.setLatitude(_serviceArea.center().latitude());

    return true;
909 910
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
911
void WimaController::_updateflightSpeed()
912
{
913 914 915 916 917 918 919 920 921
    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());
            }
        }
922
    }
923

924 925 926 927
    _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                      + (_arrivalPathLength + _returnPathLength)
                      / _arrivalReturnSpeed.rawValue().toDouble());

928 929 930 931
    if (speedItemCounter != 3)
        qWarning("WimaController::updateflightSpeed(): internal error.");
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
932
void WimaController::_updateArrivalReturnSpeed()
933 934 935 936 937 938 939 940 941 942
{
    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());
            }
        }
943
    }
944

945 946 947 948
    _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                      + (_arrivalPathLength + _returnPathLength)
                      / _arrivalReturnSpeed.rawValue().toDouble());

949 950 951
    if (speedItemCounter != 3)
        qWarning("WimaController::updateArrivalReturnSpeed(): internal error.");

952 953
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
954
void WimaController::_updateAltitude()
955 956 957 958 959 960 961 962 963 964 965
{
    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
966
void WimaController::_checkBatteryLevel()
967
{
968 969 970 971 972
    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();
973

Valentin Platzgummer's avatar
Valentin Platzgummer committed
974
    if (managerVehicle != nullptr && enabled == true) {
975 976 977 978 979 980 981 982
        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) {
983 984 985 986 987

                if (_missionController->remainingTime() <= minTime) {
                    _lowBatteryHandlingTriggered = true;
                }
                else {
988 989 990
                    _lowBatteryHandlingTriggered = true;
                    _srtlReason = BatteryLow;
                    _initSmartRTL();
991 992 993 994 995 996 997 998 999 1000 1001
                }
            }
        }
        else {
            _setVehicleHasLowBattery(false);
            _lowBatteryHandlingTriggered = false;
        }

    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1002 1003 1004 1005
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
    static EventTicker snakeEventLoopTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
1006
    static EventTicker rosBridgeTicker(EVENT_TIMER_INTERVAL, 1000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1007 1008 1009 1010 1011 1012 1013 1014 1015

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

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

1016
            _snakeProgress.clear();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1017
            long n = _scenario.getTilesENU().size();
1018
            _snakeProgress.reserve(n);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1019 1020
            std::srand(time(NULL));
            for (long i=0; i<n; ++i){
1021
                int r{rand()%200};
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1022 1023
                if ( r > 100 )
                    r = 100;
1024
                _snakeProgress.append(r);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1025 1026 1027
            }

            _snakeWorker.setScenario(_scenario);
1028
            _snakeWorker.setProgress(_snakeProgress);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1029 1030
            _snakeWorker.setLineDistance(_snakeLineDistance.rawValue().toDouble());
            _snakeWorker.setMinTransectLength(_snakeMinTransectLength.rawValue().toDouble());
1031
            _setSnakeCalcInProgress(true);
1032
            _snakeWorker.start();
1033 1034

            emit snakeProgressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1035 1036
        }
    }
1037

1038
    if (rosBridgeTicker.ready()) {
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 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 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 1143 1144
//        using namespace ros_bridge;


//        // Time
//        class Time time(1, 2);
//        rapidjson::Document doc(rapidjson::kObjectType);
//        // Write to stdout
//        cout << "Time" << endl;
//        cout << "Return value : " << time.toJson(doc, doc.GetAllocator()) << std::endl;
//        rapidjson::OStreamWrapper out(std::cout);
//        rapidjson::Writer<rapidjson::OStreamWrapper> writer(out);
//        doc.Accept(writer);
//        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 3D
//        QVector3D p1(1, 2, 3);
//        typedef Point32<QVector3D> PointWrapper;
//        PointWrapper pw1(&p1);
//        // Write to stdout
//        cout << "Point 3D" << 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;


//        // Point 2D
//        QPoint p2D(1, 2);
//        p2D.x();
//        Point32<QPoint> pw2D(&p2D);
//        // Write to stdout
//        cout << "Point 2D" << endl;
//        rapidjson::Document docI(rapidjson::kObjectType);
//        cout << "Return value : " << pw2D.toJson(docI, docI.GetAllocator()) << std::endl;
//        rapidjson::Writer<rapidjson::OStreamWrapper> writerI(out);
//        docI.Accept(writerI);
//        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{};
//        polyArray.setHeader(&header);
//        polyArray.setPolygons(&polygons);
//        // Write to stdout
//        cout << "PolygonStamped" << endl;
//        rapidjson::Document doc6(rapidjson::kObjectType);
//        cout << "Return value : " << polyArray.toJson(doc6, doc6.GetAllocator()) << std::endl;
//        rapidjson::Writer<rapidjson::OStreamWrapper> writer6(out);
//        doc6.Accept(writer6);
//        std::cout << std::endl << std::endl;
1145
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1146 1147 1148
}

void WimaController::_smartRTLCleanUp(bool flying)
1149 1150 1151 1152 1153 1154
{

    if ( !flying) { // vehicle has landed
        if (_executingSmartRTL) {
            _executingSmartRTL = false;
            _loadCurrentMissionItemsFromBuffer();
1155 1156
            _setPhaseDistance(_phaseDistanceBuffer);
            _setPhaseDuration(_phaseDurationBuffer);
1157
            _showAllMissionItems.setRawValue(true);
1158 1159
            _missionController->removeAllFromVehicle();
            _missionController->removeAll();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1160
            disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1161 1162 1163 1164
        }
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1165
void WimaController::_enableDisableLowBatteryHandling(QVariant enable)
1166 1167
{
    if (enable.toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1168
        _eventTimer.start();
1169
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1170
        _eventTimer.stop();
1171 1172 1173
    }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1174
void WimaController::_reverseChangedHandler()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1175
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1176
    disconnect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1177
    _nextPhaseStartWaypointIndex.setRawValue(_endWaypointIndex+1);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1178
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1179

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1180
    _calcNextPhase();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1181 1182
}

1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
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();
    }
}

1201 1202 1203 1204 1205 1206 1207
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();
1208 1209 1210 1211 1212 1213 1214 1215 1216
    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;
    }
1217 1218

   return true;
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235
}

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
1236
    QVector<QGeoCoordinate> returnPath;
1237 1238 1239 1240 1241 1242 1243 1244
    calcShortestPath(currentVehiclePosition, _takeoffLandPostion, returnPath);
    // successful?
    if (returnPath.isEmpty()) {
        errorSring.append(tr("Not able to calculate return path."));
        return false;
    }
    // qWarning() << "returnPath.size()" << returnPath.size();

1245
    _saveCurrentMissionItemsToBuffer();
1246 1247 1248
    _phaseDistanceBuffer = _phaseDistance;
    _phaseDurationBuffer = _phaseDuration;

1249 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

    // 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);
1278 1279
    speedItem->setCoordinate(speedItemCoordinate);
    speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313

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

1314 1315 1316 1317 1318 1319
    double dist = 0;
    double time = 0;
    if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
        qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
    _setPhaseDistance(dist);
    _setPhaseDuration(time);
1320
    _missionController->removeAll(); // remove items from _missionController, will be added on upload
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1321
    _updateAltitude();
1322

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1323
    _updateCurrentPath();
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342
    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();
    }
}

1343 1344 1345 1346 1347 1348 1349 1350
void WimaController::_initSmartRTL()
{
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

    if (_checkSmartRTLPreCondition(errorString) == true) {
        _masterController->managerVehicle()->pauseVehicle();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1351
        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
        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()
1380 1381 1382 1383 1384
{
    forceUploadToVehicle();
    masterController()->managerVehicle()->startMission();
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
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);
    }
1416
    return value;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1417 1418
}

1419 1420
void WimaController::_snakeStoreWorkerResults()
{
1421
    auto start = std::chrono::high_resolution_clock::now();
1422 1423
    _missionItems.clearAndDeleteContents();
    const WorkerResult_t &r = _snakeWorker.getResult();
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435
    _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));

1436 1437 1438 1439 1440 1441
    // Remove all items from mission controller.
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
1442
    unsigned  long endIdx = r.returnPathIdx.first();
1443
    QVector<QGeoCoordinate> waypoints;
1444 1445
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
        QGeoCoordinate wp{r.waypoints[int(i)].value<QGeoCoordinate>()};
1446
        waypoints.append(wp);
1447 1448 1449
    }

    // create SimpleMissionItem by using _missionController
1450 1451 1452 1453 1454 1455 1456
    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;
1457
    }
1458 1459 1460
    takeOffItem->setCommand(MAV_CMD_NAV_WAYPOINT);
    takeOffItem->setCoordinate(waypoints[0]);

1461 1462 1463 1464
    // 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) {
1465
            qWarning("WimaController::_snakeStoreWorkerResults(): Nullptr at SimpleMissionItem!");
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479
            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);

1480 1481
    _calcNextPhase();
    emit missionItemsChanged();
1482 1483 1484 1485 1486


    auto end = std::chrono::high_resolution_clock::now();
    double duration = std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count();
    qWarning() << "WimaController::_snakeStoreWorkerResults execution time: " << duration << " ms.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1487 1488
}

1489 1490 1491 1492 1493 1494
void WimaController::_loadCurrentMissionItemsFromBuffer()
{
    _currentMissionItems.clear();
    int numItems = _missionItemsBuffer.count();
    for (int i = 0; i < numItems; i++)
        _currentMissionItems.append(_missionItemsBuffer.removeAt(0));
1495

Valentin Platzgummer's avatar
Valentin Platzgummer committed
1496
    _updateCurrentPath();
1497 1498 1499 1500 1501 1502
}


void WimaController::_saveCurrentMissionItemsToBuffer()
{
    _missionItemsBuffer.clear();
1503 1504
    int numCurrentMissionItems = _currentMissionItems.count();
    for (int i = 0; i < numCurrentMissionItems; i++)
1505 1506 1507
        _missionItemsBuffer.append(_currentMissionItems.removeAt(0));
}

1508 1509