Skip to content
Snippets Groups Projects
WimaController.cc 46.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    #include "WimaController.h"
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    #include "utilities.h"
    
    #include "time.h"
    
    
    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";
    
    const char* WimaController::flightSpeedName             = "FlightSpeed";
    
    const char* WimaController::arrivalReturnSpeedName      = "ArrivalReturnSpeed";
    
    const char* WimaController::altitudeName                = "Altitude";
    
    const char* WimaController::reverseName                 = "Reverse";
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
    const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
    const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileAreaWidth";
    const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
    const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";
    
    using namespace snake;
    using namespace snake_geometry;
    
    WimaController::WimaController(QObject *parent)
    
        : 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])
    
        , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
    
        , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
    
        , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
        , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
    
        , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
    
        , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
    
        , _reverse                  (settingsGroup, _metaDataMap[reverseName])
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        , _endWaypointIndex         (0)
    
        , _startWaypointIndex       (0)
        , _uploadOverrideRequired   (false)
    
        , _measurementPathLength    (-1)
    
        , _arrivalPathLength        (-1)
        , _returnPathLength         (-1)
    
        , _phaseDistance            (-1)
        , _phaseDuration            (-1)
    
        , _phaseDistanceBuffer      (-1)
        , _phaseDurationBuffer      (-1)
    
        , _vehicleHasLowBattery         (false)
        , _lowBatteryHandlingTriggered  (false)
        , _executingSmartRTL            (false)
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        , _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])
    
    {
        _showAllMissionItems.setRawValue(true);
        _showCurrentMissionItems.setRawValue(true);
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        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);
    
        // setup low battery handling
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
        _eventTimer.setInterval(EVENT_TIMER_INTERVAL);
    
        Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        connect(enableLowBatteryHandling, &Fact::rawValueChanged, this, &WimaController::_enableDisableLowBatteryHandling);
        _enableDisableLowBatteryHandling(enableLowBatteryHandling->rawValue());
    
        // Snake Worker Thread.
        connect(&_snakeWorker, &SnakeWorker::resultReady, this, &WimaController::_snakeResultsReady);
    
    QmlObjectListModel* WimaController::visualItems()
    
        return &_visualItems;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    QStringList WimaController::loadNameFilters() const
    {
        QStringList filters;
    
    
        filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
                   tr("All Files (*.*)");
        return filters;
    
    }
    
    QStringList WimaController::saveNameFilters() const
    {
        QStringList filters;
    
        filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
        return filters;
    
    WimaDataContainer *WimaController::dataContainer()
    
    QmlObjectListModel *WimaController::missionItems()
    {
        return &_missionItems;
    }
    
    
    QmlObjectListModel *WimaController::currentMissionItems()
    {
        return &_currentMissionItems;
    }
    
    
    QVariantList WimaController::waypointPath()
    {
        return  _waypointPath;
    }
    
    
    QVariantList WimaController::currentWaypointPath()
    {
        return _currentWaypointPath;
    }
    
    
    Fact *WimaController::enableWimaController()
    {
        return &_enableWimaController;
    }
    
    Fact *WimaController::overlapWaypoints()
    {
        return &_overlapWaypoints;
    }
    
    Fact *WimaController::maxWaypointsPerPhase()
    {
        return &_maxWaypointsPerPhase;
    }
    
    Fact *WimaController::showAllMissionItems()
    {
        return &_showAllMissionItems;
    }
    
    Fact *WimaController::showCurrentMissionItems()
    {
        return &_showCurrentMissionItems;
    }
    
    
    Fact *WimaController::flightSpeed()
    {
        return &_flightSpeed;
    }
    
    
    Fact *WimaController::arrivalReturnSpeed()
    {
        return &_arrivalReturnSpeed;
    }
    
    
    Fact *WimaController::altitude()
    {
        return &_altitude;
    }
    
    
    Fact *WimaController::reverse()
    {
        return &_reverse;
    }
    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    Fact *WimaController::enableSnake()
    {
        return &_enableSnake;
    }
    
    
    bool WimaController::uploadOverrideRequired() const
    {
        return _uploadOverrideRequired;
    }
    
    
    double WimaController::phaseDistance() const
    {
        return _phaseDistance;
    }
    
    double WimaController::phaseDuration() const
    {
        return _phaseDuration;
    }
    
    
    bool WimaController::vehicleHasLowBattery() const
    {
        return _vehicleHasLowBattery;
    }
    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    long WimaController::snakeConnectionStatus() const
    {
        return _snakeConnectionStatus;
    }
    
    bool WimaController::snakeCalcInProgress() const
    {
        return _snakeCalcInProgress;
    }
    
    
    Fact *WimaController::startWaypointIndex()
    {
    
        return &_nextPhaseStartWaypointIndex;
    
    void WimaController::setMasterController(PlanMasterController *masterC)
    {
        _masterController = masterC;
        emit masterControllerChanged();
    }
    
    void WimaController::setMissionController(MissionController *missionC)
    {
        _missionController = missionC;
        emit missionControllerChanged();
    }
    
    
    /*!
     * \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
     */
    
    void WimaController::setDataContainer(WimaDataContainer *container)
    {
    
        if (container != nullptr) {
            if (_container != nullptr) {
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
               disconnect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
    
            _container = container;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            connect(_container, &WimaDataContainer::newDataAvailable, this, &WimaController::_fetchContainerData);
    
    
            emit dataContainerChanged();
        }
    }
    
    
    void WimaController::setUploadOverrideRequired(bool overrideRequired)
    {
        if (_uploadOverrideRequired != overrideRequired) {
            _uploadOverrideRequired = overrideRequired;
    
            emit uploadOverrideRequiredChanged();
        }
    }
    
    
    void WimaController::nextPhase()
    {
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        _calcNextPhase();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    {    
        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));
            }
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        bool reverseBool = _reverse.rawValue().toBool();
        if (!reverseBool) {
            _nextPhaseStartWaypointIndex.setRawValue(int(1));
        }
        else {
            _nextPhaseStartWaypointIndex.setRawValue(_missionItems.count());
        }
    
    bool WimaController::uploadToVehicle()
    
        if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
            && _currentMissionItems.count() > 0) {
    
            setUploadOverrideRequired(true);
            return false;
        }
    
        return forceUploadToVehicle();
    }
    
    bool WimaController::forceUploadToVehicle()
    {
        setUploadOverrideRequired(false);
    
        if (_currentMissionItems.count() < 1)
    
            return false;
    
    
        _missionController->removeAll();
        // set homeposition of settingsItem
        QmlObjectListModel* visuals = _missionController->visualItems();
        MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
        if (settingsItem == nullptr) {
            qWarning("WimaController::updateCurrentMissionItems(): nullptr");
    
            return false;
    
        }
        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());
    
            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;
    
        _masterController->sendToVehicle();
    
    
        return true;
    
    void WimaController::removeFromVehicle()
    {
        _masterController->removeAllFromVehicle();
    
        _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;
    
    void WimaController::executeSmartRTL()
    
    void WimaController::initSmartRTL()
    
        _srtlReason = UserRequest;
        _initSmartRTL();
    
    void WimaController::removeVehicleTrajectoryHistory()
    {
        Vehicle *managerVehicle = masterController()->managerVehicle();
        managerVehicle->trajectoryPoints()->clear();
    }
    
    
    void WimaController::saveToCurrent()
    
    void WimaController::saveToFile(const QString& filename)
    {
    
        QString file = filename;
    
    bool WimaController::loadFromCurrent()
    
    }
    
    bool WimaController::loadFromFile(const QString &filename)
    {
    
        QString file = filename;
    
    QJsonDocument WimaController::saveToJson(FileType fileType)
    
    bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector<QGeoCoordinate> &path)
    
    {
        using namespace GeoUtilities;
        using namespace PolygonCalculus;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        QVector<QPointF> path2D;
    
        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;
    }
    
    
    bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVector<QGeoCoordinate> &coordinateList)
    
    {
        return extractCoordinateList(missionItems, coordinateList, 0, missionItems.count()-1);
    }
    
    
    bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVector<QGeoCoordinate> &coordinateList, int startIndex, int endIndex)
    
    {
        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++) {
    
                    SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(i);
    
    
                    if (mItem == nullptr) {
                        coordinateList.clear();
                        return false;
                    }
                    coordinateList.append(mItem->coordinate());
                }
            }
        } else
            return false;
    
        return true;
    }
    
    
    bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList)
    
    {
        return extractCoordinateList(missionItems, coordinateList, 0 , missionItems.count()-1);
    }
    
    
    bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList, int startIndex, int endIndex)
    
        QVector<QGeoCoordinate> geoCoordintateList;
    
    
        bool retValue = extractCoordinateList(missionItems, geoCoordintateList, startIndex, endIndex);
    
    
        if (!retValue)
            return false;
    
    
        for (int i = 0; i < geoCoordintateList.size(); i++) {
            QGeoCoordinate vertex = geoCoordintateList[i];
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            if (   (qFuzzyIsNull(vertex.latitude()) && qFuzzyIsNull(vertex.longitude()))
                || !vertex.isValid())
    
        for (auto coordinate : geoCoordintateList)
            coordinateList.append(QVariant::fromValue(coordinate));
    
        return true;
    }
    
    
    /*!
     * \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
    bool WimaController::_fetchContainerData()
    
        // fetch only if valid, return true on sucess
    
        // reset visual items
        _visualItems.clear();
    
        _missionItems.clearAndDeleteContents();
        _currentMissionItems.clearAndDeleteContents();
    
        _waypointPath.clear();
        _currentWaypointPath.clear();
    
        emit visualItemsChanged();
        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit currentWaypointPathChanged();
    
        _localPlanDataValid = false;
    
        if (_container == nullptr) {
            qWarning("WimaController::fetchContainerData(): No container assigned!");
            return false;
        }
    
        WimaPlanData planData = _container->pull();
    
        // extract list with WimaAreas
        QList<const WimaAreaData*> areaList = planData.areaList();
    
        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];
    
            if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
                _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
                areaCounter++;
                _visualItems.append(&_serviceArea);
    
                continue;
            }
    
            if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
                _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
                areaCounter++;
                _visualItems.append(&_measurementArea);
    
                continue;
    
            if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
                _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
                areaCounter++;
                //_visualItems.append(&_corridor); // not needed
    
                continue;
            }
    
            if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
                _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
                areaCounter++;
                _visualItems.append(&_joinedArea);
    
                continue;
            }
    
            if (areaCounter >= numAreas)
                break;
        }
    
        // extract mission items
    
        QList<MissionItem> tempMissionItems = planData.missionItems();
    
        if (tempMissionItems.size() < 1)
            return false;
    
        // create mission items
        _missionController->removeAll();
        QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();
    
        // create SimpleMissionItem by using _missionController
        for ( int i = 0; i < tempMissionItems.size(); i++) {
    
            _missionController->insertSimpleMissionItem(tempMissionItems[i], missionControllerVisualItems->count());
    
        }
        // 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;
    
            SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
            _missionItems.append(visualItemCopy);
        }
        if (areaCounter != numAreas)
            return false;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if (!_setTakeoffLandPosition())
    
            return false;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        _updateWaypointPath();
    
        // set _nextPhaseStartWaypointIndex to 1
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        bool reverse = _reverse.rawValue().toBool();
        _nextPhaseStartWaypointIndex.setRawValue(reverse? _missionItems.count() : int(1));
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if(!_calcNextPhase())
    
            return false;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        // 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;
        _scenario.addArea(mArea);
        _scenario.addArea(sArea);
        _scenario.addArea(corridor);
    
        _verifyScenarioDefinedWithErrorMessage();
    
    
        emit visualItemsChanged();
    
        emit missionItemsChanged();
    
        _localPlanDataValid = true;
        return true;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    bool WimaController::_calcNextPhase()
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if (_missionItems.count() < 1) {
            _startWaypointIndex = 0;
            _endWaypointIndex = 0;
    
            return false;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        }
    
        _currentMissionItems.clearAndDeleteContents();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        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)
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            if (startIndex < 0)
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        }    
        _startWaypointIndex = startIndex;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        int maxWaypointsPerPhase = _maxWaypointsPerPhase.rawValue().toInt();
    
        // determine end waypoint index
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        bool lastMissionPhaseReached = false;
        if (!reverse) {
            _endWaypointIndex = std::min(_startWaypointIndex + maxWaypointsPerPhase - 1, _missionItems.count()-1);
    
            if (_endWaypointIndex == _missionItems.count() - 1)
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
                lastMissionPhaseReached = true;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            _endWaypointIndex = std::max(_startWaypointIndex - maxWaypointsPerPhase + 1, 0);
    
            if (_endWaypointIndex == 0)
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
                lastMissionPhaseReached = true;
    
        // extract waypoints
    
        QVector<QGeoCoordinate> CSWpList; // list with potential waypoints (from _missionItems), for _currentMissionItems
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if (!reverse) {
    
            if (!extractCoordinateList(_missionItems, CSWpList, _startWaypointIndex, _endWaypointIndex)) {
    
                qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
                return false;
            }
        } else {
    
            if (!extractCoordinateList(_missionItems, CSWpList, _endWaypointIndex, _startWaypointIndex)) {
    
                qWarning("WimaController::calcNextPhase(): error on waypoint extraction.");
                return false;
            }
    
            // reverse path
    
            QVector<QGeoCoordinate> reversePath;
            for (QGeoCoordinate c : CSWpList)
    
                reversePath.prepend(c);
    
            CSWpList.clear();
            CSWpList.append(reversePath);
    
        // calculate phase length
        _measurementPathLength = 0;
        for (int i = 0; i < CSWpList.size()-1; ++i)
            _measurementPathLength += CSWpList[i].distanceTo(CSWpList[i+1]);
    
    
    
        // set start waypoint index for next phase
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if (!lastMissionPhaseReached) {
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            if (!reverse) {
    
                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
            connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
    
        // calculate path from home to first waypoint
    
        QVector<QGeoCoordinate> arrivalPath;
    
        if (!_takeoffLandPostion.isValid()){
            qWarning("WimaController::calcNextPhase(): _takeoffLandPostion not valid.");
            return false;
        }
    
        if ( !calcShortestPath(_takeoffLandPostion, CSWpList.first(), arrivalPath) ) {
    
            qWarning("WimaController::calcNextPhase(): Not able to calc path from home to first waypoint.");
            return false;
    
    
        // calculate arrival path length
        _arrivalPathLength = 0;
        for (int i = 0; i < arrivalPath.size()-1; ++i)
            _arrivalPathLength += arrivalPath[i].distanceTo(arrivalPath[i+1]);
    
    
        arrivalPath.removeFirst();
    
    
        // calculate path from last waypoint to home
    
        QVector<QGeoCoordinate> returnPath;
        if ( !calcShortestPath(CSWpList.last(), _takeoffLandPostion, returnPath) ) {
    
            qWarning("WimaController::calcNextPhase(): Not able to calc path from home to first waypoint.");
            return false;
    
        }
    
        // calculate arrival path length
        _returnPathLength = 0;
        for (int i = 0; i < returnPath.size()-1; ++i)
            _returnPathLength += returnPath[i].distanceTo(returnPath[i+1]);
    
    
        returnPath.removeFirst();
        returnPath.removeLast();
    
        // create Mission Items
        _missionController->removeAll();
        QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();
    
    
        // set homeposition of settingsItem
        MissionSettingsItem* settingsItem = missionControllerVisuals->value<MissionSettingsItem *>(0);
        if (settingsItem == nullptr) {
    
            qWarning("WimaController::calcNextPhase(): nullptr");
            return false;
    
        }
        settingsItem->setCoordinate(_takeoffLandPostion);
    
    
        // set takeoff position for first mission item (bug)
    
        missionController()->insertSimpleMissionItem(_takeoffLandPostion, 1);
    
        SimpleMissionItem *takeoffItem = missionControllerVisuals->value<SimpleMissionItem*>(1);
        if (takeoffItem == nullptr) {
            qWarning("WimaController::calcNextPhase(): nullptr");
            return false;
    
        takeoffItem->setCoordinate(_takeoffLandPostion);
    
        // create change speed item, after take off
    
        _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
        speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED);// set coordinate must be after setCommand (setCommand sets coordinate to zero)
    
        speedItem->setCoordinate(_takeoffLandPostion);
    
        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
        speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
    
        speedItem->setCoordinate(CSWpList.first());
    
        speedItem->missionItem().setParam2(_flightSpeed.rawValue().toDouble());
    
    
        // 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
        speedItem->setCommand(MAV_CMD_DO_CHANGE_SPEED); // set coordinate must be after setCommand (setCommand sets coordinate to zero)
        speedItem->setCoordinate(CSWpList.last());
    
        speedItem->missionItem().setParam2(_arrivalReturnSpeed.rawValue().toDouble());
    
        // insert return path coordinates
        for (auto coordinate : returnPath)
            _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());
    
    
        // set land command for last mission item
    
        index = missionControllerVisuals->count();
        _missionController->insertSimpleMissionItem(_takeoffLandPostion, index);
        SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(index);
    
        if (landItem == nullptr) {
    
            qWarning("WimaController::calcNextPhase(): nullptr");
            return false;
    
        _missionController->setLandCommand(*landItem);
    
        for ( int i = 1; i < missionControllerVisuals->count(); i++) {
            SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
            if (visualItem == nullptr) {
    
                qWarning("WimaController::calcNextPhase(): Nullptr at SimpleMissionItem!");
    
                _currentMissionItems.clear();
    
                return false;
    
            SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
    
            _currentMissionItems.append(visualItemCopy);
        }
    
        double dist = 0;
        double time = 0;
        if (!_missionController->distanceTimeToMissionEnd(dist, time, 1, false))
            qWarning("WimaController::calcNextPhase: distanceTimeToMissionEnd returned false!");
        _setPhaseDistance(dist);
        _setPhaseDuration(time);
    
        _missionController->removeAll(); // remove items from _missionController, will be added on upload
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        _updateAltitude();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        _updateCurrentPath();
    
        emit currentMissionItemsChanged();
    
    
        return true;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateWaypointPath()
    
    {
        _waypointPath.clear();
    
        extractCoordinateList(_missionItems, _waypointPath, 0, _missionItems.count()-1);
    
        emit waypointPathChanged();
    }
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateCurrentPath()
    
    {
        _currentWaypointPath.clear();
    
        extractCoordinateList(_currentMissionItems, _currentWaypointPath, 0, _currentMissionItems.count()-1);
    
        emit currentWaypointPathChanged();
    }
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateNextWaypoint()
    
    {
        if (_endWaypointIndex < _missionItems.count()-2) {
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
            _nextPhaseStartWaypointIndex.setRawValue(1 + std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0));
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
            connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_recalcCurrentPhase()
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        disconnect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
    
        _nextPhaseStartWaypointIndex.setRawValue(_startWaypointIndex + 1);    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        connect(&_nextPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::_calcNextPhase);
        _calcNextPhase();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    bool WimaController::_setTakeoffLandPosition()
    
    {
        _takeoffLandPostion.setAltitude(0);
        _takeoffLandPostion.setLongitude(_serviceArea.center().longitude());
        _takeoffLandPostion.setLatitude(_serviceArea.center().latitude());
    
        return true;
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateflightSpeed()
    
        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());
                }
            }
    
        _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                          + (_arrivalPathLength + _returnPathLength)
                          / _arrivalReturnSpeed.rawValue().toDouble());
    
    
        if (speedItemCounter != 3)
            qWarning("WimaController::updateflightSpeed(): internal error.");
    }
    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateArrivalReturnSpeed()
    
    {
        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());
                }
            }
    
        _setPhaseDuration(_phaseDistance/_flightSpeed.rawValue().toDouble()
                          + (_arrivalPathLength + _returnPathLength)
                          / _arrivalReturnSpeed.rawValue().toDouble());
    
    
        if (speedItemCounter != 3)
            qWarning("WimaController::updateArrivalReturnSpeed(): internal error.");
    
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
    void WimaController::_updateAltitude()
    
    {
        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
    void WimaController::_checkBatteryLevel()
    
        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();
    
    Valentin Platzgummer's avatar
    Valentin Platzgummer committed
        if (managerVehicle != nullptr && enabled == true) {
    
            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) {
    
    
                    if (_missionController->remainingTime() <= minTime) {
                        _lowBatteryHandlingTriggered = true;
                    }
                    else {
    
                        _lowBatteryHandlingTriggered = true;
                        _srtlReason = BatteryLow;