Commit 62285253 authored by Don Gagne's avatar Don Gagne

More work on complex mission items

- Load/Save working
- Entry point working
parent 17fee7a5
......@@ -42,9 +42,12 @@
<file alias="PowerComponent.qml">src/AutoPilotPlugins/PX4/PowerComponent.qml</file>
<file alias="PowerComponentSummary.qml">src/AutoPilotPlugins/PX4/PowerComponentSummary.qml</file>
<file alias="PX4FlowSensor.qml">src/VehicleSetup/PX4FlowSensor.qml</file>
<file alias="QGroundControl/Controls/qmldir">src/QmlControls/QGroundControl.Controls.qmldir</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/ExclusiveGroupItem.qml">src/QmlControls/ExclusiveGroupItem.qml</file>
<file alias="QGroundControl/Controls/FactSliderPanel.qml">src/QmlControls/FactSliderPanel.qml</file>
<file alias="QGroundControl/Controls/IndicatorButton.qml">src/QmlControls/IndicatorButton.qml</file>
<file alias="QGroundControl/Controls/JoystickThumbPad.qml">src/QmlControls/JoystickThumbPad.qml</file>
<file alias="QGroundControl/Controls/MainToolBar.qml">src/ui/toolbar/MainToolBar.qml</file>
......@@ -73,14 +76,16 @@
<file alias="QGroundControl/Controls/QGCViewDialog.qml">src/QmlControls/QGCViewDialog.qml</file>
<file alias="QGroundControl/Controls/QGCViewMessage.qml">src/QmlControls/QGCViewMessage.qml</file>
<file alias="QGroundControl/Controls/QGCViewPanel.qml">src/QmlControls/QGCViewPanel.qml</file>
<file alias="QGroundControl/Controls/qmldir">src/QmlControls/QGroundControl.Controls.qmldir</file>
<file alias="QGroundControl/Controls/RoundButton.qml">src/QmlControls/RoundButton.qml</file>
<file alias="QGroundControl/Controls/SignalStrength.qml">src/ui/toolbar/SignalStrength.qml</file>
<file alias="QGroundControl/Controls/SubMenuButton.qml">src/QmlControls/SubMenuButton.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/ViewWidget.qml">src/ViewWidgets/ViewWidget.qml</file>
<file alias="QGroundControl/Controls/FactSliderPanel.qml">src/QmlControls/FactSliderPanel.qml</file>
<file alias="SimpleItemEditor.qml">src/MissionEditor/SimpleItemEditor.qml</file>
<file alias="SurveyItemEditor.qml">src/MissionEditor/SurveyItemEditor.qml</file>
<file alias="QGroundControl/FactControls/FactBitmask.qml">src/FactSystem/FactControls/FactBitmask.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>
......
......@@ -82,7 +82,19 @@ bool JsonHelper::toQGeoCoordinate(const QJsonValue& jsonValue, QGeoCoordinate& c
return true;
}
bool JsonHelper::validateKeyTypes(QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString)
void JsonHelper::writeQGeoCoordinate(QJsonValue& jsonValue, const QGeoCoordinate& coordinate, bool writeAltitude)
{
QJsonArray coordinateArray;
coordinateArray << coordinate.latitude() << coordinate.longitude();
if (writeAltitude) {
coordinateArray << coordinate.altitude();
}
jsonValue = QJsonValue(coordinateArray);
}
bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString)
{
for (int i=0; i<keys.count(); i++) {
if (jsonObject.contains(keys[i])) {
......
......@@ -31,10 +31,12 @@ class JsonHelper
{
public:
static bool validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString);
static bool validateKeyTypes(QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString);
static bool validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString);
static bool toQGeoCoordinate(const QJsonValue& jsonValue, QGeoCoordinate& coordinate, bool altitudeRequired, QString& errorString);
static bool parseEnum(QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString);
static void writeQGeoCoordinate(QJsonValue& jsonValue, const QGeoCoordinate& coordinate, bool writeAltitude);
static const char* _enumStringsJsonKey;
static const char* _enumValuesJsonKey;
};
......
......@@ -552,6 +552,7 @@ QGCView {
var index = controller.insertComplexMissionItem(coordinate, controller.visualItems.count)
setCurrentItem(index)
checked = false
addMissionItemsButton.checked = false
}
}
......
......@@ -49,7 +49,6 @@ Rectangle {
property real _gradient: _statusValid ? Math.atan(_currentMissionItem.altDifference / _currentMissionItem.distance) : 0
property real _gradientPercent: isNaN(_gradient) ? 0 : _gradient * 100
property real _azimuth: _statusValid ? _currentMissionItem.azimuth : -1
property real _isHomePosition: _statusValid ? _currentMissionItem.homePosition : false
property bool _statusValid: currentMissionItem != undefined
property string _distanceText: _statusValid ? _distance.toFixed(2) + " m" : ""
property string _altText: _statusValid ? _altDifference.toFixed(2) + " m" : ""
......
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.2
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
// Editor for Simple mission items
Rectangle {
id: valuesRect
width: availableWidth
height: valuesItem.height
color: qgcPal.windowShadeDark
visible: missionItem.isCurrentItem
radius: _radius
// The following properties must be available up the hierachy chain
//property real availableWidth ///< Width for control
//property var missionItem ///< Mission Item for editor
Item {
id: valuesItem
anchors.margins: _margin
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: valuesColumn.height + (_margin * 2)
Column {
id: valuesColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: _margin
QGCLabel {
width: parent.width
wrapMode: Text.WordWrap
text: missionItem.sequenceNumber == 0 ?
"Planned home position." :
(missionItem.rawEdit ?
"Provides advanced access to all commands/parameters. Be very careful!" :
missionItem.commandDescription)
}
Repeater {
model: missionItem.comboboxFacts
Item {
width: valuesColumn.width
height: comboBoxFact.height
QGCLabel {
id: comboBoxLabel
anchors.baseline: comboBoxFact.baseline
text: object.name
visible: object.name != ""
}
FactComboBox {
id: comboBoxFact
anchors.right: parent.right
width: comboBoxLabel.visible ? _editFieldWidth : parent.width
indexModel: false
model: object.enumStrings
fact: object
}
}
}
Repeater {
model: missionItem.textFieldFacts
Item {
width: valuesColumn.width
height: textField.height
QGCLabel {
id: textFieldLabel
anchors.baseline: textField.baseline
text: object.name
}
FactTextField {
id: textField
anchors.right: parent.right
width: _editFieldWidth
showUnits: true
fact: object
visible: !_root.readOnly
}
FactLabel {
anchors.baseline: textFieldLabel.baseline
anchors.right: parent.right
fact: object
visible: _root.readOnly
}
}
}
Repeater {
model: missionItem.checkboxFacts
FactCheckBox {
text: object.name
fact: object
}
}
QGCButton {
text: "Move Home to map center"
visible: missionItem.homePosition
onClicked: editorRoot.moveHomeToMapCenter()
anchors.horizontalCenter: parent.horizontalCenter
}
} // Column
} // Item
} // Rectangle
import QtQuick 2.2
import QtQuick.Controls 1.2
import QGroundControl.ScreenTools 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Palette 1.0
// Editor for Survery mission items
Rectangle {
id: _root
height: editorColumn.height + (_margin * 2)
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
// The following properties must be available up the hierachy chain
//property real availableWidth ///< Width for control
//property var missionItem ///< Mission Item for editor
property bool _addPointsMode: false
property real _margin: ScreenTools.defaultFontPixelWidth / 2
QGCPalette { id: qgcPal; colorGroupEnabled: true }
Column {
id: editorColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
width: availableWidth
spacing: _margin
Connections {
target: editorMap
onMapClicked: {
if (_addPointsMode) {
missionItem.addPolygonCoordinate(coordinate)
}
}
}
QGCLabel {
text: "Fly a grid pattern over a defined area."
wrapMode: Text.WordWrap
}
QGCButton {
text: _addPointsMode ? "Finished" : "Draw Polygon"
onClicked: {
if (_addPointsMode) {
_addPointsMode = false
} else {
missionItem.clearPolygon()
_addPointsMode = true
}
}
}
}
}
/*===================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
(c) 2009, 2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
......@@ -21,12 +21,25 @@ This file is part of the QGROUNDCONTROL project
======================================================================*/
#include "ComplexMissionItem.h"
#include "JsonHelper.h"
#include "MissionController.h"
const char* ComplexMissionItem::_jsonVersionKey = "version";
const char* ComplexMissionItem::_jsonTypeKey = "type";
const char* ComplexMissionItem::_jsonPolygonKey = "polygon";
const char* ComplexMissionItem::_jsonIdKey = "id";
const char* ComplexMissionItem::_complexType = "survey";
ComplexMissionItem::ComplexMissionItem(Vehicle* vehicle, QObject* parent)
: VisualMissionItem(vehicle, parent)
, _dirty(false)
{
MissionItem missionItem;
// FIXME: Bogus entries for testing
_missionItems += new MissionItem(this);
_missionItems += new MissionItem(this);
}
ComplexMissionItem::ComplexMissionItem(const ComplexMissionItem& other, QObject* parent)
......@@ -36,20 +49,6 @@ ComplexMissionItem::ComplexMissionItem(const ComplexMissionItem& other, QObject*
}
QVariantList ComplexMissionItem::polygonPath(void)
{
return _polygonPath;
#if 0
QVariantList list;
list << QVariant::fromValue(QGeoCoordinate(-35.362686830000001, 149.16410282999999))
<< QVariant::fromValue(QGeoCoordinate(-35.362660579999996, 149.16606619999999))
<< QVariant::fromValue(QGeoCoordinate(-35.363832989999999, 149.16505769));
return list;
#endif
}
void ComplexMissionItem::clearPolygon(void)
{
_polygonPath.clear();
......@@ -60,6 +59,11 @@ void ComplexMissionItem::addPolygonCoordinate(const QGeoCoordinate coordinate)
{
_polygonPath << QVariant::fromValue(coordinate);
emit polygonPathChanged();
// FIXME: Hack, first polygon point sets entry coordinate
if (_polygonPath.count() == 1) {
setCoordinate(coordinate);
}
}
int ComplexMissionItem::nextSequenceNumber(void) const
......@@ -72,6 +76,10 @@ void ComplexMissionItem::setCoordinate(const QGeoCoordinate& coordinate)
if (_coordinate != coordinate) {
_coordinate = coordinate;
emit coordinateChanged(_coordinate);
_missionItems[0]->setCoordinate(coordinate);
// FIXME: Hack
_setExitCoordinate(coordinate);
}
}
......@@ -83,11 +91,148 @@ void ComplexMissionItem::setDirty(bool dirty)
}
}
bool ComplexMissionItem::save(QJsonObject& missionObject, QJsonArray& missionItems, QString& errorString)
void ComplexMissionItem::save(QJsonObject& saveObject) const
{
saveObject[_jsonVersionKey] = 1;
saveObject[_jsonTypeKey] = _complexType;
saveObject[_jsonIdKey] = sequenceNumber();
// Polygon shape
QJsonArray polygonArray;
for (int i=0; i<_polygonPath.count(); i++) {
const QVariant& polyVar = _polygonPath[i];
QJsonValue jsonValue;
JsonHelper::writeQGeoCoordinate(jsonValue, polyVar.value<QGeoCoordinate>(), false /* writeAltitude */);
polygonArray += jsonValue;
}
saveObject[_jsonPolygonKey] = polygonArray;
// Base mission items
QJsonArray simpleItems;
for (int i=0; i<_missionItems.count(); i++) {
MissionItem* missionItem = _missionItems[i];
QJsonObject jsonObject;
missionItem->save(jsonObject);
simpleItems.append(jsonObject);
}
saveObject[MissionController::jsonSimpleItemsKey] = simpleItems;
}
void ComplexMissionItem::setSequenceNumber(int sequenceNumber)
{
VisualMissionItem::setSequenceNumber(sequenceNumber);
// Update internal mission items to new numbering
for (int i=0; i<_missionItems.count(); i++) {
_missionItems[i]->setSequenceNumber(sequenceNumber++);
}
}
void ComplexMissionItem::_clear(void)
{
// Clear old settings
for (int i=0; i<_missionItems.count(); i++) {
_missionItems[i]->deleteLater();
}
_missionItems.clear();
_polygonPath.clear();
}
bool ComplexMissionItem::load(const QJsonObject& complexObject, QString& errorString)
{
Q_UNUSED(missionObject);
Q_UNUSED(missionItems);
_clear();
errorString = "Complex save NYI";
// Validate requires keys
QStringList requiredKeys;
requiredKeys << _jsonVersionKey << _jsonTypeKey << _jsonIdKey << _jsonPolygonKey << MissionController::jsonSimpleItemsKey;
if (!JsonHelper::validateRequiredKeys(complexObject, requiredKeys, errorString)) {
_clear();
return false;
}
// Validate types
QStringList keyList;
QList<QJsonValue::Type> typeList;
keyList << _jsonVersionKey << _jsonTypeKey << _jsonIdKey << _jsonPolygonKey << MissionController::jsonSimpleItemsKey;
typeList << QJsonValue::Double << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Array;
if (!JsonHelper::validateKeyTypes(complexObject, keyList, typeList, errorString)) {
_clear();
return false;
}
// Version check
if (complexObject[_jsonVersionKey].toInt() != 1) {
errorString = tr("QGroundControl does not support this version of survey items");
_clear();
return false;
}
QString complexType = complexObject[_jsonTypeKey].toString();
if (complexType != _complexType) {
errorString = tr("QGroundControl does not support loading this complex mission item type: %1").arg(complexType);
_clear();
return false;
}
setSequenceNumber(complexObject[_jsonIdKey].toInt());
// Polygon shape
QJsonArray polygonArray(complexObject[_jsonPolygonKey].toArray());
for (int i=0; i<polygonArray.count(); i++) {
const QJsonValue& pointValue = polygonArray[i];
QGeoCoordinate pointCoord;
if (!JsonHelper::toQGeoCoordinate(pointValue, pointCoord, false /* altitudeRequired */, errorString)) {
_clear();
return false;
}
_polygonPath << QVariant::fromValue(pointCoord);
}
// Internal mission items
QJsonArray itemArray(complexObject[MissionController::jsonSimpleItemsKey].toArray());
for (int i=0; i<itemArray.count(); i++) {
const QJsonValue& itemValue = itemArray[i];
if (!itemValue.isObject()) {
errorString = QStringLiteral("Mission item is not an object");
_clear();
return false;
}
MissionItem* item = new MissionItem(_vehicle, this);
if (item->load(itemValue.toObject(), errorString)) {
_missionItems.append(item);
} else {
_clear();
return false;
}
}
int itemCount = _missionItems.count();
if (itemCount > 0) {
setCoordinate(_missionItems[0]->coordinate());
_setExitCoordinate(_missionItems[itemCount - 1]->coordinate());
}
return true;
}
void ComplexMissionItem::_setExitCoordinate(const QGeoCoordinate& coordinate)
{
if (_exitCoordinate != coordinate) {
_exitCoordinate = coordinate;
emit exitCoordinateChanged(coordinate);
int itemCount = _missionItems.count();
if (itemCount > 0) {
_missionItems[itemCount - 1]->setCoordinate(coordinate);
}
}
}
......@@ -2,7 +2,7 @@
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
(c) 2009 - 2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
......@@ -40,13 +40,19 @@ public:
Q_INVOKABLE void clearPolygon(void);
Q_INVOKABLE void addPolygonCoordinate(const QGeoCoordinate coordinate);
QVariantList polygonPath(void);
QVariantList polygonPath(void) { return _polygonPath; }
const QList<MissionItem*>& missionItems(void) { return _missionItems; }
QList<MissionItem*>& missionItems(void) { return _missionItems; }
/// @return The next sequence number to use after this item. Takes into account child items of the complex item
int nextSequenceNumber(void) const;
/// Load the complex mission item from Json
/// @param complexObject Complex mission item json object
/// @param[out] errorString Error if load fails
/// @return true: load success, false: load failed, errorString set
bool load(const QJsonObject& complexObject, QString& errorString);
// Overrides from VisualMissionItem
bool dirty (void) const final { return _dirty; }
......@@ -56,24 +62,36 @@ public:
QString commandDescription (void) const final { return "Survey"; }
QString commandName (void) const final { return "Survey"; }
QGeoCoordinate coordinate (void) const final { return _coordinate; }
QGeoCoordinate exitCoordinate (void) const final { return _coordinate; }
QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; }
bool coordinateHasRelativeAltitude (void) const final { return true; }
bool exitCoordinateHasRelativeAltitude (void) const final { return true; }
bool exitCoordinateSameAsEntry (void) const final { return false; }
void setDirty (bool dirty) final;
void setCoordinate (const QGeoCoordinate& coordinate);
bool save (QJsonObject& missionObject, QJsonArray& missionItems, QString& errorString) final;
void setCoordinate (const QGeoCoordinate& coordinate) final;
void setSequenceNumber (int sequenceNumber) final;
void save (QJsonObject& saveObject) const final;
signals:
void polygonPathChanged(void);
private:
void _clear(void);
void _setExitCoordinate(const QGeoCoordinate& coordinate);
bool _dirty;
QVariantList _polygonPath;
QList<MissionItem*> _missionItems;
QGeoCoordinate _coordinate;
QGeoCoordinate _exitCoordinate;
static const char* _jsonVersionKey;
static const char* _jsonTypeKey;
static const char* _jsonPolygonKey;
static const char* _jsonIdKey;
static const char* _complexType;
};
#endif
......@@ -29,6 +29,7 @@ This file is part of the QGROUNDCONTROL project
#include "QGCApplication.h"
#include "SimpleMissionItem.h"
#include "ComplexMissionItem.h"
#include "JsonHelper.h"
#ifndef __mobile__
#include "QGCFileDialog.h"
......@@ -36,11 +37,13 @@ This file is part of the QGROUNDCONTROL project
QGC_LOGGING_CATEGORY(MissionControllerLog, "MissionControllerLog")
const char* MissionController::jsonSimpleItemsKey = "items";
const char* MissionController::_settingsGroup = "MissionController";
const char* MissionController::_jsonVersionKey = "version";
const char* MissionController::_jsonGroundStationKey = "groundStation";
const char* MissionController::_jsonMavAutopilotKey = "MAV_AUTOPILOT";
const char* MissionController::_jsonItemsKey = "items";
const char* MissionController::_jsonComplexItemsKey = "complexItems";
const char* MissionController::_jsonPlannedHomePositionKey = "plannedHomePosition";
MissionController::MissionController(QObject *parent)
......@@ -140,7 +143,9 @@ void MissionController::sendMissionItems(void)
missionItem.setSequenceNumber(sequenceNumber++);
missionItems.append(&missionItem);
} else {
foreach (MissionItem* missionItem, qobject_cast<ComplexMissionItem*>(visualItem)->missionItems()) {
ComplexMissionItem* complexItem = qobject_cast<ComplexMissionItem*>(visualItem);
for (int j=0; j<complexItem->missionItems().count(); j++) {
MissionItem* missionItem = complexItem->missionItems()[i];
missionItem->setSequenceNumber(sequenceNumber++);
missionItems.append(missionItem);
}
......@@ -247,29 +252,56 @@ bool MissionController::_loadJsonMissionFile(const QByteArray& bytes, QmlObjectL
QJsonObject json = jsonDoc.object();
if (!json.contains(_jsonVersionKey)) {
errorString = QStringLiteral("File is missing version key");
// Check for required keys
QStringList requiredKeys;
requiredKeys << _jsonVersionKey << _jsonPlannedHomePositionKey;
if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) {
return false;
}
// Validate base key types
QStringList keyList;
QList<QJsonValue::Type> typeList;
keyList << jsonSimpleItemsKey << _jsonVersionKey << _jsonGroundStationKey << _jsonMavAutopilotKey << _jsonComplexItemsKey << _jsonPlannedHomePositionKey;
typeList << QJsonValue::Array << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Array << QJsonValue::Object;
if (!JsonHelper::validateKeyTypes(json, keyList, typeList, errorString)) {
return false;
}
// Version check
if (json[_jsonVersionKey].toString() != "1.0") {
errorString = QStringLiteral("QGroundControl does not support this file version");
return false;
}
if (json.contains(_jsonItemsKey)) {
if (!json[_jsonItemsKey].isArray()) {
errorString = QStringLiteral("items values must be array");
// Simple items
if (json.contains(jsonSimpleItemsKey)) {
QJsonArray itemArray(json[jsonSimpleItemsKey].toArray());
foreach (const QJsonValue& itemValue, itemArray) {
if (!itemValue.isObject()) {
errorString = QStringLiteral("Mission item is not an object");
return false;
}
SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this);
if (item->load(itemValue.toObject(), errorString)) {
missionItems->append(item);
} else {
return false;
}
}
}
QJsonArray itemArray(json[_jsonItemsKey].toArray());
// Complex items
if (json.contains(_jsonComplexItemsKey)) {
QJsonArray itemArray(json[_jsonComplexItemsKey].toArray());
foreach (const QJsonValue& itemValue, itemArray) {
if (!itemValue.isObject()) {
errorString = QStringLiteral("Mission item is not an object");
return false;
}
SimpleMissionItem* item = new SimpleMissionItem(_activeVehicle, this);
ComplexMissionItem* item = new ComplexMissionItem(_activeVehicle, this);
if (item->load(itemValue.toObject(), errorString)) {
missionItems->append(item);
} else {
......@@ -418,12 +450,12 @@ void MissionController::_saveMissionToFile(const QString& filename)
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qgcApp()->showMessage(file.errorString());
} else {
QJsonObject missionObject; // top level json object
QJsonArray missionItemArray; // array of mission items
QString errorString;
QJsonObject missionFileObject; // top level json object
QJsonArray simpleItemsObject;
QJsonArray complexItemsObject;
missionObject[_jsonVersionKey] = "1.0";
missionObject[_jsonGroundStationKey] = "QGroundControl";
missionFileObject[_jsonVersionKey] = "1.0";
missionFileObject[_jsonGroundStationKey] = "QGroundControl";
MAV_AUTOPILOT firmwareType = MAV_AUTOPILOT_GENERIC;
if (_activeVehicle) {
......@@ -435,7 +467,7 @@ void MissionController::_saveMissionToFile(const QString& filename)
QSettings settings;
firmwareType = (MAV_AUTOPILOT)settings.value("OfflineEditingFirmwareType", MAV_AUTOPILOT_ARDUPILOTMEGA).toInt();
}
missionObject[_jsonMavAutopilotKey] = firmwareType;
missionFileObject[_jsonMavAutopilotKey] = firmwareType;
// Save planned home position
QJsonObject homePositionObject;
......@@ -446,19 +478,26 @@ void MissionController::_saveMissionToFile(const QString& filename)
qgcApp()->showMessage(QStringLiteral("Internal error: VisualMissionItem at index 0 not SimpleMissionItem"));
return;
}
missionObject[_jsonPlannedHomePositionKey] = homePositionObject;
missionFileObject[_jsonPlannedHomePositionKey] = homePositionObject;
// Save the remainder of the visual items
// Save the visual items
for (int i=1; i<_visualItems->count(); i++) {
QJsonObject itemObject;
VisualMissionItem* visualItem = qobject_cast<VisualMissionItem*>(_visualItems->get(i));
if (!visualItem->save(missionObject, missionItemArray, errorString)) {
qgcApp()->showMessage(errorString);
return;
visualItem->save(itemObject);
if (visualItem->isSimpleItem()) {
simpleItemsObject.append(itemObject);
} else {
complexItemsObject.append(itemObject);
}
}
missionObject["items"] = missionItemArray;
QJsonDocument saveDoc(missionObject);
missionFileObject[jsonSimpleItemsKey] = simpleItemsObject;
missionFileObject[_jsonComplexItemsKey] = complexItemsObject;
QJsonDocument saveDoc(missionFileObject);
file.write(saveDoc.toJson());
}
......
......@@ -75,6 +75,8 @@ public:
void setAutoSync(bool autoSync);
bool syncInProgress(void);
static const char* jsonSimpleItemsKey; ///< Key for simple items in a json file
signals:
void visualItemsChanged(void);
void complexVisualItemsChanged(void);
......@@ -131,7 +133,7 @@ private:
static const char* _jsonVersionKey;
static const char* _jsonGroundStationKey;
static const char* _jsonMavAutopilotKey;
static const char* _jsonItemsKey;
static const char* _jsonComplexItemsKey;
static const char* _jsonPlannedHomePositionKey;
};
......
......@@ -148,7 +148,7 @@ MissionItem::~MissionItem()
{
}
void MissionItem::save(QJsonObject& json)
void MissionItem::save(QJsonObject& json) const
{
json[_jsonTypeKey] = _itemType;
json[_jsonIdKey] = sequenceNumber();
......
......@@ -103,7 +103,7 @@ public:
void setParam7 (double param7);
void setCoordinate (const QGeoCoordinate& coordinate);
void save(QJsonObject& json);
void save(QJsonObject& json) const;
bool load(QTextStream &loadStream);
bool load(const QJsonObject& json, QString& errorString);
......
......@@ -244,16 +244,9 @@ SimpleMissionItem::~SimpleMissionItem()
{
}
bool SimpleMissionItem::save(QJsonObject& missionObject, QJsonArray& missionItems, QString& errorString)
void SimpleMissionItem::save(QJsonObject& saveObject) const
{
Q_UNUSED(missionObject);
Q_UNUSED(errorString);
QJsonObject itemObject;
_missionItem.save(itemObject);
missionItems.append(itemObject);
return true;
_missionItem.save(saveObject);
}
bool SimpleMissionItem::load(QTextStream &loadStream)
......@@ -585,3 +578,9 @@ void SimpleMissionItem::setCoordinate(const QGeoCoordinate& coordinate)
_missionItem.setCoordinate(coordinate);
}
}
void SimpleMissionItem::setSequenceNumber(int sequenceNumber)
{
_missionItem.setSequenceNumber(sequenceNumber);
VisualMissionItem::setSequenceNumber(sequenceNumber);
}
......@@ -107,8 +107,9 @@ public:
bool exitCoordinateSameAsEntry (void) const final { return true; }
void setDirty (bool dirty) final;
void setCoordinate (const QGeoCoordinate& coordinate);
bool save (QJsonObject& missionObject, QJsonArray& missionItems, QString& errorString) final;
void setCoordinate (const QGeoCoordinate& coordinate) final;
void setSequenceNumber (int sequenceNumber) final;
void save (QJsonObject& saveObject) const final;
public slots:
void setDefaultsForCommand(void);
......
......@@ -108,10 +108,13 @@ public:
void setAltPercent (double altPercent);
void setAzimuth (double azimuth);
void setDistance (double distance);
void setSequenceNumber (int sequenceNumber);
Vehicle* vehicle(void) { return _vehicle; }
// Virtuals which may be overriden by derived classes
virtual void setSequenceNumber(int sequenceNumber);
// Pure virtuals which must be provides by derived classes
virtual bool dirty (void) const = 0;
......@@ -130,11 +133,9 @@ public:
virtual void setDirty (bool dirty) = 0;
virtual void setCoordinate (const QGeoCoordinate& coordinate) = 0;
/// Save the item in Json format
/// @param missionObject Top level object for entire mission file
/// @param missionItems Array of mission items
/// @return false: save failed, errorString set
virtual bool save(QJsonObject& missionObject, QJsonArray& missionItems, QString& errorString) = 0;
/// Save the item(s) in Json format
/// @param saveObject Save the item to this json object
virtual void save(QJsonObject& saveObject) const = 0;
signals:
void altDifferenceChanged (double altDifference);
......
......@@ -15,7 +15,7 @@ Rectangle {
id: _root
height: editorLoader.y + editorLoader.height + (_margin * 2)
color: missionItem.isCurrentItem ? qgcPal.buttonHighlight : qgcPal.windowShade
color: _currentItem ? qgcPal.buttonHighlight : qgcPal.windowShade
radius: _radius
property var missionItem ///< MissionItem associated with this editor
......@@ -27,11 +27,12 @@ Rectangle {
signal insert(int i)
signal moveHomeToMapCenter
property bool _currentItem: missionItem.isCurrentItem
property color _outerTextColor: _currentItem ? "black" : qgcPal.text
readonly property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 16
readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2
readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2
property color _outerTextColor: missionItem.isCurrentItem ? "black" : qgcPal.text
QGCPalette {
id: qgcPal
......@@ -88,7 +89,7 @@ Rectangle {
MenuItem {
text: "Show all values"
checkable: true
checked: missionItem.rawEdit
checked: missionItem.isSimpleItem ? missionItem.rawEdit : false
visible: missionItem.isSimpleItem
onTriggered: {
......@@ -142,191 +143,10 @@ Rectangle {
anchors.topMargin: _margin
anchors.left: parent.left
anchors.top: commandPicker.bottom
sourceComponent: missionItem.isSimpleItem ? simpleMissionItemEditor : complexMissionItemEditor
/// How wide the loaded component should be
property real availableWidth: _root.width - (_margin * 2)
}
Component {
id: valuesComponent
Rectangle {
id: valuesRect
height: valuesItem.height
color: qgcPal.windowShadeDark
visible: missionItem.isCurrentItem
radius: _radius
Item {
id: valuesItem
anchors.margins: _margin
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: valuesColumn.height + (_margin * 2)
Column {
id: valuesColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: _margin
QGCLabel {
width: parent.width
wrapMode: Text.WordWrap
text: missionItem.sequenceNumber == 0 ?
"Planned home position." :
(missionItem.rawEdit ?
"Provides advanced access to all commands/parameters. Be very careful!" :
missionItem.commandDescription)
}
Repeater {
model: missionItem.comboboxFacts
Item {
width: valuesColumn.width
height: comboBoxFact.height
QGCLabel {
id: comboBoxLabel
anchors.baseline: comboBoxFact.baseline
text: object.name
visible: object.name != ""
}
FactComboBox {
id: comboBoxFact
anchors.right: parent.right
width: comboBoxLabel.visible ? _editFieldWidth : parent.width
indexModel: false
model: object.enumStrings
fact: object
}
}
}
Repeater {
model: missionItem.textFieldFacts
Item {
width: valuesColumn.width
height: textField.height
QGCLabel {
id: textFieldLabel
anchors.baseline: textField.baseline
text: object.name
}
FactTextField {
id: textField
anchors.right: parent.right
width: _editFieldWidth
showUnits: true
fact: object
visible: !_root.readOnly
}
FactLabel {
anchors.baseline: textFieldLabel.baseline
anchors.right: parent.right
fact: object
visible: _root.readOnly
}
}
}
height: _currentItem && item ? item.height : 0
source: _currentItem ? (missionItem.isSimpleItem ? "qrc:/qml/SimpleItemEditor.qml" : "qrc:/qml/SurveyItemEditor.qml") : ""
Repeater {
model: missionItem.checkboxFacts
FactCheckBox {
text: object.name
fact: object
}
}
QGCButton {
text: "Move Home to map center"
visible: missionItem.homePosition
onClicked: moveHomeToMapCenter()
anchors.horizontalCenter: parent.horizontalCenter
}
} // Column
} // Item
} // Rectangle
}
Component {
id: simpleMissionItemEditor
Item {
id: innerItem
width: availableWidth
height: _showValues ? valuesLoader.y + valuesLoader.height : valuesLoader.y
property bool _showValues: missionItem.isCurrentItem
Loader {
id: valuesLoader
anchors.left: parent.left
anchors.right: parent.right
sourceComponent: _showValues ? valuesComponent : undefined
}
} // Item
} // Component - simpleMissionItemEditor
Component {
id: complexMissionItemEditor
Rectangle {
id: outerRect
height: editorColumn.height + (_margin * 2)
width: availableWidth
color: qgcPal.windowShadeDark
radius: _radius
property bool addPointsMode: false
Column {
id: editorColumn
anchors.margins: _margin
anchors.top: parent.top
anchors.left: parent.left
width: availableWidth
spacing: _margin
Connections {
target: editorMap
onMapClicked: {
if (addPointsMode) {
missionItem.addPolygonCoordinate(coordinate)
}
}
}
QGCLabel {
text: "Fly a grid pattern over a defined area."
wrapMode: Text.WordWrap
}
QGCButton {
text: addPointsMode ? "Finished" : "Draw Polygon"
onClicked: {
if (addPointsMode) {
addPointsMode = false
} else {
missionItem.clearPolygon()
addPointsMode = true
property real availableWidth: _root.width - (_margin * 2) ///< How wide the editor should be
property var editorRoot: _root
}
}
}
}
}
} // Component - complexMissionItemEditor
} // Rectangle
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