Commit fb9b1767 authored by Valentin Platzgummer's avatar Valentin Platzgummer

plan creator added, area editor improved

parent 42fe8ce4
......@@ -195,5 +195,6 @@
<file alias="Yield.svg">src/ui/toolbar/Images/Yield.svg</file>
<file alias="ZoomMinus.svg">src/FlightMap/Images/ZoomMinus.svg</file>
<file alias="ZoomPlus.svg">src/FlightMap/Images/ZoomPlus.svg</file>
<file alias="PlanCreator/MeasurementPlanCreator.png">src/MeasurementComplexItem/MeasurementPlanCreator.png</file>
</qresource>
</RCC>
......@@ -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 \
......
#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);
}
#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
......@@ -251,7 +251,7 @@ std::shared_future<QVariant>
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<QVariant>
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<QVariant> 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<QVariant> 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<ProgressArray> pArray,
std::promise<bool> 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<QVector<std::shared_ptr<const Tile>>> 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<IDArray> pIdArray) {
qDebug() << "_callRemoveTiles called";
// qDebug() << "_callRemoveTiles called";
this->_lastCall = CALL_NAME::REMOVE_TILES;
......@@ -995,7 +1020,7 @@ NemoInterface::Impl::_callRemoveTiles(std::shared_ptr<IDArray> 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<IDArray> pIdArray) {
qDebug() << "_callGetProgress called";
// qDebug() << "_callGetProgress called";
this->_lastCall = CALL_NAME::GET_PROGRESS;
......@@ -1168,7 +1193,7 @@ NemoInterface::Impl::_callGetProgress(std::shared_ptr<IDArray> 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<QVector<std::shared_ptr<const Tile>>> pTileArray,
std::promise<bool> promise) {
qDebug() << "_addTilesRemote called";
// qDebug() << "_addTilesRemote called";
auto pArrayDup = std::make_shared<QVector<std::shared_ptr<Tile>>>();
for (auto pTile : *pTileArray) {
......@@ -1292,7 +1317,7 @@ void NemoInterface::Impl::_addTilesRemote2(
std::shared_ptr<QVector<std::shared_ptr<Tile>>> pTileArray,
std::promise<bool> 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> idArray,
std::promise<bool> 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> idArray,
}
void NemoInterface::Impl::_clearTilesRemote(std::promise<bool> promise) {
qDebug() << "_clearTilesRemote called";
// qDebug() << "_clearTilesRemote called";
if (_remoteTiles.size() > 0) {
_remoteTiles.clear();
if (this->_isSynchronized()) {
......
......@@ -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
......@@ -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
}
......
......@@ -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 <QDomDocument>
#include <QJsonDocument>
#include <QFileInfo>
#include <QJsonDocument>
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<JsonHelper::KeyValidateInfo> 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<JsonHelper::KeyValidateInfo> 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();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment