WimaController.cc 26.9 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 6 7 8 9 10
#include "MissionController.h"
#include "MissionSettingsItem.h"
#include "PlanMasterController.h"
#include "QGCApplication.h"
#include "SimpleMissionItem.h"

11
#include "Snake/QNemoHeartbeat.h"
12
#include "Snake/QNemoProgress.h"
13

14
#include "QVector3D"
15
#include <QScopedPointer>
16
#include <QtConcurrentRun>
17

18 19
#include <memory>

Valentin Platzgummer's avatar
Valentin Platzgummer committed
20 21 22 23 24
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
  return static_cast<typename std::underlying_type<T>::type>(value);
}

25
#define SMART_RTL_MAX_ATTEMPTS 3       // times
26
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
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";
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
    : 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]),
      _lowBatteryHandlingTriggered(false), _measurementPathLength(-1),
Valentin Platzgummer's avatar
Valentin Platzgummer committed
75
      _nemoInterface(this),
76 77
      _batteryLevelTicker(EVENT_TIMER_INTERVAL, 1000 /*ms*/) {

Valentin Platzgummer's avatar
Valentin Platzgummer committed
78
  // Set up facts for waypoint manager.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  _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);

94 95 96 97 98 99 100 101
  // Nemo stuff.
  connect(&_nemoInterface, &NemoInterface::progressChanged, this,
          &WimaController::_progressChangedHandler);
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
          &WimaController::nemoStatusChanged);
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
          &WimaController::nemoStatusStringChanged);

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
  // 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
122 123
  // NemoInterface.
  connect(&_nemoInterface, &NemoInterface::progressChanged, this,
124
          &WimaController::_progressChangedHandler);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
125
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
126
          &WimaController::nemoStatusChanged);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
127
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
128 129
          &WimaController::nemoStatusStringChanged);

Valentin Platzgummer's avatar
Valentin Platzgummer committed
130
  // Enable/disable snake.
131 132 133
  connect(&_enableSnake, &Fact::rawValueChanged, this,
          &WimaController::_enableSnakeChangedHandler);
  _enableSnakeChangedHandler();
134 135 136 137 138
  connect(&_enableWimaController, &Fact::rawValueChanged, [this] {
    if (!this->_enableWimaController.rawValue().toBool()) {
      this->_enableSnake.setCookedValue(QVariant(false));
    }
  });
139 140 141 142 143

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

146
PlanMasterController *WimaController::masterController() {
147
  return _masterController;
148 149 150
}

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

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

QmlObjectListModel *WimaController::missionItems() {
157
  return const_cast<QmlObjectListModel *>(&_currentWM->missionItems());
158 159 160
}

QmlObjectListModel *WimaController::currentMissionItems() {
161
  return const_cast<QmlObjectListModel *>(&_currentWM->currentMissionItems());
162 163
}

164 165
QVariantList WimaController::waypointPath() {
  return const_cast<QVariantList &>(_currentWM->waypointsVariant());
166 167
}

168 169
QVariantList WimaController::currentWaypointPath() {
  return const_cast<QVariantList &>(_currentWM->currentWaypointsVariant());
170 171
}

172
Fact *WimaController::enableWimaController() { return &_enableWimaController; }
173

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

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

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

182
Fact *WimaController::showAllMissionItems() { return &_showAllMissionItems; }
183 184

Fact *WimaController::showCurrentMissionItems() {
185
  return &_showCurrentMissionItems;
186 187
}

188
Fact *WimaController::flightSpeed() { return &_flightSpeed; }
189

190
Fact *WimaController::arrivalReturnSpeed() { return &_arrivalReturnSpeed; }
191

192
Fact *WimaController::altitude() { return &_altitude; }
193

194
QmlObjectListModel *WimaController::snakeTiles() {
195
  return &this->_measurementArea.tiles();
196
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
197

198
QVariantList WimaController::snakeTileCenterPoints() {
199
  return this->_measurementArea.tileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
200 201
}

202
QVector<int> WimaController::nemoProgress() {
203
  return this->_measurementArea.progress();
204 205
}

206 207 208
double WimaController::phaseDistance() const {
  qWarning() << "using phaseDistance dummy";
  return 0.0;
209 210
}

211 212 213
double WimaController::phaseDuration() const {
  qWarning() << "using phaseDuration dummy";
  return 0.0;
214 215
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
216 217 218
int WimaController::nemoStatus() const {
  return integral(_nemoInterface.status());
}
219

220 221
QString WimaController::nemoStatusString() const {
  return _nemoStatusMap.at(nemoStatus());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
222 223
}

224
bool WimaController::snakeCalcInProgress() const {
225 226
  qWarning() << "using snakeCalcInProgress dummy";
  return true;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
227 228
}

229 230 231 232
void WimaController::setMasterController(PlanMasterController *masterC) {
  _masterController = masterC;
  _WMSettings.setMasterController(masterC);
  emit masterControllerChanged();
233 234
}

235 236 237 238
void WimaController::setMissionController(MissionController *missionC) {
  _missionController = missionC;
  _WMSettings.setMissionController(missionC);
  emit missionControllerChanged();
239 240
}

241
void WimaController::nextPhase() { _calcNextPhase(); }
242

243 244 245 246
void WimaController::previousPhase() {
  if (!_currentWM->previous()) {
    Q_ASSERT(false);
  }
247

248 249 250 251
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
252 253
}

254 255 256 257
void WimaController::resetPhase() {
  if (!_currentWM->reset()) {
    Q_ASSERT(false);
  }
258

259 260 261 262
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
263 264
}

265
void WimaController::requestSmartRTL() {
266 267 268 269 270
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::requestSmartRTL() called";
#endif
  QString errorString("Smart RTL requested.");
  if (!_SRTLPrecondition(errorString)) {
271 272 273 274
    qgcApp()->showMessage(errorString);
    return;
  }
  emit smartRTLRequestConfirm();
275 276
}

277
bool WimaController::upload() {
278 279 280 281 282 283 284 285 286 287 288
  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 {
289 290
    return false;
  }
291 292
}

293
bool WimaController::forceUpload() {
294 295 296 297 298 299 300 301
  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;
302
    return false;
303 304 305 306 307 308 309 310 311 312 313 314
  } 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());
315

316 317 318 319 320 321 322 323
      // 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;
    }
324
  }
325
}
326

327
void WimaController::removeFromVehicle() {
328 329 330 331
  if (_masterController && _missionController) {
    _masterController->removeAllFromVehicle();
    _missionController->removeAll();
  }
332 333
}

334
void WimaController::executeSmartRTL() {
335 336 337 338 339 340 341
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::executeSmartRTL() called";
#endif
  if (_masterController && _masterController->managerVehicle()) {
    forceUpload();
    _masterController->managerVehicle()->startMission();
  }
342 343
}

344 345 346 347 348 349
void WimaController::initSmartRTL() {
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::initSmartRTL() called";
#endif
  _initSmartRTL();
}
350

351
void WimaController::removeVehicleTrajectoryHistory() {
352 353 354 355 356
  if (_masterController && _masterController->managerVehicle()) {
    _masterController->managerVehicle()
        ->trajectoryPoints()
        ->clearAndDeleteContents();
  }
357 358
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
359 360
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
361 362 363 364 365 366 367 368 369
                                       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;
370

371 372 373
  bool retVal =
      PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
  toGeoList(path2D, /*origin*/ start, path);
374

375 376
  return retVal;
}
377

378
bool WimaController::setWimaPlanData(QSharedPointer<WimaPlanData> planData) {
379 380 381 382
  // reset visual items
  _areas.clear();
  _defaultWM.clear();
  _snakeWM.clear();
383 384 385 386
  _measurementArea = WimaMeasurementAreaData();
  _serviceArea = WimaServiceAreaData();
  _corridor = WimaCorridorData();
  _joinedArea = WimaJoinedAreaData();
387

388 389 390 391 392
  emit visualItemsChanged();
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
393 394
  emit snakeTilesChanged();
  emit nemoProgressChanged();
395

396
  _localPlanDataValid = false;
397

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

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

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

413
      continue;
414
    }
415

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

423
      continue;
424
    }
425

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

431
      continue;
432
    }
433

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

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

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

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

452
  emit visualItemsChanged();
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  emit snakeTilesChanged();

  // Publish tiles if necessary.
  if (!this->_nemoInterface.hasTileData(this->_measurementArea.tileData())) {
    this->_nemoInterface.publishTileData(this->_measurementArea.tileData());
    this->_measurementArea.progress() =
        QVector<int>(this->_measurementArea.tiles()->count, 0);
    emit nemoProgressChanged();
  } else if (this->_enableSnake.rawValue().toBool()) {
    const auto progess = this->_nemoInterface.progress();
    if (progess.size() == this->_measurementArea.tiles()->count()) {
      this->_measurementArea.progress() = std::move(progress);
      emit nemoProgressChanged();
    }
  }

  // Copy raw transects.
  this->_rawTransects = planData->transects();
471

472
  // Copy missionItems.
473 474 475 476 477
  auto tempMissionItems = planData->missionItems();
  if (tempMissionItems.size() < 1) {
    qWarning("WimaController: Mission items from WimaPlaner empty!");
    return false;
  }
478

479 480 481
  for (auto *item : tempMissionItems) {
    _defaultWM.push_back(item->coordinate());
  }
482
  //  // extract mission items
483 484
  _WMSettings.setHomePosition(QGeoCoordinate(
      _serviceArea.depot().latitude(), _serviceArea.depot().longitude(), 0));
485
  //  auto tempMissionItems = planData->missionItems();
486 487 488 489
  if (!_defaultWM.reset()) {
    Q_ASSERT(false);
    return false;
  }
490
  //  if (tempMissionItems.size() < 1) {
491 492 493 494
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
495 496

  qWarning("WimaController: add snake update here lkjdfl!");
497

498 499
  _localPlanDataValid = true;
  return true;
500
}
501

502
WimaController *WimaController::thisPointer() { return this; }
503

504 505 506 507 508
bool WimaController::_calcNextPhase() {
  if (!_currentWM->next()) {
    Q_ASSERT(false);
    return false;
  }
509

510 511 512 513
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
514

515
  return true;
516 517
}

518 519 520 521 522 523
bool WimaController::_setStartIndex() {
  bool value;
  _currentWM->setStartIndex(
      _nextPhaseStartWaypointIndex.rawValue().toUInt(&value) - 1);
  Q_ASSERT(value);
  (void)value;
524

525 526 527 528
  if (!_currentWM->update()) {
    Q_ASSERT(false);
    return false;
  }
529

530 531 532 533
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
534

535
  return true;
536 537
}

538 539 540 541
void WimaController::_recalcCurrentPhase() {
  if (!_currentWM->update()) {
    Q_ASSERT(false);
  }
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 618 619 620 621 622 623 624 625 626 627 628 629 630
  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() {
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
  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();
          }
656
        }
657 658
      } else {
        _lowBatteryHandlingTriggered = false;
659
      }
660
    }
661
  }
662 663
}

664 665 666 667 668 669 670 671 672 673
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
674 675
}

676
void WimaController::_smartRTLCleanUp(bool flying) {
677
  if (!flying && _missionController) { // vehicle has landed
678 679 680 681 682
    _switchWaypointManager(_defaultWM);
    _missionController->removeAll();
    disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged,
               this, &WimaController::_smartRTLCleanUp);
  }
683 684
}

685 686 687 688
void WimaController::_setPhaseDistance(double distance) {
  (void)distance;
  //    if (!qFuzzyCompare(distance, _phaseDistance)) {
  //        _phaseDistance = distance;
689

690 691
  //        emit phaseDistanceChanged();
  //    }
692 693
}

694 695 696 697
void WimaController::_setPhaseDuration(double duration) {
  (void)duration;
  //    if (!qFuzzyCompare(duration, _phaseDuration)) {
  //        _phaseDuration = duration;
698

699 700
  //        emit phaseDurationChanged();
  //    }
701 702
}

703
bool WimaController::_SRTLPrecondition(QString &errorString) {
704 705 706 707 708
  if (!_localPlanDataValid) {
    errorString.append(tr("No WiMA data available. Please define at least a "
                          "measurement and a service area."));
    return false;
  }
709

710
  return _rtlWM.checkPrecondition(errorString);
711 712
}

713 714 715 716
void WimaController::_switchWaypointManager(
    WaypointManager::ManagerBase &manager) {
  if (_currentWM != &manager) {
    _currentWM = &manager;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
717

718 719 720 721 722 723
    disconnect(&_overlapWaypoints, &Fact::rawValueChanged, this,
               &WimaController::_updateOverlap);
    disconnect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
               &WimaController::_updateMaxWaypoints);
    disconnect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
               &WimaController::_setStartIndex);
724

725 726 727
    _maxWaypointsPerPhase.setRawValue(_currentWM->N());
    _overlapWaypoints.setRawValue(_currentWM->overlap());
    _nextPhaseStartWaypointIndex.setRawValue(_currentWM->startIndex());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
728

729 730 731 732 733 734
    connect(&_overlapWaypoints, &Fact::rawValueChanged, this,
            &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
            &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
            &WimaController::_setStartIndex);
735

736
    emit missionItemsChanged();
737 738
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
739
    emit currentWaypointPathChanged();
740

741 742
    qWarning() << "WimaController::_switchWaypointManager: statistics update "
                  "missing.";
743 744 745 746 747 748
  }
}

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

751 752 753 754 755 756 757 758 759 760 761 762
  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();
      }
763 764 765 766 767 768 769 770 771 772 773 774
    }
  } 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
775
void WimaController::_threadFinishedHandler() {
776
  qWarning() << "update here as well .jisddf";
777 778
  auto start = std::chrono::high_resolution_clock::now();
#endif
779 780 781 782 783
  // Copy waypoints to waypoint manager.
  _snakeWM.clear();
  auto waypoints = 1;
  if (waypoints.size() < 1) {
    return;
784
  }
785 786
  for (auto &vertex : waypoints) {
    _snakeWM.push_back(vertex);
787
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
788

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

792 793 794 795
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
796
#ifdef SNAKE_SHOW_TIME
797
  auto end = std::chrono::high_resolution_clock::now();
798 799 800 801 802 803
  std::cout << "WimaController::_threadFinishedHandler(): waypoints: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end -
                                                                     start)
                   .count()
            << " ms" << std::endl;
#endif
804 805 806 807 808 809 810 811 812 813
}

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

814 815 816 817
void WimaController::_progressChangedHandler() {
  const auto progress = this->_nemoInterface.progress();
  if (this->_measurementArea.tiles().count() == progress.size()) {
    this->_measurementArea.progress() = std::move(progress);
818
    emit nemoProgressChanged();
819 820
  } else if (_localPlanDataValid) {
    this->_nemoInterface.publishTileData(this->_measurementArea.tileData());
821
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
822 823
}

824 825
void WimaController::_enableSnakeChangedHandler() {
  if (this->_enableSnake.rawValue().toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
826
    qDebug() << "WimaController: enabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
827
    this->_nemoInterface.start();
828
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
829
    qDebug() << "WimaController: disabling snake.";
Valentin Platzgummer's avatar
Valentin Platzgummer committed
830
    this->_nemoInterface.stop();
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
  }
}

void WimaController::_updateRoute() {
  if (_localPlanDataValid && this->_joinedArea.coordinateList().size() > 0) {
    auto safeArea = this->_joinedArea.coordinateList();
    for (auto &v : safeArea) {
      v.setAltitude(0);
    }
    const auto &origin = safeArea.front();
    snake::BoostPolygon safeAreaENU;
    snake::areaToEnu(origin, safeArea, safeAreaENU);
    const auto &depot = this->_serviceArea.depot();
    const auto &geoTransects = this->_rawTransects;
    const auto progess = this->_nemoInterface.progress();
    auto pTiles = std::make_shared<QList<QList<QGeoCoordinate>>>();

    auto generator = [origin, depot,
                      geoTransects](snake::Transects &transects) -> bool {
      snake::BoostPolygon depotENU;
      snake::toENU(origin, depot, depotENU);
      transects.push_back(snake::BoostLineString{depotENU});
      for (auto &geoTransect : geoTransects) {
        snake::BoostLineString t;
        for (auto &geoVertex : geoTransect) {
          snake::BoostPoint v;
          snake::toENU(origin, geoVertex, v);
          t.push_back(v);
        }
        transects.push_back(t);
      }
      return true;
    };
    this->_routingThread.route(safeAreaENU, generator);
865 866
  }
}