Commit d499b513 authored by Don Gagne's avatar Don Gagne

Mission raw/friendly edit support

parent f89fe8e3
...@@ -95,6 +95,8 @@ ...@@ -95,6 +95,8 @@
<file alias="Signal100.svg">src/ui/toolbar/Images/Signal100.svg</file> <file alias="Signal100.svg">src/ui/toolbar/Images/Signal100.svg</file>
<file alias="Yield.svg">src/ui/toolbar/Images/Yield.svg</file> <file alias="Yield.svg">src/ui/toolbar/Images/Yield.svg</file>
<file alias="CogWheel.svg">src/MissionManager/CogWheel.svg</file>
</qresource> </qresource>
<qresource prefix="/res"> <qresource prefix="/res">
......
...@@ -247,8 +247,9 @@ HEADERS += \ ...@@ -247,8 +247,9 @@ HEADERS += \
src/Joystick/JoystickManager.h \ src/Joystick/JoystickManager.h \
src/LogCompressor.h \ src/LogCompressor.h \
src/MG.h \ src/MG.h \
src/MissionManager/MissionManager.h \
src/MissionManager/MissionController.h \ src/MissionManager/MissionController.h \
src/MissionManager/MissionItem.h \
src/MissionManager/MissionManager.h \
src/QGC.h \ src/QGC.h \
src/QGCApplication.h \ src/QGCApplication.h \
src/QGCComboBox.h \ src/QGCComboBox.h \
...@@ -291,7 +292,6 @@ HEADERS += \ ...@@ -291,7 +292,6 @@ HEADERS += \
src/ui/toolbar/MainToolBarController.h \ src/ui/toolbar/MainToolBarController.h \
src/ui/uas/QGCUnconnectedInfoWidget.h \ src/ui/uas/QGCUnconnectedInfoWidget.h \
src/ui/uas/UASMessageView.h \ src/ui/uas/UASMessageView.h \
src/MissionItem.h \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \
src/QmlControls/QGCImageProvider.h \ src/QmlControls/QGCImageProvider.h \
...@@ -363,8 +363,9 @@ SOURCES += \ ...@@ -363,8 +363,9 @@ SOURCES += \
src/Joystick/JoystickManager.cc \ src/Joystick/JoystickManager.cc \
src/LogCompressor.cc \ src/LogCompressor.cc \
src/main.cc \ src/main.cc \
src/MissionManager/MissionManager.cc \
src/MissionManager/MissionController.cc \ src/MissionManager/MissionController.cc \
src/MissionManager/MissionItem.cc \
src/MissionManager/MissionManager.cc \
src/QGC.cc \ src/QGC.cc \
src/QGCApplication.cc \ src/QGCApplication.cc \
src/QGCComboBox.cc \ src/QGCComboBox.cc \
...@@ -402,7 +403,6 @@ SOURCES += \ ...@@ -402,7 +403,6 @@ SOURCES += \
src/ui/toolbar/MainToolBarController.cc \ src/ui/toolbar/MainToolBarController.cc \
src/ui/uas/QGCUnconnectedInfoWidget.cc \ src/ui/uas/QGCUnconnectedInfoWidget.cc \
src/ui/uas/UASMessageView.cc \ src/ui/uas/UASMessageView.cc \
src/MissionItem.cc \
src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \ src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \
src/QmlControls/QGCImageProvider.cc \ src/QmlControls/QGCImageProvider.cc \
...@@ -471,9 +471,9 @@ HEADERS += \ ...@@ -471,9 +471,9 @@ HEADERS += \
src/FactSystem/FactSystemTestBase.h \ src/FactSystem/FactSystemTestBase.h \
src/FactSystem/FactSystemTestGeneric.h \ src/FactSystem/FactSystemTestGeneric.h \
src/FactSystem/FactSystemTestPX4.h \ src/FactSystem/FactSystemTestPX4.h \
src/MissionItemTest.h \
src/MissionManager/MissionControllerTest.h \ src/MissionManager/MissionControllerTest.h \
src/MissionManager/MissionControllerManagerTest.h \ src/MissionManager/MissionControllerManagerTest.h \
src/MissionManager/MissionItemTest.h \
src/MissionManager/MissionManagerTest.h \ src/MissionManager/MissionManagerTest.h \
src/qgcunittest/GeoTest.h \ src/qgcunittest/GeoTest.h \
src/qgcunittest/FileDialogTest.h \ src/qgcunittest/FileDialogTest.h \
...@@ -494,9 +494,9 @@ SOURCES += \ ...@@ -494,9 +494,9 @@ SOURCES += \
src/FactSystem/FactSystemTestBase.cc \ src/FactSystem/FactSystemTestBase.cc \
src/FactSystem/FactSystemTestGeneric.cc \ src/FactSystem/FactSystemTestGeneric.cc \
src/FactSystem/FactSystemTestPX4.cc \ src/FactSystem/FactSystemTestPX4.cc \
src/MissionItemTest.cc \
src/MissionManager/MissionControllerTest.cc \ src/MissionManager/MissionControllerTest.cc \
src/MissionManager/MissionControllerManagerTest.cc \ src/MissionManager/MissionControllerManagerTest.cc \
src/MissionManager/MissionItemTest.cc \
src/MissionManager/MissionManagerTest.cc \ src/MissionManager/MissionManagerTest.cc \
src/qgcunittest/GeoTest.cc \ src/qgcunittest/GeoTest.cc \
src/qgcunittest/FileDialogTest.cc \ src/qgcunittest/FileDialogTest.cc \
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
<file alias="PowerComponent.qml">src/AutoPilotPlugins/PX4/PowerComponent.qml</file> <file alias="PowerComponent.qml">src/AutoPilotPlugins/PX4/PowerComponent.qml</file>
<file alias="PowerComponentSummary.qml">src/AutoPilotPlugins/PX4/PowerComponentSummary.qml</file> <file alias="PowerComponentSummary.qml">src/AutoPilotPlugins/PX4/PowerComponentSummary.qml</file>
<file alias="PX4FlowSensor.qml">src/VehicleSetup/PX4FlowSensor.qml</file> <file alias="PX4FlowSensor.qml">src/VehicleSetup/PX4FlowSensor.qml</file>
<file alias="QGroundControl/Controls/ClickableColor.qml">src/QmlControls/ClickableColor.qml</file> <file alias="QGroundControl/Controls/ClickableColor.qml">src/QmlControls/ClickableColor.qml</file>
<file alias="QGroundControl/Controls/DropButton.qml">src/QmlControls/DropButton.qml</file> <file alias="QGroundControl/Controls/DropButton.qml">src/QmlControls/DropButton.qml</file>
<file alias="QGroundControl/Controls/ExclusiveGroupItem.qml">src/QmlControls/ExclusiveGroupItem.qml</file> <file alias="QGroundControl/Controls/ExclusiveGroupItem.qml">src/QmlControls/ExclusiveGroupItem.qml</file>
...@@ -37,7 +36,6 @@ ...@@ -37,7 +36,6 @@
<file alias="QGroundControl/Controls/MainToolBarIndicators.qml">src/ui/toolbar/MainToolBarIndicators.qml</file> <file alias="QGroundControl/Controls/MainToolBarIndicators.qml">src/ui/toolbar/MainToolBarIndicators.qml</file>
<file alias="QGroundControl/Controls/MissionItemEditor.qml">src/QmlControls/MissionItemEditor.qml</file> <file alias="QGroundControl/Controls/MissionItemEditor.qml">src/QmlControls/MissionItemEditor.qml</file>
<file alias="QGroundControl/Controls/MissionItemIndexLabel.qml">src/QmlControls/MissionItemIndexLabel.qml</file> <file alias="QGroundControl/Controls/MissionItemIndexLabel.qml">src/QmlControls/MissionItemIndexLabel.qml</file>
<file alias="QGroundControl/Controls/MissionItemSummary.qml">src/QmlControls/MissionItemSummary.qml</file>
<file alias="QGroundControl/Controls/ModeSwitchDisplay.qml">src/QmlControls/ModeSwitchDisplay.qml</file> <file alias="QGroundControl/Controls/ModeSwitchDisplay.qml">src/QmlControls/ModeSwitchDisplay.qml</file>
<file alias="QGroundControl/Controls/ParameterEditor.qml">src/QmlControls/ParameterEditor.qml</file> <file alias="QGroundControl/Controls/ParameterEditor.qml">src/QmlControls/ParameterEditor.qml</file>
<file alias="QGroundControl/Controls/ParameterEditorDialog.qml">src/QmlControls/ParameterEditorDialog.qml</file> <file alias="QGroundControl/Controls/ParameterEditorDialog.qml">src/QmlControls/ParameterEditorDialog.qml</file>
...@@ -62,20 +60,17 @@ ...@@ -62,20 +60,17 @@
<file alias="QGroundControl/Controls/VehicleRotationCal.qml">src/QmlControls/VehicleRotationCal.qml</file> <file alias="QGroundControl/Controls/VehicleRotationCal.qml">src/QmlControls/VehicleRotationCal.qml</file>
<file alias="QGroundControl/Controls/VehicleSummaryRow.qml">src/QmlControls/VehicleSummaryRow.qml</file> <file alias="QGroundControl/Controls/VehicleSummaryRow.qml">src/QmlControls/VehicleSummaryRow.qml</file>
<file alias="QGroundControl/Controls/ViewWidget.qml">src/ViewWidgets/ViewWidget.qml</file> <file alias="QGroundControl/Controls/ViewWidget.qml">src/ViewWidgets/ViewWidget.qml</file>
<file alias="QGroundControl/FactControls/FactCheckBox.qml">src/FactSystem/FactControls/FactCheckBox.qml</file> <file alias="QGroundControl/FactControls/FactCheckBox.qml">src/FactSystem/FactControls/FactCheckBox.qml</file>
<file alias="QGroundControl/FactControls/FactComboBox.qml">src/FactSystem/FactControls/FactComboBox.qml</file> <file alias="QGroundControl/FactControls/FactComboBox.qml">src/FactSystem/FactControls/FactComboBox.qml</file>
<file alias="QGroundControl/FactControls/FactLabel.qml">src/FactSystem/FactControls/FactLabel.qml</file> <file alias="QGroundControl/FactControls/FactLabel.qml">src/FactSystem/FactControls/FactLabel.qml</file>
<file alias="QGroundControl/FactControls/FactPanel.qml">src/FactSystem/FactControls/FactPanel.qml</file> <file alias="QGroundControl/FactControls/FactPanel.qml">src/FactSystem/FactControls/FactPanel.qml</file>
<file alias="QGroundControl/FactControls/FactTextField.qml">src/FactSystem/FactControls/FactTextField.qml</file> <file alias="QGroundControl/FactControls/FactTextField.qml">src/FactSystem/FactControls/FactTextField.qml</file>
<file alias="QGroundControl/FactControls/qmldir">src/FactSystem/FactControls/qmldir</file> <file alias="QGroundControl/FactControls/qmldir">src/FactSystem/FactControls/qmldir</file>
<file alias="QGroundControl/FlightDisplay/FlightDisplayView.qml">src/FlightDisplay/FlightDisplayView.qml</file> <file alias="QGroundControl/FlightDisplay/FlightDisplayView.qml">src/FlightDisplay/FlightDisplayView.qml</file>
<file alias="QGroundControl/FlightDisplay/FlightDisplayViewMap.qml">src/FlightDisplay/FlightDisplayViewMap.qml</file> <file alias="QGroundControl/FlightDisplay/FlightDisplayViewMap.qml">src/FlightDisplay/FlightDisplayViewMap.qml</file>
<file alias="QGroundControl/FlightDisplay/FlightDisplayViewVideo.qml">src/FlightDisplay/FlightDisplayViewVideo.qml</file> <file alias="QGroundControl/FlightDisplay/FlightDisplayViewVideo.qml">src/FlightDisplay/FlightDisplayViewVideo.qml</file>
<file alias="QGroundControl/FlightDisplay/FlightDisplayViewWidgets.qml">src/FlightDisplay/FlightDisplayViewWidgets.qml</file> <file alias="QGroundControl/FlightDisplay/FlightDisplayViewWidgets.qml">src/FlightDisplay/FlightDisplayViewWidgets.qml</file>
<file alias="QGroundControl/FlightDisplay/qmldir">src/FlightDisplay/qmldir</file> <file alias="QGroundControl/FlightDisplay/qmldir">src/FlightDisplay/qmldir</file>
<file alias="QGroundControl/FlightMap/FlightMap.qml">src/FlightMap/FlightMap.qml</file> <file alias="QGroundControl/FlightMap/FlightMap.qml">src/FlightMap/FlightMap.qml</file>
<file alias="QGroundControl/FlightMap/MissionItemIndicator.qml">src/FlightMap/MapItems/MissionItemIndicator.qml</file> <file alias="QGroundControl/FlightMap/MissionItemIndicator.qml">src/FlightMap/MapItems/MissionItemIndicator.qml</file>
<file alias="QGroundControl/FlightMap/MissionItemView.qml">src/FlightMap/MapItems/MissionItemView.qml</file> <file alias="QGroundControl/FlightMap/MissionItemView.qml">src/FlightMap/MapItems/MissionItemView.qml</file>
...@@ -90,10 +85,8 @@ ...@@ -90,10 +85,8 @@
<file alias="QGroundControl/FlightMap/QGCVideoBackground.qml">src/FlightMap/QGCVideoBackground.qml</file> <file alias="QGroundControl/FlightMap/QGCVideoBackground.qml">src/FlightMap/QGCVideoBackground.qml</file>
<file alias="QGroundControl/FlightMap/qmldir">src/FlightMap/qmldir</file> <file alias="QGroundControl/FlightMap/qmldir">src/FlightMap/qmldir</file>
<file alias="QGroundControl/FlightMap/VehicleMapItem.qml">src/FlightMap/MapItems/VehicleMapItem.qml</file> <file alias="QGroundControl/FlightMap/VehicleMapItem.qml">src/FlightMap/MapItems/VehicleMapItem.qml</file>
<file alias="QGroundControl/ScreenTools/qmldir">src/QmlControls/QGroundControl.ScreenTools.qmldir</file> <file alias="QGroundControl/ScreenTools/qmldir">src/QmlControls/QGroundControl.ScreenTools.qmldir</file>
<file alias="QGroundControl/ScreenTools/ScreenTools.qml">src/QmlControls/ScreenTools.qml</file> <file alias="QGroundControl/ScreenTools/ScreenTools.qml">src/QmlControls/ScreenTools.qml</file>
<file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file> <file alias="QmlTest.qml">src/QmlControls/QmlTest.qml</file>
<file alias="RadioComponent.qml">src/AutoPilotPlugins/PX4/RadioComponent.qml</file> <file alias="RadioComponent.qml">src/AutoPilotPlugins/PX4/RadioComponent.qml</file>
<file alias="RadioComponentSummary.qml">src/AutoPilotPlugins/PX4/RadioComponentSummary.qml</file> <file alias="RadioComponentSummary.qml">src/AutoPilotPlugins/PX4/RadioComponentSummary.qml</file>
...@@ -107,4 +100,7 @@ ...@@ -107,4 +100,7 @@
<file alias="test.qml">src/test.qml</file> <file alias="test.qml">src/test.qml</file>
<file alias="VehicleSummary.qml">src/VehicleSetup/VehicleSummary.qml</file> <file alias="VehicleSummary.qml">src/VehicleSetup/VehicleSummary.qml</file>
</qresource> </qresource>
<qresource prefix="/json">
<file alias="MavCmdInfo.json">src/MissionManager/MavCmdInfo.json</file>
</qresource>
</RCC> </RCC>
...@@ -40,7 +40,8 @@ import QGroundControl.Controllers 1.0 ...@@ -40,7 +40,8 @@ import QGroundControl.Controllers 1.0
QGCView { QGCView {
id: _root id: _root
viewPanel: panel viewPanel: panel
topDialogMargin: height - mainWindow.availableHeight
// zOrder comes from the Loader in MainWindow.qml // zOrder comes from the Loader in MainWindow.qml
z: QGroundControl.zOrderTopMost z: QGroundControl.zOrderTopMost
...@@ -211,7 +212,6 @@ QGCView { ...@@ -211,7 +212,6 @@ QGCView {
property var missionItem property var missionItem
property var missionItemIndicator property var missionItemIndicator
property real heading: missionItem ? missionItem.heading : 0
readonly property real _radius: ScreenTools.defaultFontPixelHeight * 4 readonly property real _radius: ScreenTools.defaultFontPixelHeight * 4
readonly property real _arrowHeight: ScreenTools.defaultFontPixelHeight readonly property real _arrowHeight: ScreenTools.defaultFontPixelHeight
...@@ -328,30 +328,34 @@ QGCView { ...@@ -328,30 +328,34 @@ QGCView {
onClicked: setCurrentItem(object.sequenceNumber) onClicked: setCurrentItem(object.sequenceNumber)
Connections { function updateItemIndicator()
target: object {
if (object.isCurrentItem) {
onIsCurrentItemChanged: { _root.showDistance(object)
if (object.isCurrentItem) { if (object.specifiesCoordinate) {
_root.showDistance(object) // Setup our drag item
if (object.specifiesCoordinate) { if (object.sequenceNumber != 0) {
// Setup our drag item itemDragger.visible = true
if (object.sequenceNumber != 0) { itemDragger.missionItem = Qt.binding(function() { return object })
itemDragger.visible = true itemDragger.missionItemIndicator = Qt.binding(function() { return itemIndicator })
itemDragger.missionItem = Qt.binding(function() { return object }) } else {
itemDragger.missionItemIndicator = Qt.binding(function() { return itemIndicator }) itemDragger.clearItem()
} else {
itemDragger.clearItem()
}
// Move to the new position
editorMap.latitude = object.coordinate.latitude
editorMap.longitude = object.coordinate.longitude
} }
// Move to the new position
editorMap.latitude = object.coordinate.latitude
editorMap.longitude = object.coordinate.longitude
} }
} }
} }
Connections {
target: object
onIsCurrentItemChanged: updateItemIndicator()
onCommandChanged: updateItemIndicator()
}
// These are the non-coordinate child mission items attached to this item // These are the non-coordinate child mission items attached to this item
Row { Row {
anchors.top: parent.top anchors.top: parent.top
...@@ -380,7 +384,7 @@ QGCView { ...@@ -380,7 +384,7 @@ QGCView {
// Mission Item Editor // Mission Item Editor
Item { Item {
id: missionItemEditor id: missionItemEditor
height: mainWindow.avaiableHeight height: mainWindow.availableHeight
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
width: _rightPanelWidth width: _rightPanelWidth
...@@ -389,11 +393,10 @@ QGCView { ...@@ -389,11 +393,10 @@ QGCView {
z: QGroundControl.zOrderTopMost z: QGroundControl.zOrderTopMost
ListView { ListView {
id: missionItemSummaryList
anchors.fill: parent anchors.fill: parent
spacing: _margin / 2 spacing: _margin / 2
orientation: ListView.Vertical orientation: ListView.Vertical
model: controller.canEdit ? controller.missionItems : 0 model: controller.missionItems
property real _maxItemHeight: 0 property real _maxItemHeight: 0
...@@ -416,14 +419,6 @@ QGCView { ...@@ -416,14 +419,6 @@ QGCView {
} }
} }
} // ListView } // ListView
QGCLabel {
anchors.fill: parent
visible: !controller.canEdit
wrapMode: Text.WordWrap
text: "The set of mission items you have loaded cannot be edited by QGroundControl. " +
"You will only be able to save these to a file, or send them to a vehicle."
}
} // Item - Mission Item editor } // Item - Mission Item editor
/* /*
......
/*===================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include <QStringList>
#include <QDebug>
#include "MissionItem.h"
QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog")
const double MissionItem::defaultPitch = 15.0;
const double MissionItem::defaultHeading = 0.0;
const double MissionItem::defaultAltitude = 25.0;
const double MissionItem::defaultAcceptanceRadius = 3.0;
const double MissionItem::defaultLoiterOrbitRadius = 10.0;
const double MissionItem::defaultLoiterTurns = 1.0;
QDebug operator<<(QDebug dbg, const MissionItem& missionItem)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "MissionItem(" << missionItem.coordinate() << ")";
return dbg;
}
QDebug operator<<(QDebug dbg, const MissionItem* missionItem)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "MissionItem(" << missionItem->coordinate() << ")";
return dbg;
}
const MissionItem::MavCmd2Name_t MissionItem::_rgMavCmd2Name[_cMavCmd2Name] = {
{ MAV_CMD_NAV_WAYPOINT, "Waypoint" },
{ MAV_CMD_NAV_LOITER_UNLIM, "Loiter" },
{ MAV_CMD_NAV_LOITER_TURNS, "Loiter (turns)" },
{ MAV_CMD_NAV_LOITER_TIME, "Loiter (seconds)" },
{ MAV_CMD_NAV_RETURN_TO_LAUNCH, "Return Home" },
{ MAV_CMD_NAV_LAND, "Land" },
{ MAV_CMD_NAV_TAKEOFF, "Takeoff" },
{ MAV_CMD_CONDITION_DELAY, "Delay" },
{ MAV_CMD_DO_JUMP, "Jump To Command" },
};
MissionItem::MissionItem(QObject* parent,
int sequenceNumber,
QGeoCoordinate coordinate,
int command,
double param1,
double param2,
double param3,
double param4,
bool autocontinue,
bool isCurrentItem,
int frame)
: QObject(parent)
, _sequenceNumber(sequenceNumber)
, _frame(-1) // Forces set of _altitudeRelativeToHomeFact
, _command((MavlinkQmlSingleton::Qml_MAV_CMD)command)
, _autocontinue(autocontinue)
, _isCurrentItem(isCurrentItem)
, _reachedTime(0)
, _distance(0.0)
, _headingDegreesFact(NULL)
, _dirty(false)
, _homePositionSpecialCase(false)
, _homePositionValid(false)
{
_latitudeFact = new Fact(0, "Latitude:", FactMetaData::valueTypeDouble, this);
_longitudeFact = new Fact(0, "Longitude:", FactMetaData::valueTypeDouble, this);
_altitudeFact = new Fact(0, "Altitude:", FactMetaData::valueTypeDouble, this);
_headingDegreesFact = new Fact(0, "Heading:", FactMetaData::valueTypeDouble, this);
_loiterOrbitRadiusFact = new Fact(0, "Radius:", FactMetaData::valueTypeDouble, this);
_param1Fact = new Fact(0, QString(), FactMetaData::valueTypeDouble, this);
_param2Fact = new Fact(0, QString(), FactMetaData::valueTypeDouble, this);
_altitudeRelativeToHomeFact = new Fact(0, "Altitude is relative to home", FactMetaData::valueTypeDouble, this);
setFrame(frame);
setCoordinate(coordinate);
setParam1(param1);
setParam2(param2);
_setYawRadians(param4);
setLoiterOrbitRadius(param3);
// FIXME: Need to fill out more meta data
FactMetaData* latitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble, _latitudeFact);
latitudeMetaData->setUnits("deg");
latitudeMetaData->setDecimalPlaces(7);
FactMetaData* longitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble, _longitudeFact);
longitudeMetaData->setUnits("deg");
longitudeMetaData->setDecimalPlaces(7);
FactMetaData* altitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble, _altitudeFact);
altitudeMetaData->setUnits("meters");
FactMetaData* headingMetaData = new FactMetaData(FactMetaData::valueTypeDouble, _headingDegreesFact);
headingMetaData->setUnits("deg");
_pitchMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
_pitchMetaData->setUnits("deg");
_acceptanceRadiusMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
_acceptanceRadiusMetaData->setUnits("meters");
_holdTimeMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
_holdTimeMetaData->setUnits("seconds");
FactMetaData* loiterOrbitRadiusMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
loiterOrbitRadiusMetaData->setUnits("meters");
_loiterTurnsMetaData = new FactMetaData(FactMetaData::valueTypeInt32, this);
_loiterTurnsMetaData->setUnits("count");
_loiterSecondsMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
_loiterSecondsMetaData->setUnits("seconds");
_delaySecondsMetaData = new FactMetaData(FactMetaData::valueTypeDouble, this);
_delaySecondsMetaData->setUnits("seconds");
_jumpSequenceMetaData = new FactMetaData(FactMetaData::valueTypeInt32, this);
_jumpSequenceMetaData->setUnits("#");
_jumpRepeatMetaData = new FactMetaData(FactMetaData::valueTypeInt32, this);
_jumpRepeatMetaData->setUnits("count");
_latitudeFact->setMetaData(latitudeMetaData);
_longitudeFact->setMetaData(longitudeMetaData);
_altitudeFact->setMetaData(altitudeMetaData);
_headingDegreesFact->setMetaData(headingMetaData);
_loiterOrbitRadiusFact->setMetaData(loiterOrbitRadiusMetaData);
_connectSignals();
}
MissionItem::MissionItem(const MissionItem& other, QObject* parent)
: QObject(parent)
{
_latitudeFact = new Fact(this);
_longitudeFact = new Fact(this);
_altitudeFact = new Fact(this);
_headingDegreesFact = new Fact(this);
_loiterOrbitRadiusFact = new Fact(this);
_param1Fact = new Fact(this);
_param2Fact = new Fact(this);
_altitudeRelativeToHomeFact = new Fact(this);
_pitchMetaData = new FactMetaData(this);
_acceptanceRadiusMetaData = new FactMetaData(this);
_holdTimeMetaData = new FactMetaData(this);
_loiterTurnsMetaData = new FactMetaData(this);
_loiterSecondsMetaData = new FactMetaData(this);
_delaySecondsMetaData = new FactMetaData(this);
_jumpSequenceMetaData = new FactMetaData(this);
_jumpRepeatMetaData = new FactMetaData(this);
_connectSignals();
*this = other;
}
const MissionItem& MissionItem::operator=(const MissionItem& other)
{
_sequenceNumber = other._sequenceNumber;
_isCurrentItem = other._isCurrentItem;
_frame = other._frame;
_command = other._command;
_autocontinue = other._autocontinue;
_reachedTime = other._reachedTime;
_distance = other._distance;
_altitudeRelativeToHomeFact = other._altitudeRelativeToHomeFact;
_dirty = other._dirty;
_homePositionSpecialCase = other._homePositionSpecialCase;
_homePositionValid = other._homePositionValid;
*_latitudeFact = *other._latitudeFact;
*_longitudeFact = *other._longitudeFact;
*_altitudeFact = *other._altitudeFact;
*_headingDegreesFact = *other._headingDegreesFact;
*_loiterOrbitRadiusFact = *other._loiterOrbitRadiusFact;
*_param1Fact = *other._param1Fact;
*_param2Fact = *other._param2Fact;
*_pitchMetaData = *other._pitchMetaData;
*_acceptanceRadiusMetaData = *other._acceptanceRadiusMetaData;
*_holdTimeMetaData = *other._holdTimeMetaData;
*_loiterTurnsMetaData = *other._loiterTurnsMetaData;
*_loiterSecondsMetaData = *other._loiterSecondsMetaData;
*_delaySecondsMetaData = *other._delaySecondsMetaData;
*_jumpSequenceMetaData = *other._jumpSequenceMetaData;
*_jumpRepeatMetaData = *other._jumpRepeatMetaData;
return *this;
}
void MissionItem::_connectSignals(void)
{
// Connect to valueChanged to track dirty state
connect(_latitudeFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_longitudeFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_altitudeFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_headingDegreesFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_loiterOrbitRadiusFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_param1Fact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_param2Fact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
connect(_altitudeRelativeToHomeFact, &Fact::valueChanged, this, &MissionItem::_factValueChanged);
// Connect valueChanged signals so we can output coordinateChanged signal
connect(_latitudeFact, &Fact::valueChanged, this, &MissionItem::_coordinateFactChanged);
connect(_longitudeFact, &Fact::valueChanged, this, &MissionItem::_coordinateFactChanged);
connect(_altitudeFact, &Fact::valueChanged, this, &MissionItem::_coordinateFactChanged);
connect(_headingDegreesFact, &Fact::valueChanged, this, &MissionItem::_headingDegreesFactChanged);
connect(_altitudeRelativeToHomeFact, &Fact::valueChanged, this, &MissionItem::_altitudeRelativeToHomeFactChanged);
}
MissionItem::~MissionItem()
{
}
bool MissionItem::isNavigationType()
{
return (_command < MavlinkQmlSingleton::MAV_CMD_NAV_LAST);
}
void MissionItem::save(QTextStream &saveStream)
{
QString position("%1\t%2\t%3");
position = position.arg(x(), 0, 'g', 18);
position = position.arg(y(), 0, 'g', 18);
position = position.arg(z(), 0, 'g', 18);
QString parameters("%1\t%2\t%3\t%4");
parameters = parameters.arg(param1(), 0, 'g', 18).arg(param2(), 0, 'g', 18).arg(loiterOrbitRadius(), 0, 'g', 18).arg(_yawRadians(), 0, 'g', 18);
// FORMAT: <INDEX> <CURRENT WP> <COORD FRAME> <COMMAND> <PARAM1> <PARAM2> <PARAM3> <PARAM4> <PARAM5/X/LONGITUDE> <PARAM6/Y/LATITUDE> <PARAM7/Z/ALTITUDE> <AUTOCONTINUE> <DESCRIPTION>
// as documented here: http://qgroundcontrol.org/waypoint_protocol
saveStream << this->sequenceNumber() << "\t" << this->isCurrentItem() << "\t" << this->frame() << "\t" << this->command() << "\t" << parameters << "\t" << position << "\t" << this->autoContinue() << "\r\n"; //"\t" << this->getDescription() << "\r\n";
}
bool MissionItem::load(QTextStream &loadStream)
{
const QStringList &wpParams = loadStream.readLine().split("\t");
if (wpParams.size() == 12) {
setSequenceNumber(wpParams[0].toInt());
setIsCurrentItem(wpParams[1].toInt() == 1 ? true : false);
setFrame(wpParams[2].toInt());
setAction(wpParams[3].toInt());
setParam1(wpParams[4].toDouble());
setParam2(wpParams[5].toDouble());
setLoiterOrbitRadius(wpParams[6].toDouble());
_setYawRadians(wpParams[7].toDouble());
setLatitude(wpParams[8].toDouble());
setLongitude(wpParams[9].toDouble());
setAltitude(wpParams[10].toDouble());
_autocontinue = (wpParams[11].toInt() == 1 ? true : false);
return true;
}
return false;
}
void MissionItem::setSequenceNumber(int sequenceNumber)
{
_sequenceNumber = sequenceNumber;
emit sequenceNumberChanged(_sequenceNumber);
}
void MissionItem::setX(double x)
{
if (!isinf(x) && !isnan(x) && ((_frame == MAV_FRAME_LOCAL_NED) || (_frame == MAV_FRAME_LOCAL_ENU)))
{
setLatitude(x);
}
}
void MissionItem::setY(double y)
{
if (!isinf(y) && !isnan(y) && ((_frame == MAV_FRAME_LOCAL_NED) || (_frame == MAV_FRAME_LOCAL_ENU)))
{
setLongitude(y);
}
}
void MissionItem::setZ(double z)
{
if (!isinf(z) && !isnan(z) && ((_frame == MAV_FRAME_LOCAL_NED) || (_frame == MAV_FRAME_LOCAL_ENU)))
{
setAltitude(z);
}
}
void MissionItem::setLatitude(double lat)
{
if (_latitudeFact->value().toDouble() != lat)
{
_latitudeFact->setValue(lat);
emit coordinateChanged(coordinate());
}
}
void MissionItem::setLongitude(double lon)
{
if (_longitudeFact->value().toDouble() != lon)
{
_longitudeFact->setValue(lon);
emit coordinateChanged(coordinate());
}
}
void MissionItem::setAltitude(double altitude)
{
if (_altitudeFact->value().toDouble() != altitude)
{
_altitudeFact->setValue(altitude);
emit valueStringsChanged(valueStrings());
emit coordinateChanged(coordinate());
}
}
void MissionItem::setAction(int /*MAV_CMD*/ action)
{
if (_command != action) {
_command = (MavlinkQmlSingleton::Qml_MAV_CMD)action;
// Fix defaults according to WP type
switch (_command) {
case MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF:
setParam1(defaultPitch);
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT:
setAcceptanceRadius(defaultAcceptanceRadius);
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_UNLIM:
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_TIME:
setLoiterOrbitRadius(defaultLoiterOrbitRadius);
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_TURNS:
setLoiterOrbitRadius(defaultLoiterOrbitRadius);
setParam1(defaultLoiterTurns);
break;
default:
break;
}
setHeadingDegrees(defaultHeading);
setAltitude(defaultAltitude);
if (specifiesCoordinate()) {
if (_frame != MAV_FRAME_GLOBAL && _frame != MAV_FRAME_GLOBAL_RELATIVE_ALT) {
setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT);
}
} else {
setFrame(MAV_FRAME_MISSION);
}
emit commandNameChanged(commandName());
emit commandChanged((MavlinkQmlSingleton::Qml_MAV_CMD)_command);
emit valueLabelsChanged(valueLabels());
emit valueStringsChanged(valueStrings());
}
}
int MissionItem::frame(void) const
{
if (_altitudeRelativeToHomeFact->value().toBool()) {
return MAV_FRAME_GLOBAL_RELATIVE_ALT;
} else {
return _frame;
}
}
void MissionItem::setFrame(int /*MAV_FRAME*/ frame)
{
if (_frame != frame) {
_altitudeRelativeToHomeFact->setValue(frame == MAV_FRAME_GLOBAL_RELATIVE_ALT);
_frame = frame;
emit frameChanged(_frame);
}
}
void MissionItem::setAutocontinue(bool autoContinue)
{
if (_autocontinue != autoContinue) {
_autocontinue = autoContinue;
emit autoContinueChanged(_autocontinue);
}
}
void MissionItem::setIsCurrentItem(bool isCurrentItem)
{
if (_isCurrentItem != isCurrentItem) {
_isCurrentItem = isCurrentItem;
emit isCurrentItemChanged(isCurrentItem);
}
}
void MissionItem::setAcceptanceRadius(double radius)
{
setParam2(radius);
}
void MissionItem::setParam1(double param)
{
if (param1() != param)
{
_param1Fact->setValue(param);
emit valueStringsChanged(valueStrings());
}
}
void MissionItem::setParam2(double param)
{
if (param2() != param)
{
_param2Fact->setValue(param);
emit valueStringsChanged(valueStrings());
}
}
void MissionItem::setParam3(double param3)
{
setLoiterOrbitRadius(param3);
}
void MissionItem::setParam4(double param4)
{
_setYawRadians(param4);
}
void MissionItem::setParam5(double param5)
{
setLatitude(param5);
}
void MissionItem::setParam6(double param6)
{
setLongitude(param6);
}
void MissionItem::setParam7(double param7)
{
setAltitude(param7);
}
void MissionItem::setLoiterOrbitRadius(double radius)
{
if (loiterOrbitRadius() != radius) {
_loiterOrbitRadiusFact->setValue(radius);
emit valueStringsChanged(valueStrings());
}
}
void MissionItem::setHoldTime(int holdTime)
{
setParam1(holdTime);
}
void MissionItem::setHoldTime(double holdTime)
{
setParam1(holdTime);
}
bool MissionItem::specifiesCoordinate(void) const
{
switch (_command) {
case MAV_CMD_NAV_WAYPOINT:
case MAV_CMD_NAV_LOITER_UNLIM:
case MAV_CMD_NAV_LOITER_TURNS:
case MAV_CMD_NAV_LOITER_TIME:
case MAV_CMD_NAV_LAND:
case MAV_CMD_NAV_TAKEOFF:
return true;
default:
return false;
}
}
QString MissionItem::commandName(void)
{
QString type;
switch (_command) {
case MAV_CMD_NAV_WAYPOINT:
type = "Waypoint";
break;
case MAV_CMD_NAV_LOITER_UNLIM:
case MAV_CMD_NAV_LOITER_TURNS:
case MAV_CMD_NAV_LOITER_TIME:
type = "Loiter";
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
type = "Return Home";
break;
case MAV_CMD_NAV_LAND:
type = "Land";
break;
case MAV_CMD_NAV_TAKEOFF:
type = "Takeoff";
break;
case MAV_CMD_CONDITION_DELAY:
type = "Delay";
break;
case MAV_CMD_DO_JUMP:
type = "Jump To Command";
break;
default:
type = QString("Unknown (%1)").arg(_command);
break;
}
return type;
}
QString MissionItem::commandDescription(void)
{
QString description;
switch (_command) {
case MAV_CMD_NAV_WAYPOINT:
description = "Travel to a position in 3D space.";
break;
case MAV_CMD_NAV_LOITER_UNLIM:
description = "Travel to a position and Loiter around the specified radius indefinitely.";
break;
case MAV_CMD_NAV_LOITER_TURNS:
description = "Travel to a position and Loiter around the specified radius for a number of turns.";
break;
case MAV_CMD_NAV_LOITER_TIME:
description = "Travel to a position and Loiter around the specified radius for an amount of time.";
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
description = "Send the vehicle back to the home position.";
break;
case MAV_CMD_NAV_LAND:
description = "Land vehicle at the specified location.";
break;
case MAV_CMD_NAV_TAKEOFF:
description = "Take off from the ground and travel towards the specified position.";
break;
case MAV_CMD_CONDITION_DELAY:
description = "Delay";
break;
case MAV_CMD_DO_JUMP:
description = "Jump To Command";
break;
default:
description = QString("Unknown (%1)").arg(_command);
break;
}
return description;
}
QStringList MissionItem::valueLabels(void)
{
QStringList labels;
switch (_command) {
case MAV_CMD_NAV_WAYPOINT:
if (frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT) {
labels << "Alt (rel):";
} else {
labels << "Alt:";
}
labels << "Heading:" << "Radius:" << "Hold:";
break;
case MAV_CMD_NAV_LOITER_UNLIM:
labels << "Heading:" << "Radius:";
break;
case MAV_CMD_NAV_LOITER_TURNS:
labels << "Heading:" << "Radius:"<< "Turns:";
break;
case MAV_CMD_NAV_LOITER_TIME:
labels << "Heading:" << "Radius:" << "Seconds:";
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
break;
case MAV_CMD_NAV_LAND:
if (frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT) {
labels << "Alt (rel):";
} else {
labels << "Alt:";
}
labels << "Heading:";
break;
case MAV_CMD_NAV_TAKEOFF:
if (frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT) {
labels << "Alt (rel):";
} else {
labels << "Alt:";
}
labels << "Heading:" << "Pitch:";
break;
case MAV_CMD_CONDITION_DELAY:
labels << "Seconds:";
break;
case MAV_CMD_DO_JUMP:
labels << "Jump to:" << "Repeat:";
break;
default:
break;
}
return labels;
}
QString MissionItem::_oneDecimalString(double value)
{
return QString("%1").arg(value, 0 /* min field width */, 'f' /* format */, 1 /* precision */);
}
QStringList MissionItem::valueStrings(void)
{
QStringList list;
switch (_command) {
case MAV_CMD_NAV_WAYPOINT:
list << _oneDecimalString(_altitudeFact->value().toDouble()) << _oneDecimalString(headingDegrees()) << _oneDecimalString(param2()) << _oneDecimalString(param1());
break;
case MAV_CMD_NAV_LOITER_UNLIM:
list << _oneDecimalString(headingDegrees()) << _oneDecimalString(loiterOrbitRadius());
break;
case MAV_CMD_NAV_LOITER_TURNS:
list << _oneDecimalString(headingDegrees()) << _oneDecimalString(loiterOrbitRadius()) << _oneDecimalString(param1());
break;
case MAV_CMD_NAV_LOITER_TIME:
list << _oneDecimalString(headingDegrees()) << _oneDecimalString(loiterOrbitRadius()) << _oneDecimalString(param1());
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
break;
case MAV_CMD_NAV_LAND:
list << _oneDecimalString(_altitudeFact->value().toDouble()) << _oneDecimalString(headingDegrees());
break;
case MAV_CMD_NAV_TAKEOFF:
list << _oneDecimalString(_altitudeFact->value().toDouble()) << _oneDecimalString(headingDegrees()) << _oneDecimalString(param1());
break;
case MAV_CMD_CONDITION_DELAY:
list << _oneDecimalString(param1());
break;
case MAV_CMD_DO_JUMP:
list << _oneDecimalString(param1()) << _oneDecimalString(param2());
break;
default:
break;
}
return list;
}
QStringList MissionItem::commandNames(void) {
QStringList list;
for (int i=0; i<_cMavCmd2Name; i++) {
list += _rgMavCmd2Name[i].name;
}
return list;
}
int MissionItem::commandByIndex(void)
{
for (int i=0; i<_cMavCmd2Name; i++) {
if (_rgMavCmd2Name[i].command == (MAV_CMD)_command) {
return i;
}
}
return -1;
}
void MissionItem::setCommandByIndex(int index)
{
if (index < 0 || index >= _cMavCmd2Name) {
qWarning() << "Invalid index" << index;
return;
}
setCommand((MavlinkQmlSingleton::Qml_MAV_CMD)_rgMavCmd2Name[index].command);
}
QmlObjectListModel* MissionItem::textFieldFacts(void)
{
QmlObjectListModel* model = new QmlObjectListModel(this);
switch ((MAV_CMD)_command) {
case MAV_CMD_NAV_WAYPOINT:
_param1Fact->_setName("Hold:");
_param1Fact->setMetaData(_holdTimeMetaData);
model->append(_altitudeFact);
if (!_homePositionSpecialCase) {
model->append(_param1Fact);
}
break;
case MAV_CMD_NAV_LOITER_UNLIM:
model->append(_altitudeFact);
model->append(_loiterOrbitRadiusFact);
break;
case MAV_CMD_NAV_LOITER_TURNS:
_param1Fact->_setName("Turns:");
_param1Fact->setMetaData(_loiterTurnsMetaData);
model->append(_altitudeFact);
model->append(_loiterOrbitRadiusFact);
model->append(_param1Fact);
break;
case MAV_CMD_NAV_LOITER_TIME:
_param1Fact->_setName("Seconds:");
_param1Fact->setMetaData(_loiterSecondsMetaData);
model->append(_altitudeFact);
model->append(_loiterOrbitRadiusFact);
model->append(_param1Fact);
break;
case MAV_CMD_NAV_LAND:
model->append(_altitudeFact);
break;
case MAV_CMD_NAV_TAKEOFF:
_param1Fact->_setName("Pitch:");
_param1Fact->setMetaData(_pitchMetaData);
model->append(_altitudeFact);
model->append(_param1Fact);
break;
case MAV_CMD_CONDITION_DELAY:
_param1Fact->_setName("Seconds:");
_param1Fact->setMetaData(_delaySecondsMetaData);
model->append(_param1Fact);
break;
case MAV_CMD_DO_JUMP:
_param1Fact->_setName("Seq #:");
_param1Fact->setMetaData(_jumpSequenceMetaData);
_param2Fact->_setName("Repeat:");
_param2Fact->setMetaData(_jumpRepeatMetaData);
model->append(_param1Fact);
model->append(_param2Fact);
break;
default:
break;
}
if (specifiesHeading()) {
model->append(_headingDegreesFact);
}
return model;
}
QmlObjectListModel* MissionItem::checkboxFacts(void)
{
QmlObjectListModel* model = new QmlObjectListModel(this);
switch ((MAV_CMD)_command) {
case MAV_CMD_NAV_WAYPOINT:
if (!_homePositionSpecialCase) {
model->append(_altitudeRelativeToHomeFact);
}
break;
case MAV_CMD_NAV_LOITER_UNLIM:
model->append(_altitudeRelativeToHomeFact);
break;
case MAV_CMD_NAV_LOITER_TURNS:
model->append(_altitudeRelativeToHomeFact);
break;
case MAV_CMD_NAV_LOITER_TIME:
model->append(_altitudeRelativeToHomeFact);
break;
case MAV_CMD_NAV_RETURN_TO_LAUNCH:
break;
case MAV_CMD_NAV_LAND:
model->append(_altitudeRelativeToHomeFact);
break;
case MAV_CMD_NAV_TAKEOFF:
model->append(_altitudeRelativeToHomeFact);
break;
default:
break;
}
return model;
}
double MissionItem::headingDegrees(void) const
{
return _headingDegreesFact->value().toDouble();
}
void MissionItem::setHeadingDegrees(double headingDegrees)
{
if (_headingDegreesFact->value().toDouble() != headingDegrees) {
_headingDegreesFact->setValue(headingDegrees);
emit valueStringsChanged(valueStrings());
emit headingDegreesChanged(headingDegrees);
}
}
double MissionItem::_yawRadians(void) const
{
return _headingDegreesFact->value().toDouble() * (M_PI / 180.0);
}
void MissionItem::_setYawRadians(double yawRadians)
{
setHeadingDegrees(yawRadians * (180 / M_PI));
}
QGeoCoordinate MissionItem::coordinate(void) const
{
return QGeoCoordinate(latitude(), longitude(), altitude());
}
void MissionItem::setCoordinate(const QGeoCoordinate& coordinate)
{
setLatitude(coordinate.latitude());
setLongitude(coordinate.longitude());
setAltitude(coordinate.altitude());
}
bool MissionItem::canEdit(void)
{
bool found = false;
for (int i=0; i<_cMavCmd2Name; i++) {
if (_rgMavCmd2Name[i].command == (MAV_CMD)_command) {
found = true;
break;
}
}
if (found) {
if (!_autocontinue) {
qCDebug(MissionItemLog) << "canEdit false due to _autocontinue != true";
return false;
}
if (_frame != MAV_FRAME_GLOBAL && _frame != MAV_FRAME_GLOBAL_RELATIVE_ALT && _frame != MAV_FRAME_MISSION) {
qCDebug(MissionItemLog) << "canEdit false due unsupported frame type:" << _frame;
return false;
}
return true;
} else {
qCDebug(MissionItemLog) << "canEdit false due unsupported command:" << _command;
return false;
}
}
void MissionItem::setDirty(bool dirty)
{
if (!_homePositionSpecialCase || !dirty) {
// Home position never affects dirty bit
_dirty = dirty;
// We want to emit dirtyChanged even if _dirty didn't change. This can be handy signal for
// any value within the item changing.
emit dirtyChanged(_dirty);
}
}
void MissionItem::_factValueChanged(QVariant value)
{
Q_UNUSED(value);
setDirty(true);
}
void MissionItem::_coordinateFactChanged(QVariant value)
{
Q_UNUSED(value);
emit coordinateChanged(coordinate());
}
bool MissionItem::specifiesHeading(void) const
{
switch ((MAV_CMD)_command) {
case MAV_CMD_NAV_LAND:
case MAV_CMD_NAV_TAKEOFF:
return true;
default:
return false;
}
}
void MissionItem::_headingDegreesFactChanged(QVariant value)
{
emit headingDegreesChanged(value.toDouble());
}
void MissionItem::_altitudeRelativeToHomeFactChanged(QVariant value)
{
// Don't call setFrame, that will cause a signalling loop
int frame = value.toBool() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL;
if (_frame != frame) {
_frame = frame;
emit frameChanged(_frame);
}
}
void MissionItem::setHomePositionValid(bool homePositionValid)
{
_homePositionValid = homePositionValid;
emit homePositionValidChanged(_homePositionValid);
}
void MissionItem::setDistance(double distance)
{
_distance = distance;
emit distanceChanged(_distance);
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef MissionItem_H
#define MissionItem_H
#include <QObject>
#include <QString>
#include <QtQml>
#include <QTextStream>
#include <QGeoCoordinate>
#include "QGCMAVLink.h"
#include "QGC.h"
#include "MavlinkQmlSingleton.h"
#include "QmlObjectListModel.h"
#include "Fact.h"
#include "QGCLoggingCategory.h"
#include "QmlObjectListModel.h"
Q_DECLARE_LOGGING_CATEGORY(MissionItemLog)
class MissionItem : public QObject
{
Q_OBJECT
public:
MissionItem(QObject *parent = 0,
int sequenceNumber = 0,
QGeoCoordinate coordiante = QGeoCoordinate(),
int action = MAV_CMD_NAV_WAYPOINT,
double param1 = 0.0,
double param2 = defaultAcceptanceRadius,
double param3 = defaultLoiterOrbitRadius,
double param4 = defaultHeading,
bool autocontinue = true,
bool isCurrentItem = false,
int frame = MAV_FRAME_GLOBAL_RELATIVE_ALT);
MissionItem(const MissionItem& other, QObject* parent = NULL);
~MissionItem();
const MissionItem& operator=(const MissionItem& other);
/// Returns true if the item has been modified since the last time dirty was false
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_PROPERTY(int sequenceNumber READ sequenceNumber WRITE setSequenceNumber NOTIFY sequenceNumberChanged)
Q_PROPERTY(bool isCurrentItem READ isCurrentItem WRITE setIsCurrentItem NOTIFY isCurrentItemChanged)
Q_PROPERTY(bool specifiesCoordinate READ specifiesCoordinate NOTIFY commandChanged)
Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged)
Q_PROPERTY(bool specifiesHeading READ specifiesHeading NOTIFY commandChanged)
Q_PROPERTY(double heading READ headingDegrees WRITE setHeadingDegrees NOTIFY headingDegreesChanged)
Q_PROPERTY(QStringList commandNames READ commandNames CONSTANT)
Q_PROPERTY(QString commandName READ commandName NOTIFY commandChanged)
Q_PROPERTY(QString commandDescription READ commandDescription NOTIFY commandChanged)
Q_PROPERTY(QStringList valueLabels READ valueLabels NOTIFY commandChanged)
Q_PROPERTY(QStringList valueStrings READ valueStrings NOTIFY valueStringsChanged)
Q_PROPERTY(int commandByIndex READ commandByIndex WRITE setCommandByIndex NOTIFY commandChanged)
Q_PROPERTY(QmlObjectListModel* textFieldFacts READ textFieldFacts NOTIFY commandChanged)
Q_PROPERTY(QmlObjectListModel* checkboxFacts READ checkboxFacts NOTIFY commandChanged)
Q_PROPERTY(MavlinkQmlSingleton::Qml_MAV_CMD command READ command WRITE setCommand NOTIFY commandChanged)
Q_PROPERTY(QmlObjectListModel* childItems READ childItems CONSTANT)
/// true: this item is being used as a home position indicator
Q_PROPERTY(bool homePosition READ homePosition CONSTANT)
/// true: home position should be shown
Q_PROPERTY(bool homePositionValid READ homePositionValid WRITE setHomePositionValid NOTIFY homePositionValidChanged)
/// Distance to previous waypoint, set by UI controller
Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged)
// Property accesors
int sequenceNumber(void) const { return _sequenceNumber; }
void setSequenceNumber(int sequenceNumber);
bool isCurrentItem(void) const { return _isCurrentItem; }
void setIsCurrentItem(bool isCurrentItem);
bool specifiesCoordinate(void) const;
QGeoCoordinate coordinate(void) const;
void setCoordinate(const QGeoCoordinate& coordinate);
bool specifiesHeading(void) const;
double headingDegrees(void) const;
void setHeadingDegrees(double headingDegrees);
// This is public for unit testing
double _yawRadians(void) const;
QStringList commandNames(void);
QString commandName(void);
QString commandDescription(void);
int commandByIndex(void);
void setCommandByIndex(int index);
MavlinkQmlSingleton::Qml_MAV_CMD command(void) { return (MavlinkQmlSingleton::Qml_MAV_CMD)_command; };
void setCommand(MavlinkQmlSingleton::Qml_MAV_CMD command) { setAction(command); }
QStringList valueLabels(void);
QStringList valueStrings(void);
QmlObjectListModel* textFieldFacts(void);
QmlObjectListModel* checkboxFacts(void);
bool dirty(void) { return _dirty; }
void setDirty(bool dirty);
QmlObjectListModel* childItems(void) { return &_childItems; }
bool homePosition(void) { return _homePositionSpecialCase; }
bool homePositionValid(void) { return _homePositionValid; }
void setHomePositionValid(bool homePositionValid);
double distance(void) { return _distance; }
void setDistance(double distance);
// C++ only methods
/// Returns true if this item can be edited in the ui
bool canEdit(void);
double latitude(void) const { return _latitudeFact->value().toDouble(); }
double longitude(void) const { return _longitudeFact->value().toDouble(); }
double altitude(void) const { return _altitudeFact->value().toDouble(); }
void setLatitude(double latitude);
void setLongitude(double longitude);
void setAltitude(double altitude);
double x(void) const { return latitude(); }
double y(void) const { return longitude(); }
double z(void) const { return altitude(); }
void setX(double x);
void setY(double y);
void setZ(double z);
bool autoContinue() const {
return _autocontinue;
}
double loiterOrbitRadius() const {
return _loiterOrbitRadiusFact->value().toDouble();
}
double acceptanceRadius() const {
return param2();
}
double holdTime() const {
return param1();
}
double param1() const {
return _param1Fact->value().toDouble();
}
double param2() const {
return _param2Fact->value().toDouble();
}
double param3() const {
return loiterOrbitRadius();
}
double param4() const {
return _yawRadians();
}
double param5() const {
return latitude();
}
double param6() const {
return longitude();
}
double param7() const {
return altitude();
}
// MAV_FRAME
int frame() const;
// MAV_CMD
int command() const {
return _command;
}
/** @brief Returns true if x, y, z contain reasonable navigation data */
bool isNavigationType();
/** @brief Get the time this waypoint was reached */
quint64 reachedTime() const { return _reachedTime; }
void save(QTextStream &saveStream);
bool load(QTextStream &loadStream);
void setHomePositionSpecialCase(bool homePositionSpecialCase) { _homePositionSpecialCase = homePositionSpecialCase; }
bool relativeAltitude(void) { return _frame == MAV_FRAME_GLOBAL_RELATIVE_ALT; }
static const double defaultPitch;
static const double defaultHeading;
static const double defaultAltitude;
static const double defaultAcceptanceRadius;
static const double defaultLoiterOrbitRadius;
static const double defaultLoiterTurns;
signals:
void sequenceNumberChanged(int sequenceNumber);
void isCurrentItemChanged(bool isCurrentItem);
void coordinateChanged(const QGeoCoordinate& coordinate);
void headingDegreesChanged(double heading);
void dirtyChanged(bool dirty);
void homePositionValidChanged(bool homePostionValid);
void distanceChanged(float distance);
void frameChanged(int frame);
void commandNameChanged(QString type);
void commandChanged(MavlinkQmlSingleton::Qml_MAV_CMD command);
void valueLabelsChanged(QStringList valueLabels);
void valueStringsChanged(QStringList valueStrings);
bool autoContinueChanged(bool autoContinue);
public:
/** @brief Set the waypoint action */
void setAction (int _action);
void setFrame (int _frame);
void setAutocontinue(bool autoContinue);
void setCurrent (bool _current);
void setLoiterOrbitRadius (double radius);
void setParam1 (double _param1);
void setParam2 (double _param2);
void setParam3 (double param3);
void setParam4 (double param4);
void setParam5 (double param5);
void setParam6 (double param6);
void setParam7 (double param7);
void setAcceptanceRadius(double radius);
void setHoldTime (int holdTime);
void setHoldTime (double holdTime);
/** @brief Set waypoint as reached */
void setReached () { _reachedTime = QGC::groundTimeMilliseconds(); }
/** @brief Wether this waypoint has been reached yet */
bool isReached () { return (_reachedTime > 0); }
private slots:
void _factValueChanged(QVariant value);
void _coordinateFactChanged(QVariant value);
void _headingDegreesFactChanged(QVariant value);
void _altitudeRelativeToHomeFactChanged(QVariant value);
private:
QString _oneDecimalString(double value);
void _connectSignals(void);
void _setYawRadians(double yawRadians);
private:
typedef struct {
MAV_CMD command;
const char* name;
} MavCmd2Name_t;
int _sequenceNumber;
int _frame;
MavlinkQmlSingleton::Qml_MAV_CMD _command;
bool _autocontinue;
bool _isCurrentItem;
quint64 _reachedTime;
double _distance;
Fact* _latitudeFact;
Fact* _longitudeFact;
Fact* _altitudeFact;
Fact* _headingDegreesFact;
Fact* _loiterOrbitRadiusFact;
Fact* _param1Fact;
Fact* _param2Fact;
Fact* _altitudeRelativeToHomeFact;
FactMetaData* _pitchMetaData;
FactMetaData* _acceptanceRadiusMetaData;
FactMetaData* _holdTimeMetaData;
FactMetaData* _loiterTurnsMetaData;
FactMetaData* _loiterSecondsMetaData;
FactMetaData* _delaySecondsMetaData;
FactMetaData* _jumpSequenceMetaData;
FactMetaData* _jumpRepeatMetaData;
bool _dirty;
bool _homePositionSpecialCase; ///< true: this item is being used as a ui home position indicator
bool _homePositionValid; ///< true: home psition should be displayed
/// This is used to reference any subsequent mission items which do not specify a coordinate.
QmlObjectListModel _childItems;
static const int _cMavCmd2Name = 9;
static const MavCmd2Name_t _rgMavCmd2Name[_cMavCmd2Name];
};
QDebug operator<<(QDebug dbg, const MissionItem& missionItem);
QDebug operator<<(QDebug dbg, const MissionItem* missionItem);
#endif
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="114 186 72 72" style="enable-background:new 114 186 72 72;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{display:inline;}
.st2{display:inline;stroke:#FFFFFF;stroke-width:1.1959;stroke-miterlimit:10;}
.st3{display:inline;fill:none;stroke:#FFFFFF;stroke-width:9.567;stroke-miterlimit:10;}
.st4{display:inline;stroke:#FFFFFF;stroke-width:0.9229;stroke-miterlimit:10;}
.st5{display:inline;fill:none;stroke:#FFFFFF;stroke-width:9.2289;stroke-miterlimit:10;}
.st6{display:inline;stroke:#FFFFFF;stroke-width:1.3383;stroke-miterlimit:10;}
.st7{display:inline;fill:#FFFFFF;}
.st8{display:inline;opacity:0.7;stroke:#FFFFFF;stroke-width:1.494;stroke-miterlimit:10;enable-background:new ;}
.st9{display:inline;opacity:0.7;stroke:#FFFFFF;stroke-width:0.9492;stroke-miterlimit:10;enable-background:new ;}
.st10{display:inline;fill:none;stroke:#000000;stroke-width:23.0795;stroke-miterlimit:10;}
.st11{enable-background:new ;}
.st12{fill:none;stroke:#000000;stroke-miterlimit:10;}
.st13{display:inline;fill:#FFFFFF;stroke:#000000;stroke-miterlimit:10;}
.st14{display:inline;stroke:#000000;stroke-width:19.9759;stroke-miterlimit:10;}
.st15{display:inline;stroke:#000000;stroke-width:20.4762;stroke-miterlimit:10;}
.st16{display:inline;stroke:#000000;stroke-miterlimit:10;}
</style>
<g id="Media_play_icon" class="st0">
<polygon class="st1" points="389.5,36.1 138.1,181.2 138.1,-109 "/>
</g>
<g id="Document_icon" class="st0">
<rect x="153.8" y="-105.1" class="st2" width="219.9" height="282.3"/>
<line class="st3" x1="194.1" y1="-63.2" x2="334" y2="-63.2"/>
<line class="st3" x1="194.1" y1="-37.2" x2="297.3" y2="-37.2"/>
<line class="st3" x1="194.1" y1="-11.2" x2="310.8" y2="-11.2"/>
<line class="st3" x1="194.1" y1="14.9" x2="334" y2="14.9"/>
<line class="st3" x1="194.1" y1="40.9" x2="252.4" y2="40.9"/>
<line class="st3" x1="194.1" y1="67" x2="297.3" y2="67"/>
<line class="st3" x1="194.1" y1="93" x2="310.8" y2="93"/>
</g>
<g id="Contact_icon" class="st0">
<rect x="121.1" y="-72.8" class="st4" width="285.8" height="217.9"/>
<polyline class="st5" points="395.7,-62.9 264,39.8 133,-62.9 "/>
</g>
<g id="Image_icon" class="st0">
<rect x="126.4" y="-105.4" class="st6" width="274.7" height="283"/>
<rect x="153" y="-76.5" class="st7" width="220.5" height="220.5"/>
<polygon class="st8" points="299.9,130.5 162.5,130.3 231.4,11.4 "/>
<polygon class="st9" points="352.6,129.9 265.3,129.8 309,54.2 "/>
<circle class="st9" cx="296.9" cy="-12.5" r="23.9"/>
</g>
<g id="Map_icon" class="st0">
<circle class="st1" cx="263.8" cy="-10.1" r="99.2"/>
<polygon class="st1" points="263.8,182 191.3,56.4 336.3,56.4 "/>
<circle class="st7" cx="263.8" cy="-11" r="34.3"/>
</g>
<g id="Calendar_icon" class="st0">
<rect x="143.3" y="-82.8" class="st10" width="241.3" height="248.6"/>
<text transform="matrix(1 0 0 1 164.4731 113.2056)" class="st1" style="font-family:'Futura-Medium'; font-size:172.5264px;">31</text>
<text transform="matrix(1 0 0 1 164.4731 113.2056)" style="display:inline;fill:none;stroke:#000000;stroke-miterlimit:10; font-family:'Futura-Medium'; font-size:172.5264px;">31</text>
<polyline class="st1" points="306.1,147.8 364.8,147.8 364.8,89.1 "/>
<rect x="131.9" y="-105" class="st1" width="263.7" height="66.4"/>
<circle class="st13" cx="212.4" cy="-70" r="11.9"/>
<circle class="st13" cx="313.1" cy="-71.8" r="11.9"/>
</g>
<g id="Company_icon" class="st0">
<circle class="st14" cx="263.8" cy="-35.3" r="51.5"/>
<path class="st15" d="M173.4,172.9c0-81.1,40.5-146.9,90.4-146.9s90.4,65.8,90.4,146.9"/>
<polygon class="st16" points="202.7,173.1 216.3,173.2 209.9,116.5 "/>
<polygon class="st16" points="315.6,173.2 329.2,173.3 322.7,116.7 "/>
</g>
<g id="BOOUm1.tif">
<g>
<path d="M146.9,188c2,0,4.1,0,6.1,0c1.3,1.1,2,2.6,2.3,4.3c0.2,1.6,1.2,2.6,2.7,3.2c1.1,0.4,2.2,0.7,3.2,1.3
c2.5,1.5,4.8,0.9,7-0.5c2.3-1.4,2.9-1.3,4.8,0.5c0.5,0.5,1,1,1.5,1.5c2,2.1,2.2,3.4,0.5,5.9c-0.6,0.9-1.3,1.9-1.1,3
c0.6,2.6,1.5,5.2,3,7.4c1,1.5,3,1.8,4.8,2.2c1.9,0.4,2.4,1.9,2.3,3.5c-0.1,2.3,1,5.1-1.8,6.7c-0.4,0.2-1,0.2-1.5,0.4
c-1.3,0.4-2.7,0.8-3.7,1.8c-2.7,2.9-3.2,7.8-1.1,11.2c1,1.5,0.8,2.7-0.3,4c-0.5,0.6-1.1,1.2-1.7,1.8c-2.3,2.3-3.5,2.5-6.2,0.7
c-1.6-1.1-3.2-1.2-4.9-0.5c-0.8,0.3-1.6,0.8-2.5,1c-3.1,0.8-4.6,2.8-5,5.8c-0.3,1.8-1.4,2.6-3.1,2.7c-0.8,0-1.5,0-2.3,0
c-3.5,0-4.4-0.8-5-4.1c-0.2-0.9-0.4-1.8-1.2-2.3c-2.2-1.7-4.8-2.6-7.5-3.3c-1.7-0.4-3.3,0.8-4.7,1.7c-1.4,1-2.6,0.7-3.8-0.3
c-0.6-0.5-1.2-1.1-1.8-1.7c-2.4-2.4-2.6-3.5-0.7-6.3c1-1.5,1.1-2.9,0.5-4.5c-0.4-0.9-0.9-1.9-1.1-2.8c-0.8-3.1-3-4.5-5.9-5
c-0.9-0.1-1.6-0.4-2.1-1.2c-2.1-3.3-0.2-8.3,3.5-9.2c1.8-0.4,2.9-1.4,3.6-3.1c0.3-0.8,0.6-1.7,1-2.5c1.6-2.8,1.2-5.4-0.6-7.8
c-1.1-1.4-0.8-2.7,0.3-4c0.6-0.6,1.1-1.2,1.7-1.8c2.3-2.2,3.5-2.4,6.2-0.6c1.5,1,3,1.3,4.7,0.5c0.9-0.4,1.9-0.9,2.8-1.1
c3-0.7,4.4-2.9,4.8-5.7C145,189.4,145.7,188.6,146.9,188z M150,233.9c6.7,0,12.1-5.4,12.1-12c-0.1-6.5-5.5-11.9-12.1-11.9
c-6.6,0-11.9,5.4-11.9,12C138.2,228.7,143.4,233.9,150,233.9z"/>
</g>
</g>
</svg>
{
"version": 1,
"mavCmdInfo": [
{
"comment": "MAV_CMD_NAV_LAST: Used for fake home position waypoint",
"id": 95,
"rawName": "Home",
"friendlyName": "Home",
"description": "Home Position",
"specifiesCoordinate": true,
"friendlyEdit": true
},
{
"id": 16,
"rawName": "MAV_CMD_NAV_WAYPOINT",
"friendlyName": "Waypoint",
"description": "Travel to a position in 3D space.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param1": {
"label": "Hold:",
"units": "seconds",
"decimalPlaces": 0
}
},
{
"id": 17,
"rawName": "MAV_CMD_NAV_LOITER_UNLIM",
"friendlyName": "Loiter",
"description": "Travel to a position and Loiter around the specified radius indefinitely.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param3": {
"label": "Radius:",
"units": "meters",
"decimalPlaces": 2
}
},
{
"id": 18,
"rawName": "MAV_CMD_NAV_LOITER_TURNS",
"friendlyName": "Loiter (turns)",
"description": "Travel to a position and Loiter around the specified radius for a number of turns.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param1": {
"label": "Turns:",
"decimalPlaces": 0
},
"param3": {
"label": "Radius:",
"units": "meters",
"decimalPlaces": 2
}
},
{
"id": 19,
"rawName": "MAV_CMD_NAV_LOITER_TIME",
"friendlyName": "Loiter (time)",
"description": "Travel to a position and Loiter around the specified radius for an amount of time.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param1": {
"label": "Hold:",
"units": "seconds",
"decimalPlaces": 0
},
"param3": {
"label": "Radius:",
"units": "meters",
"decimalPlaces": 2
}
},
{
"id": 20,
"rawName": "MAV_CMD_NAV_RETURN_TO_LAUNCH",
"friendlyName": "Return Home",
"description": "Send the vehicle back to the home position.",
"friendlyEdit": true
},
{
"id": 21,
"rawName": "MAV_CMD_NAV_LAND",
"friendlyName": "Land",
"description": "Land vehicle at the specified location.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param1": {
"label": "Abort Alt:",
"units": "meters",
"decimalPlaces": 3
},
"param4": {
"label": "Heading:",
"units": "degrees",
"decimalPlaces": 2
}
},
{
"id": 22,
"rawName": "MAV_CMD_NAV_TAKEOFF",
"friendlyName": "Takeoff",
"description": "Take off from the ground and travel towards the specified position.",
"specifiesCoordinate": true,
"friendlyEdit": true,
"param1": {
"label": "Pitch:",
"units": "degrees",
"decimalPlaces": 2
},
"param4": {
"label": "Heading:",
"units": "degrees",
"decimalPlaces": 2
}
},
{ "id": 23, "rawName": "MAV_CMD_NAV_LAND_LOCAL", "friendlyName": "MAV_CMD_NAV_LAND_LOCAL" },
{ "id": 24, "rawName": "MAV_CMD_NAV_TAKEOFF_LOCAL", "friendlyName": "MAV_CMD_NAV_TAKEOFF_LOCAL" },
{ "id": 25, "rawName": "MAV_CMD_NAV_FOLLOW", "friendlyName": "MAV_CMD_NAV_FOLLOW" },
{ "id": 30, "rawName": "MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT", "friendlyName": "MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT" },
{ "id": 31, "rawName": "MAV_CMD_NAV_LOITER_TO_ALT" },
{ "id": 80, "rawName": "MAV_CMD_NAV_ROI", "friendlyName": "MAV_CMD_NAV_ROI" },
{ "id": 81, "rawName": "MAV_CMD_NAV_PATHPLANNING", "friendlyName": "MAV_CMD_NAV_PATHPLANNING" },
{ "id": 82, "rawName": "MAV_CMD_NAV_SPLINE_WAYPOINT", "friendlyName": "MAV_CMD_NAV_SPLINE_WAYPOINT" },
{ "id": 83, "rawName": "MAV_CMD_NAV_ALTITUDE_WAIT", "friendlyName": "MAV_CMD_NAV_ALTITUDE_WAIT" },
{ "id": 92, "rawName": "MAV_CMD_NAV_GUIDED_ENABLE", "friendlyName": "MAV_CMD_NAV_GUIDED_ENABLE" },
{
"id": 112,
"rawName": "MAV_CMD_CONDITION_DELAY",
"friendlyName": "Delay",
"description": "Delay the mission for the number of seconds.",
"friendlyEdit": true,
"param1": {
"label": "Hold:",
"units": "seconds",
"decimalPlaces": 0
}
},
{ "id": 113, "rawName": "MAV_CMD_CONDITION_CHANGE_ALT", "friendlyName": "MAV_CMD_CONDITION_CHANGE_ALT" },
{ "id": 114, "rawName": "MAV_CMD_CONDITION_DISTANCE", "friendlyName": "MAV_CMD_CONDITION_DISTANCE" },
{ "id": 115, "rawName": "MAV_CMD_CONDITION_YAW", "friendlyName": "MAV_CMD_CONDITION_YAW" },
{ "id": 159, "rawName": "MAV_CMD_CONDITION_LAST", "friendlyName": "MAV_CMD_CONDITION_LAST" },
{ "id": 176, "rawName": "MAV_CMD_DO_SET_MODE", "friendlyName": "MAV_CMD_DO_SET_MODE" },
{
"id": 177,
"rawName": "MAV_CMD_DO_JUMP",
"friendlyName": "Jump to item",
"description": "Mission will continue at the specified item.",
"friendlyEdit": true,
"param1": {
"label": "Seq #:",
"decimalPlaces": 0
},
"param2": {
"label": "Repeat:",
"decimalPlaces": 0
}
},
{ "id": 178, "rawName": "MAV_CMD_DO_CHANGE_SPEED", "friendlyName": "MAV_CMD_DO_CHANGE_SPEED" },
{ "id": 179, "rawName": "MAV_CMD_DO_SET_HOME", "friendlyName": "MAV_CMD_DO_SET_HOME" },
{ "id": 180, "rawName": "MAV_CMD_DO_SET_PARAMETER", "friendlyName": "MAV_CMD_DO_SET_PARAMETER" },
{ "id": 181, "rawName": "MAV_CMD_DO_SET_RELAY", "friendlyName": "MAV_CMD_DO_SET_RELAY" },
{ "id": 182, "rawName": "MAV_CMD_DO_REPEAT_RELAY", "friendlyName": "MAV_CMD_DO_REPEAT_RELAY" },
{ "id": 183, "rawName": "MAV_CMD_DO_SET_SERVO", "friendlyName": "MAV_CMD_DO_SET_SERVO" },
{ "id": 184, "rawName": "MAV_CMD_DO_REPEAT_SERVO", "friendlyName": "MAV_CMD_DO_REPEAT_SERVO" },
{ "id": 185, "rawName": "MAV_CMD_DO_FLIGHTTERMINATION", "friendlyName": "MAV_CMD_DO_FLIGHTTERMINATION" },
{ "id": 189, "rawName": "MAV_CMD_DO_LAND_START", "friendlyName": "MAV_CMD_DO_LAND_START" },
{ "id": 190, "rawName": "MAV_CMD_DO_RALLY_LAND", "friendlyName": "MAV_CMD_DO_RALLY_LAND" },
{ "id": 191, "rawName": "MAV_CMD_DO_GO_AROUND", "friendlyName": "MAV_CMD_DO_GO_AROUND" },
{ "id": 200, "rawName": "MAV_CMD_DO_CONTROL_VIDEO", "friendlyName": "MAV_CMD_DO_CONTROL_VIDEO" },
{ "id": 201, "rawName": "MAV_CMD_DO_SET_ROI", "friendlyName": "MAV_CMD_DO_SET_ROI" },
{ "id": 202, "rawName": "MAV_CMD_DO_DIGICAM_CONFIGURE", "friendlyName": "MAV_CMD_DO_DIGICAM_CONFIGURE" },
{ "id": 203, "rawName": "MAV_CMD_DO_DIGICAM_CONTROL", "friendlyName": "MAV_CMD_DO_DIGICAM_CONTROL" },
{ "id": 204, "rawName": "MAV_CMD_DO_MOUNT_CONFIGURE", "friendlyName": "MAV_CMD_DO_MOUNT_CONFIGURE" },
{ "id": 205, "rawName": "MAV_CMD_DO_MOUNT_CONTROL", "friendlyName": "MAV_CMD_DO_MOUNT_CONTROL" },
{ "id": 206, "rawName": "MAV_CMD_DO_SET_CAM_TRIGG_DIST", "friendlyName": "MAV_CMD_DO_SET_CAM_TRIGG_DIST" },
{ "id": 207, "rawName": "MAV_CMD_DO_FENCE_ENABLE", "friendlyName": "MAV_CMD_DO_FENCE_ENABLE" },
{ "id": 208, "rawName": "MAV_CMD_DO_PARACHUTE", "friendlyName": "MAV_CMD_DO_PARACHUTE" },
{ "id": 209, "rawName": "MAV_CMD_DO_MOTOR_TEST", "friendlyName": "MAV_CMD_DO_MOTOR_TEST" },
{ "id": 210, "rawName": "MAV_CMD_DO_INVERTED_FLIGHT", "friendlyName": "MAV_CMD_DO_INVERTED_FLIGHT" },
{ "id": 211, "rawName": "MAV_CMD_DO_GRIPPER", "friendlyName": "MAV_CMD_DO_GRIPPER" },
{ "id": 212, "rawName": "MAV_CMD_DO_AUTOTUNE_ENABLE", "friendlyName": "MAV_CMD_DO_AUTOTUNE_ENABLE" },
{ "id": 220, "rawName": "MAV_CMD_DO_MOUNT_CONTROL_QUAT", "friendlyName": "MAV_CMD_DO_MOUNT_CONTROL_QUAT" },
{ "id": 221, "rawName": "MAV_CMD_DO_GUIDED_MASTER", "friendlyName": "MAV_CMD_DO_GUIDED_MASTER" },
{ "id": 222, "rawName": "MAV_CMD_DO_GUIDED_LIMITS", "friendlyName": "MAV_CMD_DO_GUIDED_LIMITS" },
{ "id": 252, "rawName": "MAV_CMD_OVERRIDE_GOTO", "friendlyName": "MAV_CMD_OVERRIDE_GOTO" },
{ "id": 300, "rawName": "MAV_CMD_MISSION_START", "friendlyName": "MAV_CMD_MISSION_START" },
{ "id": 400, "rawName": "MAV_CMD_COMPONENT_ARM_DISARM", "friendlyName": "MAV_CMD_COMPONENT_ARM_DISARM" },
{ "id": 2000, "rawName": "MAV_CMD_IMAGE_START_CAPTURE", "friendlyName": "MAV_CMD_IMAGE_START_CAPTURE" },
{ "id": 2001, "rawName": "MAV_CMD_IMAGE_STOP_CAPTURE", "friendlyName": "MAV_CMD_IMAGE_STOP_CAPTURE" },
{ "id": 2003, "rawName": "MAV_CMD_DO_TRIGGER_CONTROL", "friendlyName": "MAV_CMD_DO_TRIGGER_CONTROL" },
{ "id": 2500, "rawName": "MAV_CMD_VIDEO_START_CAPTURE", "friendlyName": "MAV_CMD_VIDEO_START_CAPTURE" },
{ "id": 2501, "rawName": "MAV_CMD_VIDEO_STOP_CAPTURE", "friendlyName": "MAV_CMD_VIDEO_STOP_CAPTURE" },
{ "id": 2800, "rawName": "MAV_CMD_PANORAMA_CREATE", "friendlyName": "MAV_CMD_PANORAMA_CREATE" },
{ "id": 3000, "rawName": "MAV_CMD_DO_VTOL_TRANSITION", "friendlyName": "MAV_CMD_DO_VTOL_TRANSITION" },
{ "id": 30001, "rawName": "MAV_CMD_PAYLOAD_PREPARE_DEPLOY", "friendlyName": "MAV_CMD_PAYLOAD_PREPARE_DEPLOY" },
{ "id": 30002, "rawName": "MAV_CMD_PAYLOAD_CONTROL_DEPLOY", "friendlyName": "MAV_CMD_PAYLOAD_CONTROL_DEPLOY" }
]
}
...@@ -37,7 +37,6 @@ MissionController::MissionController(QObject *parent) ...@@ -37,7 +37,6 @@ MissionController::MissionController(QObject *parent)
: QObject(parent) : QObject(parent)
, _editMode(false) , _editMode(false)
, _missionItems(NULL) , _missionItems(NULL)
, _canEdit(true)
, _activeVehicle(NULL) , _activeVehicle(NULL)
, _liveHomePositionAvailable(false) , _liveHomePositionAvailable(false)
, _autoSync(false) , _autoSync(false)
...@@ -51,7 +50,6 @@ MissionController::MissionController(QObject *parent) ...@@ -51,7 +50,6 @@ MissionController::MissionController(QObject *parent)
MissionController::~MissionController() MissionController::~MissionController()
{ {
// Start with empty list // Start with empty list
_canEdit = true;
_missionItems = new QmlObjectListModel(this); _missionItems = new QmlObjectListModel(this);
_initAllMissionItems(); _initAllMissionItems();
} }
...@@ -129,11 +127,9 @@ void MissionController::_setupMissionItems(bool loadFromVehicle, bool forceLoad) ...@@ -129,11 +127,9 @@ void MissionController::_setupMissionItems(bool loadFromVehicle, bool forceLoad)
} }
if (!missionManager || !loadFromVehicle || missionManager->inProgress()) { if (!missionManager || !loadFromVehicle || missionManager->inProgress()) {
_canEdit = true;
_missionItems = new QmlObjectListModel(this); _missionItems = new QmlObjectListModel(this);
qCDebug(MissionControllerLog) << "creating empty set"; qCDebug(MissionControllerLog) << "creating empty set";
} else { } else {
_canEdit = missionManager->canEdit();
_missionItems = missionManager->copyMissionItems(); _missionItems = missionManager->copyMissionItems();
qCDebug(MissionControllerLog) << "loading from vehicle count"<< _missionItems->count(); qCDebug(MissionControllerLog) << "loading from vehicle count"<< _missionItems->count();
} }
...@@ -163,18 +159,15 @@ void MissionController::sendMissionItems(void) ...@@ -163,18 +159,15 @@ void MissionController::sendMissionItems(void)
int MissionController::addMissionItem(QGeoCoordinate coordinate) int MissionController::addMissionItem(QGeoCoordinate coordinate)
{ {
if (!_canEdit) { MissionItem * newItem = new MissionItem(this);
qWarning() << "addMissionItem called with _canEdit == false"; newItem->setSequenceNumber(_missionItems->count());
} newItem->setCoordinate(coordinate);
newItem->setCommand(MAV_CMD_NAV_WAYPOINT);
// Coordinate will come through without altitude
coordinate.setAltitude(MissionItem::defaultAltitude);
MissionItem * newItem = new MissionItem(this, _missionItems->count(), coordinate, MAV_CMD_NAV_WAYPOINT);
_initMissionItem(newItem); _initMissionItem(newItem);
if (_missionItems->count() == 1) { if (_missionItems->count() == 1) {
newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF); newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF);
} }
newItem->setDefaultsForCommand();
_missionItems->append(newItem); _missionItems->append(newItem);
_recalcAll(); _recalcAll();
...@@ -184,11 +177,6 @@ int MissionController::addMissionItem(QGeoCoordinate coordinate) ...@@ -184,11 +177,6 @@ int MissionController::addMissionItem(QGeoCoordinate coordinate)
void MissionController::removeMissionItem(int index) void MissionController::removeMissionItem(int index)
{ {
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->removeAt(index)); MissionItem* item = qobject_cast<MissionItem*>(_missionItems->removeAt(index));
_deinitMissionItem(item); _deinitMissionItem(item);
...@@ -221,8 +209,6 @@ void MissionController::loadMissionFromFile(void) ...@@ -221,8 +209,6 @@ void MissionController::loadMissionFromFile(void)
} }
_missionItems = new QmlObjectListModel(this); _missionItems = new QmlObjectListModel(this);
_canEdit = true;
// FIXME: This needs to handle APM files which have WP 0 in them // FIXME: This needs to handle APM files which have WP 0 in them
QFile file(filename); QFile file(filename);
...@@ -242,10 +228,6 @@ void MissionController::loadMissionFromFile(void) ...@@ -242,10 +228,6 @@ void MissionController::loadMissionFromFile(void)
if (item->load(in)) { if (item->load(in)) {
_missionItems->append(item); _missionItems->append(item);
if (!item->canEdit()) {
_canEdit = false;
}
} else { } else {
errorString = "The mission file is corrupted."; errorString = "The mission file is corrupted.";
break; break;
...@@ -422,7 +404,9 @@ void MissionController::_initAllMissionItems(void) ...@@ -422,7 +404,9 @@ void MissionController::_initAllMissionItems(void)
// Add the home position item to the front // Add the home position item to the front
homeItem = new MissionItem(this); homeItem = new MissionItem(this);
homeItem->setHomePositionSpecialCase(true); homeItem->setHomePositionSpecialCase(true);
homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT); homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_LAST);
homeItem->setFrame(MAV_FRAME_GLOBAL_RELATIVE_ALT);
homeItem->setSequenceNumber(0);
_missionItems->insert(0, homeItem); _missionItems->insert(0, homeItem);
} }
homeItem->setHomePositionValid(false); homeItem->setHomePositionValid(false);
...@@ -434,7 +418,6 @@ void MissionController::_initAllMissionItems(void) ...@@ -434,7 +418,6 @@ void MissionController::_initAllMissionItems(void)
_recalcAll(); _recalcAll();
emit missionItemsChanged(); emit missionItemsChanged();
emit canEditChanged(_canEdit);
_missionItems->setDirty(false); _missionItems->setDirty(false);
......
...@@ -42,7 +42,6 @@ public: ...@@ -42,7 +42,6 @@ public:
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems NOTIFY missionItemsChanged) Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems NOTIFY missionItemsChanged)
Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged)
Q_PROPERTY(bool canEdit READ canEdit NOTIFY canEditChanged)
Q_PROPERTY(bool liveHomePositionAvailable READ liveHomePositionAvailable NOTIFY liveHomePositionAvailableChanged) Q_PROPERTY(bool liveHomePositionAvailable READ liveHomePositionAvailable NOTIFY liveHomePositionAvailableChanged)
Q_PROPERTY(QGeoCoordinate liveHomePosition READ liveHomePosition NOTIFY liveHomePositionChanged) Q_PROPERTY(QGeoCoordinate liveHomePosition READ liveHomePosition NOTIFY liveHomePositionChanged)
Q_PROPERTY(bool autoSync READ autoSync WRITE setAutoSync NOTIFY autoSyncChanged) Q_PROPERTY(bool autoSync READ autoSync WRITE setAutoSync NOTIFY autoSyncChanged)
...@@ -60,7 +59,6 @@ public: ...@@ -60,7 +59,6 @@ public:
QmlObjectListModel* missionItems(void); QmlObjectListModel* missionItems(void);
QmlObjectListModel* waypointLines(void) { return &_waypointLines; } QmlObjectListModel* waypointLines(void) { return &_waypointLines; }
bool canEdit(void) { return _canEdit; }
bool liveHomePositionAvailable(void) { return _liveHomePositionAvailable; } bool liveHomePositionAvailable(void) { return _liveHomePositionAvailable; }
QGeoCoordinate liveHomePosition(void) { return _liveHomePosition; } QGeoCoordinate liveHomePosition(void) { return _liveHomePosition; }
bool autoSync(void) { return _autoSync; } bool autoSync(void) { return _autoSync; }
...@@ -68,7 +66,6 @@ public: ...@@ -68,7 +66,6 @@ public:
signals: signals:
void missionItemsChanged(void); void missionItemsChanged(void);
void canEditChanged(bool canEdit);
void waypointLinesChanged(void); void waypointLinesChanged(void);
void liveHomePositionAvailableChanged(bool homePositionAvailable); void liveHomePositionAvailableChanged(bool homePositionAvailable);
void liveHomePositionChanged(const QGeoCoordinate& homePosition); void liveHomePositionChanged(const QGeoCoordinate& homePosition);
...@@ -103,7 +100,6 @@ private: ...@@ -103,7 +100,6 @@ private:
bool _editMode; bool _editMode;
QmlObjectListModel* _missionItems; QmlObjectListModel* _missionItems;
QmlObjectListModel _waypointLines; QmlObjectListModel _waypointLines;
bool _canEdit; ///< true: UI can edit these items, false: can't edit, can only send to vehicle or save
Vehicle* _activeVehicle; Vehicle* _activeVehicle;
bool _liveHomePositionAvailable; bool _liveHomePositionAvailable;
QGeoCoordinate _liveHomePosition; QGeoCoordinate _liveHomePosition;
......
...@@ -48,7 +48,6 @@ void MissionControllerManagerTest::_initForFirmwareType(MAV_AUTOPILOT firmwareTy ...@@ -48,7 +48,6 @@ void MissionControllerManagerTest::_initForFirmwareType(MAV_AUTOPILOT firmwareTy
_missionManager = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->missionManager(); _missionManager = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->missionManager();
QVERIFY(_missionManager); QVERIFY(_missionManager);
_rgMissionManagerSignals[canEditChangedSignalIndex] = SIGNAL(canEditChanged(bool));
_rgMissionManagerSignals[newMissionItemsAvailableSignalIndex] = SIGNAL(newMissionItemsAvailable(void)); _rgMissionManagerSignals[newMissionItemsAvailableSignalIndex] = SIGNAL(newMissionItemsAvailable(void));
_rgMissionManagerSignals[inProgressChangedSignalIndex] = SIGNAL(inProgressChanged(bool)); _rgMissionManagerSignals[inProgressChangedSignalIndex] = SIGNAL(inProgressChanged(bool));
_rgMissionManagerSignals[errorSignalIndex] = SIGNAL(error(int, const QString&)); _rgMissionManagerSignals[errorSignalIndex] = SIGNAL(error(int, const QString&));
...@@ -61,7 +60,6 @@ void MissionControllerManagerTest::_initForFirmwareType(MAV_AUTOPILOT firmwareTy ...@@ -61,7 +60,6 @@ void MissionControllerManagerTest::_initForFirmwareType(MAV_AUTOPILOT firmwareTy
_multiSpyMissionManager->waitForSignalByIndex(newMissionItemsAvailableSignalIndex, _missionManagerSignalWaitTime); _multiSpyMissionManager->waitForSignalByIndex(newMissionItemsAvailableSignalIndex, _missionManagerSignalWaitTime);
_multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime);
QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true); QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true);
QCOMPARE(_multiSpyMissionManager->checkNoSignalByMask(canEditChangedSignalIndex), true);
} }
QVERIFY(!_missionManager->inProgress()); QVERIFY(!_missionManager->inProgress());
......
...@@ -49,16 +49,16 @@ protected: ...@@ -49,16 +49,16 @@ protected:
MissionManager* _missionManager; MissionManager* _missionManager;
typedef struct { typedef struct {
int sequenceNumber; int sequenceNumber;
QGeoCoordinate coordinate; QGeoCoordinate coordinate;
int command; MAV_CMD command;
double param1; double param1;
double param2; double param2;
double param3; double param3;
double param4; double param4;
bool autocontinue; bool autocontinue;
bool isCurrentItem; bool isCurrentItem;
int frame; MAV_FRAME frame;
} ItemInfo_t; } ItemInfo_t;
typedef struct { typedef struct {
...@@ -67,15 +67,13 @@ protected: ...@@ -67,15 +67,13 @@ protected:
} TestCase_t; } TestCase_t;
typedef enum { typedef enum {
canEditChangedSignalIndex = 0, newMissionItemsAvailableSignalIndex = 0,
newMissionItemsAvailableSignalIndex,
inProgressChangedSignalIndex, inProgressChangedSignalIndex,
errorSignalIndex, errorSignalIndex,
maxSignalIndex maxSignalIndex
} MissionManagerSignalIndex_t; } MissionManagerSignalIndex_t;
typedef enum { typedef enum {
canEditChangedSignalMask = 1 << canEditChangedSignalIndex,
newMissionItemsAvailableSignalMask = 1 << newMissionItemsAvailableSignalIndex, newMissionItemsAvailableSignalMask = 1 << newMissionItemsAvailableSignalIndex,
inProgressChangedSignalMask = 1 << inProgressChangedSignalIndex, inProgressChangedSignalMask = 1 << inProgressChangedSignalIndex,
errorSignalMask = 1 << errorSignalIndex, errorSignalMask = 1 << errorSignalIndex,
......
/*===================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include <QStringList>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QDebug>
#include "MissionItem.h"
#include "FirmwarePluginManager.h"
#include "QGCApplication.h"
QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog")
const double MissionItem::defaultTakeoffPitch = 15.0;
const double MissionItem::defaultHeading = 0.0;
const double MissionItem::defaultAltitude = 25.0;
const double MissionItem::defaultAcceptanceRadius = 3.0;
const double MissionItem::defaultLoiterOrbitRadius = 10.0;
const double MissionItem::defaultLoiterTurns = 1.0;
FactMetaData* MissionItem::_altitudeMetaData = NULL;
FactMetaData* MissionItem::_commandMetaData = NULL;
FactMetaData* MissionItem::_defaultParamMetaData = NULL;
FactMetaData* MissionItem::_frameMetaData = NULL;
FactMetaData* MissionItem::_latitudeMetaData = NULL;
FactMetaData* MissionItem::_longitudeMetaData = NULL;
FactMetaData* MissionItem::_supportedCommandMetaData = NULL;
const QString MissionItem::_decimalPlacesJsonKey (QStringLiteral("decimalPlaces"));
const QString MissionItem::_descriptionJsonKey (QStringLiteral("description"));
const QString MissionItem::_friendlyEditJsonKey (QStringLiteral("friendlyEdit"));
const QString MissionItem::_friendlyNameJsonKey (QStringLiteral("friendlyName"));
const QString MissionItem::_idJsonKey (QStringLiteral("id"));
const QString MissionItem::_labelJsonKey (QStringLiteral("label"));
const QString MissionItem::_mavCmdInfoJsonKey (QStringLiteral("mavCmdInfo"));
const QString MissionItem::_param1JsonKey (QStringLiteral("param1"));
const QString MissionItem::_param2JsonKey (QStringLiteral("param2"));
const QString MissionItem::_param3JsonKey (QStringLiteral("param3"));
const QString MissionItem::_param4JsonKey (QStringLiteral("param4"));
const QString MissionItem::_paramJsonKeyFormat (QStringLiteral("param%1"));
const QString MissionItem::_rawNameJsonKey (QStringLiteral("rawName"));
const QString MissionItem::_specifiesCoordinateJsonKey (QStringLiteral("specifiesCoordinate"));
const QString MissionItem::_unitsJsonKey (QStringLiteral("units"));
const QString MissionItem::_versionJsonKey (QStringLiteral("version"));
const QString MissionItem::_degreesUnits (QStringLiteral("degrees"));
QMap<MAV_CMD, MissionItem::MavCmdInfo_t> MissionItem::_mavCmdInfoMap;
struct EnumInfo_s {
const char * label;
MAV_FRAME frame;
};
static const struct EnumInfo_s _rgMavFrameInfo[] = {
{ "MAV_FRAME_GLOBAL", MAV_FRAME_GLOBAL },
{ "MAV_FRAME_LOCAL_NED", MAV_FRAME_LOCAL_NED },
{ "MAV_FRAME_MISSION", MAV_FRAME_MISSION },
{ "MAV_FRAME_GLOBAL_RELATIVE_ALT", MAV_FRAME_GLOBAL_RELATIVE_ALT },
{ "MAV_FRAME_LOCAL_ENU", MAV_FRAME_LOCAL_ENU },
{ "MAV_FRAME_GLOBAL_INT", MAV_FRAME_GLOBAL_INT },
{ "MAV_FRAME_GLOBAL_RELATIVE_ALT_INT", MAV_FRAME_GLOBAL_RELATIVE_ALT_INT },
{ "MAV_FRAME_LOCAL_OFFSET_NED", MAV_FRAME_LOCAL_OFFSET_NED },
{ "MAV_FRAME_BODY_NED", MAV_FRAME_BODY_NED },
{ "MAV_FRAME_BODY_OFFSET_NED", MAV_FRAME_BODY_OFFSET_NED },
{ "MAV_FRAME_GLOBAL_TERRAIN_ALT", MAV_FRAME_GLOBAL_TERRAIN_ALT },
{ "MAV_FRAME_GLOBAL_TERRAIN_ALT_INT", MAV_FRAME_GLOBAL_TERRAIN_ALT_INT },
};
QDebug operator<<(QDebug dbg, const MissionItem& missionItem)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "MissionItem(" << missionItem.coordinate() << ")";
return dbg;
}
QDebug operator<<(QDebug dbg, const MissionItem* missionItem)
{
QDebugStateSaver saver(dbg);
dbg.nospace() << "MissionItem(" << missionItem->coordinate() << ")";
return dbg;
}
MissionItem::MissionItem(QObject* parent)
: QObject(parent)
, _rawEdit(false)
, _dirty(false)
, _sequenceNumber(0)
, _isCurrentItem(false)
, _distance(0.0)
, _homePositionSpecialCase(false)
, _homePositionValid(false)
, _altitudeRelativeToHomeFact (0, "Altitude is relative to home", FactMetaData::valueTypeUint32)
, _autoContinueFact (0, "AutoContinue", FactMetaData::valueTypeUint32)
, _commandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _frameFact (0, "Frame:", FactMetaData::valueTypeUint32)
, _param1Fact (0, "Param1:", FactMetaData::valueTypeDouble)
, _param2Fact (0, "Param2:", FactMetaData::valueTypeDouble)
, _param3Fact (0, "Param3:", FactMetaData::valueTypeDouble)
, _param4Fact (0, "Param4:", FactMetaData::valueTypeDouble)
, _param5Fact (0, "Latitude:", FactMetaData::valueTypeDouble)
, _param6Fact (0, "Longitude:", FactMetaData::valueTypeDouble)
, _param7Fact (0, "Altitude:", FactMetaData::valueTypeDouble)
, _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _param1MetaData(FactMetaData::valueTypeDouble)
, _param2MetaData(FactMetaData::valueTypeDouble)
, _param3MetaData(FactMetaData::valueTypeDouble)
, _param4MetaData(FactMetaData::valueTypeDouble)
, _syncingAltitudeRelativeToHomeAndFrame (false)
, _syncingHeadingDegreesAndParam4 (false)
, _syncingSupportedCommandAndCommand (false)
{
_setupMetaData();
_connectSignals();
setAutoContinue(true);
}
MissionItem::MissionItem(int sequenceNumber,
MAV_CMD command,
MAV_FRAME frame,
double param1,
double param2,
double param3,
double param4,
double param5,
double param6,
double param7,
bool autoContinue,
bool isCurrentItem,
QObject* parent)
: QObject(parent)
, _rawEdit(false)
, _dirty(false)
, _sequenceNumber(sequenceNumber)
, _isCurrentItem(isCurrentItem)
, _distance(0.0)
, _homePositionSpecialCase(false)
, _homePositionValid(false)
, _altitudeRelativeToHomeFact (0, "Altitude is relative to home", FactMetaData::valueTypeUint32)
, _commandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _frameFact (0, "Frame:", FactMetaData::valueTypeUint32)
, _param1Fact (0, "Param1:", FactMetaData::valueTypeDouble)
, _param2Fact (0, "Param2:", FactMetaData::valueTypeDouble)
, _param3Fact (0, "Param3:", FactMetaData::valueTypeDouble)
, _param4Fact (0, "Param4:", FactMetaData::valueTypeDouble)
, _param5Fact (0, "Lat/X:", FactMetaData::valueTypeDouble)
, _param6Fact (0, "Lon/Y:", FactMetaData::valueTypeDouble)
, _param7Fact (0, "Alt/Z:", FactMetaData::valueTypeDouble)
, _supportedCommandFact (0, "Command:", FactMetaData::valueTypeUint32)
, _param1MetaData(FactMetaData::valueTypeDouble)
, _param2MetaData(FactMetaData::valueTypeDouble)
, _param3MetaData(FactMetaData::valueTypeDouble)
, _param4MetaData(FactMetaData::valueTypeDouble)
, _syncingAltitudeRelativeToHomeAndFrame (false)
, _syncingHeadingDegreesAndParam4 (false)
, _syncingSupportedCommandAndCommand (false)
{
_setupMetaData();
_connectSignals();
setCommand(command);
setFrame(frame);
setAutoContinue(autoContinue);
_syncFrameToAltitudeRelativeToHome();
_syncCommandToSupportedCommand(QVariant(this->command()));
_param1Fact.setRawValue(param1);
_param2Fact.setRawValue(param2);
_param3Fact.setRawValue(param3);
_param4Fact.setRawValue(param4);
_param5Fact.setValue(param5);
_param6Fact.setValue(param6);
_param7Fact.setValue(param7);
}
MissionItem::MissionItem(const MissionItem& other, QObject* parent)
: QObject(parent)
, _syncingAltitudeRelativeToHomeAndFrame (false)
, _syncingHeadingDegreesAndParam4 (false)
, _syncingSupportedCommandAndCommand (false)
{
_setupMetaData();
_connectSignals();
*this = other;
}
const MissionItem& MissionItem::operator=(const MissionItem& other)
{
setCommand(other.command());
setFrame(other.frame());
setRawEdit(other._rawEdit);
setDirty(other._dirty);
setSequenceNumber(other._sequenceNumber);
setAutoContinue(other.autoContinue());
setIsCurrentItem(other._isCurrentItem);
setDistance(other._distance);
setHomePositionSpecialCase(other._homePositionSpecialCase);
setHomePositionValid(other._homePositionValid);
_syncFrameToAltitudeRelativeToHome();
_syncCommandToSupportedCommand(QVariant(this->command()));
_param1Fact.setValue(other._param1Fact.value());
_param2Fact.setValue(other._param2Fact.value());
_param3Fact.setValue(other._param3Fact.value());
_param4Fact.setValue(other._param4Fact.value());
_param5Fact.setValue(other._param5Fact.value());
_param6Fact.setValue(other._param6Fact.value());
_param7Fact.setValue(other._param7Fact.value());
return *this;
}
void MissionItem::_connectSignals(void)
{
// Connect to change signals to track dirty state
connect(&_param1Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param2Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param3Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param4Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param5Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param6Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(&_param7Fact, &Fact::valueChanged, this, &MissionItem::_setDirtyFromSignal);
connect(this, &MissionItem::commandChanged, this, &MissionItem::_setDirtyFromSignal);
connect(this, &MissionItem::frameChanged, this, &MissionItem::_setDirtyFromSignal);
connect(this, &MissionItem::sequenceNumberChanged, this, &MissionItem::_setDirtyFromSignal);
// Values from these facts must propogate back and forth between the real object storage
connect(&_supportedCommandFact, &Fact::valueChanged, this, &MissionItem::_syncSupportedCommandToCommand);
connect(&_commandFact, &Fact::valueChanged, this, &MissionItem::_syncCommandToSupportedCommand);
connect(&_altitudeRelativeToHomeFact, &Fact::valueChanged, this, &MissionItem::_syncAltitudeRelativeToHomeToFrame);
connect(this, &MissionItem::frameChanged, this, &MissionItem::_syncFrameToAltitudeRelativeToHome);
// These are parameter coordinates, they must emit coordinateChanged signal
connect(&_param5Fact, &Fact::valueChanged, this, &MissionItem::_sendCoordinateChanged);
connect(&_param6Fact, &Fact::valueChanged, this, &MissionItem::_sendCoordinateChanged);
connect(&_param7Fact, &Fact::valueChanged, this, &MissionItem::_sendCoordinateChanged);
// The following changes may also change friendlyEditAllowed
connect(&_autoContinueFact, &Fact::valueChanged, this, &MissionItem::_sendFriendlyEditAllowedChanged);
connect(&_commandFact, &Fact::valueChanged, this, &MissionItem::_sendFriendlyEditAllowedChanged);
connect(this, &MissionItem::frameChanged, this, &MissionItem::_sendFriendlyEditAllowedChanged);
// Whenever these properties change the ui model changes as well
connect(this, &MissionItem::commandChanged, this, &MissionItem::_sendUiModelChanged);
connect(this, &MissionItem::rawEditChanged, this, &MissionItem::_sendUiModelChanged);
connect(&_commandFact, &Fact::valueChanged, this, &MissionItem::_sendCommandChanged);
connect(&_frameFact, &Fact::valueChanged, this, &MissionItem::_sendFrameChanged);
}
bool MissionItem::_validateKeyTypes(QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types)
{
for (int i=0; i<keys.count(); i++) {
if (jsonObject.contains(keys[i])) {
if (jsonObject.value(keys[i]).type() != types[i]) {
qWarning() << "Incorrect type key:type:expected" << keys[i] << jsonObject.value(keys[i]).type() << types[i];
return false;
}
}
}
return true;
}
bool MissionItem::_loadMavCmdInfoJson(void)
{
QFile jsonFile(":/json/MavCmdInfo.json");
if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Unable to open MavCmdInfo.json" << jsonFile.errorString();
return false;
}
QByteArray bytes = jsonFile.readAll();
jsonFile.close();
QJsonParseError jsonParseError;
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Unable to open json document" << jsonParseError.errorString();
return false;
}
QJsonObject json = doc.object();
int version = json.value(_versionJsonKey).toInt();
if (version != 1) {
qWarning() << "Invalid version" << version;
return false;
}
QJsonValue jsonValue = json.value(_mavCmdInfoJsonKey);
if (!jsonValue.isArray()) {
qWarning() << "mavCmdInfo not array";
return false;
}
QJsonArray jsonArray = jsonValue.toArray();
foreach(QJsonValue info, jsonArray) {
if (!info.isObject()) {
qWarning() << "mavCmdArray should contain objects";
return false;
}
QJsonObject jsonObject = info.toObject();
// Make sure we have the required keys
QStringList requiredKeys;
requiredKeys << _idJsonKey << _rawNameJsonKey;
foreach (QString key, requiredKeys) {
if (!jsonObject.contains(key)) {
qWarning() << "Mission required key" << key;
return false;
}
}
// Validate key types
QStringList keys;
QList<QJsonValue::Type> types;
keys << _idJsonKey << _rawNameJsonKey << _friendlyNameJsonKey << _descriptionJsonKey << _specifiesCoordinateJsonKey << _friendlyEditJsonKey
<< _param1JsonKey << _param2JsonKey << _param3JsonKey << _param4JsonKey;
types << QJsonValue::Double << QJsonValue::String << QJsonValue::String<< QJsonValue::String << QJsonValue::Bool << QJsonValue::Bool
<< QJsonValue::Object << QJsonValue::Object << QJsonValue::Object << QJsonValue::Object;
if (!_validateKeyTypes(jsonObject, keys, types)) {
return false;
}
MavCmdInfo_t mavCmdInfo;
mavCmdInfo.command = (MAV_CMD) jsonObject.value(_idJsonKey).toInt();
mavCmdInfo.rawName = jsonObject.value(_rawNameJsonKey).toString();
mavCmdInfo.friendlyName = jsonObject.value(_friendlyNameJsonKey).toString(QString());
mavCmdInfo.description = jsonObject.value(_descriptionJsonKey).toString(QString());
mavCmdInfo.specifiesCoordinate = jsonObject.value(_specifiesCoordinateJsonKey).toBool(false);
mavCmdInfo.friendlyEdit = jsonObject.value(_friendlyEditJsonKey).toBool(false);
if (_mavCmdInfoMap.contains(mavCmdInfo.command)) {
qWarning() << "Duplicate command" << mavCmdInfo.command;
return false;
}
_mavCmdInfoMap[mavCmdInfo.command] = mavCmdInfo;
// Read params
for (int i=1; i<5; i++) {
QString paramKey = QString(_paramJsonKeyFormat).arg(i);
if (jsonObject.contains(paramKey)) {
QJsonObject paramObject = jsonObject.value(paramKey).toObject();
// Validate key types
QStringList keys;
QList<QJsonValue::Type> types;
keys << _labelJsonKey << _unitsJsonKey << _decimalPlacesJsonKey;
types << QJsonValue::String << QJsonValue::String<< QJsonValue::Double;
if (!_validateKeyTypes(paramObject, keys, types)) {
return false;
}
if (paramObject.contains(_labelJsonKey)) {
_mavCmdInfoMap[mavCmdInfo.command].paramInfoMap[i].label = paramObject.value(_labelJsonKey).toString();
} else {
qWarning() << "param object missing label key" << mavCmdInfo.rawName << paramKey;
return false;
}
_mavCmdInfoMap[mavCmdInfo.command].paramInfoMap[i].units = paramObject.value(_unitsJsonKey).toString();
_mavCmdInfoMap[mavCmdInfo.command].paramInfoMap[i].decimalPlaces = paramObject.value(_decimalPlacesJsonKey).toInt(FactMetaData::defaultDecimalPlaces);
}
}
}
return true;
}
void MissionItem::_setupMetaData(void)
{
QStringList enumStrings;
QVariantList enumValues;
if (!_altitudeMetaData) {
_loadMavCmdInfoJson();
_altitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
_altitudeMetaData->setUnits("meters");
_altitudeMetaData->setDecimalPlaces(3);
enumStrings.clear();
enumValues.clear();
foreach (MavCmdInfo_t mavCmdInfo, _mavCmdInfoMap) {
enumStrings.append(mavCmdInfo.rawName);
enumValues.append(QVariant(mavCmdInfo.command));
}
_commandMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
_commandMetaData->setEnumInfo(enumStrings, enumValues);
_defaultParamMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
_defaultParamMetaData->setDecimalPlaces(7);
enumStrings.clear();
enumValues.clear();
for (size_t i=0; i<sizeof(_rgMavFrameInfo)/sizeof(_rgMavFrameInfo[0]); i++) {
const struct EnumInfo_s* mavFrameInfo = &_rgMavFrameInfo[i];
enumStrings.append(mavFrameInfo->label);
enumValues.append(QVariant(mavFrameInfo->frame));
}
_frameMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
_frameMetaData->setEnumInfo(enumStrings, enumValues);
_latitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
_latitudeMetaData->setUnits("deg");
_latitudeMetaData->setDecimalPlaces(7);
_longitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble);
_longitudeMetaData->setUnits("deg");
_longitudeMetaData->setDecimalPlaces(7);
enumStrings.clear();
enumValues.clear();
// FIXME: Hack hardcode tp PX4
QList<MAV_CMD> supportedCommands = qgcApp()->toolbox()->firmwarePluginManager()->firmwarePluginForAutopilot(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR)->supportedMissionCommands();
if (supportedCommands.count()) {
foreach (MAV_CMD command, supportedCommands) {
enumStrings.append(_mavCmdInfoMap[command].friendlyName);
enumValues.append(QVariant(command));
}
} else {
foreach (MavCmdInfo_t mavCmdInfo, _mavCmdInfoMap) {
enumStrings.append(mavCmdInfo.friendlyName);
enumValues.append(QVariant(mavCmdInfo.command));
}
}
_supportedCommandMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
_supportedCommandMetaData->setEnumInfo(enumStrings, enumValues);
}
_commandFact.setMetaData(_commandMetaData);
_frameFact.setMetaData(_frameMetaData);
_supportedCommandFact.setMetaData(_supportedCommandMetaData);
}
MissionItem::~MissionItem()
{
}
void MissionItem::save(QTextStream &saveStream)
{
// FORMAT: <INDEX> <CURRENT WP> <COORD FRAME> <COMMAND> <PARAM1> <PARAM2> <PARAM3> <PARAM4> <PARAM5/X/LONGITUDE> <PARAM6/Y/LATITUDE> <PARAM7/Z/ALTITUDE> <autoContinue> <DESCRIPTION>
// as documented here: http://qgroundcontrol.org/waypoint_protocol
saveStream << sequenceNumber() << "\t"
<< isCurrentItem() << "\t"
<< frame() << "\t"
<< command() << "\t"
<< QString("%1").arg(param1(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param2(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param3(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param4(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param5(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param6(), 0, 'g', 18) << "\t"
<< QString("%1").arg(param7(), 0, 'g', 18) << "\t"
<< this->autoContinue() << "\r\n";
}
bool MissionItem::load(QTextStream &loadStream)
{
const QStringList &wpParams = loadStream.readLine().split("\t");
if (wpParams.size() == 12) {
setSequenceNumber(wpParams[0].toInt());
setIsCurrentItem(wpParams[1].toInt() == 1 ? true : false);
setFrame((MAV_FRAME)wpParams[2].toInt());
setCommand((MAV_CMD)wpParams[3].toInt());
setParam1(wpParams[4].toDouble());
setParam2(wpParams[5].toDouble());
setParam3(wpParams[6].toDouble());
setParam4(wpParams[7].toDouble());
setParam5(wpParams[8].toDouble());
setParam6(wpParams[9].toDouble());
setParam7(wpParams[10].toDouble());
setAutoContinue(wpParams[11].toInt() == 1 ? true : false);
return true;
}
return false;
}
void MissionItem::setSequenceNumber(int sequenceNumber)
{
_sequenceNumber = sequenceNumber;
emit sequenceNumberChanged(_sequenceNumber);
}
void MissionItem::setCommand(MAV_CMD command)
{
if ((MAV_CMD)this->command() != command) {
_commandFact.setValue(command);
emit commandChanged(this->command());
}
}
void MissionItem::setCommand(MavlinkQmlSingleton::Qml_MAV_CMD command)
{
setCommand((MAV_CMD)command);
}
void MissionItem::setFrame(MAV_FRAME frame)
{
if (this->frame() != frame) {
_frameFact.setValue(frame);
}
}
void MissionItem::setAutoContinue(bool autoContinue)
{
if (this->autoContinue() != autoContinue) {
_autoContinueFact.setValue(autoContinue);
}
}
void MissionItem::setIsCurrentItem(bool isCurrentItem)
{
if (_isCurrentItem != isCurrentItem) {
_isCurrentItem = isCurrentItem;
emit isCurrentItemChanged(isCurrentItem);
}
}
void MissionItem::setParam1(double param)
{
if (param1() != param) {
_param1Fact.setRawValue(param);
}
}
void MissionItem::setParam2(double param)
{
if (param2() != param) {
_param2Fact.setRawValue(param);
}
}
void MissionItem::setParam3(double param)
{
if (param3() != param) {
_param3Fact.setRawValue(param);
}
}
void MissionItem::setParam4(double param)
{
if (param4() != param) {
_param4Fact.setRawValue(param);
}
}
void MissionItem::setParam5(double param)
{
if (param5() != param) {
_param5Fact.setRawValue(param);
}
}
void MissionItem::setParam6(double param)
{
if (param6() != param) {
_param6Fact.setRawValue(param);
}
}
void MissionItem::setParam7(double param)
{
if (param7() != param) {
_param7Fact.setRawValue(param);
}
}
bool MissionItem::specifiesCoordinate(void) const
{
return _mavCmdInfoMap[(MAV_CMD)command()].specifiesCoordinate;
}
QString MissionItem::commandDescription(void) const
{
return _mavCmdInfoMap[(MAV_CMD)command()].description;
}
void MissionItem::_clearParamMetaData(void)
{
_param1MetaData.setUnits("");
_param1MetaData.setDecimalPlaces(FactMetaData::defaultDecimalPlaces);
_param1MetaData.setTranslators(FactMetaData::defaultTranslator, FactMetaData::defaultTranslator);
_param2MetaData.setUnits("");
_param2MetaData.setDecimalPlaces(FactMetaData::defaultDecimalPlaces);
_param2MetaData.setTranslators(FactMetaData::defaultTranslator, FactMetaData::defaultTranslator);
_param3MetaData.setUnits("");
_param3MetaData.setDecimalPlaces(FactMetaData::defaultDecimalPlaces);
_param3MetaData.setTranslators(FactMetaData::defaultTranslator, FactMetaData::defaultTranslator);
_param4MetaData.setUnits("");
_param4MetaData.setDecimalPlaces(FactMetaData::defaultDecimalPlaces);
_param4MetaData.setTranslators(FactMetaData::defaultTranslator, FactMetaData::defaultTranslator);
}
QmlObjectListModel* MissionItem::textFieldFacts(void)
{
QmlObjectListModel* model = new QmlObjectListModel(this);
if (rawEdit()) {
_param1Fact._setName("Param1:");
_param1Fact.setMetaData(_defaultParamMetaData);
model->append(&_param1Fact);
_param2Fact._setName("Param2:");
_param2Fact.setMetaData(_defaultParamMetaData);
model->append(&_param2Fact);
_param3Fact._setName("Param3:");
_param3Fact.setMetaData(_defaultParamMetaData);
model->append(&_param3Fact);
_param4Fact._setName("Param4:");
_param4Fact.setMetaData(_defaultParamMetaData);
model->append(&_param4Fact);
_param5Fact._setName("Lat/X:");
_param5Fact.setMetaData(_defaultParamMetaData);
model->append(&_param5Fact);
_param6Fact._setName("Lon/Y:");
_param6Fact.setMetaData(_defaultParamMetaData);
model->append(&_param6Fact);
_param7Fact._setName("Alt/Z:");
_param7Fact.setMetaData(_defaultParamMetaData);
model->append(&_param7Fact);
} else {
_clearParamMetaData();
MAV_CMD command = (MAV_CMD)this->command();
if (_mavCmdInfoMap[command].paramInfoMap.contains(1)) {
_param1Fact._setName(_mavCmdInfoMap[command].paramInfoMap[1].label);
_param1MetaData.setUnits(_mavCmdInfoMap[command].paramInfoMap[1].units);
_param1MetaData.setDecimalPlaces(_mavCmdInfoMap[command].paramInfoMap[1].decimalPlaces);
if (_mavCmdInfoMap[command].paramInfoMap[1].units == _degreesUnits) {
_param1MetaData.setTranslators(_radiansToDegrees, _degreesToRadians);
}
_param1Fact.setMetaData(&_param1MetaData);
model->append(&_param1Fact);
}
if (_mavCmdInfoMap[command].paramInfoMap.contains(2)) {
_param2Fact._setName(_mavCmdInfoMap[command].paramInfoMap[2].label);
_param2MetaData.setUnits(_mavCmdInfoMap[command].paramInfoMap[2].units);
_param2MetaData.setDecimalPlaces(_mavCmdInfoMap[command].paramInfoMap[2].decimalPlaces);
if (_mavCmdInfoMap[command].paramInfoMap[2].units == _degreesUnits) {
_param2MetaData.setTranslators(_radiansToDegrees, _degreesToRadians);
}
_param2Fact.setMetaData(&_param2MetaData);
model->append(&_param2Fact);
}
if (_mavCmdInfoMap[command].paramInfoMap.contains(3)) {
_param3Fact._setName(_mavCmdInfoMap[command].paramInfoMap[3].label);
_param3MetaData.setUnits(_mavCmdInfoMap[command].paramInfoMap[3].units);
_param3MetaData.setDecimalPlaces(_mavCmdInfoMap[command].paramInfoMap[3].decimalPlaces);
if (_mavCmdInfoMap[command].paramInfoMap[3].units == _degreesUnits) {
_param3MetaData.setTranslators(_radiansToDegrees, _degreesToRadians);
}
_param3Fact.setMetaData(&_param3MetaData);
model->append(&_param3Fact);
}
if (_mavCmdInfoMap[command].paramInfoMap.contains(4)) {
_param4Fact._setName(_mavCmdInfoMap[command].paramInfoMap[4].label);
_param4MetaData.setUnits(_mavCmdInfoMap[command].paramInfoMap[4].units);
_param4MetaData.setDecimalPlaces(_mavCmdInfoMap[command].paramInfoMap[4].decimalPlaces);
if (_mavCmdInfoMap[command].paramInfoMap[4].units == _degreesUnits) {
_param4MetaData.setTranslators(_radiansToDegrees, _degreesToRadians);
}
_param4Fact.setMetaData(&_param4MetaData);
model->append(&_param4Fact);
}
if (specifiesCoordinate()) {
_param7Fact._setName("Altitude:");
_param7Fact.setMetaData(_altitudeMetaData);
model->append(&_param7Fact);
}
}
return model;
}
QmlObjectListModel* MissionItem::checkboxFacts(void)
{
QmlObjectListModel* model = new QmlObjectListModel(this);
if (rawEdit()) {
model->append(&_autoContinueFact);
} else if (specifiesCoordinate()) {
model->append(&_altitudeRelativeToHomeFact);
}
return model;
}
QmlObjectListModel* MissionItem::comboboxFacts(void)
{
QmlObjectListModel* model = new QmlObjectListModel(this);
if (rawEdit()) {
model->append(&_commandFact);
model->append(&_frameFact);
}
return model;
}
QGeoCoordinate MissionItem::coordinate(void) const
{
return QGeoCoordinate(_param5Fact.value().toDouble(), _param6Fact.value().toDouble(), _param7Fact.value().toDouble());
}
void MissionItem::setCoordinate(const QGeoCoordinate& coordinate)
{
setParam5(coordinate.latitude());
setParam6(coordinate.longitude());
setParam7(coordinate.altitude());
}
bool MissionItem::friendlyEditAllowed(void) const
{
if (_mavCmdInfoMap[(MAV_CMD)command()].friendlyEdit) {
if (!autoContinue()) {
return false;
}
if (specifiesCoordinate()) {
return frame() == MAV_FRAME_GLOBAL || frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT;
} else {
return frame() == MAV_FRAME_MISSION;
}
}
return false;
}
bool MissionItem::rawEdit(void) const
{
return _rawEdit || !friendlyEditAllowed();
}
void MissionItem::setRawEdit(bool rawEdit)
{
if (_rawEdit != rawEdit) {
_rawEdit = rawEdit;
emit rawEditChanged(this->rawEdit());
}
}
void MissionItem::setDirty(bool dirty)
{
if (!_homePositionSpecialCase || !dirty) {
// Home position never affects dirty bit
_dirty = dirty;
// We want to emit dirtyChanged even if _dirty didn't change. This can be handy signal for
// any value within the item changing.
emit dirtyChanged(_dirty);
}
}
void MissionItem::_setDirtyFromSignal(void)
{
setDirty(true);
}
void MissionItem::setHomePositionValid(bool homePositionValid)
{
_homePositionValid = homePositionValid;
emit homePositionValidChanged(_homePositionValid);
}
void MissionItem::setDistance(double distance)
{
_distance = distance;
emit distanceChanged(_distance);
}
void MissionItem::_sendCoordinateChanged(void)
{
emit coordinateChanged(coordinate());
}
void MissionItem::_syncAltitudeRelativeToHomeToFrame(const QVariant& value)
{
if (!_syncingAltitudeRelativeToHomeAndFrame) {
_syncingAltitudeRelativeToHomeAndFrame = true;
setFrame(value.toBool() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL);
_syncingAltitudeRelativeToHomeAndFrame = false;
}
}
void MissionItem::_syncFrameToAltitudeRelativeToHome(void)
{
if (!_syncingAltitudeRelativeToHomeAndFrame) {
_syncingAltitudeRelativeToHomeAndFrame = true;
_altitudeRelativeToHomeFact.setValue(relativeAltitude());
_syncingAltitudeRelativeToHomeAndFrame = false;
}
}
void MissionItem::_syncSupportedCommandToCommand(const QVariant& value)
{
if (!_syncingSupportedCommandAndCommand) {
qDebug() << value.toInt();
_syncingSupportedCommandAndCommand = true;
_commandFact.setValue(value.toInt());
qDebug() << _commandFact.value().toInt();
_syncingSupportedCommandAndCommand = false;
}
}
void MissionItem::_syncCommandToSupportedCommand(const QVariant& value)
{
if (!_syncingSupportedCommandAndCommand) {
_syncingSupportedCommandAndCommand = true;
_supportedCommandFact.setValue(value.toInt());
_syncingSupportedCommandAndCommand = false;
}
}
void MissionItem::setDefaultsForCommand(void)
{
switch ((MAV_CMD)command()) {
case MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF:
setParam1(_degreesToRadians(defaultTakeoffPitch).toDouble());
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT:
setParam2(defaultAcceptanceRadius);
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_UNLIM:
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_TIME:
setParam3(defaultLoiterOrbitRadius);
break;
case MavlinkQmlSingleton::MAV_CMD_NAV_LOITER_TURNS:
setParam1(defaultLoiterTurns);
setParam3(defaultLoiterOrbitRadius);
break;
default:
break;
}
setParam4(_degreesToRadians(defaultHeading).toDouble());
setParam7(defaultAltitude);
setFrame(specifiesCoordinate() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_MISSION);
}
void MissionItem::_sendUiModelChanged(void)
{
emit uiModelChanged();
}
void MissionItem::_sendFrameChanged(void)
{
emit frameChanged(frame());
}
void MissionItem::_sendCommandChanged(void)
{
emit commandChanged(command());
}
QString MissionItem::commandName(void) const
{
MavCmdInfo_t& mavCmdInfo = _mavCmdInfoMap[(MAV_CMD)command()];
return mavCmdInfo.friendlyName.isEmpty() ? mavCmdInfo.rawName : mavCmdInfo.friendlyName;
}
QVariant MissionItem::_degreesToRadians(const QVariant& degrees)
{
return QVariant(degrees.toDouble() * (M_PI / 180.0));
}
QVariant MissionItem::_radiansToDegrees(const QVariant& radians)
{
return QVariant(radians.toDouble() * (180 / M_PI));
}
void MissionItem::_sendFriendlyEditAllowedChanged(void)
{
emit friendlyEditAllowedChanged(friendlyEditAllowed());
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef MissionItem_H
#define MissionItem_H
#include <QObject>
#include <QString>
#include <QtQml>
#include <QTextStream>
#include <QGeoCoordinate>
#include <QJsonObject>
#include <QJsonValue>
#include "QGCMAVLink.h"
#include "QGC.h"
#include "MavlinkQmlSingleton.h"
#include "QmlObjectListModel.h"
#include "Fact.h"
#include "QGCLoggingCategory.h"
#include "QmlObjectListModel.h"
Q_DECLARE_LOGGING_CATEGORY(MissionItemLog)
class MissionItem : public QObject
{
Q_OBJECT
public:
MissionItem(QObject* parent = NULL);
MissionItem(int sequenceNumber,
MAV_CMD command,
MAV_FRAME frame,
double param1,
double param2,
double param3,
double param4,
double param5,
double param6,
double param7,
bool autoContinue,
bool isCurrentItem,
QObject* parent = NULL);
MissionItem(const MissionItem& other, QObject* parent = NULL);
~MissionItem();
const MissionItem& operator=(const MissionItem& other);
Q_PROPERTY(MavlinkQmlSingleton::Qml_MAV_CMD command READ command WRITE setCommand NOTIFY commandChanged)
Q_PROPERTY(QString commandDescription READ commandDescription NOTIFY commandChanged)
Q_PROPERTY(QString commandName READ commandName NOTIFY commandChanged)
Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged)
Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged) ///< Distance to previous waypoint
Q_PROPERTY(bool friendlyEditAllowed READ friendlyEditAllowed NOTIFY friendlyEditAllowedChanged)
Q_PROPERTY(bool homePosition READ homePosition CONSTANT) ///< true: This item is being used as a home position indicator
Q_PROPERTY(bool homePositionValid READ homePositionValid WRITE setHomePositionValid NOTIFY homePositionValidChanged) ///< true: Home position should be shown
Q_PROPERTY(bool isCurrentItem READ isCurrentItem WRITE setIsCurrentItem NOTIFY isCurrentItemChanged)
Q_PROPERTY(bool rawEdit READ rawEdit WRITE setRawEdit NOTIFY rawEditChanged) ///< true: raw item editing with all params
Q_PROPERTY(int sequenceNumber READ sequenceNumber WRITE setSequenceNumber NOTIFY sequenceNumberChanged)
Q_PROPERTY(bool specifiesCoordinate READ specifiesCoordinate NOTIFY commandChanged)
Q_PROPERTY(Fact* supportedCommand READ supportedCommand NOTIFY commandChanged)
// These properties are used to display the editing ui
Q_PROPERTY(QmlObjectListModel* checkboxFacts READ checkboxFacts NOTIFY uiModelChanged)
Q_PROPERTY(QmlObjectListModel* comboboxFacts READ comboboxFacts NOTIFY uiModelChanged)
Q_PROPERTY(QmlObjectListModel* textFieldFacts READ textFieldFacts NOTIFY uiModelChanged)
/// List of child mission items. Child mission item are subsequent mision items which do not specify a coordinate. They
/// are shown next to the part item in the ui.
Q_PROPERTY(QmlObjectListModel* childItems READ childItems CONSTANT)
Q_INVOKABLE void setDefaultsForCommand(void);
// Property accesors
MavlinkQmlSingleton::Qml_MAV_CMD command(void) const { return (MavlinkQmlSingleton::Qml_MAV_CMD)_commandFact.value().toInt(); };
QString commandDescription (void) const;
QString commandName (void) const;
QGeoCoordinate coordinate (void) const;
bool dirty (void) const { return _dirty; }
double distance (void) const { return _distance; }
bool friendlyEditAllowed (void) const;
bool homePosition (void) const { return _homePositionSpecialCase; }
bool homePositionValid (void) const { return _homePositionValid; }
bool isCurrentItem (void) const { return _isCurrentItem; }
bool rawEdit (void) const;
int sequenceNumber (void) const { return _sequenceNumber; }
bool specifiesCoordinate (void) const;
Fact* supportedCommand (void) { return &_supportedCommandFact; }
QmlObjectListModel* textFieldFacts (void);
QmlObjectListModel* checkboxFacts (void);
QmlObjectListModel* comboboxFacts (void);
QmlObjectListModel* childItems (void) { return &_childItems; }
void setRawEdit(bool rawEdit);
void setDirty(bool dirty);
void setSequenceNumber(int sequenceNumber);
void setIsCurrentItem(bool isCurrentItem);
void setCoordinate(const QGeoCoordinate& coordinate);
void setCommandByIndex(int index);
void setCommand(MavlinkQmlSingleton::Qml_MAV_CMD command);
void setHomePositionValid(bool homePositionValid);
void setHomePositionSpecialCase(bool homePositionSpecialCase) { _homePositionSpecialCase = homePositionSpecialCase; }
void setDistance(double distance);
// C++ only methods
MAV_FRAME frame (void) const { return (MAV_FRAME)_frameFact.value().toInt(); }
bool autoContinue(void) const { return _autoContinueFact.value().toBool(); }
double param1 (void) const { return _param1Fact.rawValue().toDouble(); }
double param2 (void) const { return _param2Fact.rawValue().toDouble(); }
double param3 (void) const { return _param3Fact.rawValue().toDouble(); }
double param4 (void) const { return _param4Fact.rawValue().toDouble(); }
double param5 (void) const { return _param5Fact.rawValue().toDouble(); }
double param6 (void) const { return _param6Fact.rawValue().toDouble(); }
double param7 (void) const { return _param7Fact.rawValue().toDouble(); }
void setCommand (MAV_CMD command);
void setFrame (MAV_FRAME frame);
void setAutoContinue(bool autoContinue);
void setParam1 (double param1);
void setParam2 (double param2);
void setParam3 (double param3);
void setParam4 (double param4);
void setParam5 (double param5);
void setParam6 (double param6);
void setParam7 (double param7);
// C++ only methods
void save(QTextStream &saveStream);
bool load(QTextStream &loadStream);
bool relativeAltitude(void) { return frame() == MAV_FRAME_GLOBAL_RELATIVE_ALT; }
static const double defaultTakeoffPitch;
static const double defaultHeading;
static const double defaultAltitude;
static const double defaultAcceptanceRadius;
static const double defaultLoiterOrbitRadius;
static const double defaultLoiterTurns;
signals:
void commandChanged (MavlinkQmlSingleton::Qml_MAV_CMD command);
void coordinateChanged (const QGeoCoordinate& coordinate);
void dirtyChanged (bool dirty);
void distanceChanged (float distance);
void frameChanged (int frame);
void friendlyEditAllowedChanged (bool friendlyEditAllowed);
void headingDegreesChanged (double heading);
void homePositionValidChanged (bool homePostionValid);
void isCurrentItemChanged (bool isCurrentItem);
void rawEditChanged (bool rawEdit);
void sequenceNumberChanged (int sequenceNumber);
void uiModelChanged (void);
private slots:
void _setDirtyFromSignal(void);
void _sendCommandChanged(void);
void _sendCoordinateChanged(void);
void _sendFrameChanged(void);
void _sendFriendlyEditAllowedChanged(void);
void _sendUiModelChanged(void);
void _syncAltitudeRelativeToHomeToFrame(const QVariant& value);
void _syncCommandToSupportedCommand(const QVariant& value);
void _syncFrameToAltitudeRelativeToHome(void);
void _syncSupportedCommandToCommand(const QVariant& value);
private:
void _clearParamMetaData(void);
void _connectSignals(void);
bool _loadMavCmdInfoJson(void);
void _setupMetaData(void);
bool _validateKeyTypes(QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types);
static QVariant _degreesToRadians(const QVariant& degrees);
static QVariant _radiansToDegrees(const QVariant& radians);
private:
typedef struct {
QString label;
QString units;
int decimalPlaces;
} ParamInfo_t;
typedef struct {
MAV_CMD command;
QString rawName;
QString friendlyName;
QString description;
bool specifiesCoordinate;
bool friendlyEdit;
QMap<int, ParamInfo_t> paramInfoMap;
} MavCmdInfo_t;
bool _rawEdit;
bool _dirty;
int _sequenceNumber;
bool _isCurrentItem;
double _distance; ///< Distance to previous waypoint
bool _homePositionSpecialCase; ///< true: This item is being used as a ui home position indicator
bool _homePositionValid; ///< true: Home psition should be displayed
Fact _altitudeRelativeToHomeFact;
Fact _autoContinueFact;
Fact _commandFact;
Fact _frameFact;
Fact _param1Fact;
Fact _param2Fact;
Fact _param3Fact;
Fact _param4Fact;
Fact _param5Fact;
Fact _param6Fact;
Fact _param7Fact;
Fact _supportedCommandFact;
static FactMetaData* _altitudeMetaData;
static FactMetaData* _commandMetaData;
static FactMetaData* _defaultParamMetaData;
static FactMetaData* _frameMetaData;
static FactMetaData* _latitudeMetaData;
static FactMetaData* _longitudeMetaData;
static FactMetaData* _supportedCommandMetaData;
FactMetaData _param1MetaData;
FactMetaData _param2MetaData;
FactMetaData _param3MetaData;
FactMetaData _param4MetaData;
/// This is used to reference any subsequent mission items which do not specify a coordinate.
QmlObjectListModel _childItems;
bool _syncingAltitudeRelativeToHomeAndFrame; ///< true: already in a sync signal, prevents signal loop
bool _syncingHeadingDegreesAndParam4; ///< true: already in a sync signal, prevents signal loop
bool _syncingSupportedCommandAndCommand; ///< true: already in a sync signal, prevents signal loop
static QMap<MAV_CMD, MavCmdInfo_t> _mavCmdInfoMap;
static const QString _decimalPlacesJsonKey;
static const QString _descriptionJsonKey;
static const QString _friendlyNameJsonKey;
static const QString _friendlyEditJsonKey;
static const QString _idJsonKey;
static const QString _labelJsonKey;
static const QString _mavCmdInfoJsonKey;
static const QString _param1JsonKey;
static const QString _param2JsonKey;
static const QString _param3JsonKey;
static const QString _param4JsonKey;
static const QString _paramJsonKeyFormat;
static const QString _rawNameJsonKey;
static const QString _specifiesCoordinateJsonKey;
static const QString _unitsJsonKey;
static const QString _versionJsonKey;
static const QString _degreesUnits;
};
QDebug operator<<(QDebug dbg, const MissionItem& missionItem);
QDebug operator<<(QDebug dbg, const MissionItem* missionItem);
#endif
...@@ -54,11 +54,12 @@ const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLoiterTurns[] = ...@@ -54,11 +54,12 @@ const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLoiterTurns[] =
const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLoiterTime[] = { const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLoiterTime[] = {
{ "Altitude:", -30.0 }, { "Altitude:", -30.0 },
{ "Radius:", 30.0 }, { "Radius:", 30.0 },
{ "Seconds:", 10.0 }, { "Hold:", 10.0 },
}; };
const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLand[] = { const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesLand[] = {
{ "Altitude:", -30.0 }, { "Altitude:", -30.0 },
{ "Abort Alt:", 10.0 },
{ "Heading:", 1.0 }, { "Heading:", 1.0 },
}; };
...@@ -69,7 +70,7 @@ const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesTakeoff[] = { ...@@ -69,7 +70,7 @@ const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesTakeoff[] = {
}; };
const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesConditionDelay[] = { const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesConditionDelay[] = {
{ "Seconds:", 10.0 }, { "Hold:", 10.0 },
}; };
const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesDoJump[] = { const MissionItemTest::FactValue_t MissionItemTest::_rgFactValuesDoJump[] = {
...@@ -101,18 +102,19 @@ void MissionItemTest::_test(void) ...@@ -101,18 +102,19 @@ void MissionItemTest::_test(void)
qDebug() << "Command:" << info->command; qDebug() << "Command:" << info->command;
MissionItem* item = new MissionItem(NULL, MissionItem* item = new MissionItem(info->sequenceNumber,
info->sequenceNumber,
info->coordinate,
info->command, info->command,
info->frame,
info->param1, info->param1,
info->param2, info->param2,
info->param3, info->param3,
info->param4, info->param4,
info->coordinate.latitude(),
info->coordinate.longitude(),
info->coordinate.altitude(),
info->autocontinue, info->autocontinue,
info->isCurrentItem, info->isCurrentItem);
info->frame);
// Validate the saving is working correctly // Validate the saving is working correctly
QString savedItemString; QString savedItemString;
QTextStream saveStream(&savedItemString, QIODevice::WriteOnly); QTextStream saveStream(&savedItemString, QIODevice::WriteOnly);
...@@ -123,6 +125,7 @@ void MissionItemTest::_test(void) ...@@ -123,6 +125,7 @@ void MissionItemTest::_test(void)
size_t factCount = 0; size_t factCount = 0;
for (int i=0; i<item->textFieldFacts()->count(); i++) { for (int i=0; i<item->textFieldFacts()->count(); i++) {
Fact* fact = qobject_cast<Fact*>(item->textFieldFacts()->get(i)); Fact* fact = qobject_cast<Fact*>(item->textFieldFacts()->get(i));
qDebug() << fact->name();
bool found = false; bool found = false;
for (size_t j=0; j<expected->cFactValues; j++) { for (size_t j=0; j<expected->cFactValues; j++) {
...@@ -130,11 +133,7 @@ void MissionItemTest::_test(void) ...@@ -130,11 +133,7 @@ void MissionItemTest::_test(void)
if (factValue->name == fact->name()) { if (factValue->name == fact->name()) {
qDebug() << factValue->name; qDebug() << factValue->name;
if (strcmp(factValue->name, "Heading:") == 0) { QCOMPARE(fact->rawValue().toDouble(), factValue->value);
QCOMPARE(fact->value().toDouble() * (M_PI / 180.0), item->_yawRadians());
} else {
QCOMPARE(fact->value().toDouble(), factValue->value);
}
factCount ++; factCount ++;
found = true; found = true;
break; break;
......
...@@ -49,14 +49,14 @@ private: ...@@ -49,14 +49,14 @@ private:
typedef struct { typedef struct {
int sequenceNumber; int sequenceNumber;
QGeoCoordinate coordinate; QGeoCoordinate coordinate;
int command; MAV_CMD command;
double param1; double param1;
double param2; double param2;
double param3; double param3;
double param4; double param4;
bool autocontinue; bool autocontinue;
bool isCurrentItem; bool isCurrentItem;
int frame; MAV_FRAME frame;
} ItemInfo_t; } ItemInfo_t;
typedef struct { typedef struct {
......
...@@ -35,7 +35,6 @@ QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog") ...@@ -35,7 +35,6 @@ QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog")
MissionManager::MissionManager(Vehicle* vehicle) MissionManager::MissionManager(Vehicle* vehicle)
: _vehicle(vehicle) : _vehicle(vehicle)
, _cMissionItems(0) , _cMissionItems(0)
, _canEdit(true)
, _ackTimeoutTimer(NULL) , _ackTimeoutTimer(NULL)
, _retryAck(AckNone) , _retryAck(AckNone)
{ {
...@@ -287,24 +286,21 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message) ...@@ -287,24 +286,21 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message)
return; return;
} }
MissionItem* item = new MissionItem(this, MissionItem* item = new MissionItem(missionItem.seq,
missionItem.seq, (MAV_CMD)missionItem.command,
QGeoCoordinate(missionItem.x, missionItem.y, missionItem.z), (MAV_FRAME)missionItem.frame,
missionItem.command,
missionItem.param1, missionItem.param1,
missionItem.param2, missionItem.param2,
missionItem.param3, missionItem.param3,
missionItem.param4, missionItem.param4,
missionItem.x,
missionItem.y,
missionItem.z,
missionItem.autocontinue, missionItem.autocontinue,
missionItem.current, missionItem.current,
missionItem.frame); this);
_missionItems.append(item); _missionItems.append(item);
if (!item->canEdit()) {
_canEdit = false;
emit canEditChanged(false);
}
int nextSequenceNumber = missionItem.seq + 1; int nextSequenceNumber = missionItem.seq + 1;
if (nextSequenceNumber == _cMissionItems) { if (nextSequenceNumber == _cMissionItems) {
_readTransactionComplete(); _readTransactionComplete();
......
...@@ -49,13 +49,11 @@ public: ...@@ -49,13 +49,11 @@ public:
Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged) Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged)
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems CONSTANT) Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems CONSTANT)
Q_PROPERTY(bool canEdit READ canEdit NOTIFY canEditChanged)
// Property accessors // Property accessors
bool inProgress(void) { return _retryAck != AckNone; } bool inProgress(void) { return _retryAck != AckNone; }
QmlObjectListModel* missionItems(void) { return &_missionItems; } QmlObjectListModel* missionItems(void) { return &_missionItems; }
bool canEdit(void) { return _canEdit; }
// C++ methods // C++ methods
...@@ -85,8 +83,6 @@ public: ...@@ -85,8 +83,6 @@ public:
static const int _maxRetryCount = 5; static const int _maxRetryCount = 5;
signals: signals:
// Public signals
void canEditChanged(bool canEdit);
void newMissionItemsAvailable(void); void newMissionItemsAvailable(void);
void inProgressChanged(bool inProgress); void inProgressChanged(bool inProgress);
void error(int errorCode, const QString& errorMsg); void error(int errorCode, const QString& errorMsg);
...@@ -123,7 +119,6 @@ private: ...@@ -123,7 +119,6 @@ private:
Vehicle* _vehicle; Vehicle* _vehicle;
int _cMissionItems; ///< Mission items on vehicle int _cMissionItems; ///< Mission items on vehicle
bool _canEdit; ///< true: Mission items are editable in the ui
QTimer* _ackTimeoutTimer; QTimer* _ackTimeoutTimer;
AckType_t _retryAck; AckType_t _retryAck;
......
...@@ -58,8 +58,7 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f ...@@ -58,8 +58,7 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f
homeItem->setHomePositionSpecialCase(true); homeItem->setHomePositionSpecialCase(true);
homeItem->setHomePositionValid(false); homeItem->setHomePositionValid(false);
homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT); homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT);
homeItem->setLatitude(47.3769); homeItem->setCoordinate(QGeoCoordinate(47.3769, 8.549444, 0));
homeItem->setLongitude(8.549444);
homeItem->setSequenceNumber(0); homeItem->setSequenceNumber(0);
list->insert(0, homeItem); list->insert(0, homeItem);
...@@ -137,8 +136,6 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f ...@@ -137,8 +136,6 @@ void MissionManagerTest::_writeItems(MockLinkMissionItemHandler::FailureMode_t f
checkExpectedMessageBox(); checkExpectedMessageBox();
} }
QCOMPARE(_missionManager->canEdit(), true);
delete list; delete list;
list = NULL; list = NULL;
_multiSpyMissionManager->clearAllSignals(); _multiSpyMissionManager->clearAllSignals();
...@@ -172,7 +169,6 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode ...@@ -172,7 +169,6 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode
// newMissionItemsAvailable signal // newMissionItemsAvailable signal
_multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime); _multiSpyMissionManager->waitForSignalByIndex(inProgressChangedSignalIndex, _missionManagerSignalWaitTime);
QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true); QCOMPARE(_multiSpyMissionManager->checkSignalByMask(newMissionItemsAvailableSignalMask | inProgressChangedSignalMask), true);
QCOMPARE(_multiSpyMissionManager->checkNoSignalByMask(canEditChangedSignalMask), true);
_checkInProgressValues(false); _checkInProgressValues(false);
} else { } else {
...@@ -233,7 +229,6 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode ...@@ -233,7 +229,6 @@ void MissionManagerTest::_roundTripItems(MockLinkMissionItemHandler::FailureMode
} }
QCOMPARE(_missionManager->missionItems()->count(), (int)cMissionItemsExpected); QCOMPARE(_missionManager->missionItems()->count(), (int)cMissionItemsExpected);
QCOMPARE(_missionManager->canEdit(), true);
size_t firstActualItem = 0; size_t firstActualItem = 0;
if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) { if (_mockLink->getFirmwareType() == MAV_AUTOPILOT_ARDUPILOTMEGA) {
......
...@@ -40,6 +40,13 @@ Rectangle { ...@@ -40,6 +40,13 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
height: valuesRect.visible ? valuesRect.y + valuesRect.height : valuesRect.y height: valuesRect.visible ? valuesRect.y + valuesRect.height : valuesRect.y
MouseArea {
anchors.fill: parent
visible: !missionItem.isCurrentItem
onClicked: _root.clicked()
}
MissionItemIndexLabel { MissionItemIndexLabel {
id: label id: label
anchors.verticalCenter: commandPicker.verticalCenter anchors.verticalCenter: commandPicker.verticalCenter
...@@ -47,21 +54,30 @@ Rectangle { ...@@ -47,21 +54,30 @@ Rectangle {
label: missionItem.sequenceNumber == 0 ? "H" : missionItem.sequenceNumber label: missionItem.sequenceNumber == 0 ? "H" : missionItem.sequenceNumber
} }
MouseArea { Image {
anchors.fill: parent id: rawEdit
visible: !missionItem.isCurrentItem anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 8
anchors.left: label.right
onClicked: _root.clicked() anchors.verticalCenter: commandPicker.verticalCenter
width: commandPicker.height
height: commandPicker.height
visible: missionItem.friendlyEditAllowed && missionItem.sequenceNumber != 0 && missionItem.isCurrentItem
source: "qrc:/qmlimages/CogWheel.svg"
MouseArea {
anchors.fill: parent
onClicked: missionItem.rawEdit = !missionItem.rawEdit
}
} }
QGCComboBox { FactComboBox {
id: commandPicker id: commandPicker
anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 10 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
anchors.left: label.right anchors.left: rawEdit.right
anchors.right: parent.right anchors.right: parent.right
currentIndex: missionItem.commandByIndex indexModel: false
model: missionItem.commandNames fact: missionItem.supportedCommand
visible: missionItem.sequenceNumber != 0 && missionItem.isCurrentItem visible: missionItem.sequenceNumber != 0 && missionItem.isCurrentItem && !missionItem.rawEdit
onActivated: missionItem.commandByIndex = index onActivated: missionItem.commandByIndex = index
} }
...@@ -69,10 +85,9 @@ Rectangle { ...@@ -69,10 +85,9 @@ Rectangle {
Rectangle { Rectangle {
anchors.fill: commandPicker anchors.fill: commandPicker
color: qgcPal.button color: qgcPal.button
visible: !commandPicker.visible visible: missionItem.sequenceNumber == 0 || !missionItem.isCurrentItem
QGCLabel { QGCLabel {
id: homeLabel
anchors.leftMargin: ScreenTools.defaultFontPixelWidth anchors.leftMargin: ScreenTools.defaultFontPixelWidth
anchors.fill: parent anchors.fill: parent
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
...@@ -89,7 +104,7 @@ Rectangle { ...@@ -89,7 +104,7 @@ Rectangle {
anchors.right: parent.right anchors.right: parent.right
height: valuesItem.height height: valuesItem.height
color: qgcPal.windowShadeDark color: qgcPal.windowShadeDark
visible: missionItem.isCurrentItem visible: missionItem.sequenceNumber != 0 && missionItem.isCurrentItem
radius: _radius radius: _radius
Item { Item {
...@@ -110,7 +125,20 @@ Rectangle { ...@@ -110,7 +125,20 @@ Rectangle {
QGCLabel { QGCLabel {
width: parent.width width: parent.width
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
text: missionItem.commandDescription text: missionItem.rawEdit ?
"Provides advanced access to all commands/parameters. Be very careful!" :
missionItem.commandDescription
}
Repeater {
model: missionItem.comboboxFacts
FactComboBox {
width: valuesColumn.width
indexModel: false
model: object.enumStrings
fact: object
}
} }
Repeater { Repeater {
...@@ -148,7 +176,6 @@ Rectangle { ...@@ -148,7 +176,6 @@ Rectangle {
model: missionItem.checkboxFacts model: missionItem.checkboxFacts
FactCheckBox { FactCheckBox {
id: textField
text: object.name text: object.name
fact: object fact: object
} }
......
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
/// Mission item summary display control
Rectangle {
property var missionItem ///< Mission Item object
width: ScreenTools.defaultFontPixelWidth * 15
height: valueColumn.height + radius
border.width: 2
border.color: "white"
color: "white"
radius: ScreenTools.defaultFontPixelWidth
MissionItemIndexLabel {
id: _indexLabel
anchors.top: parent.top
anchors.right: parent.right
isCurrentItem: missionItem.isCurrentItem
label: missionItem.sequenceNumber
}
Column {
anchors.margins: parent.radius / 2
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
QGCLabel {
color: "black"
horizontalAlignment: Text.AlignTop
font.weight: Font.Bold
text: missionItem.commandName
}
Repeater {
model: missionItem.valueLabels
QGCLabel {
color: "black"
text: modelData
}
}
}
Column {
id: valueColumn
anchors.margins: parent.radius / 2
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
QGCLabel {
font.weight: Font.Bold
text: " "
}
Repeater {
model: missionItem.valueStrings
QGCLabel {
width: valueColumn.width
color: "black"
text: modelData
horizontalAlignment: Text.AlignRight
}
}
}
}
...@@ -59,7 +59,8 @@ QGCViewDialog { ...@@ -59,7 +59,8 @@ QGCViewDialog {
validationError.text = fact.validate(validateValue, false /* convertOnly */) validationError.text = fact.validate(validateValue, false /* convertOnly */)
forceSave.visible = true forceSave.visible = true
} }
valueField.forceActiveFocus(); // This was causing problems where it would never give up focus even when hidden!
//valueField.forceActiveFocus()
} }
Column { Column {
...@@ -83,6 +84,7 @@ QGCViewDialog { ...@@ -83,6 +84,7 @@ QGCViewDialog {
QGCTextField { QGCTextField {
id: valueField id: valueField
text: validate ? validateValue : fact.valueString text: validate ? validateValue : fact.valueString
focus: true
// At this point all Facts are numeric // At this point all Facts are numeric
inputMethodHints: Qt.ImhFormattedNumbersOnly inputMethodHints: Qt.ImhFormattedNumbersOnly
......
...@@ -37,8 +37,9 @@ import QGroundControl.FactControls 1.0 ...@@ -37,8 +37,9 @@ import QGroundControl.FactControls 1.0
FactPanel { FactPanel {
id: __rootItem id: __rootItem
property var qgcView: __rootItem /// Used by Fact controls for validation dialogs property var qgcView: __rootItem ///< Used by Fact controls for validation dialogs
property bool completedSignalled: false property bool completedSignalled: false
property real topDialogMargin: 0 ///< Set a top margin for dialog
property var viewPanel property var viewPanel
...@@ -278,11 +279,13 @@ FactPanel { ...@@ -278,11 +279,13 @@ FactPanel {
// This is the main dialog panel which is anchored to the right edge // This is the main dialog panel which is anchored to the right edge
Rectangle { Rectangle {
id: __dialogPanel id: __dialogPanel
width: __dialogCharWidth == -1 ? parent.width : defaultTextWidth * __dialogCharWidth width: __dialogCharWidth == -1 ? parent.width : defaultTextWidth * __dialogCharWidth
height: parent.height anchors.topMargin: topDialogMargin
anchors.left: __transparentSection.right anchors.top: parent.top
color: __qgcPal.windowShadeDark anchors.bottom: parent.bottom
anchors.left: __transparentSection.right
color: __qgcPal.windowShadeDark
Rectangle { Rectangle {
id: __header id: __header
...@@ -344,4 +347,4 @@ FactPanel { ...@@ -344,4 +347,4 @@ FactPanel {
message: __messageDialogText message: __messageDialogText
} }
} }
} }
\ No newline at end of file
...@@ -31,7 +31,6 @@ QGCViewDialog 1.0 QGCViewDialog.qml ...@@ -31,7 +31,6 @@ QGCViewDialog 1.0 QGCViewDialog.qml
QGCViewMessage 1.0 QGCViewMessage.qml QGCViewMessage 1.0 QGCViewMessage.qml
MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml
MissionItemSummary 1.0 MissionItemSummary.qml
MissionItemEditor 1.0 MissionItemEditor.qml MissionItemEditor 1.0 MissionItemEditor.qml
MainToolBar 1.0 MainToolBar.qml MainToolBar 1.0 MainToolBar.qml
......
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