diff --git a/qgcimages.qrc b/qgcimages.qrc index f7dbf8cb08d86d14621e27255037df3c61724c7c..d85ba288cbdc5637f2c9e83b97fef2882c3f6524 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -195,5 +195,6 @@ src/ui/toolbar/Images/Yield.svg src/FlightMap/Images/ZoomMinus.svg src/FlightMap/Images/ZoomPlus.svg + src/MeasurementComplexItem/MeasurementPlanCreator.png diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 2a7ea35ac0fd0a4765c56812497c002f439515ea..43d99bb4dfdb2fe78368df6bb4595e1fc4e289fa 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -447,6 +447,7 @@ contains (DEFINES, QGC_ENABLE_PAIRING) { HEADERS += \ src/MeasurementComplexItem/IDArray.h \ src/MeasurementComplexItem/LogicalArray.h \ + src/MeasurementComplexItem/MeasurementPlanCreator.h \ src/MeasurementComplexItem/TileArray.h \ src/MeasurementComplexItem/TilePtrArray.h \ src/MeasurementComplexItem/geometry/ProgressArray.h \ @@ -527,6 +528,7 @@ contains (DEFINES, QGC_ENABLE_PAIRING) { } SOURCES += \ + src/MeasurementComplexItem/MeasurementPlanCreator.cpp \ src/MeasurementComplexItem/geometry/GeoArea.cc \ src/MeasurementComplexItem/geometry/MeasurementArea.cc \ src/MeasurementComplexItem/geometry/SafeArea.cc \ diff --git a/src/MeasurementComplexItem/MeasurementPlanCreator.cpp b/src/MeasurementComplexItem/MeasurementPlanCreator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e97386062436a1323a7177efdf8f84de4fc872e4 --- /dev/null +++ b/src/MeasurementComplexItem/MeasurementPlanCreator.cpp @@ -0,0 +1,21 @@ +#include "MeasurementPlanCreator.h" +#include "MeasurementComplexItem.h" +#include "PlanMasterController.h" + +MeasurementPlanCreator::MeasurementPlanCreator( + PlanMasterController *planMasterController, QObject *parent) + : PlanCreator( + planMasterController, MeasurementComplexItem::name, + QStringLiteral("/qmlimages/PlanCreator/MeasurementPlanCreator.png"), + parent) {} + +void MeasurementPlanCreator::createPlan(const QGeoCoordinate &mapCenterCoord) { + _planMasterController->removeAll(); + VisualMissionItem *takeoffItem = + _missionController->insertTakeoffItem(mapCenterCoord, -1); + _missionController->insertComplexMissionItem(MeasurementComplexItem::name, + mapCenterCoord, -1); + _missionController->insertLandItem(mapCenterCoord, -1); + _missionController->setCurrentPlanViewSeqNum(takeoffItem->sequenceNumber(), + true); +} diff --git a/src/MeasurementComplexItem/MeasurementPlanCreator.h b/src/MeasurementComplexItem/MeasurementPlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..abbf7a493b0adec5284acb444c7e556ab8ecc71f --- /dev/null +++ b/src/MeasurementComplexItem/MeasurementPlanCreator.h @@ -0,0 +1,16 @@ +#ifndef MEASUREMENTCOMPLEXITEMPLANCREATOR_H +#define MEASUREMENTCOMPLEXITEMPLANCREATOR_H + +#include "PlanCreator.h" + +class MeasurementPlanCreator : public PlanCreator { + Q_OBJECT + +public: + MeasurementPlanCreator(PlanMasterController *planMasterController, + QObject *parent = nullptr); + + Q_INVOKABLE void createPlan(const QGeoCoordinate &mapCenterCoord) final; +}; + +#endif // MEASUREMENTCOMPLEXITEMPLANCREATOR_H diff --git a/src/MeasurementComplexItem/MeasurementPlanCreator.png b/src/MeasurementComplexItem/MeasurementPlanCreator.png new file mode 100644 index 0000000000000000000000000000000000000000..b1a60fa9c85183a5f3349678b37b267b712b7cac Binary files /dev/null and b/src/MeasurementComplexItem/MeasurementPlanCreator.png differ diff --git a/src/MeasurementComplexItem/NemoInterface.cpp b/src/MeasurementComplexItem/NemoInterface.cpp index dc741c466e35550690a029e26d6d1442a211df2d..9d4d39c24bd691573b3ba106f6720760ef5d9f27 100644 --- a/src/MeasurementComplexItem/NemoInterface.cpp +++ b/src/MeasurementComplexItem/NemoInterface.cpp @@ -251,7 +251,7 @@ std::shared_future NemoInterface::Impl::addTiles(const TilePtrArray &tileArray) { using namespace nemo_interface; - qDebug() << "addTiles called"; + // qDebug() << "addTiles called"; if (tileArray.size() > 0) { @@ -318,7 +318,7 @@ std::shared_future NemoInterface::Impl::removeTiles(const IDArray &idArray) { using namespace nemo_interface; - qDebug() << "removeTiles called"; + // qDebug() << "removeTiles called"; if (idArray.size() > 0) { @@ -364,7 +364,7 @@ NemoInterface::Impl::removeTiles(const IDArray &idArray) { std::shared_future NemoInterface::Impl::clearTiles() { using namespace nemo_interface; - qDebug() << "clearTiles called"; + // qDebug() << "clearTiles called"; // clear local tiles (_localTiles) if (!_localTiles.empty()) { @@ -396,14 +396,27 @@ std::shared_future NemoInterface::Impl::clearTiles() { TileArray NemoInterface::Impl::getTiles(const IDArray &idArray) const { TileArray tileArray; - for (const auto &id : idArray) { - const auto it = _localTiles.find(id); - if (it != _localTiles.end()) { - MeasurementTile copy; - copy.setId(it->second->id()); - copy.setProgress(it->second->progress()); - copy.setPath(it->second->tile()); - tileArray.append(std::move(copy)); + if (this->ready()) { + for (const auto &id : idArray) { + const auto it = _remoteTiles.find(id); + if (it != _remoteTiles.end()) { + MeasurementTile copy; + copy.setId(it->second->id()); + copy.setProgress(it->second->progress()); + copy.setPath(it->second->tile()); + tileArray.append(std::move(copy)); + } + } + } else { + for (const auto &id : idArray) { + const auto it = _localTiles.find(id); + if (it != _localTiles.end()) { + MeasurementTile copy; + copy.setId(it->second->id()); + copy.setProgress(it->second->progress()); + copy.setPath(it->second->tile()); + tileArray.append(std::move(copy)); + } } } @@ -413,13 +426,25 @@ TileArray NemoInterface::Impl::getTiles(const IDArray &idArray) const { TileArray NemoInterface::Impl::getAllTiles() const { TileArray tileArray; - for (const auto &entry : _localTiles) { - auto pTile = entry.second; - MeasurementTile copy; - copy.setId(pTile->id()); - copy.setProgress(pTile->progress()); - copy.setPath(pTile->tile()); - tileArray.append(std::move(copy)); + if (this->ready()) { + for (const auto &entry : _remoteTiles) { + auto pTile = entry.second; + MeasurementTile copy; + copy.setId(pTile->id()); + copy.setProgress(pTile->progress()); + copy.setPath(pTile->tile()); + tileArray.append(std::move(copy)); + } + + } else { + for (const auto &entry : _localTiles) { + auto pTile = entry.second; + MeasurementTile copy; + copy.setId(pTile->id()); + copy.setProgress(pTile->progress()); + copy.setPath(pTile->tile()); + tileArray.append(std::move(copy)); + } } return tileArray; @@ -518,7 +543,7 @@ const QString &NemoInterface::Impl::warningString() const { void NemoInterface::Impl::_updateProgress(std::shared_ptr pArray, std::promise promise) { - qDebug() << "_updateProgress called"; + // qDebug() << "_updateProgress called"; bool error = false; for (auto itLP = pArray->begin(); itLP != pArray->end();) { @@ -799,7 +824,7 @@ void NemoInterface::Impl::_doAction() { QVariant NemoInterface::Impl::_callAddTiles( std::shared_ptr>> pTileArray) { - qDebug() << "_callAddTiles called"; + // qDebug() << "_callAddTiles called"; this->_lastCall = CALL_NAME::ADD_TILES; @@ -900,7 +925,7 @@ QVariant NemoInterface::Impl::_callAddTiles( QVariant NemoInterface::Impl::_callRemoveTiles(std::shared_ptr pIdArray) { - qDebug() << "_callRemoveTiles called"; + // qDebug() << "_callRemoveTiles called"; this->_lastCall = CALL_NAME::REMOVE_TILES; @@ -995,7 +1020,7 @@ NemoInterface::Impl::_callRemoveTiles(std::shared_ptr pIdArray) { QVariant NemoInterface::Impl::_callClearTiles() { - qDebug() << "_callClearTiles called"; + // qDebug() << "_callClearTiles called"; this->_lastCall = CALL_NAME::CLEAR_TILES; // create response handler. @@ -1068,7 +1093,7 @@ QVariant NemoInterface::Impl::_callClearTiles() { QVariant NemoInterface::Impl::_callGetProgress(std::shared_ptr pIdArray) { - qDebug() << "_callGetProgress called"; + // qDebug() << "_callGetProgress called"; this->_lastCall = CALL_NAME::GET_PROGRESS; @@ -1168,7 +1193,7 @@ NemoInterface::Impl::_callGetProgress(std::shared_ptr pIdArray) { } QVariant NemoInterface::Impl::_callGetAllProgress() { - qDebug() << "_callGetAllProgress called"; + // qDebug() << "_callGetAllProgress called"; this->_lastCall = CALL_NAME::GET_ALL_PROGRESS; @@ -1279,7 +1304,7 @@ void NemoInterface::Impl::_addTilesRemote( std::shared_ptr>> pTileArray, std::promise promise) { - qDebug() << "_addTilesRemote called"; + // qDebug() << "_addTilesRemote called"; auto pArrayDup = std::make_shared>>(); for (auto pTile : *pTileArray) { @@ -1292,7 +1317,7 @@ void NemoInterface::Impl::_addTilesRemote2( std::shared_ptr>> pTileArray, std::promise promise) { - qDebug() << "_addTilesRemote2 called"; + // qDebug() << "_addTilesRemote2 called"; bool anyChange = false; bool error = false; @@ -1326,7 +1351,7 @@ void NemoInterface::Impl::_addTilesRemote2( void NemoInterface::Impl::_removeTilesRemote(std::shared_ptr idArray, std::promise promise) { - qDebug() << "_removeTilesRemote called"; + // qDebug() << "_removeTilesRemote called"; bool anyChange = false; for (const auto id : *idArray) { @@ -1351,7 +1376,7 @@ void NemoInterface::Impl::_removeTilesRemote(std::shared_ptr idArray, } void NemoInterface::Impl::_clearTilesRemote(std::promise promise) { - qDebug() << "_clearTilesRemote called"; + // qDebug() << "_clearTilesRemote called"; if (_remoteTiles.size() > 0) { _remoteTiles.clear(); if (this->_isSynchronized()) { diff --git a/src/MeasurementComplexItem/qml/AreaDataEditor.qml b/src/MeasurementComplexItem/qml/AreaDataEditor.qml index a1aa3582cae4dd915749fac9e6d7f5d18cb2cb47..d6828d2aa7eeabbbb6f764cc14287e4fb42fa4eb 100644 --- a/src/MeasurementComplexItem/qml/AreaDataEditor.qml +++ b/src/MeasurementComplexItem/qml/AreaDataEditor.qml @@ -12,227 +12,240 @@ import QGroundControl.Palette 1.0 Rectangle { id: _root - width: mainGrid.width - height: mainGrid.height + width: mainColumn.width + height: mainColumn.height color: qgcPal.windowShadeDark - property bool checked: true + property bool editing: missionItem.editing property var missionItem: undefined property int availableWidth: 300 property bool areasCorrect: false property string errorString: "" - signal abort - property var _areaData: missionItem.areaData property real _margin: ScreenTools.defaultFontPixelWidth / 2 Component.onCompleted: { console.assert(missionItem !== undefined, "please set the missionItem property") - if (checked) { + if (editing) { areasCorrectTimer.start() } } - onCheckedChanged: { - if (checked) { + onEditingChanged: { + if (editing){ areasCorrectTimer.start() } else { areasCorrectTimer.stop() } } - GridLayout { - id: mainGrid - + ColumnLayout { + id: mainColumn width: availableWidth - columnSpacing: _margin - rowSpacing: _margin - columns: 2 - - QGCLabel { - text: _root.errorString - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignLeft - color: "orange" - Layout.columnSpan: parent.columns + spacing: _margin + + QGCButton { + id: editButton + text: _root.editing ? qsTr("Done") : qsTr("Edit") + enabled: (_root.editing && _root.areasCorrect) || !_root.editing + onClicked: { + if (_root.editing) { + _root.missionItem.stopEditing() + } else { + _root.missionItem.startEditing() + } + } Layout.fillWidth: true - visible: !_root.areasCorrect } - ExclusiveGroup { - id: areaGroup - } - - Repeater { - id: areaSelector + GridLayout { - property int selectedIndex: -1 + width: availableWidth + columnSpacing: _margin + Layout.fillWidth: true + rowSpacing: _margin + columns: 2 + enabled: _root.editing - model: _missionItem.areaData.areaList - delegate: QGCRadioButton { - text: object.objectName - checkable: _root.checked + QGCLabel { + text: _root.errorString + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignLeft + color: "orange" + Layout.columnSpan: parent.columns Layout.fillWidth: true - Layout.columnSpan: 2 - - onCheckedChanged: { - if (checked) { - areaSelector.selectedIndex = index - } - } + visible: !_root.areasCorrect + } - Component.onCompleted: { - if (index === 0) { - checked = true - } - object.interactive = Qt.binding(function () { - return checked && _root.checked - }) - } + ExclusiveGroup { + id: areaGroup } - } // area Repeater - ColumnLayout { - id: editorParent - Layout.fillWidth: true - Layout.maximumWidth: parent.width - Layout.columnSpan: 2 - } + Repeater { + id: areaSelector - Repeater { - id: areaEditorRepeater - - Layout.maximumWidth: parent.width - - model: _missionItem.areaData.areaList - delegate: Item { - id: editor - visible: index == areaSelector.selectedIndex - - property var _visualItem: undefined - property var geoArea: object - - Component.onCompleted: { - if (geoArea.editorQML && !_visualItem) { - var component = Qt.createComponent(geoArea.editorQML) - if (component.status === Component.Error) { - console.log("Error loading Qml: ", - geoArea.editorQML, - component.errorString()) - } else { - _visualItem = component.createObject(editorParent, { - "geoArea": editor.geoArea, - "visible": Qt.binding( - function () { - return editor.visible - }), - "availableWidth": Qt.binding(function () { - return editorParent.width - }) - }) + property int selectedIndex: -1 + + model: _missionItem.areaData.areaList + delegate: QGCRadioButton { + text: object.objectName + Layout.fillWidth: true + Layout.columnSpan: 2 + + onCheckedChanged: { + if (checked) { + areaSelector.selectedIndex = index } } - } - Component.onDestruction: { - if (_visualItem) { - _visualItem.destroy() + Component.onCompleted: { + if (index === 0) { + checked = true + } + object.interactive = Qt.binding(function () { + return checked && _root.editing && _missionItem.isCurrentItem + }) } } - } // editor - } // areaEditorRepeater + } // area Repeater - SectionHeader { - id: commandsHeader - Layout.fillWidth: true - Layout.columnSpan: parent.columns - text: qsTr("Commands") - } + ColumnLayout { + id: editorParent + Layout.fillWidth: true + Layout.maximumWidth: parent.width + Layout.columnSpan: 2 + } - GridLayout { - columnSpacing: _margin - rowSpacing: _margin - columns: 2 - Layout.columnSpan: 2 - Layout.fillWidth: true - visible: commandsHeader.checked + Repeater { + id: areaEditorRepeater + + Layout.maximumWidth: parent.width + + model: _missionItem.areaData.areaList + delegate: Item { + id: editor + visible: index == areaSelector.selectedIndex + + property var _visualItem: undefined + property var geoArea: object + + Component.onCompleted: { + if (geoArea.editorQML && !_visualItem) { + var component = Qt.createComponent( + geoArea.editorQML) + if (component.status === Component.Error) { + console.log("Error loading Qml: ", + geoArea.editorQML, + component.errorString()) + } else { + _visualItem = component.createObject( + editorParent, { + "geoArea": editor.geoArea, + "visible": Qt.binding( + function () { + return editor.visible + }), + "availableWidth": Qt.binding( + function () { + return editorParent.width + }) + }) + } + } + } + + Component.onDestruction: { + if (_visualItem) { + _visualItem.destroy() + } + } + } // editor + } // areaEditorRepeater - QGCButton { - text: "Intersection" - enabled: _root.checked + SectionHeader { + id: commandsHeader Layout.fillWidth: true Layout.columnSpan: parent.columns - onClicked: { - _areaData.intersection() - } + text: qsTr("Commands") } - QGCButton { - text: "Reset" - onClicked: { - missionItem.reset() - } + GridLayout { + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + Layout.columnSpan: 2 Layout.fillWidth: true - } + visible: commandsHeader.checked + + QGCButton { + text: "Intersection" + Layout.fillWidth: true + Layout.columnSpan: parent.columns + onClicked: { + _areaData.intersection() + } + } - QGCButton { - text: "Abort" - onClicked: { - _root.abort() + QGCButton { + text: "Reset" + Layout.fillWidth: true + Layout.columnSpan: parent.columns + onClicked: { + missionItem.reset() + } } - Layout.fillWidth: true } - } - SectionHeader { - id: hintHeader - Layout.fillWidth: true - Layout.columnSpan: parent.columns - text: qsTr("Hints") - } + SectionHeader { + id: hintHeader + Layout.fillWidth: true + Layout.columnSpan: parent.columns + text: qsTr("Hints") + } - GridLayout { - columnSpacing: _margin - rowSpacing: _margin - columns: 2 - Layout.columnSpan: 2 - Layout.fillWidth: true - visible: hintHeader.checked + GridLayout { + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + Layout.columnSpan: 2 + Layout.fillWidth: true + visible: hintHeader.checked - QGCLabel { - id: hintLabel - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignLeft - text: qsTr("Use the Intersection button to clip the Measurement Area(s). + QGCLabel { + id: hintLabel + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignLeft + text: qsTr("Use the Intersection button to clip the Measurement Area(s). Use the Reset button to restore the areas to the state before entering this tab. Use the Abort button to reset the areas and leave the tab.") - Layout.fillWidth: true - Layout.columnSpan: parent.columns + Layout.fillWidth: true + Layout.columnSpan: parent.columns + } } - } - Timer { - id: areasCorrectTimer - running: false - interval: 100 - repeat: true - - onTriggered: { - _root.areasCorrect = _missionItem.areaData.isCorrect( - false /*show gui message*/ - ) - if (!_root.areasCorrect) { - _root.errorString = _missionItem.areaData.errorString - } else { - _root.errorString = "" + Timer { + id: areasCorrectTimer + running: false + interval: 100 + repeat: true + + onTriggered: { + _root.areasCorrect = _missionItem.areaData.isCorrect( + false /*show gui message*/ + ) + if (!_root.areasCorrect) { + _root.errorString = _missionItem.areaData.errorString + } else { + _root.errorString = "" + } } } - } - Settings { - property alias showHint: hintHeader.checked - } + Settings { + property alias showHint: hintHeader.checked + } + } // GridLayout } // GridLayout } // Rectangle diff --git a/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml b/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml index dad0ecdd4e7a8d4322e116d436c1e9746b8db2b7..f5efc17514caf29eaa770d13feb115a909151e71 100644 --- a/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml +++ b/src/MeasurementComplexItem/qml/MeasurementItemEditor.qml @@ -49,13 +49,12 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right - enabled: !editing || editing && correct + enabled: !editing readonly property int areaEditorIndex: 0 readonly property int parameterEditorIndex: 1 readonly property int nemoEditorIndex: 2 property bool editing: _missionItem.editing - property bool correct: false Component.onCompleted: currentIndex = editing ? areaEditorIndex : parameterEditorIndex @@ -71,47 +70,18 @@ Rectangle { icon.source: "qrc:/res/fish.svg" icon.height: ScreenTools.defaultFontPixelHeight } - - onEditingChanged: { - if (editing) { - areasCorrectTimer.start() - } else { - areasCorrectTimer.stop() - } - } - - onCurrentIndexChanged: { - if (currentIndex === areaEditorIndex) { - _missionItem.startEditing() - } else { - _missionItem.stopEditing() - } - } - - Timer { - id: areasCorrectTimer - running: false - interval: 100 - repeat: true - - onTriggered: { - tabBar.correct = _missionItem.areaData.isCorrect( - false /*show gui message*/ - ) - } - } } MCI.AreaDataEditor { id: areaEditor visible: tabBar.currentIndex === tabBar.areaEditorIndex - checked: visible missionItem: _root._missionItem availableWidth: mainColumn.width - onAbort: { - missionItem.abortEditing() - tabBar.currentIndex = tabBar.parameterEditorIndex + onVisibleChanged:{ + if (visible){ + _missionItem.startEditing() + } } } @@ -119,7 +89,6 @@ Rectangle { id: parameterEditor visible: tabBar.currentIndex === tabBar.parameterEditorIndex - checked: visible missionItem: _root._missionItem availableWidth: mainColumn.width } @@ -128,7 +97,6 @@ Rectangle { id: nemoEditor visible: tabBar.currentIndex === tabBar.nemoEditorIndex - checked: visible missionItem: _root._missionItem availableWidth: mainColumn.width } diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index 3b74b6cb6bf73fd751892095fbae80f922d7188f..e672ad47e36794e0b64ca090ab6db5e1c00540e9 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -8,651 +8,722 @@ ****************************************************************************/ #include "PlanMasterController.h" -#include "QGCApplication.h" -#include "QGCCorePlugin.h" -#include "MultiVehicleManager.h" -#include "SettingsManager.h" #include "AppSettings.h" +#include "BlankPlanCreator.h" +#include "CorridorScanPlanCreator.h" #include "JsonHelper.h" -#include "MissionManager.h" #include "KMLPlanDomDocument.h" -#include "SurveyPlanCreator.h" +#include "MeasurementPlanCreator.h" +#include "MissionManager.h" +#include "MultiVehicleManager.h" +#include "QGCApplication.h" +#include "QGCCorePlugin.h" +#include "SettingsManager.h" #include "StructureScanPlanCreator.h" -#include "CorridorScanPlanCreator.h" -#include "BlankPlanCreator.h" +#include "SurveyPlanCreator.h" #if defined(QGC_AIRMAP_ENABLED) #include "AirspaceFlightPlanProvider.h" #endif #include -#include #include +#include QGC_LOGGING_CATEGORY(PlanMasterControllerLog, "PlanMasterControllerLog") -const int PlanMasterController::kPlanFileVersion = 1; -const char* PlanMasterController::kPlanFileType = "Plan"; -const char* PlanMasterController::kJsonMissionObjectKey = "mission"; -const char* PlanMasterController::kJsonGeoFenceObjectKey = "geoFence"; -const char* PlanMasterController::kJsonRallyPointsObjectKey = "rallyPoints"; +const int PlanMasterController::kPlanFileVersion = 1; +const char *PlanMasterController::kPlanFileType = "Plan"; +const char *PlanMasterController::kJsonMissionObjectKey = "mission"; +const char *PlanMasterController::kJsonGeoFenceObjectKey = "geoFence"; +const char *PlanMasterController::kJsonRallyPointsObjectKey = "rallyPoints"; -PlanMasterController::PlanMasterController(QObject* parent) - : QObject (parent) - , _multiVehicleMgr (qgcApp()->toolbox()->multiVehicleManager()) - , _controllerVehicle (new Vehicle(Vehicle::MAV_AUTOPILOT_TRACK, Vehicle::MAV_TYPE_TRACK, qgcApp()->toolbox()->firmwarePluginManager(), this)) - , _managerVehicle (_controllerVehicle) - , _missionController (this) - , _geoFenceController (this) - , _rallyPointController (this) -{ - _commonInit(); +PlanMasterController::PlanMasterController(QObject *parent) + : QObject(parent), + _multiVehicleMgr(qgcApp()->toolbox()->multiVehicleManager()), + _controllerVehicle( + new Vehicle(Vehicle::MAV_AUTOPILOT_TRACK, Vehicle::MAV_TYPE_TRACK, + qgcApp()->toolbox()->firmwarePluginManager(), this)), + _managerVehicle(_controllerVehicle), _missionController(this), + _geoFenceController(this), _rallyPointController(this) { + _commonInit(); } #ifdef QT_DEBUG -PlanMasterController::PlanMasterController(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, QObject* parent) - : QObject (parent) - , _multiVehicleMgr (qgcApp()->toolbox()->multiVehicleManager()) - , _controllerVehicle (new Vehicle(firmwareType, vehicleType, qgcApp()->toolbox()->firmwarePluginManager())) - , _managerVehicle (_controllerVehicle) - , _missionController (this) - , _geoFenceController (this) - , _rallyPointController (this) -{ - _commonInit(); +PlanMasterController::PlanMasterController(MAV_AUTOPILOT firmwareType, + MAV_TYPE vehicleType, + QObject *parent) + : QObject(parent), + _multiVehicleMgr(qgcApp()->toolbox()->multiVehicleManager()), + _controllerVehicle( + new Vehicle(firmwareType, vehicleType, + qgcApp()->toolbox()->firmwarePluginManager())), + _managerVehicle(_controllerVehicle), _missionController(this), + _geoFenceController(this), _rallyPointController(this) { + _commonInit(); } #endif -void PlanMasterController::_commonInit(void) -{ - connect(&_missionController, &MissionController::dirtyChanged, this, &PlanMasterController::dirtyChanged); - connect(&_geoFenceController, &GeoFenceController::dirtyChanged, this, &PlanMasterController::dirtyChanged); - connect(&_rallyPointController, &RallyPointController::dirtyChanged, this, &PlanMasterController::dirtyChanged); +void PlanMasterController::_commonInit(void) { + connect(&_missionController, &MissionController::dirtyChanged, this, + &PlanMasterController::dirtyChanged); + connect(&_geoFenceController, &GeoFenceController::dirtyChanged, this, + &PlanMasterController::dirtyChanged); + connect(&_rallyPointController, &RallyPointController::dirtyChanged, this, + &PlanMasterController::dirtyChanged); - connect(&_missionController, &MissionController::containsItemsChanged, this, &PlanMasterController::containsItemsChanged); - connect(&_geoFenceController, &GeoFenceController::containsItemsChanged, this, &PlanMasterController::containsItemsChanged); - connect(&_rallyPointController, &RallyPointController::containsItemsChanged, this, &PlanMasterController::containsItemsChanged); + connect(&_missionController, &MissionController::containsItemsChanged, this, + &PlanMasterController::containsItemsChanged); + connect(&_geoFenceController, &GeoFenceController::containsItemsChanged, this, + &PlanMasterController::containsItemsChanged); + connect(&_rallyPointController, &RallyPointController::containsItemsChanged, + this, &PlanMasterController::containsItemsChanged); - connect(&_missionController, &MissionController::syncInProgressChanged, this, &PlanMasterController::syncInProgressChanged); - connect(&_geoFenceController, &GeoFenceController::syncInProgressChanged, this, &PlanMasterController::syncInProgressChanged); - connect(&_rallyPointController, &RallyPointController::syncInProgressChanged, this, &PlanMasterController::syncInProgressChanged); + connect(&_missionController, &MissionController::syncInProgressChanged, this, + &PlanMasterController::syncInProgressChanged); + connect(&_geoFenceController, &GeoFenceController::syncInProgressChanged, + this, &PlanMasterController::syncInProgressChanged); + connect(&_rallyPointController, &RallyPointController::syncInProgressChanged, + this, &PlanMasterController::syncInProgressChanged); - // Offline vehicle can change firmware/vehicle type - connect(_controllerVehicle, &Vehicle::vehicleTypeChanged, this, &PlanMasterController::_updatePlanCreatorsList); + // Offline vehicle can change firmware/vehicle type + connect(_controllerVehicle, &Vehicle::vehicleTypeChanged, this, + &PlanMasterController::_updatePlanCreatorsList); } +PlanMasterController::~PlanMasterController() {} -PlanMasterController::~PlanMasterController() -{ - -} - -void PlanMasterController::start(void) -{ - _missionController.start (_flyView); - _geoFenceController.start (_flyView); - _rallyPointController.start (_flyView); +void PlanMasterController::start(void) { + _missionController.start(_flyView); + _geoFenceController.start(_flyView); + _rallyPointController.start(_flyView); - _activeVehicleChanged(_multiVehicleMgr->activeVehicle()); - connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &PlanMasterController::_activeVehicleChanged); + _activeVehicleChanged(_multiVehicleMgr->activeVehicle()); + connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, + &PlanMasterController::_activeVehicleChanged); - _updatePlanCreatorsList(); + _updatePlanCreatorsList(); #if defined(QGC_AIRMAP_ENABLED) - //-- This assumes there is one single instance of PlanMasterController in edit mode. - if(!flyView) { - // Wait for signal confirming AirMap client connection before starting flight planning - connect(qgcApp()->toolbox()->airspaceManager(), &AirspaceManager::connectStatusChanged, this, &PlanMasterController::_startFlightPlanning); - } + //-- This assumes there is one single instance of PlanMasterController in edit + //mode. + if (!flyView) { + // Wait for signal confirming AirMap client connection before starting + // flight planning + connect(qgcApp()->toolbox()->airspaceManager(), + &AirspaceManager::connectStatusChanged, this, + &PlanMasterController::_startFlightPlanning); + } #endif } -void PlanMasterController::startStaticActiveVehicle(Vehicle* vehicle, bool deleteWhenSendCompleted) -{ - _flyView = true; - _deleteWhenSendCompleted = deleteWhenSendCompleted; - _missionController.start(_flyView); - _geoFenceController.start(_flyView); - _rallyPointController.start(_flyView); - _activeVehicleChanged(vehicle); -} - -void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle) -{ - if (_managerVehicle == activeVehicle) { - // We are already setup for this vehicle - return; - } - - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged" << activeVehicle; - - if (_managerVehicle) { - // Disconnect old vehicle. Be careful of wildcarding disconnect too much since _managerVehicle may equal _controllerVehicle - disconnect(_managerVehicle->missionManager(), nullptr, nullptr, nullptr); - disconnect(_managerVehicle->geoFenceManager(), nullptr, nullptr, nullptr); - disconnect(_managerVehicle->rallyPointManager(), nullptr, nullptr, nullptr); - } - - bool newOffline = false; - if (activeVehicle == nullptr) { - // Since there is no longer an active vehicle we use the offline controller vehicle as the manager vehicle - _managerVehicle = _controllerVehicle; - newOffline = true; +void PlanMasterController::startStaticActiveVehicle( + Vehicle *vehicle, bool deleteWhenSendCompleted) { + _flyView = true; + _deleteWhenSendCompleted = deleteWhenSendCompleted; + _missionController.start(_flyView); + _geoFenceController.start(_flyView); + _rallyPointController.start(_flyView); + _activeVehicleChanged(vehicle); +} + +void PlanMasterController::_activeVehicleChanged(Vehicle *activeVehicle) { + if (_managerVehicle == activeVehicle) { + // We are already setup for this vehicle + return; + } + + qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged" << activeVehicle; + + if (_managerVehicle) { + // Disconnect old vehicle. Be careful of wildcarding disconnect too much + // since _managerVehicle may equal _controllerVehicle + disconnect(_managerVehicle->missionManager(), nullptr, nullptr, nullptr); + disconnect(_managerVehicle->geoFenceManager(), nullptr, nullptr, nullptr); + disconnect(_managerVehicle->rallyPointManager(), nullptr, nullptr, nullptr); + } + + bool newOffline = false; + if (activeVehicle == nullptr) { + // Since there is no longer an active vehicle we use the offline controller + // vehicle as the manager vehicle + _managerVehicle = _controllerVehicle; + newOffline = true; + } else { + newOffline = false; + _managerVehicle = activeVehicle; + + // Update controllerVehicle to the currently connected vehicle + AppSettings *appSettings = + qgcApp()->toolbox()->settingsManager()->appSettings(); + appSettings->offlineEditingFirmwareClass()->setRawValue( + QGCMAVLink::firmwareClass(_managerVehicle->firmwareType())); + appSettings->offlineEditingVehicleClass()->setRawValue( + QGCMAVLink::vehicleClass(_managerVehicle->vehicleType())); + + // We use these signals to sequence upload and download to the multiple + // controller/managers + connect(_managerVehicle->missionManager(), + &MissionManager::newMissionItemsAvailable, this, + &PlanMasterController::_loadMissionComplete); + connect(_managerVehicle->geoFenceManager(), &GeoFenceManager::loadComplete, + this, &PlanMasterController::_loadGeoFenceComplete); + connect(_managerVehicle->rallyPointManager(), + &RallyPointManager::loadComplete, this, + &PlanMasterController::_loadRallyPointsComplete); + connect(_managerVehicle->missionManager(), &MissionManager::sendComplete, + this, &PlanMasterController::_sendMissionComplete); + connect(_managerVehicle->geoFenceManager(), &GeoFenceManager::sendComplete, + this, &PlanMasterController::_sendGeoFenceComplete); + connect(_managerVehicle->rallyPointManager(), + &RallyPointManager::sendComplete, this, + &PlanMasterController::_sendRallyPointsComplete); + } + + _offline = newOffline; + emit offlineChanged(offline()); + emit managerVehicleChanged(_managerVehicle); + + if (_flyView) { + // We are in the Fly View + if (newOffline) { + // No active vehicle, clear mission + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Fly View - No active vehicle, clearing " + "stale plan"; + removeAll(); } else { - newOffline = false; - _managerVehicle = activeVehicle; - - // Update controllerVehicle to the currently connected vehicle - AppSettings* appSettings = qgcApp()->toolbox()->settingsManager()->appSettings(); - appSettings->offlineEditingFirmwareClass()->setRawValue(QGCMAVLink::firmwareClass(_managerVehicle->firmwareType())); - appSettings->offlineEditingVehicleClass()->setRawValue(QGCMAVLink::vehicleClass(_managerVehicle->vehicleType())); - - // We use these signals to sequence upload and download to the multiple controller/managers - connect(_managerVehicle->missionManager(), &MissionManager::newMissionItemsAvailable, this, &PlanMasterController::_loadMissionComplete); - connect(_managerVehicle->geoFenceManager(), &GeoFenceManager::loadComplete, this, &PlanMasterController::_loadGeoFenceComplete); - connect(_managerVehicle->rallyPointManager(), &RallyPointManager::loadComplete, this, &PlanMasterController::_loadRallyPointsComplete); - connect(_managerVehicle->missionManager(), &MissionManager::sendComplete, this, &PlanMasterController::_sendMissionComplete); - connect(_managerVehicle->geoFenceManager(), &GeoFenceManager::sendComplete, this, &PlanMasterController::_sendGeoFenceComplete); - connect(_managerVehicle->rallyPointManager(), &RallyPointManager::sendComplete, this, &PlanMasterController::_sendRallyPointsComplete); - } - - _offline = newOffline; - emit offlineChanged(offline()); - emit managerVehicleChanged(_managerVehicle); - - if (_flyView) { - // We are in the Fly View + // Fly view has changed to a new active vehicle, update to show correct + // mission + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Fly View - New active vehicle, loading " + "new plan from manager vehicle"; + _showPlanFromManagerVehicle(); + } + } else { + // We are in the Plan view. + if (containsItems()) { + // The plan view has a stale plan in it + if (dirty()) { + // Plan is dirty, the user must decide what to do in all cases + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Plan View - Previous dirty plan exists, " + "no new active vehicle, sending " + "promptForPlanUsageOnVehicleChange signal"; + emit promptForPlanUsageOnVehicleChange(); + } else { + // Plan is not dirty if (newOffline) { - // No active vehicle, clear mission - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly View - No active vehicle, clearing stale plan"; - removeAll(); + // The active vehicle went away with no new active vehicle + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Plan View - Previous clean plan " + "exists, no new active vehicle, clear stale plan"; + removeAll(); } else { - // Fly view has changed to a new active vehicle, update to show correct mission - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Fly View - New active vehicle, loading new plan from manager vehicle"; - _showPlanFromManagerVehicle(); + // We are transitioning from one active vehicle to another. Show the + // plan from the new vehicle. + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Plan View - Previous clean plan " + "exists, new active vehicle, loading from new manager vehicle"; + _showPlanFromManagerVehicle(); } + } } else { - // We are in the Plan view. - if (containsItems()) { - // The plan view has a stale plan in it - if (dirty()) { - // Plan is dirty, the user must decide what to do in all cases - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan View - Previous dirty plan exists, no new active vehicle, sending promptForPlanUsageOnVehicleChange signal"; - emit promptForPlanUsageOnVehicleChange(); - } else { - // Plan is not dirty - if (newOffline) { - // The active vehicle went away with no new active vehicle - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan View - Previous clean plan exists, no new active vehicle, clear stale plan"; - removeAll(); - } else { - // We are transitioning from one active vehicle to another. Show the plan from the new vehicle. - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan View - Previous clean plan exists, new active vehicle, loading from new manager vehicle"; - _showPlanFromManagerVehicle(); - } - } - } else { - // There is no previous Plan in the view - if (newOffline) { - // Nothing special to do in this case - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan View - No previous plan, no longer connected to vehicle, nothing to do"; - } else { - // Just show the plan from the new vehicle - qCDebug(PlanMasterControllerLog) << "_activeVehicleChanged: Plan View - No previous plan, new active vehicle, loading from new manager vehicle"; - _showPlanFromManagerVehicle(); - } - } - } - - // Vehicle changed so we need to signal everything - emit containsItemsChanged(containsItems()); - emit syncInProgressChanged(); - emit dirtyChanged(dirty()); - - _updatePlanCreatorsList(); -} - -void PlanMasterController::loadFromVehicle(void) -{ - if (_managerVehicle->vehicleLinkManager()->primaryLink()->linkConfiguration()->isHighLatency()) { - qgcApp()->showAppMessage(tr("Download not supported on high latency links.")); - return; - } - - if (offline()) { - qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while offline"; - } else if (_flyView) { - qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called from Fly view"; - } else if (syncInProgress()) { - qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while syncInProgress"; + // There is no previous Plan in the view + if (newOffline) { + // Nothing special to do in this case + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Plan View - No previous plan, no longer " + "connected to vehicle, nothing to do"; + } else { + // Just show the plan from the new vehicle + qCDebug(PlanMasterControllerLog) + << "_activeVehicleChanged: Plan View - No previous plan, new " + "active vehicle, loading from new manager vehicle"; + _showPlanFromManagerVehicle(); + } + } + } + + // Vehicle changed so we need to signal everything + emit containsItemsChanged(containsItems()); + emit syncInProgressChanged(); + emit dirtyChanged(dirty()); + + _updatePlanCreatorsList(); +} + +void PlanMasterController::loadFromVehicle(void) { + if (_managerVehicle->vehicleLinkManager() + ->primaryLink() + ->linkConfiguration() + ->isHighLatency()) { + qgcApp()->showAppMessage( + tr("Download not supported on high latency links.")); + return; + } + + if (offline()) { + qCWarning(PlanMasterControllerLog) + << "PlanMasterController::loadFromVehicle called while offline"; + } else if (_flyView) { + qCWarning(PlanMasterControllerLog) + << "PlanMasterController::loadFromVehicle called from Fly view"; + } else if (syncInProgress()) { + qCWarning(PlanMasterControllerLog) + << "PlanMasterController::loadFromVehicle called while syncInProgress"; + } else { + _loadGeoFence = true; + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::loadFromVehicle calling " + "_missionController.loadFromVehicle"; + _missionController.loadFromVehicle(); + setDirty(false); + } +} + +void PlanMasterController::_loadMissionComplete(void) { + if (!_flyView && _loadGeoFence) { + _loadGeoFence = false; + _loadRallyPoints = true; + if (_geoFenceController.supported()) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_loadMissionComplete calling " + "_geoFenceController.loadFromVehicle"; + _geoFenceController.loadFromVehicle(); } else { - _loadGeoFence = true; - qCDebug(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle calling _missionController.loadFromVehicle"; - _missionController.loadFromVehicle(); - setDirty(false); - } -} - - -void PlanMasterController::_loadMissionComplete(void) -{ - if (!_flyView && _loadGeoFence) { - _loadGeoFence = false; - _loadRallyPoints = true; - if (_geoFenceController.supported()) { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete calling _geoFenceController.loadFromVehicle"; - _geoFenceController.loadFromVehicle(); - } else { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete GeoFence not supported skipping"; - _geoFenceController.removeAll(); - _loadGeoFenceComplete(); - } - setDirty(false); - } -} - -void PlanMasterController::_loadGeoFenceComplete(void) -{ - if (!_flyView && _loadRallyPoints) { - _loadRallyPoints = false; - if (_rallyPointController.supported()) { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadGeoFenceComplete calling _rallyPointController.loadFromVehicle"; - _rallyPointController.loadFromVehicle(); - } else { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadMissionComplete Rally Points not supported skipping"; - _rallyPointController.removeAll(); - _loadRallyPointsComplete(); - } - setDirty(false); + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_loadMissionComplete GeoFence not " + "supported skipping"; + _geoFenceController.removeAll(); + _loadGeoFenceComplete(); + } + setDirty(false); + } +} + +void PlanMasterController::_loadGeoFenceComplete(void) { + if (!_flyView && _loadRallyPoints) { + _loadRallyPoints = false; + if (_rallyPointController.supported()) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_loadGeoFenceComplete calling " + "_rallyPointController.loadFromVehicle"; + _rallyPointController.loadFromVehicle(); + } else { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_loadMissionComplete Rally Points not " + "supported skipping"; + _rallyPointController.removeAll(); + _loadRallyPointsComplete(); } + setDirty(false); + } } -void PlanMasterController::_loadRallyPointsComplete(void) -{ - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_loadRallyPointsComplete"; +void PlanMasterController::_loadRallyPointsComplete(void) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_loadRallyPointsComplete"; } -void PlanMasterController::_sendMissionComplete(void) -{ - if (_sendGeoFence) { - _sendGeoFence = false; - _sendRallyPoints = true; - if (_geoFenceController.supported()) { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start GeoFence sendToVehicle"; - _geoFenceController.sendToVehicle(); - } else { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle GeoFence not supported skipping"; - _sendGeoFenceComplete(); - } - setDirty(false); +void PlanMasterController::_sendMissionComplete(void) { + if (_sendGeoFence) { + _sendGeoFence = false; + _sendRallyPoints = true; + if (_geoFenceController.supported()) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle start GeoFence sendToVehicle"; + _geoFenceController.sendToVehicle(); + } else { + qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle " + "GeoFence not supported skipping"; + _sendGeoFenceComplete(); } + setDirty(false); + } } -void PlanMasterController::_sendGeoFenceComplete(void) -{ - if (_sendRallyPoints) { - _sendRallyPoints = false; - if (_rallyPointController.supported()) { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start rally sendToVehicle"; - _rallyPointController.sendToVehicle(); - } else { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle Rally Points not support skipping"; - _sendRallyPointsComplete(); - } +void PlanMasterController::_sendGeoFenceComplete(void) { + if (_sendRallyPoints) { + _sendRallyPoints = false; + if (_rallyPointController.supported()) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle start rally sendToVehicle"; + _rallyPointController.sendToVehicle(); + } else { + qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle " + "Rally Points not support skipping"; + _sendRallyPointsComplete(); } + } } -void PlanMasterController::_sendRallyPointsComplete(void) -{ - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle Rally Point send complete"; - if (_deleteWhenSendCompleted) { - this->deleteLater(); - } +void PlanMasterController::_sendRallyPointsComplete(void) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle Rally Point send complete"; + if (_deleteWhenSendCompleted) { + this->deleteLater(); + } } #if defined(QGC_AIRMAP_ENABLED) void PlanMasterController::_startFlightPlanning(void) { - if (qgcApp()->toolbox()->airspaceManager()->connected()) { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::_startFlightPlanning client connected, start flight planning"; - qgcApp()->toolbox()->airspaceManager()->flightPlan()->startFlightPlanning(this); - } + if (qgcApp()->toolbox()->airspaceManager()->connected()) { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::_startFlightPlanning client connected, start " + "flight planning"; + qgcApp()->toolbox()->airspaceManager()->flightPlan()->startFlightPlanning( + this); + } } #endif -void PlanMasterController::sendToVehicle(void) -{ - if (_managerVehicle->vehicleLinkManager()->primaryLink()->linkConfiguration()->isHighLatency()) { - qgcApp()->showAppMessage(tr("Upload not supported on high latency links.")); - return; - } - - if (offline()) { - qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while offline"; - } else if (syncInProgress()) { - qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while syncInProgress"; +void PlanMasterController::sendToVehicle(void) { + if (_managerVehicle->vehicleLinkManager() + ->primaryLink() + ->linkConfiguration() + ->isHighLatency()) { + qgcApp()->showAppMessage(tr("Upload not supported on high latency links.")); + return; + } + + if (offline()) { + qCWarning(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle called while offline"; + } else if (syncInProgress()) { + qCWarning(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle called while syncInProgress"; + } else { + qCDebug(PlanMasterControllerLog) + << "PlanMasterController::sendToVehicle start mission sendToVehicle"; + _sendGeoFence = true; + _missionController.sendToVehicle(); + setDirty(false); + } +} + +void PlanMasterController::loadFromFile(const QString &filename) { + QString errorString; + QString errorMessage = + tr("Error loading Plan file (%1). %2").arg(filename).arg("%1"); + + if (filename.isEmpty()) { + return; + } + + QFileInfo fileInfo(filename); + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + errorString = file.errorString() + QStringLiteral(" ") + filename; + qgcApp()->showAppMessage(errorMessage.arg(errorString)); + return; + } + + bool success = false; + if (fileInfo.suffix() == AppSettings::planFileExtension) { + QJsonDocument jsonDoc; + QByteArray bytes = file.readAll(); + + if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); + return; + } + + QJsonObject json = jsonDoc.object(); + //-- Allow plugins to pre process the load + qgcApp()->toolbox()->corePlugin()->preLoadFromJson(this, json); + + int version; + if (!JsonHelper::validateExternalQGCJsonFile( + json, kPlanFileType, kPlanFileVersion, kPlanFileVersion, version, + errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); + return; + } + + QList rgKeyInfo = { + {kJsonMissionObjectKey, QJsonValue::Object, true}, + {kJsonGeoFenceObjectKey, QJsonValue::Object, true}, + {kJsonRallyPointsObjectKey, QJsonValue::Object, true}, + }; + if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); + return; + } + + if (!_missionController.load(json[kJsonMissionObjectKey].toObject(), + errorString) || + !_geoFenceController.load(json[kJsonGeoFenceObjectKey].toObject(), + errorString) || + !_rallyPointController.load(json[kJsonRallyPointsObjectKey].toObject(), + errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); } else { - qCDebug(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle start mission sendToVehicle"; - _sendGeoFence = true; - _missionController.sendToVehicle(); - setDirty(false); - } -} - -void PlanMasterController::loadFromFile(const QString& filename) -{ - QString errorString; - QString errorMessage = tr("Error loading Plan file (%1). %2").arg(filename).arg("%1"); - - if (filename.isEmpty()) { - return; + //-- Allow plugins to post process the load + qgcApp()->toolbox()->corePlugin()->postLoadFromJson(this, json); + success = true; } - - QFileInfo fileInfo(filename); - QFile file(filename); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - errorString = file.errorString() + QStringLiteral(" ") + filename; - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - return; - } - - bool success = false; - if(fileInfo.suffix() == AppSettings::planFileExtension) { - QJsonDocument jsonDoc; - QByteArray bytes = file.readAll(); - - if (!JsonHelper::isJsonFile(bytes, jsonDoc, errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - return; - } - - QJsonObject json = jsonDoc.object(); - //-- Allow plugins to pre process the load - qgcApp()->toolbox()->corePlugin()->preLoadFromJson(this, json); - - int version; - if (!JsonHelper::validateExternalQGCJsonFile(json, kPlanFileType, kPlanFileVersion, kPlanFileVersion, version, errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - return; - } - - QList rgKeyInfo = { - { kJsonMissionObjectKey, QJsonValue::Object, true }, - { kJsonGeoFenceObjectKey, QJsonValue::Object, true }, - { kJsonRallyPointsObjectKey, QJsonValue::Object, true }, - }; - if (!JsonHelper::validateKeys(json, rgKeyInfo, errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - return; - } - - if (!_missionController.load(json[kJsonMissionObjectKey].toObject(), errorString) || - !_geoFenceController.load(json[kJsonGeoFenceObjectKey].toObject(), errorString) || - !_rallyPointController.load(json[kJsonRallyPointsObjectKey].toObject(), errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - } else { - //-- Allow plugins to post process the load - qgcApp()->toolbox()->corePlugin()->postLoadFromJson(this, json); - success = true; - } - } else if (fileInfo.suffix() == AppSettings::missionFileExtension) { - if (!_missionController.loadJsonFile(file, errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - } else { - success = true; - } - } else if (fileInfo.suffix() == AppSettings::waypointsFileExtension || fileInfo.suffix() == QStringLiteral("txt")) { - if (!_missionController.loadTextFile(file, errorString)) { - qgcApp()->showAppMessage(errorMessage.arg(errorString)); - } else { - success = true; - } + } else if (fileInfo.suffix() == AppSettings::missionFileExtension) { + if (!_missionController.loadJsonFile(file, errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); } else { - //-- TODO: What then? + success = true; } - - if(success){ - _currentPlanFile = QString::asprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), fileInfo.completeBaseName().toLocal8Bit().data(), AppSettings::planFileExtension); + } else if (fileInfo.suffix() == AppSettings::waypointsFileExtension || + fileInfo.suffix() == QStringLiteral("txt")) { + if (!_missionController.loadTextFile(file, errorString)) { + qgcApp()->showAppMessage(errorMessage.arg(errorString)); } else { - _currentPlanFile.clear(); - } + success = true; + } + } else { + //-- TODO: What then? + } + + if (success) { + _currentPlanFile = + QString::asprintf("%s/%s.%s", fileInfo.path().toLocal8Bit().data(), + fileInfo.completeBaseName().toLocal8Bit().data(), + AppSettings::planFileExtension); + } else { + _currentPlanFile.clear(); + } + emit currentPlanFileChanged(); + + if (!offline()) { + setDirty(true); + } +} + +QJsonDocument PlanMasterController::saveToJson() { + QJsonObject planJson; + qgcApp()->toolbox()->corePlugin()->preSaveToJson(this, planJson); + QJsonObject missionJson; + QJsonObject fenceJson; + QJsonObject rallyJson; + JsonHelper::saveQGCJsonFileHeader(planJson, kPlanFileType, kPlanFileVersion); + //-- Allow plugin to preemptly add its own keys to mission + qgcApp()->toolbox()->corePlugin()->preSaveToMissionJson(this, missionJson); + _missionController.save(missionJson); + //-- Allow plugin to add its own keys to mission + qgcApp()->toolbox()->corePlugin()->postSaveToMissionJson(this, missionJson); + _geoFenceController.save(fenceJson); + _rallyPointController.save(rallyJson); + planJson[kJsonMissionObjectKey] = missionJson; + planJson[kJsonGeoFenceObjectKey] = fenceJson; + planJson[kJsonRallyPointsObjectKey] = rallyJson; + qgcApp()->toolbox()->corePlugin()->postSaveToJson(this, planJson); + return QJsonDocument(planJson); +} + +void PlanMasterController::saveToCurrent() { + if (!_currentPlanFile.isEmpty()) { + saveToFile(_currentPlanFile); + } +} + +void PlanMasterController::saveToFile(const QString &filename) { + if (filename.isEmpty()) { + return; + } + + QString planFilename = filename; + if (!QFileInfo(filename).fileName().contains(".")) { + planFilename += QString(".%1").arg(fileExtension()); + } + + QFile file(planFilename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qgcApp()->showAppMessage( + tr("Plan save error %1 : %2").arg(filename).arg(file.errorString())); + _currentPlanFile.clear(); emit currentPlanFileChanged(); - - if (!offline()) { - setDirty(true); - } -} - -QJsonDocument PlanMasterController::saveToJson() -{ - QJsonObject planJson; - qgcApp()->toolbox()->corePlugin()->preSaveToJson(this, planJson); - QJsonObject missionJson; - QJsonObject fenceJson; - QJsonObject rallyJson; - JsonHelper::saveQGCJsonFileHeader(planJson, kPlanFileType, kPlanFileVersion); - //-- Allow plugin to preemptly add its own keys to mission - qgcApp()->toolbox()->corePlugin()->preSaveToMissionJson(this, missionJson); - _missionController.save(missionJson); - //-- Allow plugin to add its own keys to mission - qgcApp()->toolbox()->corePlugin()->postSaveToMissionJson(this, missionJson); - _geoFenceController.save(fenceJson); - _rallyPointController.save(rallyJson); - planJson[kJsonMissionObjectKey] = missionJson; - planJson[kJsonGeoFenceObjectKey] = fenceJson; - planJson[kJsonRallyPointsObjectKey] = rallyJson; - qgcApp()->toolbox()->corePlugin()->postSaveToJson(this, planJson); - return QJsonDocument(planJson); -} - -void -PlanMasterController::saveToCurrent() -{ - if(!_currentPlanFile.isEmpty()) { - saveToFile(_currentPlanFile); - } -} - -void PlanMasterController::saveToFile(const QString& filename) -{ - if (filename.isEmpty()) { - return; - } - - QString planFilename = filename; - if (!QFileInfo(filename).fileName().contains(".")) { - planFilename += QString(".%1").arg(fileExtension()); - } - - QFile file(planFilename); - - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qgcApp()->showAppMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString())); - _currentPlanFile.clear(); - emit currentPlanFileChanged(); - } else { - QJsonDocument saveDoc = saveToJson(); - file.write(saveDoc.toJson()); - if(_currentPlanFile != planFilename) { - _currentPlanFile = planFilename; - emit currentPlanFileChanged(); - } - } - - // Only clear dirty bit if we are offline - if (offline()) { - setDirty(false); - } -} - -void PlanMasterController::saveToKml(const QString& filename) -{ - if (filename.isEmpty()) { - return; - } - - QString kmlFilename = filename; - if (!QFileInfo(filename).fileName().contains(".")) { - kmlFilename += QString(".%1").arg(kmlFileExtension()); - } - - QFile file(kmlFilename); - - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - qgcApp()->showAppMessage(tr("KML save error %1 : %2").arg(filename).arg(file.errorString())); - } else { - KMLPlanDomDocument planKML; - _missionController.addMissionToKML(planKML); - QTextStream stream(&file); - stream << planKML.toString(); - file.close(); - } + } else { + QJsonDocument saveDoc = saveToJson(); + file.write(saveDoc.toJson()); + if (_currentPlanFile != planFilename) { + _currentPlanFile = planFilename; + emit currentPlanFileChanged(); + } + } + + // Only clear dirty bit if we are offline + if (offline()) { + setDirty(false); + } +} + +void PlanMasterController::saveToKml(const QString &filename) { + if (filename.isEmpty()) { + return; + } + + QString kmlFilename = filename; + if (!QFileInfo(filename).fileName().contains(".")) { + kmlFilename += QString(".%1").arg(kmlFileExtension()); + } + + QFile file(kmlFilename); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + qgcApp()->showAppMessage( + tr("KML save error %1 : %2").arg(filename).arg(file.errorString())); + } else { + KMLPlanDomDocument planKML; + _missionController.addMissionToKML(planKML); + QTextStream stream(&file); + stream << planKML.toString(); + file.close(); + } +} + +void PlanMasterController::removeAll(void) { + _missionController.removeAll(); + _geoFenceController.removeAll(); + _rallyPointController.removeAll(); + if (_offline) { + _missionController.setDirty(false); + _geoFenceController.setDirty(false); + _rallyPointController.setDirty(false); + _currentPlanFile.clear(); + emit currentPlanFileChanged(); + } } -void PlanMasterController::removeAll(void) -{ - _missionController.removeAll(); - _geoFenceController.removeAll(); - _rallyPointController.removeAll(); - if (_offline) { - _missionController.setDirty(false); - _geoFenceController.setDirty(false); - _rallyPointController.setDirty(false); - _currentPlanFile.clear(); - emit currentPlanFileChanged(); +void PlanMasterController::removeAllFromVehicle(void) { + if (!offline()) { + _missionController.removeAllFromVehicle(); + if (_geoFenceController.supported()) { + _geoFenceController.removeAllFromVehicle(); } -} - -void PlanMasterController::removeAllFromVehicle(void) -{ - if (!offline()) { - _missionController.removeAllFromVehicle(); - if (_geoFenceController.supported()) { - _geoFenceController.removeAllFromVehicle(); - } - if (_rallyPointController.supported()) { - _rallyPointController.removeAllFromVehicle(); - } - setDirty(false); - } else { - qWarning() << "PlanMasterController::removeAllFromVehicle called while offline"; + if (_rallyPointController.supported()) { + _rallyPointController.removeAllFromVehicle(); } + setDirty(false); + } else { + qWarning() + << "PlanMasterController::removeAllFromVehicle called while offline"; + } } -bool PlanMasterController::containsItems(void) const -{ - return _missionController.containsItems() || _geoFenceController.containsItems() || _rallyPointController.containsItems(); +bool PlanMasterController::containsItems(void) const { + return _missionController.containsItems() || + _geoFenceController.containsItems() || + _rallyPointController.containsItems(); } -bool PlanMasterController::dirty(void) const -{ - return _missionController.dirty() || _geoFenceController.dirty() || _rallyPointController.dirty(); +bool PlanMasterController::dirty(void) const { + return _missionController.dirty() || _geoFenceController.dirty() || + _rallyPointController.dirty(); } -void PlanMasterController::setDirty(bool dirty) -{ - _missionController.setDirty(dirty); - _geoFenceController.setDirty(dirty); - _rallyPointController.setDirty(dirty); +void PlanMasterController::setDirty(bool dirty) { + _missionController.setDirty(dirty); + _geoFenceController.setDirty(dirty); + _rallyPointController.setDirty(dirty); } -QString PlanMasterController::fileExtension(void) const -{ - return AppSettings::planFileExtension; +QString PlanMasterController::fileExtension(void) const { + return AppSettings::planFileExtension; } -QString PlanMasterController::kmlFileExtension(void) const -{ - return AppSettings::kmlFileExtension; +QString PlanMasterController::kmlFileExtension(void) const { + return AppSettings::kmlFileExtension; } -QStringList PlanMasterController::loadNameFilters(void) const -{ - QStringList filters; +QStringList PlanMasterController::loadNameFilters(void) const { + QStringList filters; - filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)").arg(AppSettings::planFileExtension).arg(AppSettings::missionFileExtension).arg(AppSettings::waypointsFileExtension).arg("txt") << - tr("All Files (*.*)"); - return filters; + filters << tr("Supported types (*.%1 *.%2 *.%3 *.%4)") + .arg(AppSettings::planFileExtension) + .arg(AppSettings::missionFileExtension) + .arg(AppSettings::waypointsFileExtension) + .arg("txt") + << tr("All Files (*.*)"); + return filters; } +QStringList PlanMasterController::saveNameFilters(void) const { + QStringList filters; -QStringList PlanMasterController::saveNameFilters(void) const -{ - QStringList filters; - - filters << tr("Plan Files (*.%1)").arg(fileExtension()) << tr("All Files (*.*)"); - return filters; + filters << tr("Plan Files (*.%1)").arg(fileExtension()) + << tr("All Files (*.*)"); + return filters; } -void PlanMasterController::sendPlanToVehicle(Vehicle* vehicle, const QString& filename) -{ - // Use a transient PlanMasterController to accomplish this - PlanMasterController* controller = new PlanMasterController(); - controller->startStaticActiveVehicle(vehicle, true /* deleteWhenSendCompleted */); - controller->loadFromFile(filename); - controller->sendToVehicle(); +void PlanMasterController::sendPlanToVehicle(Vehicle *vehicle, + const QString &filename) { + // Use a transient PlanMasterController to accomplish this + PlanMasterController *controller = new PlanMasterController(); + controller->startStaticActiveVehicle(vehicle, + true /* deleteWhenSendCompleted */); + controller->loadFromFile(filename); + controller->sendToVehicle(); } -void PlanMasterController::_showPlanFromManagerVehicle(void) -{ - if (!_managerVehicle->initialPlanRequestComplete() && !syncInProgress()) { - // Something went wrong with initial load. All controllers are idle, so just force it off - _managerVehicle->forceInitialPlanRequestComplete(); - } +void PlanMasterController::_showPlanFromManagerVehicle(void) { + if (!_managerVehicle->initialPlanRequestComplete() && !syncInProgress()) { + // Something went wrong with initial load. All controllers are idle, so just + // force it off + _managerVehicle->forceInitialPlanRequestComplete(); + } - // The crazy if structure is to handle the load propagating by itself through the system - if (!_missionController.showPlanFromManagerVehicle()) { - if (!_geoFenceController.showPlanFromManagerVehicle()) { - _rallyPointController.showPlanFromManagerVehicle(); - } + // The crazy if structure is to handle the load propagating by itself through + // the system + if (!_missionController.showPlanFromManagerVehicle()) { + if (!_geoFenceController.showPlanFromManagerVehicle()) { + _rallyPointController.showPlanFromManagerVehicle(); } + } } -bool PlanMasterController::syncInProgress(void) const -{ - return _missionController.syncInProgress() || - _geoFenceController.syncInProgress() || - _rallyPointController.syncInProgress(); +bool PlanMasterController::syncInProgress(void) const { + return _missionController.syncInProgress() || + _geoFenceController.syncInProgress() || + _rallyPointController.syncInProgress(); } -bool PlanMasterController::isEmpty(void) const -{ - return _missionController.isEmpty() && - _geoFenceController.isEmpty() && - _rallyPointController.isEmpty(); +bool PlanMasterController::isEmpty(void) const { + return _missionController.isEmpty() && _geoFenceController.isEmpty() && + _rallyPointController.isEmpty(); } -void PlanMasterController::_updatePlanCreatorsList(void) -{ - if (!_flyView) { - if (!_planCreators) { - _planCreators = new QmlObjectListModel(this); - _planCreators->append(new BlankPlanCreator(this, this)); - _planCreators->append(new SurveyPlanCreator(this, this)); - _planCreators->append(new CorridorScanPlanCreator(this, this)); - emit planCreatorsChanged(_planCreators); - } - - if (_managerVehicle->fixedWing()) { - if (_planCreators->count() == 4) { - _planCreators->removeAt(_planCreators->count() - 1); - } - } else { - if (_planCreators->count() != 4) { - _planCreators->append(new StructureScanPlanCreator(this, this)); - } - } +void PlanMasterController::_updatePlanCreatorsList(void) { + if (!_flyView) { + if (!_planCreators) { + _planCreators = new QmlObjectListModel(this); + _planCreators->append(new BlankPlanCreator(this, this)); + _planCreators->append(new SurveyPlanCreator(this, this)); + _planCreators->append(new CorridorScanPlanCreator(this, this)); + _planCreators->append(new MeasurementPlanCreator(this, this)); + emit planCreatorsChanged(_planCreators); } -} -void PlanMasterController::showPlanFromManagerVehicle(void) -{ - if (offline()) { - // There is no new vehicle so clear any previous plan - qCDebug(PlanMasterControllerLog) << "showPlanFromManagerVehicle: Plan View - No new vehicle, clear any previous plan"; - removeAll(); + if (_managerVehicle->fixedWing()) { + if (_planCreators->count() == 5) { + _planCreators->removeAt(_planCreators->count() - 1); + } } else { - // We have a new active vehicle, show the plan from that - qCDebug(PlanMasterControllerLog) << "showPlanFromManagerVehicle: Plan View - New vehicle available, show plan from new manager vehicle"; - _showPlanFromManagerVehicle(); - } + if (_planCreators->count() != 5) { + _planCreators->append(new StructureScanPlanCreator(this, this)); + } + } + } +} + +void PlanMasterController::showPlanFromManagerVehicle(void) { + if (offline()) { + // There is no new vehicle so clear any previous plan + qCDebug(PlanMasterControllerLog) + << "showPlanFromManagerVehicle: Plan View - No new vehicle, clear any " + "previous plan"; + removeAll(); + } else { + // We have a new active vehicle, show the plan from that + qCDebug(PlanMasterControllerLog) + << "showPlanFromManagerVehicle: Plan View - New vehicle available, " + "show plan from new manager vehicle"; + _showPlanFromManagerVehicle(); + } }