Commit 63080c1e authored by Don Gagne's avatar Don Gagne

Support load/save

Also understand the concept of missions which cannot be edited
parent 09f33f6b
......@@ -25,6 +25,7 @@ This file is part of the QGROUNDCONTROL project
#include "ScreenToolsController.h"
#include "MultiVehicleManager.h"
#include "MissionManager.h"
#include "QGCFileDialog.h"
#include <QQmlContext>
#include <QQmlEngine>
......@@ -35,6 +36,7 @@ const char* MissionEditor::_settingsGroup = "MissionEditor";
MissionEditor::MissionEditor(QWidget *parent)
: QGCQmlWidgetHolder(parent)
, _missionItems(NULL)
, _canEdit(true)
{
// Get rid of layout default margins
QLayout* pl = layout();
......@@ -66,10 +68,14 @@ void MissionEditor::_newMissionItemsAvailable(void)
_missionItems->deleteLater();
}
_missionItems = MultiVehicleManager::instance()->activeVehicle()->missionManager()->copyMissionItems();
MissionManager* missionManager = MultiVehicleManager::instance()->activeVehicle()->missionManager();
_canEdit = missionManager->canEdit();
_missionItems = missionManager->copyMissionItems();
_reSequence();
emit missionItemsChanged();
emit canEditChanged(_canEdit);
}
void MissionEditor::getMissionItems(void)
......@@ -77,6 +83,8 @@ void MissionEditor::getMissionItems(void)
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (activeVehicle) {
MissionManager* missionManager = activeVehicle->missionManager();
connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionEditor::_newMissionItemsAvailable);
activeVehicle->missionManager()->requestMissionItems();
}
}
......@@ -92,6 +100,10 @@ void MissionEditor::setMissionItems(void)
int MissionEditor::addMissionItem(QGeoCoordinate coordinate)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
}
MissionItem * newItem = new MissionItem(this, _missionItems->count(), coordinate);
if (_missionItems->count() == 0) {
newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF);
......@@ -111,12 +123,22 @@ void MissionEditor::_reSequence(void)
void MissionEditor::removeMissionItem(int index)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
_missionItems->removeAt(index);
_reSequence();
}
void MissionEditor::moveUp(int index)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
if (_missionItems->count() < 2 || index <= 0 || index >= _missionItems->count()) {
return;
}
......@@ -135,6 +157,11 @@ void MissionEditor::moveUp(int index)
void MissionEditor::moveDown(int index)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
if (_missionItems->count() < 2 || index >= _missionItems->count() - 1) {
return;
}
......@@ -150,3 +177,76 @@ void MissionEditor::moveDown(int index)
_reSequence();
}
void MissionEditor::loadMissionFromFile(void)
{
QString errorString;
QString filename = QGCFileDialog::getOpenFileName(NULL, "Select Mission File to load");
if (filename.isEmpty()) {
return;
}
_missionItems->clear();
_canEdit = true;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
errorString = file.errorString();
} else {
QTextStream in(&file);
const QStringList& version = in.readLine().split(" ");
if (!(version.size() == 3 && version[0] == "QGC" && version[1] == "WPL" && version[2] == "120")) {
errorString = "The mission file is not compatible with the current version of QGroundControl.";
} else {
while (!in.atEnd()) {
MissionItem* item = new MissionItem();
if (item->load(in)) {
_missionItems->append(item);
if (!item->canEdit()) {
_canEdit = false;
}
} else {
errorString = "The mission file is corrupted.";
break;
}
}
}
}
if (!errorString.isEmpty()) {
_missionItems->clear();
}
emit canEditChanged(_canEdit);
}
void MissionEditor::saveMissionToFile(void)
{
QString errorString;
QString filename = QGCFileDialog::getSaveFileName(NULL, "Select file to save mission to");
if (filename.isEmpty()) {
return;
}
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
errorString = file.errorString();
} else {
QTextStream out(&file);
out << "QGC WPL 120\r\n"; // Version string
for (int i=0; i<_missionItems->count(); i++) {
qobject_cast<MissionItem*>(_missionItems->get(i))->save(out);
}
}
}
......@@ -35,11 +35,14 @@ public:
MissionEditor(QWidget* parent = NULL);
~MissionEditor();
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItemsModel NOTIFY missionItemsChanged)
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItemsModel NOTIFY missionItemsChanged)
Q_PROPERTY(bool canEdit READ canEdit NOTIFY canEditChanged)
Q_INVOKABLE int addMissionItem(QGeoCoordinate coordinate);
Q_INVOKABLE void getMissionItems(void);
Q_INVOKABLE void setMissionItems(void);
Q_INVOKABLE void loadMissionFromFile(void);
Q_INVOKABLE void saveMissionToFile(void);
Q_INVOKABLE void removeMissionItem(int index);
Q_INVOKABLE void moveUp(int index);
Q_INVOKABLE void moveDown(int index);
......@@ -47,9 +50,11 @@ public:
// Property accessors
QmlObjectListModel* missionItemsModel(void) { return _missionItems; }
bool canEdit(void) { return _canEdit; }
signals:
void missionItemsChanged(void);
void canEditChanged(bool canEdit);
private slots:
void _newMissionItemsAvailable();
......@@ -59,6 +64,7 @@ private:
private:
QmlObjectListModel* _missionItems;
bool _canEdit; ///< true: UI can edit these items, false: can't edit, can only send to vehicle or save
static const char* _settingsGroup;
};
......
......@@ -40,8 +40,8 @@ QGCView {
readonly property real _defaultLatitude: 37.803784
readonly property real _defaultLongitude: -122.462276
readonly property int _decimalPlaces: 7
readonly property real _horizontalMargin: ScreenTools.defaultFontPixelWidth / 2
readonly property real _verticalMargin: ScreenTools.defaultFontPixelHeight / 2
readonly property real _horizontalMargin: ScreenTools.defaultFontPixelWidth / 2
readonly property real _verticalMargin: ScreenTools.defaultFontPixelHeight / 2
readonly property var _activeVehicle: multiVehicleManager.activeVehicle
property var _missionItems: controller.missionItems
......@@ -153,6 +153,20 @@ QGCView {
onTriggered: controller.setMissionItems()
}
MenuSeparator { }
MenuItem {
text: "Load mission from file..."
onTriggered: controller.loadMissionFromFile()
}
MenuItem {
text: "Save mission to file..."
onTriggered: controller.saveMissionToFile()
}
}
}
......@@ -166,7 +180,7 @@ QGCView {
anchors.bottom: parent.bottom
spacing: _verticalMargin
orientation: ListView.Vertical
model: controller.missionItems
model: controller.canEdit ? controller.missionItems : 0
property real _maxItemHeight: 0
......@@ -201,6 +215,18 @@ QGCView {
wrapMode: Text.WordWrap
text: "Click in the map to add Mission Items"
}
QGCLabel {
anchors.topMargin: _verticalMargin
anchors.left: parent.left
anchors.right: parent.right
anchors.top: toolsButton.bottom
anchors.bottom: parent.bottom
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
} // Rectangle - mission item list
} // Item - split view container
......
......@@ -34,6 +34,7 @@ This file is part of the QGROUNDCONTROL project
#include "MissionItem.h"
QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog")
QDebug operator<<(QDebug dbg, const MissionItem& missionItem)
{
......@@ -313,12 +314,20 @@ void MissionItem::setAction(int /*MAV_CMD*/ action)
if (_command != action) {
_command = (MavlinkQmlSingleton::Qml_MAV_CMD)action;
// Flick defaults according to WP type
// Fix defaults according to WP type
if (_command == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF) {
// We default to 15 degrees minimum takeoff pitch
setParam1(15.0);
}
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 changed(this);
emit commandNameChanged(commandName());
......@@ -758,3 +767,32 @@ void MissionItem::setCoordinate(const QGeoCoordinate& coordinate)
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;
}
}
......@@ -35,6 +35,9 @@
#include "MavlinkQmlSingleton.h"
#include "QmlObjectListModel.h"
#include "Fact.h"
#include "QGCLoggingCategory.h"
Q_DECLARE_LOGGING_CATEGORY(MissionItemLog)
class MissionItem : public QObject
{
......@@ -104,6 +107,9 @@ public:
void setYawDegrees(double yaw);
// 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(); }
......
......@@ -34,6 +34,7 @@ MissionManager::MissionManager(Vehicle* vehicle)
: QThread()
, _vehicle(vehicle)
, _cMissionItems(0)
, _canEdit(true)
, _ackTimeoutTimer(NULL)
, _retryAck(AckNone)
{
......@@ -231,6 +232,11 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message)
missionItem.command);
_missionItems.append(item);
if (!item->canEdit()) {
_canEdit = false;
emit canEditChanged(false);
}
int nextSequenceNumber = missionItem.seq + 1;
if (nextSequenceNumber == _cMissionItems) {
_sendTransactionComplete();
......
......@@ -47,13 +47,17 @@ public:
MissionManager(Vehicle* vehicle);
~MissionManager();
Q_PROPERTY(bool inProgress READ inProgress CONSTANT)
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems CONSTANT)
Q_PROPERTY(bool inProgress READ inProgress CONSTANT)
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems CONSTANT)
Q_PROPERTY(bool canEdit READ canEdit NOTIFY canEditChanged)
// Property accessors
bool inProgress(void) { return _retryAck != AckNone; }
QmlObjectListModel* missionItems(void) { return &_missionItems; }
bool canEdit(void) { return _canEdit; }
// C++ methods
void requestMissionItems(void);
......@@ -63,9 +67,13 @@ public:
/// Returns a copy of the current set of mission items. Caller is responsible for
/// freeing returned object.
QmlObjectListModel* copyMissionItems(void);
signals:
// Public signals
void canEditChanged(bool canEdit);
void newMissionItemsAvailable(void);
// Internal signals
void _requestMissionItemsOnThread(void);
void _writeMissionItemsOnThread(void);
......@@ -100,6 +108,7 @@ private:
Vehicle* _vehicle;
int _cMissionItems; ///< Mission items on vehicle
bool _canEdit; ///< true: Mission items are editable in the ui
QTimer* _ackTimeoutTimer;
AckType_t _retryAck;
......
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