Skip to content
WimaController.cc 30.8 KiB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
#include "WimaController.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
#include "utilities.h"
#include "ros_bridge/include/JsonMethodes.h"
#include "ros_bridge/rapidjson/include/rapidjson/document.h"
#include "ros_bridge/rapidjson/include/rapidjson/writer.h"
#include "ros_bridge/rapidjson/include/rapidjson/ostreamwrapper.h"
Valentin Platzgummer's avatar
Valentin Platzgummer committed
#include "ros_bridge/include/CasePacker.h"
#include "Snake/QtROSJsonFactory.h"
#include "Snake/QtROSTypeFactory.h"
#include "Snake/QNemoProgress.h"
#include "Snake/QNemoHeartbeat.h"
#include "QVector3D"
#include <QScopedPointer>
// const char* WimaController::wimaFileExtension           = "wima";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
const char* WimaController::snakeTileWidthName          = "SnakeTileWidth";
const char* WimaController::snakeTileHeightName         = "SnakeTileHeight";
const char* WimaController::snakeMinTileAreaName        = "SnakeMinTileArea";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
const char* WimaController::snakeLineDistanceName       = "SnakeLineDistance";
const char* WimaController::snakeMinTransectLengthName  = "SnakeMinTransectLength";

WimaController::StatusMap WimaController::_nemoStatusMap{std::make_pair<int, QString>(0, "No Heartbeat"),
                                                         std::make_pair<int, QString>(1, "Connected"),
                                                         std::make_pair<int, QString>(-1, "Timeout")};

Valentin Platzgummer's avatar
Valentin Platzgummer committed
using namespace snake;
using namespace snake_geometry;
WimaController::WimaController(QObject *parent)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    : QObject                   (parent)
    , _joinedArea               ()
    , _measurementArea          ()
    , _serviceArea              ()
    , _corridor                 ()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _localPlanDataValid       (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _areaInterface            (&_measurementArea, &_serviceArea, &_corridor, &_joinedArea)
    , _managerSettings          ()
    , _defaultManager           (_managerSettings, _areaInterface)
    , _snakeManager             (_managerSettings, _areaInterface)
    , _rtlManager               (_managerSettings, _areaInterface)
    , _currentManager           (&_defaultManager)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _metaDataMap              (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/WimaController.SettingsGroup.json"), this))
    , _enableWimaController     (settingsGroup, _metaDataMap[enableWimaControllerName])
    , _overlapWaypoints         (settingsGroup, _metaDataMap[overlapWaypointsName])
    , _maxWaypointsPerPhase     (settingsGroup, _metaDataMap[maxWaypointsPerPhaseName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _nextPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])    
    , _flightSpeed              (settingsGroup, _metaDataMap[flightSpeedName])
    , _arrivalReturnSpeed       (settingsGroup, _metaDataMap[arrivalReturnSpeedName])
    , _altitude                 (settingsGroup, _metaDataMap[altitudeName])
    , _measurementPathLength    (-1)
    , _lowBatteryHandlingTriggered  (false)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _snakeCalcInProgress      (false)
    , _scenarioDefinedBool      (false)
    , _snakeTileWidth           (settingsGroup, _metaDataMap[snakeTileWidthName])
    , _snakeTileHeight          (settingsGroup, _metaDataMap[snakeTileHeightName])
    , _snakeMinTileArea         (settingsGroup, _metaDataMap[snakeMinTileAreaName])
    , _snakeLineDistance        (settingsGroup, _metaDataMap[snakeLineDistanceName])
    , _snakeMinTransectLength   (settingsGroup, _metaDataMap[snakeMinTransectLengthName])
    , _nemoHeartbeat            (0 /*status: not connected*/)
    , _fallbackStatus            (0 /*status: not connected*/)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    , _pRosBridge               (new ROSBridge::ROSBridge())
    // Set up facts.
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
    connect(&_overlapWaypoints,             &Fact::rawValueChanged, this, &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase,         &Fact::rawValueChanged, this, &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex,  &Fact::rawValueChanged, this, &WimaController::_setStartIndex);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    connect(&_flightSpeed,                  &Fact::rawValueChanged, this, &WimaController::_updateflightSpeed);
    connect(&_arrivalReturnSpeed,           &Fact::rawValueChanged, this, &WimaController::_updateArrivalReturnSpeed);
    connect(&_altitude,                     &Fact::rawValueChanged, this, &WimaController::_updateAltitude);

    _defaultManager.setOverlap(_overlapWaypoints.rawValue().toUInt());
    _defaultManager.setN(_maxWaypointsPerPhase.rawValue().toUInt());
    _defaultManager.setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    connect(&_eventTimer, &QTimer::timeout, this, &WimaController::_eventTimerHandler);
    //_eventTimer.setInterval(EVENT_TIMER_INTERVAL);
    _eventTimer.start(EVENT_TIMER_INTERVAL);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    // Snake Worker Thread.
    connect(&_snakeWorker, &SnakeWorker::finished, this, &WimaController::_snakeStoreWorkerResults);
    connect(this, &WimaController::nemoProgressChanged, this, &WimaController::_initStartSnakeWorker);
    connect(this, &QObject::destroyed, &this->_snakeWorker, &SnakeWorker::quit);
Valentin Platzgummer's avatar
Valentin Platzgummer committed

Valentin Platzgummer's avatar
Valentin Platzgummer committed
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_startStopRosBridge);
    _startStopRosBridge();
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_initStartSnakeWorker);
    _initStartSnakeWorker();
    connect(&_enableSnake, &Fact::rawValueChanged, this, &WimaController::_switchSnakeManager);
    _switchSnakeManager(_enableSnake.rawValue());
PlanMasterController *WimaController::masterController() {
    return _masterController;
}

MissionController *WimaController::missionController() {
    return _missionController;
}

QmlObjectListModel *WimaController::visualItems() {
    return &_areas;
}

QmlObjectListModel *WimaController::missionItems() {
    return const_cast<QmlObjectListModel*>(&_currentManager->missionItems());
}

QmlObjectListModel *WimaController::currentMissionItems() {
    return const_cast<QmlObjectListModel*>(&_currentManager->currentMissionItems());
}

QVariantList WimaController::waypointPath()
{
    return const_cast<QVariantList&>(_currentManager->waypointsVariant());
QVariantList WimaController::currentWaypointPath()
    return const_cast<QVariantList&>(_currentManager->currentWaypointsVariant());
Fact *WimaController::enableWimaController() {
    return &_enableWimaController;
}

Fact *WimaController::overlapWaypoints() {
    return &_overlapWaypoints;
}

Fact *WimaController::maxWaypointsPerPhase() {
    return &_maxWaypointsPerPhase;
}

Fact *WimaController::startWaypointIndex() {
    return &_nextPhaseStartWaypointIndex;
}

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

QVector<int> WimaController::nemoProgress() {
    if ( _nemoProgress.progress().size() == _snakeTileCenterPoints.size() )
        return _nemoProgress.progress();
    else
        return QVector<int>(_snakeTileCenterPoints.size(), 0);
}

double WimaController::phaseDistance() const
{
    return 0.0;
}

double WimaController::phaseDuration() const
{
    return 0.0;
int WimaController::nemoStatus() const
{
    return _nemoHeartbeat.status();
}

QString WimaController::nemoStatusString() const
    return _nemoStatusMap.at(_nemoHeartbeat.status());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
}

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

void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    _managerSettings.setMasterController(masterC);
    emit masterControllerChanged();
}

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

void WimaController::nextPhase()
{
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    _calcNextPhase();
    if ( !_currentManager->previous() ) {
        Q_ASSERT(false);

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
    if ( !_currentManager->reset() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
    QString errorString("Smart RTL requested. ");
    if ( !_checkSmartRTLPreCondition(errorString) ){
        qgcApp()->showMessage(errorString);
        return;
    }
    emit smartRTLRequestConfirm();
}

bool WimaController::upload()
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (   !_serviceArea.containsCoordinate(_masterController->managerVehicle()->coordinate())
        && currentMissionItems.count() > 0) {
    auto &currentMissionItems = _defaultManager.currentMissionItems();
    if (currentMissionItems.count() < 1)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        return false;

    _missionController->removeAll();
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        return false;
    settingsItem->setCoordinate(_managerSettings.homePosition());
    // Copy mission items to _missionController.
    for (int i = 1; i < currentMissionItems.count(); i++){
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
        _missionController->insertSimpleMissionItem(*item, visuals->count());
    }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
    _masterController->sendToVehicle();
Valentin Platzgummer's avatar
Valentin Platzgummer committed

    return true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
    _missionController->removeAll();
}

void WimaController::executeSmartRTL()
    forceUpload();
    masterController()->managerVehicle()->startMission();
void WimaController::initSmartRTL()
void WimaController::removeVehicleTrajectoryHistory()
{
    Vehicle *managerVehicle = masterController()->managerVehicle();
    managerVehicle->trajectoryPoints()->clear();
}

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
    QPolygonF polygon2D;
    toCartesianList(_joinedArea.coordinateList(), /*origin*/ start, polygon2D);
    QPointF start2D(0,0);
    QPointF end2D;
    toCartesian(destination, start, end2D);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    QVector<QPointF> path2D;
Valentin Platzgummer's avatar
Valentin Platzgummer committed

    bool retVal = PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
    toGeoList(path2D, /*origin*/ start, path);
/*!
 * \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
 */
bool WimaController::setWimaPlanData(const WimaPlanData &planData)
    // fetch only if valid, return true on success
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    // reset visual items
    _areas.clear();
    _defaultManager.clear();
    _snakeTiles.polygons().clear();
    _snakeTilesLocal.polygons().clear();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    emit visualItemsChanged();
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    emit currentWaypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    _localPlanDataValid = false;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    // extract list with WimaAreas
    QList<const WimaAreaData*> areaList = planData.areaList();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    int areaCounter = 0;
    const int numAreas = 4; // extract only numAreas Areas, if there are more they are invalid and ignored
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    for (int i = 0; i < areaList.size(); i++) {
        const WimaAreaData *areaData = areaList[i];
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        if (areaData->type() == WimaServiceAreaData::typeString) { // is it a service area?
            _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
            areaCounter++;
            _areas.append(&_serviceArea);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
            continue;
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
            _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
            areaCounter++;
            _areas.append(&_measurementArea);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
            continue;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
            _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
            areaCounter++;
            //_visualItems.append(&_corridor); // not needed
Valentin Platzgummer's avatar
Valentin Platzgummer committed
            continue;
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
            _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
            areaCounter++;
            _areas.append(&_joinedArea);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
            continue;
        }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        if (areaCounter >= numAreas)
            break;
    }
    if (areaCounter != numAreas) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        return false;
    }
    // extract mission items
    QList<MissionItem> tempMissionItems = planData.missionItems();
    if (tempMissionItems.size() < 1) {
        qWarning("WimaController: Mission items from WimaPlaner empty!");
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        return false;
    for (auto item : tempMissionItems) {
        _defaultManager.push_back(item.coordinate());
    _managerSettings.setHomePosition( QGeoCoordinate(_serviceArea.center().latitude(),
                                                     _serviceArea.center().longitude(),
                                                     0) );
    if( !_defaultManager.reset() ){
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        return false;
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();



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;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    _scenario.addArea(mArea);
    _scenario.addArea(sArea);
    _scenario.addArea(corridor);

    // Check if scenario is defined.
    if ( !_isScenarioDefinedErrorMessage() ) {
        Q_ASSERT(false);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        // Get tiles and origin.
        auto origin = _scenario.getOrigin();
        _snakeOrigin.setLatitude(origin[0]);
        _snakeOrigin.setLongitude(origin[1]);
        _snakeOrigin.setAltitude(origin[2]);
        const auto &tiles = _scenario.getTiles();
        const auto &cps = _scenario.getTileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        for ( unsigned int i=0; i < 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));
    }

    {
        // Get local tiles.
        const auto &tiles = _scenario.getTilesENU();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        for ( unsigned int i=0; i < 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.path().append(QVertex);
            }
            _snakeTilesLocal.polygons().append(Tile);
        }
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
WimaController *WimaController::thisPointer()
{
    return this;
}

bool WimaController::_calcNextPhase()
{
    if ( !_currentManager->next() ) {
        Q_ASSERT(false);
        return false;
    }
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    return true;
bool WimaController::_setStartIndex()
    _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed

    return true;
void WimaController::_recalcCurrentPhase()
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
}

void WimaController::_updateOverlap()
    bool value;
    _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
        assert(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
void WimaController::_updateMaxWaypoints()
    bool value;
    _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
    Q_ASSERT(value);
    (void)value;
    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::_updateflightSpeed()
    _managerSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::_updateArrivalReturnSpeed()
    _managerSettings.setArrivalReturnSpeed(_arrivalReturnSpeed.rawValue().toDouble(&value));
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::_updateAltitude()
    _managerSettings.setAltitude(_altitude.rawValue().toDouble(&value));
    Q_ASSERT(value);
    (void)value;

    if ( !_currentManager->update() ) {
        Q_ASSERT(false);
    }

    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
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) {
            if (!_lowBatteryHandlingTriggered) {                
                _lowBatteryHandlingTriggered = true;
                if ( !(_missionController->remainingTime() <= minTime) ) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::_eventTimerHandler()
{
    static EventTicker batteryLevelTicker(EVENT_TIMER_INTERVAL, CHECK_BATTERY_INTERVAL);
    static EventTicker snakeEventLoopTicker(EVENT_TIMER_INTERVAL, SNAKE_EVENT_LOOP_INTERVAL);
    static EventTicker rosBridgeTicker(EVENT_TIMER_INTERVAL, 1000);
    static EventTicker nemoStatusTicker(EVENT_TIMER_INTERVAL, 5000);
Valentin Platzgummer's avatar
Valentin Platzgummer committed

    // Battery level check necessary?
    Fact *enableLowBatteryHandling = qgcApp()->toolbox()->settingsManager()->wimaSettings()->enableLowBatteryHandling();
    if ( enableLowBatteryHandling->rawValue().toBool() && batteryLevelTicker.ready() )
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        _checkBatteryLevel();

    // Snake flight plan update necessary?
//    if ( snakeEventLoopTicker.ready() ) {
//        if ( _enableSnake.rawValue().toBool() && _localPlanDataValid && !_snakeCalcInProgress && _scenarioDefinedBool) {
//        }
//    }
    if ( nemoStatusTicker.ready() ) {
        this->_nemoHeartbeat.setStatus(_fallbackStatus);
        emit WimaController::nemoStatusChanged();
        emit WimaController::nemoStatusStringChanged();
    }

    if (_enableSnake.rawValue().toBool() && rosBridgeTicker.ready()) {
        qWarning() << "Connectable: " << _pRosBridge->connected() << "\n";


        auto start = std::chrono::high_resolution_clock::now();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        _pRosBridge->publish(_snakeOrigin, "/snake/origin");
        _pRosBridge->publish(_snakeTilesLocal, "/snake/tiles");
Valentin Platzgummer's avatar
Valentin Platzgummer committed

//        using namespace std::placeholders;
//        auto progressCallBack = std::bind(&WimaController::_progressFromJson,
//                                          this,
//                                          _1,
//                                          std::ref(_nemoProgress));
//        _pRosBridge->subscribe("/nemo/progress", progressCallBack);
//        _pRosBridge->subscribe("/nemo/heartbeat", [this, &nemoStatusTicker](JsonDocUPtr pDoc){
//            if ( !this->_pRosBridge->casePacker()->unpack(pDoc, this->_nemoHeartbeat) ) {
//                if ( this->_nemoHeartbeat.status() == this->_fallbackStatus )
//                    return;
//                this->_nemoHeartbeat.setStatus(this->_fallbackStatus);
//            }

//            nemoStatusTicker.reset();
//            this->_fallbackStatus = -1; /*Timeout*/
//            emit WimaController::nemoStatusChanged();
//            emit WimaController::nemoStatusStringChanged();
//        });

        auto end = std::chrono::high_resolution_clock::now();
        qWarning() << "Duration: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()
                   << " ms\n";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
}

void WimaController::_smartRTLCleanUp(bool flying)
    if ( !flying) { // vehicle has landed
        _switchWaypointManager(_defaultManager);
        _missionController->removeAllFromVehicle();
        _missionController->removeAll();
        disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
void WimaController::_setPhaseDistance(double distance)
{
    (void)distance;
//    if (!qFuzzyCompare(distance, _phaseDistance)) {
//        _phaseDistance = distance;
//        emit phaseDistanceChanged();
//    }
}

void WimaController::_setPhaseDuration(double duration)
{
    (void)duration;
//    if (!qFuzzyCompare(duration, _phaseDuration)) {
//        _phaseDuration = duration;
//        emit phaseDurationChanged();
//    }
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;
    return _rtlManager.checkPrecondition(errorString);
void WimaController::_switchWaypointManager(WaypointManager::ManagerBase &manager)
    if (_currentManager != &manager) {
        _currentManager = &manager;

        bool value;
        _currentManager->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
        Q_ASSERT(value);
        _currentManager->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
        Q_ASSERT(value);
        _currentManager->setStartIndex(_nextPhaseStartWaypointIndex.rawValue().toUInt(&value)-1);
        Q_ASSERT(value);
        (void)value;

        emit missionItemsChanged();
        emit currentMissionItemsChanged();
        emit waypointPathChanged();
        emit currentWaypointPathChanged();
        qWarning() << "WimaController::_switchWaypointManager: statistics update missing.";
void WimaController::_initSmartRTL()
{
    QString errorString;
    static int attemptCounter = 0;
    attemptCounter++;

    if ( _checkSmartRTLPreCondition(errorString) ) {
        _masterController->managerVehicle()->pauseVehicle();
        connect(masterController()->managerVehicle(), &Vehicle::flyingChanged, this, &WimaController::_smartRTLCleanUp);
        if ( _rtlManager.update() ) { // Calculate return path.
            _switchWaypointManager(_rtlManager);
            attemptCounter = 0;
            emit smartRTLPathConfirm();
            return;
        }
    } else if (attemptCounter > SMART_RTL_MAX_ATTEMPTS) {
        errorString.append(tr("Smart RTL: No success after maximum number of attempts."));
        qgcApp()->showMessage(errorString);
        attemptCounter = 0;
    } else {
        _smartRTLTimer.singleShot(SMART_RTL_ATTEMPT_INTERVAL, this, &WimaController::_initSmartRTL);
    }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
void WimaController::_setSnakeCalcInProgress(bool inProgress)
{
    if (_snakeCalcInProgress != inProgress){
        _snakeCalcInProgress = inProgress;
        emit snakeCalcInProgressChanged();
    }
}

bool WimaController::_isScenarioDefined()
    _scenarioDefinedBool = _scenario.defined(_snakeTileWidth.rawValue().toDouble(),
                                             _snakeTileHeight.rawValue().toDouble(),
                                             _snakeMinTileArea.rawValue().toDouble());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    return _scenarioDefinedBool;
}

bool WimaController::_isScenarioDefinedErrorMessage()
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    if (!value){
        QString errorString;
        for (auto c : _scenario.errorString)
            errorString.push_back(c);
        qgcApp()->showMessage(errorString);
    }
    return value;
void WimaController::_snakeStoreWorkerResults()
{
    auto start = std::chrono::high_resolution_clock::now();
    _snakeManager.clear();

    const WorkerResult_t &r = _snakeWorker.getResult();
    _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;

    // Create QVector<QGeoCoordinate> containing all waypoints;
    unsigned long startIdx = r.arrivalPathIdx.last();
    unsigned  long endIdx = r.returnPathIdx.first();
    for (unsigned long i = startIdx; i <= endIdx; ++i) {
        _snakeManager.push_back(r.waypoints[int(i)].value<QGeoCoordinate>());
    _snakeManager.update();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();

    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
void WimaController::_startStopRosBridge()
{
    if ( _enableSnake.rawValue().toBool() ) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
        _pRosBridge->start();
    } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
    }
}

void WimaController::_initStartSnakeWorker()
{
    if ( !_enableSnake.rawValue().toBool() )
        return;

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

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

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

void WimaController::_switchSnakeManager(QVariant variant)
{
    if (variant.value<bool>()){
        _switchWaypointManager(_snakeManager);
    } else {
        _switchWaypointManager(_defaultManager);
    }
}

void WimaController::_progressFromJson(JsonDocUPtr pDoc,
                                       QNemoProgress &progress)
Valentin Platzgummer's avatar
Valentin Platzgummer committed
{
    int requiredSize = _snakeTilesLocal.polygons().size();
    if ( !_pRosBridge->casePacker()->unpack(pDoc, progress)
         || progress.progress().size() != requiredSize ) {
        progress.progress().fill(0, requiredSize);
    }

    emit WimaController::nemoProgressChanged();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
}