diff --git a/ChangeLog.md b/ChangeLog.md index 571e5a35e6a8dd5fa36a75cb3b007de33b1afc4f..36b7e0ba7470ac61166a780b62778d5863932972 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,6 +6,7 @@ Note: This file only contains high level features or important fixes. ### 3.6.0 - Daily Build +* Plan View: New create plan UI for initial plan creation * New Corridor editing tools ui. Includes ability to trace polyline by clicking. * New Polygon editing tools ui. Includes ability to trace polygon by clicking. * ArduCopter/Rover: Follow Me setup page diff --git a/qgcimages.qrc b/qgcimages.qrc index a224f5e2782b30a4bbef4ddb6280efb03edafb03..ffb1b4cffdb0ea7a5bd91319823b67b6e423ee59 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -127,6 +127,10 @@ src/FlightMap/Images/pipHide.svg src/FlightMap/Images/pipResize.svg src/ui/toolbar/Images/Plan.svg + src/MissionManager/CustomPlanCreator.png + src/MissionManager/CorridorScanPlanCreator.png + src/MissionManager/StructureScanPlanCreator.png + src/MissionManager/SurveyPlanCreator.png src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_01cell.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_02cell.svg src/AutoPilotPlugins/PX4/Images/PowerComponentBattery_03cell.svg diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 7957132db5213287f5832bfae807de71490cc47b..536f6155be31199ef81d01b6efae9b574d77842c 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -591,6 +591,8 @@ HEADERS += \ src/MissionManager/CameraSpec.h \ src/MissionManager/ComplexMissionItem.h \ src/MissionManager/CorridorScanComplexItem.h \ + src/MissionManager/CorridorScanPlanCreator.h \ + src/MissionManager/CustomPlanCreator.h \ src/MissionManager/FixedWingLandingComplexItem.h \ src/MissionManager/GeoFenceController.h \ src/MissionManager/GeoFenceManager.h \ @@ -603,6 +605,7 @@ HEADERS += \ src/MissionManager/MissionManager.h \ src/MissionManager/MissionSettingsItem.h \ src/MissionManager/PlanElementController.h \ + src/MissionManager/PlanCreator.h \ src/MissionManager/PlanManager.h \ src/MissionManager/PlanMasterController.h \ src/MissionManager/QGCFenceCircle.h \ @@ -617,7 +620,9 @@ HEADERS += \ src/MissionManager/Section.h \ src/MissionManager/SpeedSection.h \ src/MissionManager/StructureScanComplexItem.h \ + src/MissionManager/StructureScanPlanCreator.h \ src/MissionManager/SurveyComplexItem.h \ + src/MissionManager/SurveyPlanCreator.h \ src/MissionManager/TransectStyleComplexItem.h \ src/MissionManager/VisualMissionItem.h \ src/PositionManager/PositionManager.h \ @@ -817,6 +822,8 @@ SOURCES += \ src/MissionManager/CameraSpec.cc \ src/MissionManager/ComplexMissionItem.cc \ src/MissionManager/CorridorScanComplexItem.cc \ + src/MissionManager/CorridorScanPlanCreator.cc \ + src/MissionManager/CustomPlanCreator.cc \ src/MissionManager/FixedWingLandingComplexItem.cc \ src/MissionManager/GeoFenceController.cc \ src/MissionManager/GeoFenceManager.cc \ @@ -829,6 +836,7 @@ SOURCES += \ src/MissionManager/MissionManager.cc \ src/MissionManager/MissionSettingsItem.cc \ src/MissionManager/PlanElementController.cc \ + src/MissionManager/PlanCreator.cc \ src/MissionManager/PlanManager.cc \ src/MissionManager/PlanMasterController.cc \ src/MissionManager/QGCFenceCircle.cc \ @@ -842,7 +850,9 @@ SOURCES += \ src/MissionManager/SimpleMissionItem.cc \ src/MissionManager/SpeedSection.cc \ src/MissionManager/StructureScanComplexItem.cc \ + src/MissionManager/StructureScanPlanCreator.cc \ src/MissionManager/SurveyComplexItem.cc \ + src/MissionManager/SurveyPlanCreator.cc \ src/MissionManager/TransectStyleComplexItem.cc \ src/MissionManager/VisualMissionItem.cc \ src/PositionManager/PositionManager.cpp \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index fb2a636586da75ea2b0e38636b630e257f343181..99908ea6b068c45618a3bcfc558df8129086c1ee 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -102,6 +102,7 @@ src/QmlControls/ParameterEditorDialog.qml src/QmlControls/PIDTuning.qml src/PlanView/PlanEditToolbar.qml + src/PlanView/PlanStartOverlay.qml src/QmlControls/PreFlightCheckButton.qml src/QmlControls/PreFlightCheckGroup.qml src/QmlControls/PreFlightCheckModel.qml diff --git a/src/MissionManager/CorridorScanComplexItem.cc b/src/MissionManager/CorridorScanComplexItem.cc index 0ae723c17c9b1ea67cae624236d6f0a6d8092f7e..c9d4614dc8359020ce57b4176606c4eb6af1a57d 100644 --- a/src/MissionManager/CorridorScanComplexItem.cc +++ b/src/MissionManager/CorridorScanComplexItem.cc @@ -51,6 +51,8 @@ CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, bool flyView, connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon); connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_rebuildCorridorPolygon); + connect(&_corridorPolyline, &QGCMapPolyline::isValidChanged,this, &CorridorScanComplexItem::readyForSaveStateChanged); + if (!kmlFile.isEmpty()) { _corridorPolyline.loadKMLFile(kmlFile); _corridorPolyline.setDirty(false); @@ -490,9 +492,9 @@ void CorridorScanComplexItem::_recalcCameraShots(void) emit cameraShotsChanged(); } -bool CorridorScanComplexItem::readyForSave(void) const +CorridorScanComplexItem::ReadyForSaveState CorridorScanComplexItem::readyForSaveState(void) const { - return TransectStyleComplexItem::readyForSave(); + return TransectStyleComplexItem::readyForSaveState(); } double CorridorScanComplexItem::timeBetweenShots(void) diff --git a/src/MissionManager/CorridorScanComplexItem.h b/src/MissionManager/CorridorScanComplexItem.h index d2b9006489de5ec7927f4f3f71e10c4b3c58b497..ab1604c1f45ce353a8b37cc5aa3e340a06e3c15c 100644 --- a/src/MissionManager/CorridorScanComplexItem.h +++ b/src/MissionManager/CorridorScanComplexItem.h @@ -48,11 +48,11 @@ public: QString mapVisualQML (void) const final { return QStringLiteral("CorridorScanMapVisual.qml"); } // Overrides from VisualMissionionItem - QString commandDescription (void) const final { return tr("Corridor Scan"); } - QString commandName (void) const final { return tr("Corridor Scan"); } - QString abbreviation (void) const final { return tr("C"); } - bool readyForSave (void) const; - double additionalTimeDelay (void) const final { return 0; } + QString commandDescription (void) const final { return tr("Corridor Scan"); } + QString commandName (void) const final { return tr("Corridor Scan"); } + QString abbreviation (void) const final { return tr("C"); } + ReadyForSaveState readyForSaveState (void) const final; + double additionalTimeDelay (void) const final { return 0; } static const char* jsonComplexItemTypeValue; diff --git a/src/MissionManager/CorridorScanComplexItemTest.cc b/src/MissionManager/CorridorScanComplexItemTest.cc index 75f330ac4e9f53f72caccc4364cafaa441a51bce..7b02d31b0def8768c30212c5b2961d7d9f3882b2 100644 --- a/src/MissionManager/CorridorScanComplexItemTest.cc +++ b/src/MissionManager/CorridorScanComplexItemTest.cc @@ -139,7 +139,7 @@ void CorridorScanComplexItemTest::_waitForReadyForSave(void) { int loops = 0; while (loops++ < 8) { - if (_corridorItem->readyForSave()) { + if (_corridorItem->readyForSaveState() == CorridorScanComplexItem::ReadyForSave) { return; } QTest::qWait(500); diff --git a/src/MissionManager/CorridorScanPlanCreator.cc b/src/MissionManager/CorridorScanPlanCreator.cc new file mode 100644 index 0000000000000000000000000000000000000000..5f361861ada80b0fe9aa7e99d28a5d6bf20f89b4 --- /dev/null +++ b/src/MissionManager/CorridorScanPlanCreator.cc @@ -0,0 +1,38 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "CorridorScanPlanCreator.h" +#include "PlanMasterController.h" +#include "MissionSettingsItem.h" + +CorridorScanPlanCreator::CorridorScanPlanCreator(PlanMasterController* planMasterController, QObject* parent) + : PlanCreator(planMasterController, MissionController::patternCorridorScanName, QStringLiteral("/qmlimages/PlanCreator/CorridorScanPlanCreator.png"), parent) +{ + +} + +void CorridorScanPlanCreator::createPlan(const QGeoCoordinate& mapCenterCoord) +{ + _planMasterController->removeAll(); + int seqNum = _missionController->insertComplexMissionItem( + MissionController::patternCorridorScanName, + mapCenterCoord, + _missionController->visualItems()->count()); + if (_planMasterController->managerVehicle()->fixedWing()) { + _missionController->insertComplexMissionItem( + MissionController::patternFWLandingName, + mapCenterCoord, + _missionController->visualItems()->count()); + } else { + MissionSettingsItem* settingsItem = _missionController->visualItems()->value(0); + settingsItem->setMissionEndRTL(true); + } + _missionController->setCurrentPlanViewIndex(seqNum, false); + +} diff --git a/src/MissionManager/CorridorScanPlanCreator.h b/src/MissionManager/CorridorScanPlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..7e68480475166a795a2c6866ae6fc5bb3c512a65 --- /dev/null +++ b/src/MissionManager/CorridorScanPlanCreator.h @@ -0,0 +1,22 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "PlanCreator.h" + +class CorridorScanPlanCreator : public PlanCreator +{ + Q_OBJECT + +public: + CorridorScanPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); + + Q_INVOKABLE void createPlan(const QGeoCoordinate& mapCenterCoord) final; +}; diff --git a/src/MissionManager/CorridorScanPlanCreator.png b/src/MissionManager/CorridorScanPlanCreator.png new file mode 100644 index 0000000000000000000000000000000000000000..d50b0a273749a2bde7b278f7662092cbd6e1125f Binary files /dev/null and b/src/MissionManager/CorridorScanPlanCreator.png differ diff --git a/src/MissionManager/CustomPlanCreator.cc b/src/MissionManager/CustomPlanCreator.cc new file mode 100644 index 0000000000000000000000000000000000000000..08626ffb600553f7bd3dbc54cbe7720215f7d49e --- /dev/null +++ b/src/MissionManager/CustomPlanCreator.cc @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "CustomPlanCreator.h" +#include "PlanMasterController.h" +#include "MissionSettingsItem.h" + +CustomPlanCreator::CustomPlanCreator(PlanMasterController* planMasterController, QObject* parent) + : PlanCreator(planMasterController, tr("Custom"), QStringLiteral("/qmlimages/PlanCreator/CustomPlanCreator.png"), parent) +{ + +} + +void CustomPlanCreator::createPlan(const QGeoCoordinate& mapCenterCoord) +{ + _planMasterController->removeAll(); + int seqNum = _missionController->insertSimpleMissionItem(mapCenterCoord, _missionController->visualItems()->count()); + _missionController->insertSimpleMissionItem(mapCenterCoord.atDistanceAndAzimuth(50, 135), _missionController->visualItems()->count()); + _missionController->insertSimpleMissionItem(mapCenterCoord.atDistanceAndAzimuth(50, -135), _missionController->visualItems()->count()); + if (_planMasterController->managerVehicle()->fixedWing()) { + _missionController->insertComplexMissionItem( + MissionController::patternFWLandingName, + mapCenterCoord, + _missionController->visualItems()->count()); + } else { + MissionSettingsItem* settingsItem = _missionController->visualItems()->value(0); + settingsItem->setMissionEndRTL(true); + } + _missionController->setCurrentPlanViewIndex(seqNum, false); + +} diff --git a/src/MissionManager/CustomPlanCreator.h b/src/MissionManager/CustomPlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..56b0900686cb24b36263f19ec74fa76b637590ea --- /dev/null +++ b/src/MissionManager/CustomPlanCreator.h @@ -0,0 +1,22 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "PlanCreator.h" + +class CustomPlanCreator : public PlanCreator +{ + Q_OBJECT + +public: + CustomPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); + + Q_INVOKABLE void createPlan(const QGeoCoordinate& mapCenterCoord) final; +}; diff --git a/src/MissionManager/CustomPlanCreator.png b/src/MissionManager/CustomPlanCreator.png new file mode 100644 index 0000000000000000000000000000000000000000..ca0eb9860c55af3bc53b93d04da0eff290598f88 Binary files /dev/null and b/src/MissionManager/CustomPlanCreator.png differ diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index bb418830646a988c984a7707b1dd9f13dffab0d9..9e7a8390e9c825a4052958a49e7f98d0017968ff 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -100,6 +100,7 @@ FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, bool connect(this, &FixedWingLandingComplexItem::altitudesAreRelativeChanged, this, &FixedWingLandingComplexItem::coordinateHasRelativeAltitudeChanged); connect(this, &FixedWingLandingComplexItem::altitudesAreRelativeChanged, this, &FixedWingLandingComplexItem::exitCoordinateHasRelativeAltitudeChanged); + connect(this, &FixedWingLandingComplexItem::landingCoordSetChanged, this, &FixedWingLandingComplexItem::readyForSaveStateChanged); if (vehicle->apmFirmware()) { // ArduPilot does not support camera commands _stopTakingVideoFact.setRawValue(false); @@ -708,3 +709,8 @@ void FixedWingLandingComplexItem::_signalLastSequenceNumberChanged(void) { emit lastSequenceNumberChanged(lastSequenceNumber()); } + +FixedWingLandingComplexItem::ReadyForSaveState FixedWingLandingComplexItem::readyForSaveState(void) const +{ + return _landingCoordSet ? ReadyForSave : NotReadyForSaveData; +} diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 9f81b56954c7f9cd53d9654ec6f38287eb0777d3..f79b92d3f71ed2842cbf176341ab566acacade4e 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -74,7 +74,6 @@ public: QString mapVisualQML (void) const final { return QStringLiteral("FWLandingPatternMapVisual.qml"); } // Overrides from VisualMissionItem - bool dirty (void) const final { return _dirty; } bool isSimpleItem (void) const final { return false; } bool isStandaloneCoordinate (void) const final { return false; } @@ -92,6 +91,7 @@ public: void appendMissionItems (QList& items, QObject* missionItemParent) final; void applyNewAltitude (double newAltitude) final; double additionalTimeDelay (void) const final { return 0; } + ReadyForSaveState readyForSaveState (void) const final; bool coordinateHasRelativeAltitude (void) const final { return _altitudesAreRelative; } bool exitCoordinateHasRelativeAltitude (void) const final { return _altitudesAreRelative; } diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 3d8bd26290ecef4bc32493b2f1d79daf8297975b..e21df318b3b8de3d9e3950cb7657d9e03f515ece 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -51,9 +51,10 @@ const char* MissionController::_jsonMavAutopilotKey = "MAV_AUTOPILOT"; const int MissionController::_missionFileVersion = 2; -const QString MissionController::patternFWLandingName (QT_TRANSLATE_NOOP("MissionController", "Fixed Wing Landing")); -const QString MissionController::patternStructureScanName (QT_TRANSLATE_NOOP("MissionController", "Structure Scan")); -const QString MissionController::patternCorridorScanName (QT_TRANSLATE_NOOP("MissionController", "Corridor Scan")); +const QString MissionController::patternSurveyName (QT_TRANSLATE_NOOP("MissionController", "Survey")); +const QString MissionController::patternFWLandingName (QT_TRANSLATE_NOOP("MissionController", "Fixed Wing Landing")); +const QString MissionController::patternStructureScanName (QT_TRANSLATE_NOOP("MissionController", "Structure Scan")); +const QString MissionController::patternCorridorScanName (QT_TRANSLATE_NOOP("MissionController", "Corridor Scan")); MissionController::MissionController(PlanMasterController* masterController, QObject *parent) : PlanElementController (masterController, parent) @@ -64,7 +65,6 @@ MissionController::MissionController(PlanMasterController* masterController, QOb , _firstItemsFromVehicle (false) , _itemsRequested (false) , _inRecalcSequence (false) - , _surveyMissionItemName (tr("Survey")) , _appSettings (qgcApp()->toolbox()->settingsManager()->appSettings()) , _progressPct (0) , _currentPlanViewIndex (-1) @@ -417,7 +417,7 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate } int sequenceNumber = _nextSequenceNumber(); - if (itemName == _surveyMissionItemName) { + if (itemName == patternSurveyName) { newItem = new SurveyComplexItem(_controllerVehicle, _flyView, QString() /* kmlFile */, _visualItems /* parent */); newItem->setCoordinate(mapCenterCoordinate); } else if (itemName == patternFWLandingName) { @@ -438,7 +438,7 @@ int MissionController::insertComplexMissionItemFromKMLOrSHP(QString itemName, QS { ComplexMissionItem* newItem; - if (itemName == _surveyMissionItemName) { + if (itemName == patternSurveyName) { newItem = new SurveyComplexItem(_controllerVehicle, _flyView, file, _visualItems); } else if (itemName == patternStructureScanName) { newItem = new StructureScanComplexItem(_controllerVehicle, _flyView, file, _visualItems); @@ -987,7 +987,7 @@ bool MissionController::readyForSaveSend(void) const { for (int i=0; i<_visualItems->count(); i++) { VisualMissionItem* visualItem = qobject_cast(_visualItems->get(i)); - if (!visualItem->readyForSave()) { + if (visualItem->readyForSaveState() != VisualMissionItem::ReadyForSave) { return false; } } @@ -2006,7 +2006,7 @@ QStringList MissionController::complexMissionItemNames(void) const { QStringList complexItems; - complexItems.append(_surveyMissionItemName); + complexItems.append(patternSurveyName); complexItems.append(patternCorridorScanName); if (_controllerVehicle->fixedWing()) { complexItems.append(patternFWLandingName); @@ -2112,7 +2112,6 @@ VisualMissionItem* MissionController::currentPlanViewItem(void) const void MissionController::setCurrentPlanViewIndex(int sequenceNumber, bool force) { - qDebug() << "setCurrentPlanViewIndex" << sequenceNumber << force << _currentPlanViewIndex; if(_visualItems && (force || sequenceNumber != _currentPlanViewIndex)) { _splitSegment = nullptr; _currentPlanViewItem = nullptr; diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 892ff41eae37727190f714a995d8c84c8f608d05..7848bcc452c69a6eb7524c765547c184e9dc404c 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -180,7 +180,7 @@ public: QGeoCoordinate plannedHomePosition (void) const; VisualMissionItem* currentPlanViewItem (void) const; double progressPct (void) const { return _progressPct; } - QString surveyComplexItemName (void) const { return _surveyMissionItemName; } + QString surveyComplexItemName (void) const { return patternSurveyName; } QString corridorScanComplexItemName (void) const { return patternCorridorScanName; } QString structureScanComplexItemName(void) const { return patternStructureScanName; } @@ -207,6 +207,7 @@ public: static const QString patternFWLandingName; static const QString patternStructureScanName; static const QString patternCorridorScanName; + static const QString patternSurveyName; signals: void visualItemsChanged (void); @@ -299,7 +300,6 @@ private: bool _itemsRequested; bool _inRecalcSequence; MissionFlightStatus_t _missionFlightStatus; - QString _surveyMissionItemName; AppSettings* _appSettings; double _progressPct; int _currentPlanViewIndex; diff --git a/src/MissionManager/PlanCreator.cc b/src/MissionManager/PlanCreator.cc new file mode 100644 index 0000000000000000000000000000000000000000..4ce04935d0cea11320508fa878b162c9d1299a0d --- /dev/null +++ b/src/MissionManager/PlanCreator.cc @@ -0,0 +1,21 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "PlanCreator.h" +#include "PlanMasterController.h" + +PlanCreator::PlanCreator(PlanMasterController* planMasterController, QString name, QString imageResource, QObject* parent) + : QObject (parent) + , _planMasterController (planMasterController) + , _missionController (planMasterController->missionController()) + , _name (name) + , _imageResource (imageResource) +{ + +} diff --git a/src/MissionManager/PlanCreator.h b/src/MissionManager/PlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..a58865ed280cfdc6ca891f7a2643309c34cebf3b --- /dev/null +++ b/src/MissionManager/PlanCreator.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include +#include "QGeoCoordinate" + +class PlanMasterController; +class MissionController; + +/// Base class for PlanCreator objects which are used to create a full plan in a single step. +class PlanCreator : public QObject +{ + Q_OBJECT + +public: + PlanCreator(PlanMasterController* planMasterController, QString name, QString imageResource, QObject* parent = nullptr); + + Q_PROPERTY(QString name MEMBER _name CONSTANT) + Q_PROPERTY(QString imageResource MEMBER _imageResource CONSTANT) + + Q_INVOKABLE virtual void createPlan(const QGeoCoordinate& mapCenterCoord) = 0; + +protected: + PlanMasterController* _planMasterController; + MissionController* _missionController; + QString _name; + QString _imageResource; +}; diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index 6cc9c256bee41adde985760f918cbefaa53814e1..db2dd88f935592c6a8f27475a0ea27d789ae8ba9 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -16,6 +16,10 @@ #include "JsonHelper.h" #include "MissionManager.h" #include "KML.h" +#include "SurveyPlanCreator.h" +#include "StructureScanPlanCreator.h" +#include "CorridorScanPlanCreator.h" +#include "CustomPlanCreator.h" #if defined(QGC_AIRMAP_ENABLED) #include "AirspaceFlightPlanProvider.h" #endif @@ -50,6 +54,7 @@ PlanMasterController::PlanMasterController(QObject* parent) , _sendGeoFence (false) , _sendRallyPoints (false) , _deleteWhenSendCompleted (false) + , _planCreators (nullptr) { connect(&_missionController, &MissionController::dirtyChanged, this, &PlanMasterController::dirtyChanged); connect(&_geoFenceController, &GeoFenceController::dirtyChanged, this, &PlanMasterController::dirtyChanged); @@ -79,6 +84,8 @@ void PlanMasterController::start(bool flyView) connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &PlanMasterController::_activeVehicleChanged); _activeVehicleChanged(_multiVehicleMgr->activeVehicle()); + _updatePlanCreatorsList(); + #if defined(QGC_AIRMAP_ENABLED) //-- This assumes there is one single instance of PlanMasterController in edit mode. if(!flyView) { @@ -115,12 +122,15 @@ void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle) disconnect(_managerVehicle->missionManager(), &MissionManager::sendComplete, this, &PlanMasterController::_sendMissionComplete); disconnect(_managerVehicle->geoFenceManager(), &GeoFenceManager::sendComplete, this, &PlanMasterController::_sendGeoFenceComplete); disconnect(_managerVehicle->rallyPointManager(), &RallyPointManager::sendComplete, this, &PlanMasterController::_sendRallyPointsComplete); + disconnect(_managerVehicle, &Vehicle::vehicleTypeChanged, this, &PlanMasterController::_updatePlanCreatorsList); } 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; + // The vehicle type can change on the offline vehicle. Keep the creators list in sync with that. + connect(_managerVehicle, &Vehicle::vehicleTypeChanged, this, &PlanMasterController::_updatePlanCreatorsList); newOffline = true; } else { newOffline = false; @@ -172,6 +182,8 @@ void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle) _showPlanFromManagerVehicle(); } } + + _updatePlanCreatorsList(); } void PlanMasterController::loadFromVehicle(void) @@ -583,3 +595,26 @@ bool PlanMasterController::isEmpty(void) const _geoFenceController.isEmpty() && _rallyPointController.isEmpty(); } + +void PlanMasterController::_updatePlanCreatorsList(void) +{ + if (!_flyView) { + if (!_planCreators) { + _planCreators = new QmlObjectListModel(this); + _planCreators->append(new SurveyPlanCreator(this, this)); + _planCreators->append(new CorridorScanPlanCreator(this, this)); + _planCreators->append(new CustomPlanCreator(this, this)); + emit planCreatorsChanged(_planCreators); + } + + if (_managerVehicle->fixedWing()) { + if (_planCreators->count() == 4) { + _planCreators->removeAt(_planCreators->count() - 2); + } + } else { + if (_planCreators->count() != 4) { + _planCreators->insert(_planCreators->count() - 1, new StructureScanPlanCreator(this, this)); + } + } + } +} diff --git a/src/MissionManager/PlanMasterController.h b/src/MissionManager/PlanMasterController.h index 829f550955a790cb25807c4f92fab972f47263c4..95ab6013b0a2da7367f149fd467ee3fb992f2aba 100644 --- a/src/MissionManager/PlanMasterController.h +++ b/src/MissionManager/PlanMasterController.h @@ -17,6 +17,7 @@ #include "Vehicle.h" #include "MultiVehicleManager.h" #include "QGCLoggingCategory.h" +#include "QmlObjectListModel.h" Q_DECLARE_LOGGING_CATEGORY(PlanMasterControllerLog) @@ -29,21 +30,20 @@ public: PlanMasterController(QObject* parent = nullptr); ~PlanMasterController(); - Q_PROPERTY(MissionController* missionController READ missionController CONSTANT) - Q_PROPERTY(GeoFenceController* geoFenceController READ geoFenceController CONSTANT) - Q_PROPERTY(RallyPointController* rallyPointController READ rallyPointController CONSTANT) - - Q_PROPERTY(Vehicle* controllerVehicle MEMBER _controllerVehicle CONSTANT) - Q_PROPERTY(bool offline READ offline NOTIFY offlineChanged) ///< true: controller is not connected to an active vehicle - Q_PROPERTY(bool containsItems READ containsItems NOTIFY containsItemsChanged) ///< true: Elemement is non-empty - Q_PROPERTY(bool syncInProgress READ syncInProgress NOTIFY syncInProgressChanged) ///< true: Information is currently being saved/sent, false: no active save/send in progress - Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) ///< true: Unsaved/sent changes are present, false: no changes since last save/send - Q_PROPERTY(QString fileExtension READ fileExtension CONSTANT) ///< File extension for missions - Q_PROPERTY(QString kmlFileExtension READ kmlFileExtension CONSTANT) - Q_PROPERTY(QString currentPlanFile READ currentPlanFile NOTIFY currentPlanFileChanged) - ///< kml file extension for missions - Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT) ///< File filter list loading plan files - Q_PROPERTY(QStringList saveNameFilters READ saveNameFilters CONSTANT) ///< File filter list saving plan files + Q_PROPERTY(MissionController* missionController READ missionController CONSTANT) + Q_PROPERTY(GeoFenceController* geoFenceController READ geoFenceController CONSTANT) + Q_PROPERTY(RallyPointController* rallyPointController READ rallyPointController CONSTANT) + Q_PROPERTY(Vehicle* controllerVehicle MEMBER _controllerVehicle CONSTANT) + Q_PROPERTY(bool offline READ offline NOTIFY offlineChanged) ///< true: controller is not connected to an active vehicle + Q_PROPERTY(bool containsItems READ containsItems NOTIFY containsItemsChanged) ///< true: Elemement is non-empty + Q_PROPERTY(bool syncInProgress READ syncInProgress NOTIFY syncInProgressChanged) ///< true: Information is currently being saved/sent, false: no active save/send in progress + Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) ///< true: Unsaved/sent changes are present, false: no changes since last save/send + Q_PROPERTY(QString fileExtension READ fileExtension CONSTANT) ///< File extension for missions + Q_PROPERTY(QString kmlFileExtension READ kmlFileExtension CONSTANT) + Q_PROPERTY(QString currentPlanFile READ currentPlanFile NOTIFY currentPlanFileChanged) + Q_PROPERTY(QStringList loadNameFilters READ loadNameFilters CONSTANT) ///< File filter list loading plan files + Q_PROPERTY(QStringList saveNameFilters READ saveNameFilters CONSTANT) ///< File filter list saving plan files + Q_PROPERTY(QmlObjectListModel* planCreators MEMBER _planCreators NOTIFY planCreatorsChanged) /// Should be called immediately upon Component.onCompleted. Q_INVOKABLE void start(bool flyView); @@ -103,17 +103,19 @@ signals: void dirtyChanged (bool dirty); void offlineChanged (bool offlineEditing); void currentPlanFileChanged (); + void planCreatorsChanged (QmlObjectListModel* planCreators); private slots: - void _activeVehicleChanged(Vehicle* activeVehicle); - void _loadMissionComplete(void); - void _loadGeoFenceComplete(void); - void _loadRallyPointsComplete(void); - void _sendMissionComplete(void); - void _sendGeoFenceComplete(void); - void _sendRallyPointsComplete(void); + void _activeVehicleChanged (Vehicle* activeVehicle); + void _loadMissionComplete (void); + void _loadGeoFenceComplete (void); + void _loadRallyPointsComplete (void); + void _sendMissionComplete (void); + void _sendGeoFenceComplete (void); + void _sendRallyPointsComplete (void); + void _updatePlanCreatorsList (void); #if defined(QGC_AIRMAP_ENABLED) - void _startFlightPlanning(void); + void _startFlightPlanning (void); #endif private: @@ -133,5 +135,6 @@ private: bool _sendRallyPoints; QString _currentPlanFile; bool _deleteWhenSendCompleted; + QmlObjectListModel* _planCreators; }; diff --git a/src/MissionManager/QGCMapPolygon.cc b/src/MissionManager/QGCMapPolygon.cc index f1ec6cafc835964a990b054eb4bbe859531b6859..e86d655938f3ca41572a75865a5d990d641ed35d 100644 --- a/src/MissionManager/QGCMapPolygon.cc +++ b/src/MissionManager/QGCMapPolygon.cc @@ -51,7 +51,10 @@ void QGCMapPolygon::_init(void) { connect(&_polygonModel, &QmlObjectListModel::dirtyChanged, this, &QGCMapPolygon::_polygonModelDirtyChanged); connect(&_polygonModel, &QmlObjectListModel::countChanged, this, &QGCMapPolygon::_polygonModelCountChanged); - connect(this, &QGCMapPolygon::pathChanged, this, &QGCMapPolygon::_updateCenter); + + connect(this, &QGCMapPolygon::pathChanged, this, &QGCMapPolygon::_updateCenter); + connect(this, &QGCMapPolygon::countChanged, this, &QGCMapPolygon::isValidChanged); + connect(this, &QGCMapPolygon::countChanged, this, &QGCMapPolygon::isEmptyChanged); } const QGCMapPolygon& QGCMapPolygon::operator=(const QGCMapPolygon& other) diff --git a/src/MissionManager/QGCMapPolygon.h b/src/MissionManager/QGCMapPolygon.h index 4a023eb4a6ae0d8666abf8ee392a7fd4f5894581..086b18aa436caade0235d0e7e161cbd9b27fae98 100644 --- a/src/MissionManager/QGCMapPolygon.h +++ b/src/MissionManager/QGCMapPolygon.h @@ -36,8 +36,8 @@ public: Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) Q_PROPERTY(bool centerDrag READ centerDrag WRITE setCenterDrag NOTIFY centerDragChanged) Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) - Q_PROPERTY(bool isValid READ isValid NOTIFY countChanged) - Q_PROPERTY(bool empty READ empty NOTIFY countChanged) + Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) + Q_PROPERTY(bool empty READ empty NOTIFY isEmptyChanged) Q_INVOKABLE void clear(void); Q_INVOKABLE void appendVertex(const QGeoCoordinate& coordinate); @@ -122,6 +122,8 @@ signals: void centerChanged (QGeoCoordinate center); void centerDragChanged (bool centerDrag); void interactiveChanged (bool interactive); + bool isValidChanged (void); + bool isEmptyChanged (void); private slots: void _polygonModelCountChanged(int count); diff --git a/src/MissionManager/QGCMapPolygonVisuals.qml b/src/MissionManager/QGCMapPolygonVisuals.qml index 69976773bead07f130162ca8d75105932e8db58b..7c51c0473585a5785199e53010cb37f05feab54f 100644 --- a/src/MissionManager/QGCMapPolygonVisuals.qml +++ b/src/MissionManager/QGCMapPolygonVisuals.qml @@ -516,13 +516,11 @@ Item { id: toolbarComponent PlanEditToolbar { - x: mapControl.centerViewport.left + _margins - y: mapControl.centerViewport.top + _margins - width: mapControl.centerViewport.width - (_margins * 2) + x: mapControl.centerViewport.left + y: mapControl.centerViewport.top + width: mapControl.centerViewport.width z: QGroundControl.zOrderMapItems + 2 - property real _margins: ScreenTools.defaultFontPixelWidth - QGCButton { _horizontalPadding: 0 text: qsTr("Basic") diff --git a/src/MissionManager/QGCMapPolyline.cc b/src/MissionManager/QGCMapPolyline.cc index 16ddf12d908cbab501437539ceef421ec38ff5e4..a2bc6434d557d837414046558287125cd349955f 100644 --- a/src/MissionManager/QGCMapPolyline.cc +++ b/src/MissionManager/QGCMapPolyline.cc @@ -61,6 +61,9 @@ void QGCMapPolyline::_init(void) { connect(&_polylineModel, &QmlObjectListModel::dirtyChanged, this, &QGCMapPolyline::_polylineModelDirtyChanged); connect(&_polylineModel, &QmlObjectListModel::countChanged, this, &QGCMapPolyline::_polylineModelCountChanged); + + connect(this, &QGCMapPolyline::countChanged, this, &QGCMapPolyline::isValidChanged); + connect(this, &QGCMapPolyline::countChanged, this, &QGCMapPolyline::isEmptyChanged); } void QGCMapPolyline::clear(void) diff --git a/src/MissionManager/QGCMapPolyline.h b/src/MissionManager/QGCMapPolyline.h index 5f16f8e39e02e6600ad4f80a701e2ebd899b20cb..0804f623a1f2f1a8978b296913f46e17cb9ad3cc 100644 --- a/src/MissionManager/QGCMapPolyline.h +++ b/src/MissionManager/QGCMapPolyline.h @@ -30,8 +30,8 @@ public: Q_PROPERTY(QmlObjectListModel* pathModel READ qmlPathModel CONSTANT) Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) - Q_PROPERTY(bool isValid READ isValid NOTIFY countChanged) - Q_PROPERTY(bool empty READ empty NOTIFY countChanged) + Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged) + Q_PROPERTY(bool empty READ empty NOTIFY isEmptyChanged) Q_INVOKABLE void clear(void); Q_INVOKABLE void appendVertex(const QGeoCoordinate& coordinate); @@ -104,6 +104,8 @@ signals: void dirtyChanged (bool dirty); void cleared (void); void interactiveChanged (bool interactive); + void isValidChanged (void); + void isEmptyChanged (void); private slots: void _polylineModelCountChanged(int count); diff --git a/src/MissionManager/QGCMapPolylineVisuals.qml b/src/MissionManager/QGCMapPolylineVisuals.qml index 234b2c51d9b73e0b7760e09ea5a28bbd9161f7ac..fbd9a816a617e97cac2772a965671e8993fd697f 100644 --- a/src/MissionManager/QGCMapPolylineVisuals.qml +++ b/src/MissionManager/QGCMapPolylineVisuals.qml @@ -314,13 +314,11 @@ Item { id: toolbarComponent PlanEditToolbar { - x: mapControl.centerViewport.left + _margins - y: mapControl.centerViewport.top + _margins - width: mapControl.centerViewport.width - (_margins * 2) + x: mapControl.centerViewport.left + y: mapControl.centerViewport.top + width: mapControl.centerViewport.width z: QGroundControl.zOrderMapItems + 2 - property real _margins: ScreenTools.defaultFontPixelWidth - QGCButton { _horizontalPadding: 0 text: qsTr("Basic") diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 20de9b0c2c19574cb576b7fdd0246a9756061001..486b49885a91322efa007dd5fdcc530e020baf71 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -710,25 +710,24 @@ void SimpleMissionItem::_terrainAltChanged(void) } if (qIsNaN(terrainAltitude())) { - qDebug() << "1"; // Set NaNs to signal we are waiting on terrain data _missionItem._param7Fact.setRawValue(qQNaN()); _amslAltAboveTerrainFact.setRawValue(qQNaN()); } else { double newAboveTerrain = terrainAltitude() + _altitudeFact.rawValue().toDouble(); double oldAboveTerrain = _amslAltAboveTerrainFact.rawValue().toDouble(); - qDebug() << "2" << newAboveTerrain << oldAboveTerrain; if (qIsNaN(oldAboveTerrain) || !qFuzzyCompare(newAboveTerrain, oldAboveTerrain)) { - qDebug() << "3"; _missionItem._param7Fact.setRawValue(newAboveTerrain); _amslAltAboveTerrainFact.setRawValue(newAboveTerrain); } } + emit readyForSaveStateChanged(); } -bool SimpleMissionItem::readyForSave(void) const +SimpleMissionItem::ReadyForSaveState SimpleMissionItem::readyForSaveState(void) const { - return !specifiesAltitude() || !qIsNaN(_missionItem._param7Fact.rawValue().toDouble()); + bool terrainReady = !specifiesAltitude() || !qIsNaN(_missionItem._param7Fact.rawValue().toDouble()); + return terrainReady ? ReadyForSave : NotReadyForSaveTerrain; } void SimpleMissionItem::_setDefaultsForCommand(void) diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index aa97b712fc6499c6a79789c2507d92fc741aaadd..ff89b3b5da5b516c328ed1687fc44a293d310dd1 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -52,7 +52,7 @@ public: /// This should be called before changing the command. It is needed if the command changes /// from an item which does not include a coordinate to an item which requires a coordinate. /// It uses this value to set that new coordinate. - Q_INVOKABLE void setMapCenterHintForCommandChange(QGeoCoordinate mapCenter) { _mapCenterHint = mapCenter; }; + Q_INVOKABLE void setMapCenterHintForCommandChange(QGeoCoordinate mapCenter) { _mapCenterHint = mapCenter; } /// Scans the loaded items for additional section settings /// @param visualItems List of all visual items @@ -118,7 +118,7 @@ public: void appendMissionItems (QList& items, QObject* missionItemParent) final; void applyNewAltitude (double newAltitude) final; void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; - bool readyForSave (void) const final; + ReadyForSaveState readyForSaveState (void) const final; double additionalTimeDelay (void) const final; bool coordinateHasRelativeAltitude (void) const final { return _missionItem.relativeAltitude(); } diff --git a/src/MissionManager/StructureScanComplexItem.cc b/src/MissionManager/StructureScanComplexItem.cc index 06b527665a5d796072cd5e266fa469329cdcb964..0a74efe46edcbb662310ded02f42bedc2103f649 100644 --- a/src/MissionManager/StructureScanComplexItem.cc +++ b/src/MissionManager/StructureScanComplexItem.cc @@ -69,6 +69,7 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, bool flyVie connect(&_structurePolygon, &QGCMapPolygon::dirtyChanged, this, &StructureScanComplexItem::_polygonDirtyChanged); connect(&_structurePolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_rebuildFlightPolygon); + connect(&_structurePolygon, &QGCMapPolygon::isValidChanged, this, &StructureScanComplexItem::readyForSaveStateChanged); connect(&_structurePolygon, &QGCMapPolygon::countChanged, this, &StructureScanComplexItem::_updateLastSequenceNumber); connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_updateLastSequenceNumber); @@ -617,3 +618,8 @@ void StructureScanComplexItem::_recalcScanDistance() << _layersFact.rawValue().toInt() << " structure height: " << surfaceHeight << " scanDistance: " << _scanDistance; } + +StructureScanComplexItem::ReadyForSaveState StructureScanComplexItem::readyForSaveState(void) const +{ + return _structurePolygon.isValid() ? ReadyForSave : NotReadyForSaveData; +} diff --git a/src/MissionManager/StructureScanComplexItem.h b/src/MissionManager/StructureScanComplexItem.h index 85f5b680fc2e01a03363e5c596fba3443601f5aa..4d0359bc81c516f081f326e164726f4848b3bdbc 100644 --- a/src/MissionManager/StructureScanComplexItem.h +++ b/src/MissionManager/StructureScanComplexItem.h @@ -70,7 +70,6 @@ public: QString mapVisualQML (void) const final { return QStringLiteral("StructureScanMapVisual.qml"); } // Overrides from VisualMissionItem - bool dirty (void) const final { return _dirty; } bool isSimpleItem (void) const final { return false; } bool isStandaloneCoordinate (void) const final { return false; } @@ -89,6 +88,7 @@ public: void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; void applyNewAltitude (double newAltitude) final; double additionalTimeDelay (void) const final { return 0; } + ReadyForSaveState readyForSaveState (void) const final; bool coordinateHasRelativeAltitude (void) const final { return true; } bool exitCoordinateHasRelativeAltitude (void) const final { return true; } diff --git a/src/MissionManager/StructureScanPlanCreator.cc b/src/MissionManager/StructureScanPlanCreator.cc new file mode 100644 index 0000000000000000000000000000000000000000..902ba1179860aa82b16c4f6e54ff2976c7cb410c --- /dev/null +++ b/src/MissionManager/StructureScanPlanCreator.cc @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "StructureScanPlanCreator.h" +#include "PlanMasterController.h" +#include "MissionSettingsItem.h" + +StructureScanPlanCreator::StructureScanPlanCreator(PlanMasterController* planMasterController, QObject* parent) + : PlanCreator(planMasterController, MissionController::patternStructureScanName, QStringLiteral("/qmlimages/PlanCreator/StructureScanPlanCreator.png"), parent) +{ + +} + +void StructureScanPlanCreator::createPlan(const QGeoCoordinate& mapCenterCoord) +{ + _planMasterController->removeAll(); + int seqNum = _missionController->insertComplexMissionItem( + MissionController::patternStructureScanName, + mapCenterCoord, + _missionController->visualItems()->count()); + if (_planMasterController->managerVehicle()->fixedWing()) { + _missionController->insertComplexMissionItem( + MissionController::patternFWLandingName, + mapCenterCoord, + _missionController->visualItems()->count()); + } else { + MissionSettingsItem* settingsItem = _missionController->visualItems()->value(0); + settingsItem->setMissionEndRTL(true); + } + _missionController->setCurrentPlanViewIndex(seqNum, false); +} diff --git a/src/MissionManager/StructureScanPlanCreator.h b/src/MissionManager/StructureScanPlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..e23538067f1919b3fcadf4b8567256fd6b1acf74 --- /dev/null +++ b/src/MissionManager/StructureScanPlanCreator.h @@ -0,0 +1,22 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "PlanCreator.h" + +class StructureScanPlanCreator : public PlanCreator +{ + Q_OBJECT + +public: + StructureScanPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); + + Q_INVOKABLE void createPlan(const QGeoCoordinate& mapCenterCoord) final; +}; diff --git a/src/MissionManager/StructureScanPlanCreator.png b/src/MissionManager/StructureScanPlanCreator.png new file mode 100644 index 0000000000000000000000000000000000000000..ac9f1bf5c26dd091a0f6112c1a69da6381bdb944 Binary files /dev/null and b/src/MissionManager/StructureScanPlanCreator.png differ diff --git a/src/MissionManager/SurveyComplexItem.cc b/src/MissionManager/SurveyComplexItem.cc index f1aa771f0d1716ebfa175becbdf73d1c84d71ec5..ea764a9a923ec0ae70f734c2b9cb6db1f655bc4c 100644 --- a/src/MissionManager/SurveyComplexItem.cc +++ b/src/MissionManager/SurveyComplexItem.cc @@ -1496,9 +1496,9 @@ void SurveyComplexItem::applyNewAltitude(double newAltitude) _cameraCalc.setDistanceToSurfaceRelative(true); } -bool SurveyComplexItem::readyForSave(void) const +SurveyComplexItem::ReadyForSaveState SurveyComplexItem::readyForSaveState(void) const { - return TransectStyleComplexItem::readyForSave(); + return TransectStyleComplexItem::readyForSaveState(); } void SurveyComplexItem::appendMissionItems(QList& items, QObject* missionItemParent) diff --git a/src/MissionManager/SurveyComplexItem.h b/src/MissionManager/SurveyComplexItem.h index 6d1348fea700af9909eb678e908daaa1b3e81fc1..087ee38b55a8ddc153a66ae5122a9ee8fbf91b57 100644 --- a/src/MissionManager/SurveyComplexItem.h +++ b/src/MissionManager/SurveyComplexItem.h @@ -51,11 +51,11 @@ public: double timeBetweenShots (void) final; // Overrides from VisualMissionionItem - QString commandDescription (void) const final { return tr("Survey"); } - QString commandName (void) const final { return tr("Survey"); } - QString abbreviation (void) const final { return tr("S"); } - bool readyForSave (void) const final; - double additionalTimeDelay (void) const final; + QString commandDescription (void) const final { return tr("Survey"); } + QString commandName (void) const final { return tr("Survey"); } + QString abbreviation (void) const final { return tr("S"); } + ReadyForSaveState readyForSaveState (void) const final; + double additionalTimeDelay (void) const final; // Must match json spec for GridEntryLocation enum EntryLocation { diff --git a/src/MissionManager/SurveyPlanCreator.cc b/src/MissionManager/SurveyPlanCreator.cc new file mode 100644 index 0000000000000000000000000000000000000000..83146592ef20bfe67cd0cb559808cc9b374b5566 --- /dev/null +++ b/src/MissionManager/SurveyPlanCreator.cc @@ -0,0 +1,37 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "SurveyPlanCreator.h" +#include "PlanMasterController.h" +#include "MissionSettingsItem.h" + +SurveyPlanCreator::SurveyPlanCreator(PlanMasterController* planMasterController, QObject* parent) + : PlanCreator(planMasterController, MissionController::patternSurveyName, QStringLiteral("/qmlimages/PlanCreator/SurveyPlanCreator.png"), parent) +{ + +} + +void SurveyPlanCreator::createPlan(const QGeoCoordinate& mapCenterCoord) +{ + _planMasterController->removeAll(); + int seqNum = _missionController->insertComplexMissionItem( + MissionController::patternSurveyName, + mapCenterCoord, + _missionController->visualItems()->count()); + if (_planMasterController->managerVehicle()->fixedWing()) { + _missionController->insertComplexMissionItem( + MissionController::patternFWLandingName, + mapCenterCoord, + _missionController->visualItems()->count()); + } else { + MissionSettingsItem* settingsItem = _missionController->visualItems()->value(0); + settingsItem->setMissionEndRTL(true); + } + _missionController->setCurrentPlanViewIndex(seqNum, false); +} diff --git a/src/MissionManager/SurveyPlanCreator.h b/src/MissionManager/SurveyPlanCreator.h new file mode 100644 index 0000000000000000000000000000000000000000..91590cd1a15e08da13f0c0675750a4a0214278c7 --- /dev/null +++ b/src/MissionManager/SurveyPlanCreator.h @@ -0,0 +1,22 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "PlanCreator.h" + +class SurveyPlanCreator : public PlanCreator +{ + Q_OBJECT + +public: + SurveyPlanCreator(PlanMasterController* planMasterController, QObject* parent = nullptr); + + Q_INVOKABLE void createPlan(const QGeoCoordinate& mapCenterCoord) final; +}; diff --git a/src/MissionManager/SurveyPlanCreator.png b/src/MissionManager/SurveyPlanCreator.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca4e15b193bd42d7f2e0dfafeb2cab80a3f2069 Binary files /dev/null and b/src/MissionManager/SurveyPlanCreator.png differ diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc index fcb9a423df8f3e0a8e0be4697ed9f1f4e13909d8..e75621ea0270b739d958f7f3c3a3ef1f9acc010a 100644 --- a/src/MissionManager/TransectStyleComplexItem.cc +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -109,6 +109,8 @@ TransectStyleComplexItem::TransectStyleComplexItem(Vehicle* vehicle, bool flyVie connect(this, &TransectStyleComplexItem::followTerrainChanged, this, &TransectStyleComplexItem::_followTerrainChanged); + connect(&_surveyAreaPolygon, &QGCMapPolygon::isValidChanged, this, &TransectStyleComplexItem::readyForSaveStateChanged); + setDirty(false); } @@ -417,6 +419,7 @@ void TransectStyleComplexItem::_rebuildTransects(void) void TransectStyleComplexItem::_queryTransectsPathHeightInfo(void) { _transectsPathHeightInfo.clear(); + emit readyForSaveStateChanged(); if (_transects.count()) { // We don't actually send the query until this timer times out. This way we only send @@ -461,6 +464,7 @@ void TransectStyleComplexItem::_reallyQueryTransectsPathHeightInfo(void) void TransectStyleComplexItem::_polyPathTerrainData(bool success, const QList& rgPathHeightInfo) { _transectsPathHeightInfo.clear(); + emit readyForSaveStateChanged(); if (success) { // Break out into individual transects @@ -473,6 +477,7 @@ void TransectStyleComplexItem::_polyPathTerrainData(bool success, const QListshowMessage(tr("INTERNAL ERROR: TransectStyleComplexItem::_adjustTransectPointsForTerrain called when terrain data not ready. Plan will be incorrect.")); return; diff --git a/src/MissionManager/TransectStyleComplexItem.h b/src/MissionManager/TransectStyleComplexItem.h index 12871f71e3372db860de6bbcaecaf9caf280ed8d..5dda95cf312c2095419d13a4e4e422fe6cae071e 100644 --- a/src/MissionManager/TransectStyleComplexItem.h +++ b/src/MissionManager/TransectStyleComplexItem.h @@ -99,7 +99,7 @@ public: double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; - bool readyForSave (void) const override; + ReadyForSaveState readyForSaveState (void) const override; QString commandDescription (void) const override { return tr("Transect"); } QString commandName (void) const override { return tr("Transect"); } QString abbreviation (void) const override { return tr("T"); } diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index 16679f9c01aea7e5e48497acc8346453f7dad86e..dc35d8a6911be72c0df30f603f22dbcb36e3a821 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -40,6 +40,13 @@ public: const VisualMissionItem& operator=(const VisualMissionItem& other); + enum ReadyForSaveState { + ReadyForSave, + NotReadyForSaveTerrain, + NotReadyForSaveData, + }; + Q_ENUM(ReadyForSaveState) + Q_PROPERTY(bool homePosition READ homePosition CONSTANT) ///< true: This item is being used as a home position indicator Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) ///< This is the entry point for a waypoint line into the item. For a simple item it is also the location of the item Q_PROPERTY(double terrainAltitude READ terrainAltitude NOTIFY terrainAltitudeChanged) ///< The altitude of terrain at the coordinate position, NaN if not known @@ -67,6 +74,7 @@ public: Q_PROPERTY(double missionGimbalYaw READ missionGimbalYaw NOTIFY missionGimbalYawChanged) ///< Current gimbal yaw state at this point in mission Q_PROPERTY(double missionVehicleYaw READ missionVehicleYaw NOTIFY missionVehicleYawChanged) ///< Expected vehicle yaw at this point in mission Q_PROPERTY(bool flyView READ flyView CONSTANT) + Q_PROPERTY(ReadyForSaveState readyForSaveState READ readyForSaveState NOTIFY readyForSaveStateChanged) Q_PROPERTY(QGCGeoBoundingCube* boundingCube READ boundingCube NOTIFY boundingCubeChanged) @@ -139,10 +147,8 @@ public: virtual void setSequenceNumber (int sequenceNumber) = 0; virtual int lastSequenceNumber (void) const = 0; - /// Specifies whether the item has all the data it needs such that it can be saved. Currently the only - /// case where this returns false is if it has not determined terrain values yet. - /// @return true: Ready to save, false: Still waiting on information - virtual bool readyForSave(void) const { return true; } + /// @return Returns whether the item is ready for save and if not, why + virtual ReadyForSaveState readyForSaveState(void) const { return ReadyForSave; } /// Save the item(s) in Json format /// @param missionItems Current set of mission items, new items should be appended to the end @@ -198,6 +204,7 @@ signals: void terrainAltitudeChanged (double terrainAltitude); void additionalTimeDelayChanged (void); void boundingCubeChanged (void); + void readyForSaveStateChanged (void); void coordinateHasRelativeAltitudeChanged (bool coordinateHasRelativeAltitude); void exitCoordinateHasRelativeAltitudeChanged (bool exitCoordinateHasRelativeAltitude); diff --git a/src/PlanView/MissionItemEditor.qml b/src/PlanView/MissionItemEditor.qml index 9307a7024c6dc774f21fe6e7eb2fd08c14664a87..c81b53d5a83dd421c505a9bc910669337afdcf43 100644 --- a/src/PlanView/MissionItemEditor.qml +++ b/src/PlanView/MissionItemEditor.qml @@ -18,6 +18,7 @@ Rectangle { height: editorLoader.visible ? (editorLoader.y + editorLoader.height + (_margin * 2)) : (commandPicker.y + commandPicker.height + _margin / 2) color: _currentItem ? qgcPal.missionItemEditor : qgcPal.windowShade radius: _radius + opacity: _currentItem ? 1.0 : 0.7 property var map ///< Map control property var masterController @@ -70,16 +71,27 @@ Rectangle { } } - /* - Trying no sequence numbers in ui - QGCLabel { - id: label + Rectangle { anchors.verticalCenter: commandPicker.verticalCenter anchors.leftMargin: _margin anchors.left: parent.left - text: missionItem.homePosition ? "P" : missionItem.sequenceNumber - color: _outerTextColor - }*/ + width: readyForSaveLabel.contentHeight + height: width + border.width: 1 + border.color: "red" + color: "white" + radius: width / 2 + visible: missionItem.readyForSaveState !== VisualMissionItem.ReadyForSave + + QGCLabel { + id: readyForSaveLabel + anchors.centerIn: parent + //: Indicator in Plan view to show mission item is not ready for save/send + text: qsTr("?") + color: "red" + font.pointSize: ScreenTools.smallFontPointSize + } + } QGCColoredImage { id: hamburger diff --git a/src/PlanView/PlanStartOverlay.qml b/src/PlanView/PlanStartOverlay.qml new file mode 100644 index 0000000000000000000000000000000000000000..e830270756b675e78b40f31de1b97eb48c33c39a --- /dev/null +++ b/src/PlanView/PlanStartOverlay.qml @@ -0,0 +1,128 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 2.11 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 + +Item { + id: _root + + property var planMasterController + property var mapControl + + property real _radius: ScreenTools.defaultFontPixelWidth / 2 + property real _margins: ScreenTools.defaultFontPixelWidth + + function _mapCenter() { + var centerPoint = Qt.point(mapControl.centerViewport.left + (mapControl.centerViewport.width / 2), mapControl.centerViewport.top + (mapControl.centerViewport.height / 2)) + return mapControl.toCoordinate(centerPoint, false /* clipToViewPort */) + } + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + Rectangle { + anchors.fill: parent + radius: _radius + color: "white" + opacity: 0.75 + } + + // Close Icon + QGCColoredImage { + anchors.margins: ScreenTools.defaultFontPixelWidth / 2 + anchors.top: parent.top + anchors.right: parent.right + width: ScreenTools.defaultFontPixelHeight + height: width + sourceSize.height: width + source: "/res/XDelete.svg" + fillMode: Image.PreserveAspectFit + mipmap: true + smooth: true + color: "black" + QGCMouseArea { + fillItem: parent + onClicked: _root.visible = false + } + } + + QGCLabel { + id: title + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: qsTr("Create Plan") + color: "black" + } + + QGCFlickable { + id: flickable + anchors.margins: _margins + anchors.top: title.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + contentHeight: creatorFlow.height + contentWidth: creatorFlow.width + + Flow { + id: creatorFlow + width: flickable.width + spacing: _margins + + Repeater { + model: _planMasterController.planCreators + + Rectangle { + id: button + width: ScreenTools.defaultFontPixelHeight * 10 + height: width + color: button.pressed || button.highlighted ? qgcPal.buttonHighlight : qgcPal.button + + property bool highlighted: mouseArea.containsMouse + property bool pressed: mouseArea.pressed + + Image { + anchors.margins: _margins + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + source: object.imageResource + fillMode: Image.PreserveAspectFit + mipmap: true + } + + QGCLabel { + anchors.margins: _margins + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + text: object.name + color: button.pressed || button.highlighted ? qgcPal.buttonHighlightText : qgcPal.buttonText + } + + QGCMouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + preventStealing: true + onClicked: { object.createPlan(_mapCenter()); _root.visible = false } + } + } + } + } + } +} diff --git a/src/PlanView/PlanView.qml b/src/PlanView/PlanView.qml index 7ff8650397b6bb11e9a88fc803ff1db8fa28fc13..42f6be7ca85d5135278e39c5f6a6ca150ca7a953 100644 --- a/src/PlanView/PlanView.qml +++ b/src/PlanView/PlanView.qml @@ -32,11 +32,10 @@ Item { property bool planControlColapsed: false readonly property int _decimalPlaces: 8 - readonly property real _horizontalMargin: ScreenTools.defaultFontPixelWidth * 0.5 readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5 + readonly property real _toolsTopMargin: ScreenTools.defaultFontPixelHeight * 0.5 readonly property real _radius: ScreenTools.defaultFontPixelWidth * 0.5 readonly property real _rightPanelWidth: Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30) - readonly property real _toolButtonTopMargin: ScreenTools.defaultFontPixelHeight * 0.5 readonly property var _defaultVehicleCoordinate: QtPositioning.coordinate(37.803784, -122.462276) readonly property bool _waypointsOnlyMode: QGroundControl.corePlugin.options.missionWaypointsOnly @@ -405,7 +404,7 @@ Item { planView: true // This is the center rectangle of the map which is not obscured by tools - property rect centerViewport: Qt.rect(_leftToolWidth, 0, editorMap.width - _leftToolWidth - _rightToolWidth, mapScale.y) + property rect centerViewport: Qt.rect(_leftToolWidth + _margin, _toolsTopMargin, editorMap.width - _leftToolWidth - _rightToolWidth - (_margin * 2), mapScale.y - _margin - _toolsTopMargin) property real _leftToolWidth: toolStrip.x + toolStrip.width property real _rightToolWidth: rightPanel.width + rightPanel.anchors.rightMargin @@ -455,6 +454,18 @@ Item { } } + PlanStartOverlay { + id: startOverlay + x: editorMap.centerViewport.left + y: editorMap.centerViewport.top + width: editorMap.centerViewport.width + height: editorMap.centerViewport.height + z: QGroundControl.zOrderMapItems + 2 + visible: !_planMasterController.containsItems + planMasterController: _planMasterController + mapControl: editorMap + } + // Add the mission item visuals to the map Repeater { model: _editingLayer == _layerMission ? _missionController.visualItems : undefined @@ -571,7 +582,7 @@ Item { id: toolStrip anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 anchors.left: parent.left - anchors.topMargin: ScreenTools.defaultFontPixelHeight * 0.5 + anchors.topMargin: _toolsTopMargin anchors.top: parent.top z: QGroundControl.zOrderWidgets maxHeight: mapScale.y - toolStrip.y @@ -670,7 +681,7 @@ Item { // Right Panel Controls Item { anchors.fill: rightPanel - anchors.topMargin: _toolButtonTopMargin + anchors.topMargin: _toolsTopMargin DeadMouseArea { anchors.fill: parent } @@ -917,6 +928,7 @@ Item { _planMasterController.removeAllFromVehicle() } _missionController.setCurrentPlanViewIndex(0, true) + startOverlay.visible = true hideDialog() } } @@ -929,6 +941,7 @@ Item { function accept() { _planMasterController.removeAllFromVehicle() _missionController.setCurrentPlanViewIndex(0, true) + startOverlay.visible = true hideDialog() } } diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index e14f8d5149a1b6f88fd48534cebd310c536e8b2a..d8a7b1728f687bc8d371a5c8d5129dbb17163db7 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -467,6 +467,7 @@ QGCApplication::~QGCApplication() void QGCApplication::_initCommon() { static const char* kRefOnly = "Reference only"; + static const char* kQGroundControl = "QGroundControl"; static const char* kQGCControllers = "QGroundControl.Controllers"; static const char* kQGCVehicle = "QGroundControl.Vehicle"; @@ -478,7 +479,6 @@ void QGCApplication::_initCommon() qmlRegisterType ("QGroundControl.Palette", 1, 0, "QGCMapPalette"); qmlRegisterUncreatableType (kQGCVehicle, 1, 0, "Vehicle", kRefOnly); - qmlRegisterUncreatableType (kQGCVehicle, 1, 0, "MissionItem", kRefOnly); qmlRegisterUncreatableType (kQGCVehicle, 1, 0, "MissionManager", kRefOnly); qmlRegisterUncreatableType (kQGCVehicle, 1, 0, "ParameterManager", kRefOnly); qmlRegisterUncreatableType (kQGCVehicle, 1, 0, "VehicleObjectAvoidance", kRefOnly); @@ -489,19 +489,19 @@ void QGCApplication::_initCommon() qmlRegisterUncreatableType (kQGCControllers, 1, 0, "MissionController", kRefOnly); qmlRegisterUncreatableType (kQGCControllers, 1, 0, "GeoFenceController", kRefOnly); qmlRegisterUncreatableType (kQGCControllers, 1, 0, "RallyPointController", kRefOnly); - qmlRegisterUncreatableType (kQGCControllers, 1, 0, "VisualMissionItem", kRefOnly); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "CoordinateVector", kRefOnly); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "QmlObjectListModel", kRefOnly); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "MissionCommandTree", kRefOnly); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "CameraCalc", kRefOnly); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LogReplayLink", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "MissionItem", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "VisualMissionItem", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "CoordinateVector", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "QmlObjectListModel", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "MissionCommandTree", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "CameraCalc", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "LogReplayLink", kRefOnly); + qmlRegisterType (kQGroundControl, 1, 0, "LogReplayLinkController"); #if defined(QGC_ENABLE_PAIRING) - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "PairingManager", kRefOnly); + qmlRegisterUncreatableType (kQGroundControl, 1, 0, "PairingManager", kRefOnly); #endif - qmlRegisterType ("QGroundControl", 1, 0, "LogReplayLinkController"); - qmlRegisterUncreatableType ("QGroundControl.AutoPilotPlugin", 1, 0, "AutoPilotPlugin", kRefOnly); qmlRegisterUncreatableType ("QGroundControl.AutoPilotPlugin", 1, 0, "VehicleComponent", kRefOnly); qmlRegisterUncreatableType ("QGroundControl.JoystickManager", 1, 0, "JoystickManager", kRefOnly); diff --git a/src/QmlControls/QGroundControl/Controls/qmldir b/src/QmlControls/QGroundControl/Controls/qmldir index fae59ca2d0aa2dbf6937ea1a1ba846b3c7feffae..8d3abf8b978a55fef6836b857c163d878045c1f2 100644 --- a/src/QmlControls/QGroundControl/Controls/qmldir +++ b/src/QmlControls/QGroundControl/Controls/qmldir @@ -40,6 +40,7 @@ ParameterEditor 1.0 ParameterEditor.qml ParameterEditorDialog 1.0 ParameterEditorDialog.qml PIDTuning 1.0 PIDTuning.qml PlanEditToolbar 1.0 PlanEditToolbar.qml +PlanStartOverlay 1.0 PlanStartOverlay.qml PreFlightCheckButton 1.0 PreFlightCheckButton.qml PreFlightCheckGroup 1.0 PreFlightCheckGroup.qml PreFlightCheckModel 1.0 PreFlightCheckModel.qml