WimaController.cc 32.5 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
#include "MissionController.h"
#include "MissionSettingsItem.h"
#include "PlanMasterController.h"
#include "QGCApplication.h"
9
#include "SettingsManager.h"
10 11
#include "SimpleMissionItem.h"

12 13
#include "WimaSettings.h"

14
#include "Snake/QNemoHeartbeat.h"
15
#include "Snake/QNemoProgress.h"
16
#include "Snake/SnakeTile.h"
17

18
#include "QVector3D"
19
#include <QScopedPointer>
20 21 22

#define CLIPPER_SCALE 10000
#include "clipper/clipper.hpp"
23

24 25
#include <memory>

Valentin Platzgummer's avatar
Valentin Platzgummer committed
26 27 28 29 30
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value) {
  return static_cast<typename std::underlying_type<T>::type>(value);
}

31
#define SMART_RTL_MAX_ATTEMPTS 3       // times
32
#define SMART_RTL_ATTEMPT_INTERVAL 200 // ms
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#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
48

Valentin Platzgummer's avatar
Valentin Platzgummer committed
49 50 51
WimaController::StatusMap WimaController::_nemoStatusMap{
    std::make_pair<int, QString>(0, "No Heartbeat"),
    std::make_pair<int, QString>(1, "Connected"),
52 53
    std::make_pair<int, QString>(-1, "Timeout"),
    std::make_pair<int, QString>(-2, "Error")};
54

55
WimaController::WimaController(QObject *parent)
56
    : QObject(parent), _joinedArea(), _measurementArea(), _serviceArea(),
57
      _corridor(), _planDataValid(false),
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
      _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
81
      _nemoInterface(this),
82 83
      _batteryLevelTicker(EVENT_TIMER_INTERVAL, 1000 /*ms*/) {

Valentin Platzgummer's avatar
Valentin Platzgummer committed
84
  // Set up facts for waypoint manager.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
  _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);

100 101 102 103 104 105 106 107
  // Nemo stuff.
  connect(&_nemoInterface, &NemoInterface::progressChanged, this,
          &WimaController::_progressChangedHandler);
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
          &WimaController::nemoStatusChanged);
  connect(&_nemoInterface, &NemoInterface::statusChanged, this,
          &WimaController::nemoStatusStringChanged);

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

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

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

  // Routing
  connect(&_routingThread, &RoutingThread::result, this,
          &WimaController::_storeRoute);
  connect(&_routingThread, &RoutingThread::calculatingChanged, this,
          &WimaController::snakeCalcInProgressChanged);
156 157
}

158
PlanMasterController *WimaController::masterController() {
159
  return _masterController;
160 161 162
}

MissionController *WimaController::missionController() {
163
  return _missionController;
164 165
}

166
QmlObjectListModel *WimaController::visualItems() { return &_areas; }
167 168

QmlObjectListModel *WimaController::missionItems() {
169
  return const_cast<QmlObjectListModel *>(&_currentWM->missionItems());
170 171 172
}

QmlObjectListModel *WimaController::currentMissionItems() {
173
  return const_cast<QmlObjectListModel *>(&_currentWM->currentMissionItems());
174 175
}

176 177
QVariantList WimaController::waypointPath() {
  return const_cast<QVariantList &>(_currentWM->waypointsVariant());
178 179
}

180 181
QVariantList WimaController::currentWaypointPath() {
  return const_cast<QVariantList &>(_currentWM->currentWaypointsVariant());
182 183
}

184
Fact *WimaController::enableWimaController() { return &_enableWimaController; }
185

186
Fact *WimaController::overlapWaypoints() { return &_overlapWaypoints; }
187

188
Fact *WimaController::maxWaypointsPerPhase() { return &_maxWaypointsPerPhase; }
189 190

Fact *WimaController::startWaypointIndex() {
191
  return &_nextPhaseStartWaypointIndex;
192 193
}

194
Fact *WimaController::showAllMissionItems() { return &_showAllMissionItems; }
195 196

Fact *WimaController::showCurrentMissionItems() {
197
  return &_showCurrentMissionItems;
198 199
}

200
Fact *WimaController::flightSpeed() { return &_flightSpeed; }
201

202
Fact *WimaController::arrivalReturnSpeed() { return &_arrivalReturnSpeed; }
203

204
Fact *WimaController::altitude() { return &_altitude; }
205

206
QmlObjectListModel *WimaController::snakeTiles() {
207
  return this->_measurementArea.tiles();
208
}
Valentin Platzgummer's avatar
Valentin Platzgummer committed
209

210
QVariantList WimaController::snakeTileCenterPoints() {
211
  return this->_measurementArea.tileCenterPoints();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
212 213
}

214
QVector<int> WimaController::nemoProgress() {
215
  return this->_measurementArea.progress();
216 217
}

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

223 224 225
double WimaController::phaseDuration() const {
  qWarning() << "using phaseDuration dummy";
  return 0.0;
226 227
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
228 229 230
int WimaController::nemoStatus() const {
  return integral(_nemoInterface.status());
}
231

232 233
QString WimaController::nemoStatusString() const {
  return _nemoStatusMap.at(nemoStatus());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
234 235
}

236
bool WimaController::snakeCalcInProgress() const {
237
  return _routingThread.calculating();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
238 239
}

240 241 242 243
void WimaController::setMasterController(PlanMasterController *masterC) {
  _masterController = masterC;
  _WMSettings.setMasterController(masterC);
  emit masterControllerChanged();
244 245
}

246 247 248 249
void WimaController::setMissionController(MissionController *missionC) {
  _missionController = missionC;
  _WMSettings.setMissionController(missionC);
  emit missionControllerChanged();
250 251
}

252
void WimaController::nextPhase() { _calcNextPhase(); }
253

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

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

265 266 267 268
void WimaController::resetPhase() {
  if (!_currentWM->reset()) {
    Q_ASSERT(false);
  }
269

270 271 272 273
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
274 275
}

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

288
bool WimaController::upload() {
289 290 291 292 293 294 295 296 297 298 299
  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 {
300 301
    return false;
  }
302 303
}

304
bool WimaController::forceUpload() {
305 306 307 308 309 310 311 312
  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;
313
    return false;
314 315 316 317 318 319 320 321 322 323 324 325
  } 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());
326

327 328 329 330 331 332 333 334
      // 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;
    }
335
  }
336
}
337

338
void WimaController::removeFromVehicle() {
339 340 341 342
  if (_masterController && _missionController) {
    _masterController->removeAllFromVehicle();
    _missionController->removeAll();
  }
343 344
}

345
void WimaController::executeSmartRTL() {
346 347 348 349 350 351 352
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::executeSmartRTL() called";
#endif
  if (_masterController && _masterController->managerVehicle()) {
    forceUpload();
    _masterController->managerVehicle()->startMission();
  }
353 354
}

355 356 357 358 359 360
void WimaController::initSmartRTL() {
#ifdef DEBUG_SRTL
  qWarning() << "WimaController::initSmartRTL() called";
#endif
  _initSmartRTL();
}
361

362
void WimaController::removeVehicleTrajectoryHistory() {
363 364 365 366 367
  if (_masterController && _masterController->managerVehicle()) {
    _masterController->managerVehicle()
        ->trajectoryPoints()
        ->clearAndDeleteContents();
  }
368 369
}

Valentin Platzgummer's avatar
Valentin Platzgummer committed
370 371
bool WimaController::_calcShortestPath(const QGeoCoordinate &start,
                                       const QGeoCoordinate &destination,
372 373 374 375 376 377 378 379 380
                                       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;
381

382 383 384
  bool retVal =
      PolygonCalculus::shortestPath(polygon2D, start2D, end2D, path2D);
  toGeoList(path2D, /*origin*/ start, path);
385

386 387
  return retVal;
}
388

389
bool WimaController::setWimaPlanData(QSharedPointer<WimaPlanData> planData) {
390 391 392 393
  // reset visual items
  _areas.clear();
  _defaultWM.clear();
  _snakeWM.clear();
394 395 396 397
  _measurementArea = WimaMeasurementAreaData();
  _serviceArea = WimaServiceAreaData();
  _corridor = WimaCorridorData();
  _joinedArea = WimaJoinedAreaData();
398

399 400 401 402 403
  emit visualItemsChanged();
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit waypointPathChanged();
  emit currentWaypointPathChanged();
404 405
  emit snakeTilesChanged();
  emit nemoProgressChanged();
406

407
  _planDataValid = false;
408

409
  // extract list with WimaAreas
410
  QList<const WimaAreaData *> areaList = planData->areaList();
411

412
  int areaCounter = 0;
413 414
  const int numAreas = 4; // extract only numAreas Areas, if there are more
                          // they are invalid and ignored
415 416
  for (int i = 0; i < areaList.size(); i++) {
    const WimaAreaData *areaData = areaList[i];
417

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

424
      continue;
425
    }
426

427 428 429 430 431 432
    if (areaData->type() ==
        WimaMeasurementAreaData::typeString) { // is it a measurement area?
      _measurementArea =
          *qobject_cast<const WimaMeasurementAreaData *>(areaData);
      areaCounter++;
      _areas.append(&_measurementArea);
433

434
      continue;
435
    }
436

437 438 439 440
    if (areaData->type() == WimaCorridorData::typeString) { // is it a corridor?
      _corridor = *qobject_cast<const WimaCorridorData *>(areaData);
      areaCounter++;
      //_visualItems.append(&_corridor); // not needed
441

442
      continue;
443
    }
444

445 446 447 448 449
    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
450

451
      continue;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
452 453
    }

454 455 456
    if (areaCounter >= numAreas)
      break;
  }
457

458 459 460 461
  if (areaCounter != numAreas) {
    Q_ASSERT(false);
    return false;
  }
462

463
  emit visualItemsChanged();
464 465 466 467
  emit snakeTilesChanged();

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

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

476
  for (auto *item : tempMissionItems) {
477
    _snakeWM.push_back(item->coordinate());
478 479 480 481
    _defaultWM.push_back(item->coordinate());
  }
  _WMSettings.setHomePosition(QGeoCoordinate(
      _serviceArea.depot().latitude(), _serviceArea.depot().longitude(), 0));
482
  //  auto tempMissionItems = planData->missionItems();
483 484 485 486
  if (!_defaultWM.reset()) {
    Q_ASSERT(false);
    return false;
  }
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
  if (!_snakeWM.reset()) {
    Q_ASSERT(false);
    return false;
  }

  if (_currentWM == &_defaultWM || _currentWM == &_snakeWM) {
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
    emit currentWaypointPathChanged();
  }

  // Publish tiles, set progress if necessary.
  if (!this->_nemoInterface.hasTileData(this->_measurementArea.tileData())) {
    if (_enableSnake.rawValue().toBool()) {
      this->_nemoInterface.publishTileData(this->_measurementArea.tileData());
    }
    this->_measurementArea.progress() =
        QVector<int>(this->_measurementArea.tiles()->count(), 0);
    emit nemoProgressChanged();
507

508 509 510 511 512 513 514 515
  } else if (this->_enableSnake.rawValue().toBool()) {
    const auto progress = this->_nemoInterface.progress();
    if (progress.size() == this->_measurementArea.tiles()->count()) {
      this->_measurementArea.progress() = std::move(progress);
      emit nemoProgressChanged();
      this->_updateRoute();
    }
  }
516

517
  _planDataValid = true;
518
  return true;
519
}
520

521
WimaController *WimaController::thisPointer() { return this; }
522

523 524 525 526 527
bool WimaController::_calcNextPhase() {
  if (!_currentWM->next()) {
    Q_ASSERT(false);
    return false;
  }
528

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

534
  return true;
535 536
}

537 538 539 540 541 542
bool WimaController::_setStartIndex() {
  bool value;
  _currentWM->setStartIndex(
      _nextPhaseStartWaypointIndex.rawValue().toUInt(&value) - 1);
  Q_ASSERT(value);
  (void)value;
543

544 545 546 547
  if (!_currentWM->update()) {
    Q_ASSERT(false);
    return false;
  }
548

549 550 551 552
  emit missionItemsChanged();
  emit currentMissionItemsChanged();
  emit currentWaypointPathChanged();
  emit waypointPathChanged();
553

554
  return true;
555 556
}

557 558 559 560
void WimaController::_recalcCurrentPhase() {
  if (!_currentWM->update()) {
    Q_ASSERT(false);
  }
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 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
  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() {
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
  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();
          }
675
        }
676 677
      } else {
        _lowBatteryHandlingTriggered = false;
678
      }
679
    }
680
  }
681 682
}

683 684 685 686 687 688 689 690 691 692
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
693 694
}

695
void WimaController::_smartRTLCleanUp(bool flying) {
696
  if (!flying && _missionController) { // vehicle has landed
697 698 699 700 701
    _switchWaypointManager(_defaultWM);
    _missionController->removeAll();
    disconnect(masterController()->managerVehicle(), &Vehicle::flyingChanged,
               this, &WimaController::_smartRTLCleanUp);
  }
702 703
}

704 705 706 707
void WimaController::_setPhaseDistance(double distance) {
  (void)distance;
  //    if (!qFuzzyCompare(distance, _phaseDistance)) {
  //        _phaseDistance = distance;
708

709 710
  //        emit phaseDistanceChanged();
  //    }
711 712
}

713 714 715 716
void WimaController::_setPhaseDuration(double duration) {
  (void)duration;
  //    if (!qFuzzyCompare(duration, _phaseDuration)) {
  //        _phaseDuration = duration;
717

718 719
  //        emit phaseDurationChanged();
  //    }
720 721
}

722
bool WimaController::_SRTLPrecondition(QString &errorString) {
723
  if (!_planDataValid) {
724 725 726 727
    errorString.append(tr("No WiMA data available. Please define at least a "
                          "measurement and a service area."));
    return false;
  }
728

729
  return _rtlWM.checkPrecondition(errorString);
730 731
}

732 733 734 735
void WimaController::_switchWaypointManager(
    WaypointManager::ManagerBase &manager) {
  if (_currentWM != &manager) {
    _currentWM = &manager;
Valentin Platzgummer's avatar
Valentin Platzgummer committed
736

737 738 739 740 741 742
    disconnect(&_overlapWaypoints, &Fact::rawValueChanged, this,
               &WimaController::_updateOverlap);
    disconnect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
               &WimaController::_updateMaxWaypoints);
    disconnect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
               &WimaController::_setStartIndex);
743

744 745 746
    _maxWaypointsPerPhase.setRawValue(_currentWM->N());
    _overlapWaypoints.setRawValue(_currentWM->overlap());
    _nextPhaseStartWaypointIndex.setRawValue(_currentWM->startIndex());
Valentin Platzgummer's avatar
Valentin Platzgummer committed
747

748 749 750 751 752 753
    connect(&_overlapWaypoints, &Fact::rawValueChanged, this,
            &WimaController::_updateOverlap);
    connect(&_maxWaypointsPerPhase, &Fact::rawValueChanged, this,
            &WimaController::_updateMaxWaypoints);
    connect(&_nextPhaseStartWaypointIndex, &Fact::rawValueChanged, this,
            &WimaController::_setStartIndex);
754

755
    emit missionItemsChanged();
756 757
    emit currentMissionItemsChanged();
    emit waypointPathChanged();
758
    emit currentWaypointPathChanged();
759

760 761
    qWarning() << "WimaController::_switchWaypointManager: statistics update "
                  "missing.";
762 763 764 765 766 767
  }
}

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

770 771 772 773 774 775 776 777 778 779 780 781
  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();
      }
782 783 784 785 786 787 788 789 790 791 792 793
    }
  } 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);
  }
}

794 795
void WimaController::_storeRoute(RoutingThread::PtrRoutingData data) {
#ifdef SNAKE_SHOW_TIME
796 797
  auto start = std::chrono::high_resolution_clock::now();
#endif
798 799
  // Copy waypoints to waypoint manager.
  _snakeWM.clear();
Valentin Platzgummer's avatar
Valentin Platzgummer committed
800

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
  if (data->route.size() > 0) {
    // Store route.
    const auto &transectsENU = data->transects;
    const auto &transectsInfo = data->transectsInfo;
    const auto &route = data->route;
    // Find index of first waypoint.
    std::size_t idxFirst = 0;
    const auto &infoFirst = transectsInfo.front();
    const auto &firstTransect = transectsENU[infoFirst.index];
    const auto &firstWaypoint =
        infoFirst.reversed ? firstTransect.back() : firstTransect.front();
    double th = 0.001;
    for (std::size_t i = 0; i < route.size(); ++i) {
      auto dist = bg::distance(route[i], firstWaypoint);
      if (dist < th) {
        idxFirst = i;
        break;
      }
    }
    // Find index of last waypoint.
    std::size_t idxLast = route.size() - 1;
    const auto &infoLast = transectsInfo.back();
    const auto &lastTransect = transectsENU[infoLast.index];
    const auto &lastWaypoint =
        infoLast.reversed ? lastTransect.front() : lastTransect.back();
    for (long i = route.size() - 1; i >= 0; --i) {
      auto dist = bg::distance(route[i], lastWaypoint);
      if (dist < th) {
        idxLast = i;
        break;
      }
    }
    // Convert to geo coordinates and append to waypoint manager.
    const auto &ori = this->_origin;
    for (std::size_t i = idxFirst; i <= idxLast; ++i) {
      auto &vertex = route[i];
      QGeoCoordinate c;
      snake::fromENU(ori, vertex, c);
      _snakeWM.push_back(c);
    }
    // Do update.
    this->_snakeWM.update(); // this can take a while (ca. 200ms)
843

844 845 846 847 848
    emit missionItemsChanged();
    emit currentMissionItemsChanged();
    emit currentWaypointPathChanged();
    emit waypointPathChanged();
  }
849
#ifdef SNAKE_SHOW_TIME
850
  auto end = std::chrono::high_resolution_clock::now();
851 852 853 854 855 856
  std::cout << "WimaController::_threadFinishedHandler(): waypoints: "
            << std::chrono::duration_cast<std::chrono::milliseconds>(end -
                                                                     start)
                   .count()
            << " ms" << std::endl;
#endif
857 858 859 860 861 862 863 864 865 866
}

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

867 868
void WimaController::_progressChangedHandler() {
  const auto progress = this->_nemoInterface.progress();
869
  if (this->_measurementArea.tiles()->count() == progress.size()) {
870
    this->_measurementArea.progress() = std::move(progress);
871
    emit nemoProgressChanged();
872 873
    this->_updateRoute();
  } else if (_planDataValid) {
874
    this->_nemoInterface.publishTileData(this->_measurementArea.tileData());
875
  }
Valentin Platzgummer's avatar
Valentin Platzgummer committed
876 877
}

878 879
void WimaController::_enableSnakeChangedHandler() {
  if (this->_enableSnake.rawValue().toBool()) {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
880
    qDebug() << "WimaController: enabling snake.";
881
    _switchWaypointManager(_snakeWM);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
882
    this->_nemoInterface.start();
883 884 885 886 887 888
    if (_planDataValid) {
      if (!this->_nemoInterface.hasTileData(
              this->_measurementArea.tileData())) {
        this->_nemoInterface.publishTileData(this->_measurementArea.tileData());
      }
    }
889
  } else {
Valentin Platzgummer's avatar
Valentin Platzgummer committed
890
    qDebug() << "WimaController: disabling snake.";
891
    _switchWaypointManager(_defaultWM);
Valentin Platzgummer's avatar
Valentin Platzgummer committed
892
    this->_nemoInterface.stop();
893 894 895 896
  }
}

void WimaController::_updateRoute() {
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
  // precondtion
  if (_planDataValid && this->_joinedArea.coordinateList().size() > 0) {
    const auto progress = this->_nemoInterface.progress();
    const auto *tiles = this->_measurementArea.tiles();
    // precondtion
    if (tiles->count() > 0 && progress.size() == tiles->count() &&
        this->_rawTransects.size() > 0 &&
        this->_joinedArea.coordinateList().size() > 0) {
      // Fetch tiles and convert to ENU.
      this->_origin = this->_joinedArea.coordinateList().first();
      this->_origin.setAltitude(0);
      const auto &origin = this->_origin;
      auto pTiles = std::make_shared<std::vector<snake::BoostPolygon>>();
      std::size_t numTiles = 0;
      for (int i = 0; i < progress.size(); ++i) {
        if (progress[i] == 100) {
          ++numTiles;
          const auto *obj = tiles->get(i);
          const auto *tile = qobject_cast<const SnakeTile *>(obj);
          if (tile != nullptr) {
            snake::BoostPolygon t;
            snake::areaToEnu(origin, tile->coordinateList(), t);
            pTiles->push_back(std::move(t));
          } else {
            return;
          }
923 924
        }
      }
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
      if (numTiles > 0) {
        // Fetch safe area and convert to ENU.
        auto safeArea = this->_joinedArea.coordinateList();
        for (auto &v : safeArea) {
          v.setAltitude(0);
        }
        snake::BoostPolygon safeAreaENU;
        snake::areaToEnu(origin, safeArea, safeAreaENU);
        const auto &depot = this->_serviceArea.depot();
        snake::BoostPoint depotENU;
        snake::toENU(origin, depot, depotENU);
        // Fetch geo transects and convert to ENU.
        const auto &geoTransects = this->_rawTransects;
        auto pUnclippedTransects = std::make_shared<snake::Transects>();
        for (auto &geoTransect : geoTransects) {
          snake::BoostLineString t;
          for (auto &geoVertex : geoTransect) {
            snake::BoostPoint v;
            snake::toENU(origin, geoVertex, v);
            t.push_back(v);
          }
          pUnclippedTransects->push_back(t);
        }
        std::size_t minLength = 1; // meter
        qWarning() << "using minLength dummy lkjfdslooij";
        auto generator = [depotENU, pUnclippedTransects, pTiles,
                          minLength](snake::Transects &transects) -> bool {
          // Convert transects to clipper path.
          vector<ClipperLib::Path> transectsClipper;
          for (const auto &t : *pUnclippedTransects) {
            ClipperLib::Path path;
            for (const auto &v : t) {
              ClipperLib::IntPoint c{
                  static_cast<ClipperLib::cInt>(v.get<0>() * CLIPPER_SCALE),
                  static_cast<ClipperLib::cInt>(v.get<1>() * CLIPPER_SCALE)};
              path.push_back(c);
            }
            transectsClipper.push_back(std::move(path));
          }
          // Add transects to clipper.
          ClipperLib::Clipper clipper;
          clipper.AddPaths(transectsClipper, ClipperLib::ptSubject, false);
          // Add tiles to clipper.
          vector<ClipperLib::Path> tilesClipper;
          for (const auto &t : *pTiles) {
            ClipperLib::Path path;
            for (const auto &v : t.outer()) {
              path.push_back(ClipperLib::IntPoint{
                  static_cast<ClipperLib::cInt>(v.get<0>() * CLIPPER_SCALE),
                  static_cast<ClipperLib::cInt>(v.get<1>() * CLIPPER_SCALE)});
            }
            tilesClipper.push_back(std::move(path));
          }

          clipper.AddPaths(tilesClipper, ClipperLib::ptClip, true);
          // Clip transects.
          ClipperLib::PolyTree clippedTransecs;
          clipper.Execute(ClipperLib::ctDifference, clippedTransecs,
                          ClipperLib::pftNonZero, ClipperLib::pftNonZero);
          // Extract transects from  PolyTree and convert them to
          // BoostLineString
          transects.push_back(snake::BoostLineString{depotENU});
          for (const auto &child : clippedTransecs.Childs) {
            snake::BoostLineString transect;
            for (const auto &v : child->Contour) {
              snake::BoostPoint c{static_cast<double>(v.X) / CLIPPER_SCALE,
                                  static_cast<double>(v.Y) / CLIPPER_SCALE};
              transect.push_back(c);
            }

            if (bg::length(transect) >= minLength) {
              transects.push_back(std::move(transect));
            }
          }
          if (transects.size() > 1) {
            return true;
          } else {
            return false;
          }
        }; // generator
        this->_routingThread.route(safeAreaENU, generator);
      } else {
        this->_storeRoute(RoutingThread::PtrRoutingData(new RoutingData()));
      }
    }
1010 1011
  }
}