#include "DefaultManager.h" #include "Wima/Geometry/GeoUtilities.h" #include "Wima/Geometry/PolygonCalculus.h" #include "MissionSettingsItem.h" #include "SimpleMissionItem.h" WaypointManager::DefaultManager::DefaultManager(Settings &settings, AreaInterface &interface) : ManagerBase(settings), _areaInterface(&interface) {} void WaypointManager::DefaultManager::clear() { _dirty = true; _waypoints.clear(); _currentWaypoints.clear(); _missionItems.clearAndDeleteContents(); _currentMissionItems.clearAndDeleteContents(); _waypointsVariant.clear(); _currentWaypointsVariant.clear(); } bool WaypointManager::DefaultManager::update() { // extract waypoints _currentWaypoints.clear(); Slicer::update(_waypoints, _currentWaypoints); return _worker(); } bool WaypointManager::DefaultManager::next() { // extract waypoints _currentWaypoints.clear(); Slicer::next(_waypoints, _currentWaypoints); return _worker(); } bool WaypointManager::DefaultManager::previous() { // extract waypoints _currentWaypoints.clear(); Slicer::previous(_waypoints, _currentWaypoints); return _worker(); } bool WaypointManager::DefaultManager::reset() { // extract waypoints _currentWaypoints.clear(); Slicer::reset(_waypoints, _currentWaypoints); return _worker(); } bool WaypointManager::DefaultManager::_insertMissionItem( const QGeoCoordinate &c, size_t index, QmlObjectListModel &list, bool doUpdate) { using namespace WaypointManager::Utils; if (!insertMissionItem(c, index /*insertion index*/, list, _settings->masterController(), _settings->isFlyView(), &list /*parent*/, doUpdate /*do update*/)) { qWarning( "WaypointManager::DefaultManager::next(): insertMissionItem failed."); Q_ASSERT(false); return false; } return true; } bool WaypointManager::DefaultManager::_insertMissionItem( const QGeoCoordinate &c, size_t index, bool doUpdate) { return _insertMissionItem(c, index, _currentMissionItems, doUpdate); } bool WaypointManager::DefaultManager::_calcShortestPath( const QGeoCoordinate &start, const QGeoCoordinate &destination, QVector &path) { using namespace GeoUtilities; using namespace PolygonCalculus; QPolygonF joinedArea2D; toCartesianList(_areaInterface->joinedArea()->coordinateList(), /*origin*/ start, joinedArea2D); QPointF start2D(0, 0); QPointF end2D; toCartesian(destination, start, end2D); QVector path2DOut; bool retVal = PolygonCalculus::shortestPath(joinedArea2D, start2D, end2D, path2DOut); toGeoList(path2DOut, /*origin*/ start, path); return retVal; } bool WaypointManager::DefaultManager::_worker() { // Precondition: // _waypoints must contain valid coordinates. // Slicer must be called befor invoking this function. // E.g. Slicer::reset(_waypoints, _currentWaypoints); using namespace WaypointManager::Utils; if (_waypoints.count() < 1 || !_settings->valid()) { return false; } if (_dirty) { _missionItems.clearAndDeleteContents(); _waypointsVariant.clear(); // No initialization of _missionItems, don't need MissionSettingsItem here. for (size_t i = 0; i < size_t(_waypoints.size()); ++i) { auto &c = _waypoints.at(i); _insertMissionItem(c, _missionItems.count(), _missionItems, false /*update list*/); _waypointsVariant.push_back(QVariant::fromValue(c)); } updateHirarchy(_missionItems); updateSequenceNumbers( _missionItems, 1); // Start with 1, since MissionSettingsItem missing. _dirty = false; } _currentMissionItems.clearAndDeleteContents(); initialize(_currentMissionItems, _settings->masterController(), _settings->isFlyView()); // Calculate path from home to first waypoint. QVector arrivalPath; if (!_calcShortestPath(_settings->homePosition(), _currentWaypoints.first(), arrivalPath)) { qWarning("WaypointManager::DefaultManager::next(): Not able to calc path " "from home position to first waypoint."); qWarning() << "from: " << _settings->homePosition(); qWarning() << "to: " << _currentWaypoints.first(); return false; } if (!arrivalPath.empty()) arrivalPath.removeFirst(); if (!arrivalPath.empty()) arrivalPath.removeLast(); // calculate path from last waypoint to home QVector returnPath; if (!_calcShortestPath(_currentWaypoints.last(), _settings->homePosition(), returnPath)) { qWarning("WaypointManager::DefaultManager::next(): Not able to calc path " "from home position to last waypoint."); qWarning() << "from: " << _currentWaypoints.last(); qWarning() << "to: " << _settings->homePosition(); return false; } if (!returnPath.empty()) returnPath.removeFirst(); if (!returnPath.empty()) returnPath.removeLast(); // Create mission items. // Set home position of MissionSettingsItem. MissionSettingsItem *settingsItem = _currentMissionItems.value(0); if (settingsItem == nullptr) { Q_ASSERT(false); qWarning("WaypointManager::DefaultManager::next(): nullptr."); return false; } settingsItem->setCoordinate(_settings->homePosition()); // First mission item is takeoff command. _insertMissionItem(_settings->homePosition(), 1 /*insertion index*/, false /*do update*/); SimpleMissionItem *takeoffItem = _currentMissionItems.value(1); if (takeoffItem == nullptr) { qWarning("WaypointManager::DefaultManager::next(): nullptr."); Q_ASSERT(takeoffItem != nullptr); return false; } makeTakeOffCmd(takeoffItem, _settings->masterController()); // Create change speed item. _insertMissionItem(_settings->homePosition(), 2 /*insertion index*/, false /*do update*/); SimpleMissionItem *speedItem = _currentMissionItems.value(2); if (speedItem == nullptr) { qWarning("WaypointManager::DefaultManager::next(): nullptr."); Q_ASSERT(speedItem != nullptr); return false; } makeSpeedCmd(speedItem, _settings->arrivalReturnSpeed()); // insert arrival path for (auto coordinate : arrivalPath) { _insertMissionItem(coordinate, _currentMissionItems.count() /*insertion index*/, false /*do update*/); } // Create change speed item (after arrival path). int index = _currentMissionItems.count(); _insertMissionItem(_currentWaypoints.first(), index /*insertion index*/, false /*do update*/); speedItem = _currentMissionItems.value(index); if (speedItem == nullptr) { Q_ASSERT(false); qWarning("WaypointManager::DefaultManager::next(): nullptr."); return false; } makeSpeedCmd(speedItem, _settings->flightSpeed()); // Insert slice. for (auto coordinate : _currentWaypoints) { _insertMissionItem(coordinate, _currentMissionItems.count() /*insertion index*/, false /*do update*/); } // Create change speed item, after slice. index = _currentMissionItems.count(); _insertMissionItem(_currentWaypoints.last(), index /*insertion index*/, false /*do update*/); speedItem = _currentMissionItems.value(index); if (speedItem == nullptr) { Q_ASSERT(false); qWarning("WaypointManager::DefaultManager::next(): nullptr."); return false; } makeSpeedCmd(speedItem, _settings->arrivalReturnSpeed()); // Insert return path coordinates. for (auto coordinate : returnPath) { _insertMissionItem(coordinate, _currentMissionItems.count() /*insertion index*/, false /*do update*/); } // Set land command for last mission item. index = _currentMissionItems.count(); _insertMissionItem(_settings->homePosition(), index /*insertion index*/, false /*do update*/); SimpleMissionItem *landItem = _currentMissionItems.value(index); if (landItem == nullptr) { Q_ASSERT(false); qWarning("WaypointManager::DefaultManager::next(): nullptr."); return false; } makeLandCmd(landItem, _settings->masterController()); // Set altitude. for (int i = 1; i < _currentMissionItems.count(); ++i) { SimpleMissionItem *item = _currentMissionItems.value(i); if (item == nullptr) { Q_ASSERT(false); qWarning("WimaController::updateAltitude(): nullptr"); return false; } item->altitude()->setRawValue(_settings->altitude()); } // Update list _currentMissionItems. updateHirarchy(_currentMissionItems); updateSequenceNumbers(_currentMissionItems); // Prepend arrival path to slice. for (long i = arrivalPath.size() - 1; i >= 0; --i) _currentWaypoints.push_front(arrivalPath[i]); _currentWaypoints.push_front(_settings->homePosition()); // Append return path to slice. for (auto c : returnPath) _currentWaypoints.push_back(c); _currentWaypoints.push_back(_settings->homePosition()); // Create variant list. _currentWaypointsVariant.clear(); for (auto c : _currentWaypoints) _currentWaypointsVariant.push_back(QVariant::fromValue(c)); return true; }