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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
3
#include "utilities.h"
4

5
#include "Snake/QNemoHeartbeat.h"
6
#include "Snake/QNemoProgress.h"
7

8
#include "QVector3D"
9
#include <QScopedPointer>
10
#include <QtConcurrentRun>
11

12 13
#include <memory>

Valentin Platzgummer's avatar
Valentin Platzgummer committed
14 15 16 17 18
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
  return static_cast<typename std::underlying_type<T>::type>(value);
}

19
#define SMART_RTL_MAX_ATTEMPTS 3       // times
20
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#define EVENT_TIMER_INTERVAL 50        // ms

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::snakeTileWidthName = "SnakeTileWidth";
const char *WimaController::snakeTileHeightName = "SnakeTileHeight";
const char *WimaController::snakeMinTileAreaName = "SnakeMinTileArea";
const char *WimaController::snakeLineDistanceName = "SnakeLineDistance";
const char *WimaController::snakeMinTransectLengthName =
    "SnakeMinTransectLength";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
42

Valentin Platzgummer's avatar
Valentin Platzgummer committed
43 44 45
WimaController::StatusMap WimaController::_nemoStatusMap{
    std::make_pair<int, QString>(0, "No Heartbeat"),
    std::make_pair<int, QString>(1, "Connected"),
46 47
    std::make_pair<int, QString>(-1, "Timeout"),
    std::make_pair<int, QString>(-2, "Error")};
48

49
WimaController::WimaController(QObject *parent)
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    : QObject(parent), _joinedArea(), _measurementArea(), _serviceArea(),
      _corridor(), _localPlanDataValid(false),
      _areaInterface(&_measurementArea, &_serviceArea, &_corridor,
                     &_joinedArea),
      _WMSettings(), _defaultWM(_WMSettings, _areaInterface),
      _snakeWM(_WMSettings, _areaInterface),
      _rtlWM(_WMSettings, _areaInterface),
      _currentWM(&_defaultWM), _WMList{&_defaultWM, &_snakeWM, &_rtlWM},
      _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]),
      _snakeTileWidth(settingsGroup, _metaDataMap[snakeTileWidthName]),
      _snakeTileHeight(settingsGroup, _metaDataMap[snakeTileHeightName]),
      _snakeMinTileArea(settingsGroup, _metaDataMap[snakeMinTileAreaName]),
      _snakeLineDistance(settingsGroup, _metaDataMap[snakeLineDistanceName]),
      _snakeMinTransectLength(settingsGroup,
                              _metaDataMap[snakeMinTransectLengthName]),
      _lowBatteryHandlingTriggered(false), _measurementPathLength(-1),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
81 82
      _snakeThread(this), _emptyThread(this), _currentThread(&_emptyThread),
      _nemoInterface(this),
83 84
      _batteryLevelTicker(EVENT_TIMER_INTERVAL, 1000 /*ms*/) {

Valentin Platzgummer's avatar
Valentin Platzgummer committed
85
  // Set up facts for waypoint manager.
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 112 113 114 115 116 117 118 119 120
  _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);
  connect(&_flightSpeed, &Fact::rawValueChanged, this,
          &WimaController::_updateflightSpeed);
  connect(&_arrivalReturnSpeed, &Fact::rawValueChanged, this,
          &WimaController::_updateArrivalReturnSpeed);
  connect(&_altitude, &Fact::rawValueChanged, this,
          &WimaController::_updateAltitude);

  // Init waypoint managers.
  bool value;
  size_t overlap = _overlapWaypoints.rawValue().toUInt(&value);
  Q_ASSERT(value);
  size_t N = _maxWaypointsPerPhase.rawValue().toUInt(&value);
  Q_ASSERT(value);
  size_t startIdx = _nextPhaseStartWaypointIndex.rawValue().toUInt(&value);
  Q_ASSERT(value);
  (void)value;
  for (auto manager : _WMList) {
    manager->setOverlap(overlap);
    manager->setN(N);
    manager->setStartIndex(startIdx);
  }

  // Periodic.
  connect(&_eventTimer, &QTimer::timeout, this,
          &WimaController::_eventTimerHandler);
  _eventTimer.start(EVENT_TIMER_INTERVAL);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
121 122 123 124 125 126 127 128 129 130
  // SnakeThread.
  connect(_currentThread, &SnakeThread::finished, this,
          &WimaController::_threadFinishedHandler);
  connect(_currentThread, &SnakeThread::calcInProgressChanged, this,
          &WimaController::snakeCalcInProgressChanged);
  connect(this, &QObject::destroyed, &this->_snakeThread, &SnakeThread::quit);
  connect(this, &QObject::destroyed, &this->_emptyThread, &SnakeThread::quit);

  // NemoInterface.
  connect(&_nemoInterface, &NemoInterface::progressChanged, this,
131
          &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
132
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
133
          &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
134
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
135 136
          &WimaController::nemoStatusStringChanged);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
137
  // Enable/disable snake.
138 139 140
  connect(&_enableSnake, &Fact::rawValueChanged, this,
          &WimaController::_enableSnakeChangedHandler);
  _enableSnakeChangedHandler();
141 142 143 144 145
  connect(&_enableWimaController, &Fact::rawValueChanged, [this] {
    if (!this->_enableWimaController.rawValue().toBool()) {
      this->_enableSnake.setCookedValue(QVariant(false));
    }
  });
146 147 148 149 150

  // Snake Waypoint Manager.
  connect(&_enableSnake, &Fact::rawValueChanged, this,
          &WimaController::_switchToSnakeWaypointManager);
  _switchToSnakeWaypointManager(_enableSnake.rawValue());
151 152
}

153
PlanMasterController *WimaController::masterController() {
154
  return _masterController;
155 156 157
}

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

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

QmlObjectListModel *WimaController::missionItems() {
164
  return const_cast<QmlObjectListModel *>(&_currentWM->missionItems());
165 166 167
}

QmlObjectListModel *WimaController::currentMissionItems() {
168
  return const_cast<QmlObjectListModel *>(&_currentWM->currentMissionItems());
169 170
}

171 172
QVariantList WimaController::waypointPath() {
  return const_cast<QVariantList &>(_currentWM->waypointsVariant());
173 174
}

175 176
QVariantList WimaController::currentWaypointPath() {
  return const_cast<QVariantList &>(_currentWM->currentWaypointsVariant());
177 178
}

179
Fact *WimaController::enableWimaController() { return &_enableWimaController; }
180

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

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

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

189
Fact *WimaController::showAllMissionItems() { return &_showAllMissionItems; }
190 191

Fact *WimaController::showCurrentMissionItems() {
192
  return &_showCurrentMissionItems;
193 194
}

195
Fact *WimaController::flightSpeed() { return &_flightSpeed; }
196

197
Fact *WimaController::arrivalReturnSpeed() { return &_arrivalReturnSpeed; }
198

199
Fact *WimaController::altitude() { return &_altitude; }
200

201 202 203 204
QmlObjectListModel *WimaController::snakeTiles() {
  static QmlObjectListModel list;
  return &list;
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
205

206
QVariantList WimaController::snakeTileCenterPoints() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
207
  return _currentThread->tileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
208 209
}

210
QVector<int> WimaController::nemoProgress() {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
211
  return _currentThread->progress();
212 213
}

214 215 216
double WimaController::phaseDistance() const {
  qWarning() << "using phaseDistance dummy";
  return 0.0;
217 218
}

219 220 221
double WimaController::phaseDuration() const {
  qWarning() << "using phaseDuration dummy";
  return 0.0;
222 223
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
224 225 226
int WimaController::nemoStatus() const {
  return integral(_nemoInterface.status());
}
227

228 229
QString WimaController::nemoStatusString() const {
  return _nemoStatusMap.at(nemoStatus());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
230 231
}

232
bool WimaController::snakeCalcInProgress() const {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
233
  return _currentThread->calcInProgress();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
234 235
}

236 237 238 239
void WimaController::setMasterController(PlanMasterController *masterC) {
  _masterController = masterC;
  _WMSettings.setMasterController(masterC);
  emit masterControllerChanged();
240 241
}

242 243 244 245
void WimaController::setMissionController(MissionController *missionC) {
  _missionController = missionC;
  _WMSettings.setMissionController(missionC);
  emit missionControllerChanged();
246 247
}

248
void WimaController::nextPhase() { _calcNextPhase(); }
249

250 251 252 253
void WimaController::previousPhase() {
  if (!_currentWM->previous()) {
    Q_ASSERT(false);
  }
254

255 256 257 258
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
259 260
}

261 262 263 264
void WimaController::resetPhase() {
  if (!_currentWM->reset()) {
    Q_ASSERT(false);
  }
265

266 267 268 269
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
270 271
}

272
void WimaController::requestSmartRTL() {
273 274 275 276 277
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::requestSmartRTL() called";
#endif
  QString errorString("Smart RTL requested.");
  if (!_SRTLPrecondition(errorString)) {
278 279 280 281
    qgcApp()->showMessage(errorString);
    return;
  }
  emit smartRTLRequestConfirm();
282 283
}

284
bool WimaController::upload() {
285 286 287 288 289 290 291 292 293 294 295
  auto &items = _currentWM->currentMissionItems();
  if (_masterController && _masterController->managerVehicle() &&
      items.count() > 0) {
    if (!_joinedArea.containsCoordinate(
            _masterController->managerVehicle()->coordinate())) {
      emit forceUploadConfirm();
      return false;
    } else {
      return forceUpload();
    }
  } else {
296 297
    return false;
  }
298 299
}

300
bool WimaController::forceUpload() {
301 302 303 304 305 306 307 308
  auto &currentMissionItems = _currentWM->currentMissionItems();
  if (currentMissionItems.count() < 1 || !_missionController ||
      !_masterController) {
    qWarning() << "WimaController::forceUpload(): error:";
    qWarning() << "currentMissionItems.count(): "
               << currentMissionItems.count();
    qWarning() << "_missionController: " << _missionController;
    qWarning() << "_masterController: " << _masterController;
309
    return false;
310 311 312 313 314 315 316 317 318 319 320 321
  } else {
    _missionController->removeAll();
    // Set homeposition of settingsItem.
    QmlObjectListModel *visuals = _missionController->visualItems();
    MissionSettingsItem *settingsItem =
        visuals->value<MissionSettingsItem *>(0);
    if (settingsItem == nullptr) {
      Q_ASSERT(false);
      qWarning("WimaController::updateCurrentMissionItems(): nullptr");
      return false;
    } else {
      settingsItem->setCoordinate(_WMSettings.homePosition());
322

323 324 325 326 327 328 329 330
      // Copy mission items to _missionController and send them.
      for (int i = 1; i < currentMissionItems.count(); i++) {
        auto *item = currentMissionItems.value<const SimpleMissionItem *>(i);
        _missionController->insertSimpleMissionItem(*item, visuals->count());
      }
      _masterController->sendToVehicle();
      return true;
    }
331
  }
332
}
333

334
void WimaController::removeFromVehicle() {
335 336 337 338
  if (_masterController && _missionController) {
    _masterController->removeAllFromVehicle();
    _missionController->removeAll();
  }
339 340
}

341
void WimaController::executeSmartRTL() {
342 343 344 345 346 347 348
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::executeSmartRTL() called";
#endif
  if (_masterController && _masterController->managerVehicle()) {
    forceUpload();
    _masterController->managerVehicle()->startMission();
  }
349 350
}

351 352 353 354 355 356
void WimaController::initSmartRTL() {
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::initSmartRTL() called";
#endif
  _initSmartRTL();
}
357

358
void WimaController::removeVehicleTrajectoryHistory() {
359 360 361 362 363
  if (_masterController && _masterController->managerVehicle()) {
    _masterController->managerVehicle()
        ->trajectoryPoints()
        ->clearAndDeleteContents();
  }
364 365
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
366 367
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
368 369 370 371 372 373 374 375 376
                                       QVector<QGeoCoordinate> &path) {
  using namespace GeoUtilities;
  using namespace PolygonCalculus;
  QPolygonF polygon2D;
  toCartesianList(_joinedArea.coordinateList(), /*origin*/ start, polygon2D);
  QPointF start2D(0, 0);
  QPointF end2D;
  toCartesian(destination, start, end2D);
  QVector<QPointF> path2D;
377

378 379 380
  bool retVal =
      PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
  toGeoList(path2D, /*origin*/ start, path);
381

382 383
  return retVal;
}
384

385
bool WimaController::setWimaPlanData(QSharedPointer<WimaPlanData> planData) {
386 387 388 389
  // reset visual items
  _areas.clear();
  _defaultWM.clear();
  _snakeWM.clear();
390

391 392 393 394 395
  emit visualItemsChanged();
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
396

397
  _localPlanDataValid = false;
398

399
  // extract list with WimaAreas
400
  QList<const WimaAreaData *> areaList = planData->areaList();
401

402
  int areaCounter = 0;
403 404
  const int numAreas = 4; // extract only numAreas Areas, if there are more
                          // they are invalid and ignored
405 406
  for (int i = 0; i < areaList.size(); i++) {
    const WimaAreaData *areaData = areaList[i];
407

408 409 410 411 412
    if (areaData->type() ==
        WimaServiceAreaData::typeString) { // is it a service area?
      _serviceArea = *qobject_cast<const WimaServiceAreaData *>(areaData);
      areaCounter++;
      _areas.append(&_serviceArea);
413

414
      continue;
415
    }
416

417 418 419 420 421 422
    if (areaData->type() ==
        WimaMeasurementAreaData::typeString) { // is it a measurement area?
      _measurementArea =
          *qobject_cast<const WimaMeasurementAreaData *>(areaData);
      areaCounter++;
      _areas.append(&_measurementArea);
423

424
      continue;
425
    }
426

427 428 429 430
    if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
      _corridor = *qobject_cast<const WimaCorridorData *>(areaData);
      areaCounter++;
      //_visualItems.append(&_corridor); // not needed
431

432
      continue;
433
    }
434

435 436 437 438 439
    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
440

441
      continue;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
442 443
    }

444 445 446
    if (areaCounter >= numAreas)
      break;
  }
447

448 449 450 451
  if (areaCounter != numAreas) {
    Q_ASSERT(false);
    return false;
  }
452

453
  emit visualItemsChanged();
454

455
  // Copy transects.
456 457 458 459 460
  auto tempMissionItems = planData->missionItems();
  if (tempMissionItems.size() < 1) {
    qWarning("WimaController: Mission items from WimaPlaner empty!");
    return false;
  }
461

462 463 464
  for (auto *item : tempMissionItems) {
    _defaultWM.push_back(item->coordinate());
  }
465
  //  // extract mission items
466 467
  _WMSettings.setHomePosition(QGeoCoordinate(
      _serviceArea.depot().latitude(), _serviceArea.depot().longitude(), 0));
468
  //  auto tempMissionItems = planData->missionItems();
469 470 471 472
  if (!_defaultWM.reset()) {
    Q_ASSERT(false);
    return false;
  }
473
  //  if (tempMissionItems.size() < 1) {
474 475 476 477
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
478 479
  //    qWarning("WimaController: Mission items from WimaPlaner empty!");
  //    return false;
480 481 482 483
  _snakeThread.setMeasurementArea(_measurementArea.coordinateList());
  _snakeThread.setServiceArea(_serviceArea.coordinateList());
  _snakeThread.setCorridor(_corridor.coordinateList());
  _currentThread->start();
484

485 486
  _localPlanDataValid = true;
  return true;
487
}
488

489
WimaController *WimaController::thisPointer() { return this; }
490

491 492 493 494 495
bool WimaController::_calcNextPhase() {
  if (!_currentWM->next()) {
    Q_ASSERT(false);
    return false;
  }
496

497 498 499 500
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
501

502
  return true;
503 504
}

505 506 507 508 509 510
bool WimaController::_setStartIndex() {
  bool value;
  _currentWM->setStartIndex(
      _nextPhaseStartWaypointIndex.rawValue().toUInt(&value) - 1);
  Q_ASSERT(value);
  (void)value;
511

512 513 514 515
  if (!_currentWM->update()) {
    Q_ASSERT(false);
    return false;
  }
516

517 518 519 520
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
521

522
  return true;
523 524
}

525 526 527 528
void WimaController::_recalcCurrentPhase() {
  if (!_currentWM->update()) {
    Q_ASSERT(false);
  }
529

530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_updateOverlap() {
  bool value;
  _currentWM->setOverlap(_overlapWaypoints.rawValue().toUInt(&value));
  Q_ASSERT(value);
  (void)value;

  if (!_currentWM->update()) {
    assert(false);
  }

  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_updateMaxWaypoints() {
  bool value;
  _currentWM->setN(_maxWaypointsPerPhase.rawValue().toUInt(&value));
  Q_ASSERT(value);
  (void)value;

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

  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_updateflightSpeed() {
  bool value;
  _WMSettings.setFlightSpeed(_flightSpeed.rawValue().toDouble(&value));
  Q_ASSERT(value);
  (void)value;

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

  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_updateArrivalReturnSpeed() {
  bool value;
  _WMSettings.setArrivalReturnSpeed(
      _arrivalReturnSpeed.rawValue().toDouble(&value));
  Q_ASSERT(value);
  (void)value;

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

  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_updateAltitude() {
  bool value;
  _WMSettings.setAltitude(_altitude.rawValue().toDouble(&value));
  Q_ASSERT(value);
  (void)value;

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

  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
}

void WimaController::_checkBatteryLevel() {
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
  if (_missionController && _masterController &&
      _masterController->managerVehicle()) {
    Vehicle *managerVehicle = masterController()->managerVehicle();
    WimaSettings *wimaSettings =
        qgcApp()->toolbox()->settingsManager()->wimaSettings();
    int threshold = wimaSettings->lowBatteryThreshold()->rawValue().toInt();
    bool enabled = _enableWimaController.rawValue().toBool();
    unsigned int minTime =
        wimaSettings->minimalRemainingMissionTime()->rawValue().toUInt();

    if (enabled) {
      Fact *battery1percentRemaining =
          managerVehicle->battery1FactGroup()->getFact(
              VehicleBatteryFactGroup::_percentRemainingFactName);
      Fact *battery2percentRemaining =
          managerVehicle->battery2FactGroup()->getFact(
              VehicleBatteryFactGroup::_percentRemainingFactName);

      if (battery1percentRemaining->rawValue().toDouble() < threshold &&
          battery2percentRemaining->rawValue().toDouble() < threshold) {
        if (!_lowBatteryHandlingTriggered) {
          _lowBatteryHandlingTriggered = true;
          if (!(_missionController->remainingTime() <= minTime)) {
            requestSmartRTL();
          }
643
        }
644 645
      } else {
        _lowBatteryHandlingTriggered = false;
646
      }
647
    }
648
  }
649 650
}

651 652 653 654 655 656 657 658 659 660
void WimaController::_eventTimerHandler() {
  // Battery level check necessary?
  Fact *enableLowBatteryHandling = qgcApp()
                                       ->toolbox()
                                       ->settingsManager()
                                       ->wimaSettings()
                                       ->enableLowBatteryHandling();
  if (enableLowBatteryHandling->rawValue().toBool() &&
      _batteryLevelTicker.ready())
    _checkBatteryLevel();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
661 662
}

663
void WimaController::_smartRTLCleanUp(bool flying) {
664
  if (!flying && _missionController) { // vehicle has landed
665 666 667 668 669
    _switchWaypointManager(_defaultWM);
    _missionController->removeAll();
    disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged,
               this, &WimaController::_smartRTLCleanUp);
  }
670 671
}

672 673 674 675
void WimaController::_setPhaseDistance(double distance) {
  (void)distance;
  //    if (!qFuzzyCompare(distance, _phaseDistance)) {
  //        _phaseDistance = distance;
676

677 678
  //        emit phaseDistanceChanged();
  //    }
679 680
}

681 682 683 684
void WimaController::_setPhaseDuration(double duration) {
  (void)duration;
  //    if (!qFuzzyCompare(duration, _phaseDuration)) {
  //        _phaseDuration = duration;
685

686 687
  //        emit phaseDurationChanged();
  //    }
688 689
}

690
bool WimaController::_SRTLPrecondition(QString &errorString) {
691 692 693 694 695
  if (!_localPlanDataValid) {
    errorString.append(tr("No WiMA data available. Please define at least a "
                          "measurement and a service area."));
    return false;
  }
696

697
  return _rtlWM.checkPrecondition(errorString);
698 699
}

700 701 702 703
void WimaController::_switchWaypointManager(
    WaypointManager::ManagerBase &manager) {
  if (_currentWM != &manager) {
    _currentWM = &manager;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
704

705 706 707 708 709 710
    disconnect(&_overlapWaypoints, &Fact::rawValueChanged, this,
               &WimaController::_updateOverlap);
    disconnect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
               &WimaController::_updateMaxWaypoints);
    disconnect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
               &WimaController::_setStartIndex);
711

712 713 714
    _maxWaypointsPerPhase.setRawValue(_currentWM->N());
    _overlapWaypoints.setRawValue(_currentWM->overlap());
    _nextPhaseStartWaypointIndex.setRawValue(_currentWM->startIndex());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
715

716 717 718 719 720 721
    connect(&_overlapWaypoints, &Fact::rawValueChanged, this,
            &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
            &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
            &WimaController::_setStartIndex);
722

723
    emit missionItemsChanged();
724 725
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
726
    emit currentWaypointPathChanged();
727

728 729
    qWarning() << "WimaController::_switchWaypointManager: statistics update "
                  "missing.";
730 731 732 733 734 735
  }
}

void WimaController::_initSmartRTL() {
  static int attemptCounter = 0;
  attemptCounter++;
736
  QString errorString;
737

738 739 740 741 742 743 744 745 746 747 748 749
  if (_SRTLPrecondition(errorString)) {
    if (_missionController && _masterController &&
        _masterController->managerVehicle()) {
      _masterController->managerVehicle()->pauseVehicle();
      connect(_masterController->managerVehicle(), &Vehicle::flyingChanged,
              this, &WimaController::_smartRTLCleanUp);
      if (_rtlWM.update()) { // Calculate return path.
        _switchWaypointManager(_rtlWM);
        removeFromVehicle();
        attemptCounter = 0;
        emit smartRTLPathConfirm();
      }
750 751 752 753 754 755 756 757 758 759 760 761
    }
  } 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
762
void WimaController::_threadFinishedHandler() {
763 764 765
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
766 767
  if (!_snakeThread.errorMessage().isEmpty()) {
    qDebug() << _snakeThread.errorMessage();
768 769
  }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
770 771 772 773 774 775 776 777 778 779 780
  if (_snakeThread.tilesUpdated()) {
    this->tiles.clearAndDeleteContents();
    auto tls = _snakeThread.tiles().polygons();
    for (const auto &t : tls) {
      this->tiles.append(new SnakeTile(t, &tiles));
    }
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();

    _nemoInterface.setTilesENU(_snakeThread.tilesENU());
    _nemoInterface.setENUOrigin(_snakeThread.ENUOrigin());
781
  }
782 783 784 785 786 787 788 789 790
#ifdef SNAKE_SHOW_TIME
  auto end = std::chrono::high_resolution_clock::now();
  std::cout << "WimaController::_threadFinishedHandler(): tiles: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end -
                                                                     start)
                   .count()
            << " ms" << std::endl;
  start = std::chrono::high_resolution_clock::now();
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
791 792
  if (_snakeThread.progressUpdated()) {
    emit nemoProgressChanged();
793
  }
794 795 796 797 798 799 800 801 802
#ifdef SNAKE_SHOW_TIME
  end = std::chrono::high_resolution_clock::now();
  std::cout << "WimaController::_threadFinishedHandler(): progress: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end -
                                                                     start)
                   .count()
            << " ms" << std::endl;
  start = std::chrono::high_resolution_clock::now();
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
803 804 805 806 807 808 809 810 811 812 813 814 815
  if (_snakeThread.waypointsUpdated()) {
    // Copy waypoints to waypoint manager.
    _snakeWM.clear();
    auto waypoints = _snakeThread.waypoints();
    if (waypoints.size() < 1) {
      return;
    }
    for (auto &vertex : waypoints) {
      _snakeWM.push_back(vertex);
    }

    // Do update.
    this->_snakeWM.update(); // this can take a while (ca. 200ms)
816

Valentin Platzgummer's avatar
Valentin Platzgummer committed
817 818 819 820 821
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
  }
822 823 824 825 826 827 828 829
#ifdef SNAKE_SHOW_TIME
  end = std::chrono::high_resolution_clock::now();
  std::cout << "WimaController::_threadFinishedHandler(): waypoints: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end -
                                                                     start)
                   .count()
            << " ms" << std::endl;
#endif
830 831 832 833 834 835 836 837 838 839
}

void WimaController::_switchToSnakeWaypointManager(QVariant variant) {
  if (variant.value<bool>()) {
    _switchWaypointManager(_snakeWM);
  } else {
    _switchWaypointManager(_defaultWM);
  }
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
840 841 842 843 844 845 846 847 848
void WimaController::_switchThreadObject(SnakeThread &thread) {
  if (_currentThread != &thread) {
    // SnakeThread.
    disconnect(_currentThread, &SnakeThread::finished, this,
               &WimaController::_threadFinishedHandler);
    disconnect(_currentThread, &SnakeThread::calcInProgressChanged, this,
               &WimaController::snakeCalcInProgressChanged);
    // NemoInterface.
    disconnect(&_nemoInterface, &NemoInterface::progressChanged, this,
849
               &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
850
    disconnect(&_nemoInterface, &NemoInterface::statusChanged, this,
851
               &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
852
    disconnect(&_nemoInterface, &NemoInterface::statusChanged, this,
853 854
               &WimaController::nemoStatusStringChanged);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
855
    _currentThread = &thread;
856

Valentin Platzgummer's avatar
Valentin Platzgummer committed
857 858 859 860 861 862 863
    // SnakeThread.
    connect(_currentThread, &SnakeThread::finished, this,
            &WimaController::_threadFinishedHandler);
    connect(_currentThread, &SnakeThread::calcInProgressChanged, this,
            &WimaController::snakeCalcInProgressChanged);
    // NemoInterface.
    connect(&_nemoInterface, &NemoInterface::progressChanged, this,
864
            &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
865
    connect(&_nemoInterface, &NemoInterface::statusChanged, this,
866
            &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
867
    connect(&_nemoInterface, &NemoInterface::statusChanged, this,
868 869 870 871 872 873 874 875 876
            &WimaController::nemoStatusStringChanged);

    emit snakeCalcInProgressChanged();
    emit snakeTilesChanged();
    emit snakeTileCenterPointsChanged();
    emit nemoProgressChanged();
    emit nemoStatusChanged();
    emit nemoStatusStringChanged();
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
877 878
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
879 880 881 882
void WimaController::_progressChangedHandler() {
  this->_snakeThread.setProgress(this->_nemoInterface.progress());
  this->_snakeThread.start();
}
883

884 885
void WimaController::_enableSnakeChangedHandler() {
  if (this->_enableSnake.rawValue().toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
886
    qDebug() << "WimaController: enabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
887 888 889
    _switchThreadObject(this->_snakeThread);
    this->_nemoInterface.start();
    this->_snakeThread.start();
890
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
891
    qDebug() << "WimaController: disabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
892 893 894
    this->_nemoInterface.stop();
    this->_snakeThread.quit();
    _switchThreadObject(_emptyThread);
895 896
  }
}