WimaController.cc 27.4 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 357 358 359 360
bool WimaController::setWimaPlanData(const WimaPlanData &planData) {
  // 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 371
  // extract list with WimaAreas
  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 427 428 429 430 431
  // extract mission items
  QList<MissionItem> tempMissionItems = planData.missionItems();
  if (tempMissionItems.size() < 1) {
    qWarning("WimaController: Mission items from WimaPlaner empty!");
    return false;
  }
432

433 434 435
  for (auto item : tempMissionItems) {
    _defaultWM.push_back(item.coordinate());
  }
436

437 438
  _WMSettings.setHomePosition(QGeoCoordinate(
      _serviceArea.center().latitude(), _serviceArea.center().longitude(), 0));
439

440 441 442 443
  if (!_defaultWM.reset()) {
    Q_ASSERT(false);
    return false;
  }
444

445 446 447 448
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
449

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

456 457
  _localPlanDataValid = true;
  return true;
458
}
459

460
WimaController *WimaController::thisPointer() { return this; }
461

462 463 464 465 466
bool WimaController::_calcNextPhase() {
  if (!_currentWM->next()) {
    Q_ASSERT(false);
    return false;
  }
467

468 469 470 471
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
472

473
  return true;
474 475
}

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

483 484 485 486
  if (!_currentWM->update()) {
    Q_ASSERT(false);
    return false;
  }
487

488 489 490 491
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
492

493
  return true;
494 495
}

496 497 498 499
void WimaController::_recalcCurrentPhase() {
  if (!_currentWM->update()) {
    Q_ASSERT(false);
  }
500

501 502 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
  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();
612
        }
613 614 615
      }
    } else {
      _lowBatteryHandlingTriggered = false;
616
    }
617
  }
618 619
}

620 621 622 623 624 625 626 627 628 629
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
630 631
}

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

642 643 644 645
void WimaController::_setPhaseDistance(double distance) {
  (void)distance;
  //    if (!qFuzzyCompare(distance, _phaseDistance)) {
  //        _phaseDistance = distance;
646

647 648
  //        emit phaseDistanceChanged();
  //    }
649 650
}

651 652 653 654
void WimaController::_setPhaseDuration(double duration) {
  (void)duration;
  //    if (!qFuzzyCompare(duration, _phaseDuration)) {
  //        _phaseDuration = duration;
655

656 657
  //        emit phaseDurationChanged();
  //    }
658 659
}

660 661 662 663 664 665
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;
  }
666

667
  return _rtlWM.checkPrecondition(errorString);
668 669
}

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

675 676 677 678 679 680
    disconnect(&_overlapWaypoints, &Fact::rawValueChanged, this,
               &WimaController::_updateOverlap);
    disconnect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
               &WimaController::_updateMaxWaypoints);
    disconnect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
               &WimaController::_setStartIndex);
681

682 683 684
    _maxWaypointsPerPhase.setRawValue(_currentWM->N());
    _overlapWaypoints.setRawValue(_currentWM->overlap());
    _nextPhaseStartWaypointIndex.setRawValue(_currentWM->startIndex());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
685

686 687 688 689 690 691
    connect(&_overlapWaypoints, &Fact::rawValueChanged, this,
            &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
            &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
            &WimaController::_setStartIndex);
692

693
    emit missionItemsChanged();
694 695
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
696
    emit currentWaypointPathChanged();
697

698 699 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
    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
729
void WimaController::_threadFinishedHandler() {
730 731 732
#ifdef SNAKE_SHOW_TIME
  auto start = std::chrono::high_resolution_clock::now();
#endif
Valentin Platzgummer's avatar
Valentin Platzgummer committed
733 734
  if (!_snakeThread.errorMessage().isEmpty()) {
    qDebug() << _snakeThread.errorMessage();
735 736
  }

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

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

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

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

Valentin Platzgummer's avatar
Valentin Platzgummer committed
822
    _currentThread = &thread;
823

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

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

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

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