#include "SnakeDataManager.h" #include #include #include "QGCApplication.h" #include "QGCToolbox.h" #include "SettingsFact.h" #include "SettingsManager.h" #include "WimaSettings.h" #include #include #include #include "Wima/Snake/SnakeTiles.h" #include "Wima/Snake/SnakeTilesLocal.h" #include "snake.h" using QVariantList = QList; using UniqueLock = std::unique_lock; using SharedLock = std::shared_lock; class SnakeDataManager::SnakeImpl { public: struct Input { Input() : tileWidth(5 * si::meter), tileHeight(5 * si::meter), minTileArea(1 * si::meter * si::meter), lineDistance(2 * si::meter), minTransectLength(1 * si::meter), scenarioChanged(true), routeParametersChanged(true) {} QList mArea; QGeoCoordinate ENUOrigin; QList sArea; QList corridor; Length tileWidth; Length tileHeight; Area minTileArea; std::atomic_bool scenarioChanged; Length lineDistance; Length minTransectLength; std::atomic_bool routeParametersChanged; mutable std::shared_timed_mutex mutex; }; struct Output { SnakeTiles tiles; QmlObjectListModel tilesQml; QVariantList tileCenterPoints; SnakeTilesLocal tilesENU; QVector tileCenterPointsENU; QVector waypoints; QVector arrivalPath; QVector returnPath; QVector waypointsENU; QVector arrivalPathENU; QVector returnPathENU; QString errorMessage; std::atomic_bool calcInProgress; mutable std::shared_timed_mutex mutex; }; SnakeImpl(SnakeDataManager *p); bool precondition() const; bool doTopicServiceSetup(); // Internal data. snake::Scenario scenario; SnakeDataManager *parent; Input input; // Output Output output; }; SnakeDataManager::SnakeImpl::SnakeImpl(SnakeDataManager *p) : rosBridgeEnabeled(false), topicServiceSetupDone(false), parent(p) {} bool SnakeDataManager::SnakeImpl::precondition() const { return true; } template class CommandRAII { public: CommandRAII(Callable &fun) : _fun(fun) {} ~CommandRAII() { _fun(); } private: Callable _fun; }; SnakeDataManager::SnakeDataManager(QObject *parent) : QThread(parent), pImpl(std::make_unique(this)) {} // SnakeDataManager::~SnakeDataManager() {} void SnakeDataManager::setMeasurementArea( const QList &measurementArea) { this.input.scenarioChanged = true; UniqueLock lk(this->pImpl->input.mutex); this->pImpl->inptu.mArea = measurementArea; for (auto &vertex : this->pImpl->input.mArea) { vertex.setAltitude(0); } } void SnakeDataManager::setServiceArea( const QList &serviceArea) { this.input.scenarioChanged = true; UniqueLock lk(this->pImpl->input.mutex); this->pImpl->input.sArea = serviceArea; for (auto &vertex : this->pImpl->input.sArea) { vertex.setAltitude(0); } } void SnakeDataManager::setCorridor(const QList &corridor) { this.input.scenarioChanged = true; UniqueLock lk(this->pImpl->input.mutex); this->pImpl->input.corridor = corridor; for (auto &vertex : this->pImpl->input.corridor) { vertex.setAltitude(0); } } const QmlObjectListModel *SnakeDataManager::tiles() const { SharedLock lk(this->pImpl->output.mutex); return &this->pImpl->output.tilesQml; } QVariantList SnakeDataManager::tileCenterPoints() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.tileCenterPoints; } QNemoProgress SnakeDataManager::nemoProgress() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.qProgress; } int SnakeDataManager::nemoStatus() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.heartbeat.status(); } bool SnakeDataManager::calcInProgress() const { return this->pImpl->output.calcInProgress.load(); } QString SnakeDataManager::errorMessage() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.errorMessage; } bool SnakeDataManager::success() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.errorMessage.isEmpty() ? true : false; } QVector SnakeDataManager::waypoints() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.waypoints; } QVector SnakeDataManager::arrivalPath() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.arrivalPath; } QVector SnakeDataManager::returnPath() const { SharedLock lk(this->pImpl->output.mutex); return this->pImpl->output.returnPath; } Length SnakeDataManager::lineDistance() const { SharedLock lk(this->pImpl->mutex); return this->pImpl->lineDistance; } void SnakeDataManager::setLineDistance(Length lineDistance) { UniqueLock lk(this->pImpl->mutex); routeParametersChanged = true; this->pImpl->lineDistance = lineDistance; } Area SnakeDataManager::minTileArea() const { SharedLock lk(this->pImpl->mutex); return this->pImpl->scenario.minTileArea(); } void SnakeDataManager::setMinTileArea(Area minTileArea) { scenarioChanged = true; UniqueLock lk(this->pImpl->mutex); this->pImpl->scenario.setMinTileArea(minTileArea); } Length SnakeDataManager::tileHeight() const { SharedLock lk(this->pImpl->mutex); return this->pImpl->scenario.tileHeight(); } void SnakeDataManager::setTileHeight(Length tileHeight) { scenarioChanged = true; UniqueLock lk(this->pImpl->mutex); this->pImpl->scenario.setTileHeight(tileHeight); } Length SnakeDataManager::tileWidth() const { SharedLock lk(this->pImpl->mutex); return this->pImpl->scenario.tileWidth(); } void SnakeDataManager::setTileWidth(Length tileWidth) { UniqueLock lk(this->pImpl->mutex); scenarioChanged = true; this->pImpl->scenario.setTileWidth(tileWidth); } void SnakeDataManager::enableRosBridge() { this->pImpl->rosBridgeEnabeled = true; } void SnakeDataManager::disableRosBride() { this->pImpl->rosBridgeEnabeled = false; } void SnakeDataManager::run() { #ifndef NDEBUG auto startTime = std::chrono::high_resolution_clock::now(); #endif this->pImpl->calcInProgress.store(true); emit calcInProgressChanged(this->pImpl->calcInProgress.load()); auto onExit = [this, &startTime] { #ifndef NDEBUG qDebug() << "SnakeDataManager::run() execution time: " << std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - startTime) .count() << " ms"; #endif this->pImpl->calcInProgress.store(false); emit calcInProgressChanged(this->pImpl->calcInProgress.load()); }; CommandRAII onExitRAII(onExit); // Check preconditions. SharedLock sLock(this->pImpl->mutex); if (!this->pImpl->precondition()) return; if (this->pImpl->mArea.size() < 3) { this->pImpl->errorMessage = "Measurement area invalid: size < 3."; return; } if (this->pImpl->sArea.size() < 3) { this->pImpl->errorMessage = "Service area invalid: size < 3."; return; } // Update Scenario if necessary if (this->pImpl->scenarioChanged) { // Set Origin this->pImpl->ENUOrigin = this->pImpl->mArea.front(); auto &origin = this->pImpl->ENUOrigin; // Update measurement area. auto &mAreaEnu = this->pImpl->scenario.measurementArea(); auto &mArea = this->pImpl->mArea; mAreaEnu.clear(); for (auto geoVertex : mArea) { snake::BoostPoint p; snake::toENU(origin, geoVertex, p); mAreaEnu.outer().push_back(p); } // Update service area. auto &sAreaEnu = this->pImpl->scenario.serviceArea(); auto &sArea = this->pImpl->sArea; sAreaEnu.clear(); for (auto geoVertex : sArea) { snake::BoostPoint p; snake::toENU(origin, geoVertex, p); sAreaEnu.outer().push_back(p); } // Update corridor. auto &corridorEnu = this->pImpl->scenario.corridor(); auto &corridor = this->pImpl->corridor; corridor.clear(); for (auto geoVertex : corridor) { snake::BoostPoint p; snake::toENU(origin, geoVertex, p); corridorEnu.outer().push_back(p); } if (!this->pImpl->scenario.update()) { this->pImpl->errorMessage = this->pImpl->scenario.errorString.c_str(); return; } } sLock.unlock; bool storeProgress = false; bool storeRoute = false; if (this->pImpl->scenarioChanged || this->pImpl->routeParametersChanged) { storeRoute = true; // Sample data SharedLock lk(this->pImpl->mutex); size_t nTiles = this->pImpl->tiles.polygons().size(); if (this->pImpl->progress.size() != nTiles) { } const auto scenario = this->pImpl->scenario; std::vector progress; size_t nTiles = this->pImpl->tiles.polygons().size(); if (this->pImpl->progress.size() != nTiles) { progress.reserve(nTiles); for (size_t i = 0; i < nTiles; ++i) { progress.push_back(0); } storeProgress = true; } else { progress = this->pImpl->progress; } const auto lineDistance = this->pImpl->lineDistance; const auto minTransectLength = this->pImpl->minTransectLength; lk.unlock(); // Create transects. std::string errorString; snake::Angle alpha(-scenario.mAreaBoundingBox().angle * degree::degree); snake::flight_plan::Transects transects; transects.push_back( bg::model::linestring{scenario.homePositon()}); bool value = snake::flight_plan::transectsFromScenario( lineDistance, minTransectLength, alpha, scenario, progress, transects, errorString); if (!value) { UniqueLock lk(this->pImpl->mutex); this->pImpl->errorMessage = errorString.c_str(); storeRoute = false; } // Route transects if (transects.size() > 1) { snake::flight_plan::Transects transectsRouted; snake::flight_plan::Route route; value = snake::flight_plan::route(scenario.joinedArea(), transects, transectsRouted, route, errorString); if (!value) { this->pImpl->errorMessage = errorString.c_str(); return; } // Store arrival path. const auto &firstWaypoint = transectsRouted.front().front(); long startIdx = 0; for (long i = 0; i < long(route.size()); ++i) { const auto &boostVertex = route[i]; if (boostVertex == firstWaypoint) { startIdx = i; break; } QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QGeoCoordinate geoVertex; snake::fromENU(origin, boostVertex, geoVertex); this->pImpl->arrivalPathENU.push_back(enuVertex); this->pImpl->arrivalPath.push_back(geoVertex); } // Store return path. long endIdx = 0; const auto &lastWaypoint = transectsRouted.back().back(); for (long i = route.size() - 1; i >= 0; --i) { const auto &boostVertex = route[i]; if (boostVertex == lastWaypoint) { endIdx = i; break; } QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QGeoCoordinate geoVertex; snake::fromENU(origin, boostVertex, geoVertex); this->pImpl->returnPathENU.push_back(enuVertex); this->pImpl->returnPath.push_back(geoVertex); } // Store waypoints. for (long i = startIdx; i <= endIdx; ++i) { const auto &boostVertex = route[i]; QPointF enuVertex{boostVertex.get<0>(), boostVertex.get<1>()}; QGeoCoordinate geoVertex; snake::fromENU(origin, boostVertex, geoVertex); this->pImpl->waypointsENU.push_back(enuVertex); this->pImpl->waypoints.push_back(geoVertex); } } } UniqueLock uLock(this->pImpl->mutex); // Store scenario. if (this->pImpl->scenarioChanged) { // Clear some output data. this->pImpl->tiles.polygons().clear(); this->pImpl->tilesQml.clearAndDeleteContents(); this->pImpl->tileCenterPoints.clear(); this->pImpl->tilesENU.polygons().clear(); this->pImpl->tileCenterPointsENU.clear(); // Convert and store scenario data. const auto &tiles = this->pImpl->scenario.tiles(); const auto ¢erPoints = this->pImpl->scenario.tileCenterPoints(); for (unsigned int i = 0; i < tiles.size(); ++i) { const auto &tile = tiles[i]; SnakeTile geoTile; SnakeTileLocal enuTile; for (size_t i = 0; i < tile.outer().size() - 1; ++i) { auto &p = tile.outer()[i]; QPointF enuVertex(p.get<0>(), p.get<1>()); QGeoCoordinate geoVertex; snake::fromENU(origin, p, geoVertex); enuTile.polygon().points().push_back(enuVertex); geoTile.push_back(geoVertex); } const auto &boostPoint = centerPoints[i]; QPointF enuVertex(boostPoint.get<0>(), boostPoint.get<1>()); QGeoCoordinate geoVertex; snake::fromENU(origin, boostPoint, geoVertex); geoTile.setCenter(geoVertex); this->pImpl->tiles.polygons().push_back(geoTile); auto *geoTileCopy = new SnakeTile(geoTile); geoTileCopy->moveToThread(this->pImpl->tilesQml.thread()); this->pImpl->tilesQml.append(geoTileCopy); this->pImpl->tileCenterPoints.push_back(QVariant::fromValue(geoVertex)); this->pImpl->tilesENU.polygons().push_back(enuTile); this->pImpl->tileCenterPointsENU.push_back(enuVertex); } } // Store route etc. if (this->pImpl->scenarioChanged || this->pImpl->routeParametersChanged) { } uLock.unlock; this->pImpl->scenarioChanged = false; this->pImpl->routeParametersChanged = false; }