WimaController.cc 21.9 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
1
#include "WimaController.h"
2

3 4 5 6 7 8 9 10 11 12
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";
13

14
WimaController::WimaController(QObject *parent)
15 16 17 18 19 20 21 22 23 24 25
    : 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])
26
    , _nestPhaseStartWaypointIndex       (settingsGroup, _metaDataMap[startWaypointIndexName])
27 28 29
    , _showAllMissionItems      (settingsGroup, _metaDataMap[showAllMissionItemsName])
    , _showCurrentMissionItems  (settingsGroup, _metaDataMap[showCurrentMissionItemsName])
{
30
    _nestPhaseStartWaypointIndex.setRawValue(int(1));
31 32 33
    _showAllMissionItems.setRawValue(true);
    _showCurrentMissionItems.setRawValue(true);
    connect(&_overlapWaypoints,     &Fact::rawValueChanged, this, &WimaController::updateNextWaypoint);
34 35
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this, &WimaController::recalcCurrentPhase);
    connect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
36 37
}

38
QmlObjectListModel* WimaController::visualItems()
39
{
40
    return &_visualItems;
41 42
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
43 44 45 46
QStringList WimaController::loadNameFilters() const
{
    QStringList filters;

47
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension) <<
Valentin Platzgummer's avatar
Valentin Platzgummer committed
48 49
               tr("All Files (*.*)");
    return filters;
50 51 52 53 54
}

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

56 57
    filters << tr("Supported types (*.%1 *.%2)").arg(wimaFileExtension).arg(AppSettings::planFileExtension);
    return filters;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
58 59
}

60
WimaDataContainer *WimaController::dataContainer()
61
{
62
    return _container;
63 64
}

65 66 67 68 69
QmlObjectListModel *WimaController::missionItems()
{
    return &_missionItems;
}

70 71 72 73 74
QmlObjectListModel *WimaController::currentMissionItems()
{
    return &_currentMissionItems;
}

75 76 77 78 79
QVariantList WimaController::waypointPath()
{
    return  _waypointPath;
}

80 81 82 83 84
QVariantList WimaController::currentWaypointPath()
{
    return _currentWaypointPath;
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
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::startWaypointIndex()
{
112
    return &_nestPhaseStartWaypointIndex;
113 114
}

115 116 117 118 119 120 121 122 123 124 125 126
void WimaController::setMasterController(PlanMasterController *masterC)
{
    _masterController = masterC;
    emit masterControllerChanged();
}

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

127 128 129 130 131 132
/*!
 * \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
 */
133 134
void WimaController::setDataContainer(WimaDataContainer *container)
{
135 136
    if (container != nullptr) {
        if (_container != nullptr) {
137
           disconnect(_container, &WimaDataContainer::dataValidChanged, this, &WimaController::fetchContainerData);
138 139
        }

140
        _container = container;
141
        connect(_container, &WimaDataContainer::dataValidChanged, this, &WimaController::fetchContainerData);
142 143 144 145 146

        emit dataContainerChanged();
    }
}

147 148
void WimaController::nextPhase()
{
149
    calcNextPhase();
150 151
}

152 153
void WimaController::previousPhase()
{
154 155 156 157
    if (_nestPhaseStartWaypointIndex.rawValue().toInt() > 0) {
        _nestPhaseStartWaypointIndex.setRawValue(   1 + std::max(_startWaypointIndex
                                                  - _maxWaypointsPerPhase.rawValue().toInt()
                                                  + _overlapWaypoints.rawValue().toInt(), 0));
158
    }
159 160 161 162
}

void WimaController::resetPhase()
{
163
    _nestPhaseStartWaypointIndex.setRawValue(int(1));
164 165 166 167
}

void WimaController::uploadToVehicle()
{
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    if (_currentMissionItems.count() < 1)
        return;

    _missionController->removeAll();
    // set homeposition of settingsItem
    QmlObjectListModel* visuals = _missionController->visualItems();
    MissionSettingsItem* settingsItem = visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
        return;
    }
    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());
    }

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

//    // check vehicle type, before setting land command
//    Vehicle* controllerVehicle = _masterController->controllerVehicle();
//    MAV_CMD landCmd = controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_LAND : MAV_CMD_NAV_LAND;
//    if (controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains(landCmd)) {
//        landItem->setCommand(landCmd);
//    } else {
//        _missionController->removeAll();
//        return;
//    }

    for (int i = 1; i < visuals->count(); i++) {
        SimpleMissionItem* item = visuals->value<SimpleMissionItem *>(i);
        qWarning() << item->coordinate();
        qWarning() << item->command();
        qWarning() << item->missionItem().param1();
        qWarning() << item->missionItem().param2();
        qWarning() << item->missionItem().param3();
        qWarning() << item->missionItem().param4();
        qWarning() << item->missionItem().param5();
        qWarning() << item->missionItem().param6();
        qWarning() << item->missionItem().param7();
        qWarning(" ");
    }
218 219
    _masterController->sendToVehicle();
}
220

221 222 223
void WimaController::removeFromVehicle()
{
    _masterController->removeAllFromVehicle();
224 225
}

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
void WimaController::startMission()
{

}

void WimaController::abortMission()
{

}

void WimaController::pauseMission()
{

}

void WimaController::resumeMission()
{

}

246
void WimaController::saveToCurrent()
247 248 249
{
}

250 251
void WimaController::saveToFile(const QString& filename)
{
252
    QString file = filename;
253 254
}

255
bool WimaController::loadFromCurrent()
256
{
257
    return true;
258 259 260 261
}

bool WimaController::loadFromFile(const QString &filename)
{
262
    QString file = filename;
263
    return true;
264 265 266
}


267

268
QJsonDocument WimaController::saveToJson(FileType fileType)
269
{
270 271 272 273
    if(fileType)
    {

    }
274
    return QJsonDocument();
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
bool WimaController::calcShortestPath(const QGeoCoordinate &start, const QGeoCoordinate &destination, QList<QGeoCoordinate> &path)
{
    using namespace GeoUtilities;
    using namespace PolygonCalculus;
    QList<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;
}

292
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QList<QGeoCoordinate> &coordinateList)
293 294 295 296
{
    return extractCoordinateList(missionItems, coordinateList, 0, missionItems.count()-1);
}

297
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QList<QGeoCoordinate> &coordinateList, int startIndex, int endIndex)
298 299 300 301 302 303 304 305 306 307 308 309
{
    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++) {
310
                SimpleMissionItem *mItem = missionItems.value<SimpleMissionItem *>(i);
311 312 313 314 315 316 317 318 319 320 321 322 323 324

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

    return true;
}

325
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList)
326 327 328 329
{
    return extractCoordinateList(missionItems, coordinateList, 0 , missionItems.count()-1);
}

330
bool WimaController::extractCoordinateList(QmlObjectListModel &missionItems, QVariantList &coordinateList, int startIndex, int endIndex)
331 332 333 334 335 336 337 338 339 340 341 342 343 344
{
    QList<QGeoCoordinate> geoCoordintateList;

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

    if (!retValue)
        return false;

    for (auto coordinate : geoCoordintateList)
        coordinateList.append(QVariant::fromValue(coordinate));

    return true;
}

345 346 347 348 349 350 351
/*!
 * \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
 */
352
void WimaController::fetchContainerData(bool valid)
353
{
354 355 356 357 358
    if ( valid ) {
        if (_container == nullptr) {
            qWarning("WimaController::containerDataValidChanged(): No container assigned!");
        }
        _localPlanDataValid = false;
359
        _visualItems.clear();
360
        _missionItems.clear();
361 362 363 364 365 366 367 368 369 370 371
        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?
372
                _serviceArea = *qobject_cast<const WimaServiceAreaData*>(areaData);
373
                areaCounter++;
374
                _visualItems.append(&_serviceArea);
375 376 377 378

                continue;
            }

379 380
            if (areaData->type() == WimaMeasurementAreaData::typeString) { // is it a measurement area?
                _measurementArea =  *qobject_cast<const WimaMeasurementAreaData*>(areaData);
381
                areaCounter++;
382
                _visualItems.append(&_measurementArea);
383 384 385 386

                continue;
            }

387 388
            if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
                _corridor =  *qobject_cast<const WimaCorridorData*>(areaData);
389
                areaCounter++;
390
                //_visualItems.append(&_corridor); // not needed
391 392 393 394

                continue;
            }

395 396
            if (areaData->type() == WimaJoinedAreaData::typeString) { // is it a corridor?
                _joinedArea =  *qobject_cast<const WimaJoinedAreaData*>(areaData);
397
                areaCounter++;
398
                _visualItems.append(&_joinedArea);
399 400

                continue;
401
            }
402 403 404 405

            if (areaCounter >= numAreas)
                break;
        }
406 407
        QList<const MissionItem*> tempMissionItems = planData.missionItems();

408
        // create mission items
409
        _missionController->removeAll();
410
        QmlObjectListModel* missionControllerVisualItems = _missionController->visualItems();
411 412 413 414 415


        // find takeoff command
        int begin = -1;
        for (int i = 0; i < tempMissionItems.size(); i++) {
416
            const MissionItem *missionItem = tempMissionItems[i];
417 418 419
            if (    missionItem->command() == MAV_CMD_NAV_VTOL_TAKEOFF
                 || missionItem->command() == MAV_CMD_NAV_TAKEOFF) {

420
                _missionController->insertSimpleMissionItem(*missionItem, missionControllerVisualItems->count());
421 422 423 424 425 426 427 428

                QGeoCoordinate coordinate = missionItem->coordinate();
                _takeoffLandPostion.setAltitude(coordinate.altitude());
                _takeoffLandPostion.setLatitude(coordinate.latitude());
                _takeoffLandPostion.setLongitude(coordinate.longitude());

                begin = i + 1;
                break;
429
            }
430 431 432 433 434 435 436 437 438 439 440 441
        }

        // check if takeoff command found
        if (begin < 0) {
            qWarning("WimaController::containerDataValidChanged(): No takeoff found.");
            return;
        }

        // copy mission items and create SimpleMissionItem by using _missionController
        for ( int i = begin; i < tempMissionItems.size(); i++) {
            const MissionItem *missionItem = tempMissionItems[i];
            _missionController->insertSimpleMissionItem(*missionItem, missionControllerVisualItems->count());
442 443 444

            if (    missionItem->command() == MAV_CMD_NAV_VTOL_LAND
                 || missionItem->command() == MAV_CMD_NAV_LAND)
445
                 break;
446
        }
447 448 449 450 451 452 453 454 455 456 457

        for ( int i = 1; i < missionControllerVisualItems->count(); i++) {
            SimpleMissionItem *visualItem     = qobject_cast<SimpleMissionItem *>((*missionControllerVisualItems)[i]);
            if (visualItem == nullptr) {
                qWarning("WimaController::containerDataValidChanged(): Nullptr at SimpleMissionItem!");
                return;
            }
            SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
            _missionItems.append(visualItemCopy);
        }

458 459
        if (areaCounter == numAreas)
            _localPlanDataValid = true;
460

461
        updateWaypointPath();
462 463
        _nestPhaseStartWaypointIndex.setRawValue(int(1));
        calcNextPhase();
464

465 466
    } else {
        _localPlanDataValid = false;
467 468
        _visualItems.clear();
        _missionItems.clear();
469
    }
470 471

    emit visualItemsChanged();
472
    emit missionItemsChanged();
473 474 475 476 477

#ifdef QT_DEBUG
    //qWarning("Mission Items count: ");
    //qWarning() << _missionItems.count();
#endif
478 479
}

480
void WimaController::calcNextPhase()
481
{
482
    _startWaypointIndex = _nestPhaseStartWaypointIndex.rawValue().toInt()-1;
483
    // check if data was fetched and mission end is not reached yet
484
    if (_missionItems.count() < 1 || !_localPlanDataValid || _startWaypointIndex >= _missionItems.count()-2)
485 486
        return;

487 488 489
    int maxWaypointsPerPhaseInt = _maxWaypointsPerPhase.rawValue().toInt();

    // determine end waypoint index
490
    _endWaypointIndex = std::min(_startWaypointIndex + maxWaypointsPerPhaseInt - 1, _missionItems.count()-2); // -2 -> last item is land item
491

492
    // extract waypoints
493
    QList<QGeoCoordinate> geoCoordinateList; // list with potential waypoints (from _missionItems), for _currentMissionItems
494
    if (!extractCoordinateList(_missionItems, geoCoordinateList, _startWaypointIndex, _endWaypointIndex)) {
495
        qWarning("WimaController::updateCurrentMissionItems(): error on waypoint extraction.");
496
        _currentMissionItems.clear();
497
        return;
498
    }
499 500 501

    // set start waypoint index for next phase
    if (_endWaypointIndex < _missionItems.count()-2) {
502
        _startWaypointIndexList.append(_nestPhaseStartWaypointIndex.rawValue().toInt());
503

504 505 506 507
        disconnect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
        int temp = 1 + std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0);
        _nestPhaseStartWaypointIndex.setRawValue(std::min(temp , _missionItems.count()-1));
        connect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
508 509
    }
    else {
510 511 512
        disconnect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
        _nestPhaseStartWaypointIndex.setRawValue(_missionItems.count()-1); // marks that end of mission is reached
        connect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
513
    }
514 515 516

    // calculate path from home to first waypoint
    QList<QGeoCoordinate> path;
517
    if ( !calcShortestPath(_takeoffLandPostion, geoCoordinateList[0], path) ) {
518
        qWarning("WimaController::updateCurrentMissionItems(): Not able to calc path from home to first waypoint.");
519
        _currentMissionItems.clear();
520 521 522 523 524 525 526 527
        return;
    }
    // prepend to geoCoordinateList
    for (int i = path.size()-2; i >= 0; i--) //  i = path.size()-2 : last coordinate already in geoCoordinateList
        geoCoordinateList.prepend(path[i]);

    // calculate path from last waypoint to home
    path.clear();
528
    if ( !calcShortestPath(geoCoordinateList.last(), _takeoffLandPostion, path) ) {
529
        qWarning("WimaController::updateCurrentMissionItems(): Not able to calc path from home to first waypoint.");
530
        _currentMissionItems.clear();
531 532 533 534 535
        return;
    }
    path.removeFirst(); // first coordinate already in geoCoordinateList
    geoCoordinateList.append(path);

536

537 538 539
    // create Mission Items
    _missionController->removeAll();
    QmlObjectListModel* missionControllerVisuals = _missionController->visualItems();
540 541 542 543 544 545 546 547 548 549 550

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

    for (auto coordinate : geoCoordinateList) {
551
        _missionController->insertSimpleMissionItem(coordinate, missionControllerVisuals->count());
552 553 554 555 556 557 558 559 560 561 562
    }

    // set homeposition for take off item (somehow not working with insertSimpleMissionItem)
    SimpleMissionItem* takeoff = missionControllerVisuals->value<SimpleMissionItem *>(1);
    if (takeoff == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
        _currentMissionItems.clear();
        return;
    }
    takeoff->setCoordinate(_takeoffLandPostion);

563 564 565 566 567

    // set land command for last mission item
    SimpleMissionItem *landItem = missionControllerVisuals->value<SimpleMissionItem*>(missionControllerVisuals->count()-1);
    if (landItem == nullptr) {
        qWarning("WimaController::updateCurrentMissionItems(): nullptr");
568
        _currentMissionItems.clear();
569 570 571 572 573 574 575
        return;
    }
    // check vehicle type, before setting land command
    Vehicle* controllerVehicle = _masterController->controllerVehicle();
    MAV_CMD landCmd = controllerVehicle->vtol() ? MAV_CMD_NAV_VTOL_LAND : MAV_CMD_NAV_LAND;
    if (controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains(landCmd)) {
        landItem->setCommand(landCmd);
576 577
    } else {
        _currentMissionItems.clear();
578
        return;
579
    }
580

581
    // copy to _currentMissionItems
582
    _currentMissionItems.clear();
583 584 585
    for ( int i = 1; i < missionControllerVisuals->count(); i++) {
        SimpleMissionItem *visualItem     = missionControllerVisuals->value<SimpleMissionItem*>(i);
        if (visualItem == nullptr) {
586 587
            qWarning("WimaController::updateCurrentMissionItems(): Nullptr at SimpleMissionItem!");            
            _currentMissionItems.clear();
588 589
            return;
        }
590

591
        SimpleMissionItem *visualItemCopy = new SimpleMissionItem(*visualItem, true, this);
592 593
        _currentMissionItems.append(visualItemCopy);
    }
594

595 596
    _missionController->removeAll(); // remove items from _missionController, will be added on upload

597
    updateCurrentPath();
598
    emit currentMissionItemsChanged();
599
}
600

601 602 603
void WimaController::updateWaypointPath()
{
    _waypointPath.clear();
604
    extractCoordinateList(_missionItems, _waypointPath, 0, _missionItems.count()-1);
605

606 607 608 609 610
    emit waypointPathChanged();
}
void WimaController::updateCurrentPath()
{
    _currentWaypointPath.clear();
611
    extractCoordinateList(_currentMissionItems, _currentWaypointPath, 0, _currentMissionItems.count()-1);
612

613 614
    emit currentWaypointPathChanged();
}
615

616 617 618
void WimaController::updateNextWaypoint()
{
    if (_endWaypointIndex < _missionItems.count()-2) {
619 620 621
        disconnect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
        _nestPhaseStartWaypointIndex.setRawValue(1 + std::max(_endWaypointIndex + 1 - _overlapWaypoints.rawValue().toInt(), 0));
        connect(&_nestPhaseStartWaypointIndex,   &Fact::rawValueChanged, this, &WimaController::calcNextPhase);
622 623 624
    }
}

625 626 627 628 629
void WimaController::recalcCurrentPhase()
{
    _nestPhaseStartWaypointIndex.setRawValue(_startWaypointIndex + 1);
}

630 631