WimaController.cc 27.6 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 273 274 275 276 277 278
void WimaController::requestSmartRTL() {
  QString errorString("Smart RTL requested. ");
  if (!_checkSmartRTLPreCondition(errorString)) {
    qgcApp()->showMessage(errorString);
    return;
  }
  emit smartRTLRequestConfirm();
279 280
}

281 282 283 284 285 286 287 288
bool WimaController::upload() {
  auto &currentMissionItems = _defaultWM.currentMissionItems();
  if (!_serviceArea.containsCoordinate(
          _masterController->managerVehicle()->coordinate()) &&
      currentMissionItems.count() > 0) {
    emit forceUploadConfirm();
    return false;
  }
289

290
  return forceUpload();
291 292
}

293 294 295 296
bool WimaController::forceUpload() {
  auto &currentMissionItems = _defaultWM.currentMissionItems();
  if (currentMissionItems.count() < 1)
    return false;
297

298 299 300 301 302 303 304 305 306 307
  _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;
  }
  settingsItem->setCoordinate(_WMSettings.homePosition());
308

309 310 311 312 313
  // 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());
  }
314

315
  _masterController->sendToVehicle();
316

317
  return true;
318
}
319

320 321 322
void WimaController::removeFromVehicle() {
  _masterController->removeAllFromVehicle();
  _missionController->removeAll();
323 324
}

325 326 327
void WimaController::executeSmartRTL() {
  forceUpload();
  masterController()->managerVehicle()->startMission();
328 329
}

330
void WimaController::initSmartRTL() { _initSmartRTL(); }
331

332 333 334
void WimaController::removeVehicleTrajectoryHistory() {
  Vehicle *managerVehicle = masterController()->managerVehicle();
  managerVehicle->trajectoryPoints()->clear();
335 336
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
337 338
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
339 340 341 342 343 344 345 346 347
                                       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;
348

349 350 351
  bool retVal =
      PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
  toGeoList(path2D, /*origin*/ start, path);
352

353 354
  return retVal;
}
355

356
bool WimaController::setWimaPlanData(QSharedPointer<WimaPlanData> planData) {
357 358 359 360
  // reset visual items
  _areas.clear();
  _defaultWM.clear();
  _snakeWM.clear();
361

362 363 364 365 366
  emit visualItemsChanged();
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
367

368
  _localPlanDataValid = false;
369

370
  // extract list with WimaAreas
371
  QList<const WimaAreaData *> areaList = planData->areaList();
372

373 374 375 376 377
  int areaCounter = 0;
  const 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];
378

379 380 381 382 383
    if (areaData->type() ==
        WimaServiceAreaData::typeString) { // is it a service area?
      _serviceArea = *qobject_cast<const WimaServiceAreaData *>(areaData);
      areaCounter++;
      _areas.append(&_serviceArea);
384

385
      continue;
386
    }
387

388 389 390 391 392 393
    if (areaData->type() ==
        WimaMeasurementAreaData::typeString) { // is it a measurement area?
      _measurementArea =
          *qobject_cast<const WimaMeasurementAreaData *>(areaData);
      areaCounter++;
      _areas.append(&_measurementArea);
394

395
      continue;
396
    }
397

398 399 400 401
    if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
      _corridor = *qobject_cast<const WimaCorridorData *>(areaData);
      areaCounter++;
      //_visualItems.append(&_corridor); // not needed
402

403
      continue;
404
    }
405

406 407 408 409 410
    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
411

412
      continue;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
413 414
    }

415 416 417
    if (areaCounter >= numAreas)
      break;
  }
418

419 420 421 422
  if (areaCounter != numAreas) {
    Q_ASSERT(false);
    return false;
  }
423

424
  emit visualItemsChanged();
425

426
  // extract mission items
427
  auto tempMissionItems = planData->missionItems();
428 429 430 431
  if (tempMissionItems.size() < 1) {
    qWarning("WimaController: Mission items from WimaPlaner empty!");
    return false;
  }
432

433 434 435 436
  qWarning() << "WimaController:";
  for (auto *item : tempMissionItems) {
    qWarning() << item->coordinate();
    _defaultWM.push_back(item->coordinate());
437
  }
438

439
  _WMSettings.setHomePosition(QGeoCoordinate(
440
      _serviceArea.depot().latitude(), _serviceArea.depot().longitude(), 0));
441
  qWarning() << "service area depot: " << _serviceArea.depot();
442

443
  if (!_defaultWM.reset()) {
444
    qWarning() << "_defaultWM.reset() failed";
445 446
    return false;
  }
447

448 449 450 451
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
452

453
  // Update Snake Data Manager
Valentin Platzgummer's avatar
Valentin Platzgummer committed
454 455 456 457
  _snakeThread.setMeasurementArea(_measurementArea.coordinateList());
  _snakeThread.setServiceArea(_serviceArea.coordinateList());
  _snakeThread.setCorridor(_corridor.coordinateList());
  _currentThread->start();
458

459 460
  _localPlanDataValid = true;
  return true;
461
}
462

463
WimaController *WimaController::thisPointer() { return this; }
464

465 466 467 468 469
bool WimaController::_calcNextPhase() {
  if (!_currentWM->next()) {
    Q_ASSERT(false);
    return false;
  }
470

471 472 473 474
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
475

476
  return true;
477 478
}

479 480 481 482 483 484
bool WimaController::_setStartIndex() {
  bool value;
  _currentWM->setStartIndex(
      _nextPhaseStartWaypointIndex.rawValue().toUInt(&value) - 1);
  Q_ASSERT(value);
  (void)value;
485

486 487 488 489
  if (!_currentWM->update()) {
    Q_ASSERT(false);
    return false;
  }
490

491 492 493 494
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
495

496
  return true;
497 498
}

499 500 501 502
void WimaController::_recalcCurrentPhase() {
  if (!_currentWM->update()) {
    Q_ASSERT(false);
  }
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 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
  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() {
  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();

  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)) {
          requestSmartRTL();
615
        }
616 617 618
      }
    } else {
      _lowBatteryHandlingTriggered = false;
619
    }
620
  }
621 622
}

623 624 625 626 627 628 629 630 631 632
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
633 634
}

635 636 637 638 639 640 641 642
void WimaController::_smartRTLCleanUp(bool flying) {
  if (!flying) { // vehicle has landed
    _switchWaypointManager(_defaultWM);
    _missionController->removeAllFromVehicle();
    _missionController->removeAll();
    disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged,
               this, &WimaController::_smartRTLCleanUp);
  }
643 644
}

645 646 647 648
void WimaController::_setPhaseDistance(double distance) {
  (void)distance;
  //    if (!qFuzzyCompare(distance, _phaseDistance)) {
  //        _phaseDistance = distance;
649

650 651
  //        emit phaseDistanceChanged();
  //    }
652 653
}

654 655 656 657
void WimaController::_setPhaseDuration(double duration) {
  (void)duration;
  //    if (!qFuzzyCompare(duration, _phaseDuration)) {
  //        _phaseDuration = duration;
658

659 660
  //        emit phaseDurationChanged();
  //    }
661 662
}

663 664 665 666 667 668
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;
  }
669

670
  return _rtlWM.checkPrecondition(errorString);
671 672
}

673 674 675 676
void WimaController::_switchWaypointManager(
    WaypointManager::ManagerBase &manager) {
  if (_currentWM != &manager) {
    _currentWM = &manager;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
677

678 679 680 681 682 683
    disconnect(&_overlapWaypoints, &Fact::rawValueChanged, this,
               &WimaController::_updateOverlap);
    disconnect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
               &WimaController::_updateMaxWaypoints);
    disconnect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
               &WimaController::_setStartIndex);
684

685 686 687
    _maxWaypointsPerPhase.setRawValue(_currentWM->N());
    _overlapWaypoints.setRawValue(_currentWM->overlap());
    _nextPhaseStartWaypointIndex.setRawValue(_currentWM->startIndex());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
688

689 690 691 692 693 694
    connect(&_overlapWaypoints, &Fact::rawValueChanged, this,
            &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
            &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
            &WimaController::_setStartIndex);
695

696
    emit missionItemsChanged();
697 698
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
699
    emit currentWaypointPathChanged();
700

701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
    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 (_rtlWM.update()) { // Calculate return path.
      _switchWaypointManager(_rtlWM);
      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
732
void WimaController::_threadFinishedHandler() {
733 734 735
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
736 737
  if (!_snakeThread.errorMessage().isEmpty()) {
    qDebug() << _snakeThread.errorMessage();
738 739
  }

Valentin Platzgummer's avatar
Valentin Platzgummer committed
740 741 742 743 744 745 746 747 748 749 750
  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());
751
  }
752 753 754 755 756 757 758 759 760
#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
761 762
  if (_snakeThread.progressUpdated()) {
    emit nemoProgressChanged();
763
  }
764 765 766 767 768 769 770 771 772
#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
773 774 775 776 777 778 779 780 781 782 783 784 785
  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)
786

Valentin Platzgummer's avatar
Valentin Platzgummer committed
787 788 789 790 791
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
  }
792 793 794 795 796 797 798 799
#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
800 801 802 803 804 805 806 807 808 809
}

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
810 811 812 813 814 815 816 817 818
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,
819
               &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
820
    disconnect(&_nemoInterface, &NemoInterface::statusChanged, this,
821
               &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
822
    disconnect(&_nemoInterface, &NemoInterface::statusChanged, this,
823 824
               &WimaController::nemoStatusStringChanged);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
825
    _currentThread = &thread;
826

Valentin Platzgummer's avatar
Valentin Platzgummer committed
827 828 829 830 831 832 833
    // SnakeThread.
    connect(_currentThread, &SnakeThread::finished, this,
            &WimaController::_threadFinishedHandler);
    connect(_currentThread, &SnakeThread::calcInProgressChanged, this,
            &WimaController::snakeCalcInProgressChanged);
    // NemoInterface.
    connect(&_nemoInterface, &NemoInterface::progressChanged, this,
834
            &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
835
    connect(&_nemoInterface, &NemoInterface::statusChanged, this,
836
            &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
837
    connect(&_nemoInterface, &NemoInterface::statusChanged, this,
838 839 840 841 842 843 844 845 846
            &WimaController::nemoStatusStringChanged);

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
849 850 851 852
void WimaController::_progressChangedHandler() {
  this->_snakeThread.setProgress(this->_nemoInterface.progress());
  this->_snakeThread.start();
}
853

854 855
void WimaController::_enableSnakeChangedHandler() {
  if (this->_enableSnake.rawValue().toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
856
    qDebug() << "WimaController: enabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
857 858 859
    _switchThreadObject(this->_snakeThread);
    this->_nemoInterface.start();
    this->_snakeThread.start();
860
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
861
    qDebug() << "WimaController: disabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
862 863 864
    this->_nemoInterface.stop();
    this->_snakeThread.quit();
    _switchThreadObject(_emptyThread);
865 866
  }
}