Commit f0bd33ef authored by Don Gagne's avatar Don Gagne

Add support for APM Mission quirks

Also merged MissionController and MissionEditorController into single
MissionController implementation
parent 9866b603
...@@ -228,7 +228,6 @@ HEADERS += \ ...@@ -228,7 +228,6 @@ HEADERS += \
src/Joystick/JoystickManager.h \ src/Joystick/JoystickManager.h \
src/LogCompressor.h \ src/LogCompressor.h \
src/MG.h \ src/MG.h \
src/MissionEditor/MissionEditorController.h \
src/MissionManager/MissionManager.h \ src/MissionManager/MissionManager.h \
src/MissionManager/MissionController.h \ src/MissionManager/MissionController.h \
src/QGC.h \ src/QGC.h \
...@@ -347,7 +346,6 @@ SOURCES += \ ...@@ -347,7 +346,6 @@ SOURCES += \
src/Joystick/JoystickManager.cc \ src/Joystick/JoystickManager.cc \
src/LogCompressor.cc \ src/LogCompressor.cc \
src/main.cc \ src/main.cc \
src/MissionEditor/MissionEditorController.cc \
src/MissionManager/MissionManager.cc \ src/MissionManager/MissionManager.cc \
src/MissionManager/MissionController.cc \ src/MissionManager/MissionController.cc \
src/QGC.cc \ src/QGC.cc \
......
...@@ -45,7 +45,7 @@ void FactSystemTestBase::_init(MAV_AUTOPILOT autopilot) ...@@ -45,7 +45,7 @@ void FactSystemTestBase::_init(MAV_AUTOPILOT autopilot)
UnitTest::init(); UnitTest::init();
MockLink* link = new MockLink(); MockLink* link = new MockLink();
link->setAutopilotType(autopilot); link->setFirmwareType(autopilot);
LinkManager::instance()->_addLink(link); LinkManager::instance()->_addLink(link);
LinkManager::instance()->connectLink(link); LinkManager::instance()->connectLink(link);
......
...@@ -73,21 +73,26 @@ Item { ...@@ -73,21 +73,26 @@ Item {
readonly property var _flightMap: flightMap readonly property var _flightMap: flightMap
property real _roll: _activeVehicle ? (isNaN(_activeVehicle.roll) ? _defaultRoll : _activeVehicle.roll) : _defaultRoll property real _roll: _activeVehicle ? (isNaN(_activeVehicle.roll) ? _defaultRoll : _activeVehicle.roll) : _defaultRoll
property real _pitch: _activeVehicle ? (isNaN(_activeVehicle.pitch) ? _defaultPitch : _activeVehicle.pitch) : _defaultPitch property real _pitch: _activeVehicle ? (isNaN(_activeVehicle.pitch) ? _defaultPitch : _activeVehicle.pitch) : _defaultPitch
property real _heading: _activeVehicle ? (isNaN(_activeVehicle.heading) ? _defaultHeading : _activeVehicle.heading) : _defaultHeading property real _heading: _activeVehicle ? (isNaN(_activeVehicle.heading) ? _defaultHeading : _activeVehicle.heading) : _defaultHeading
property var _vehicleCoordinate: _activeVehicle ? _activeVehicle.coordinate : _defaultVehicleCoordinate property var _vehicleCoordinate: _activeVehicle ? (_activeVehicle.satelliteLock >= 2 ? _activeVehicle.coordinate : _defaultVehicleCoordinate) : _defaultVehicleCoordinate
property real _altitudeWGS84: _activeVehicle ? _activeVehicle.altitudeWGS84 : _defaultAltitudeWGS84 property real _altitudeWGS84: _activeVehicle ? _activeVehicle.altitudeWGS84 : _defaultAltitudeWGS84
property real _groundSpeed: _activeVehicle ? _activeVehicle.groundSpeed : _defaultGroundSpeed property real _groundSpeed: _activeVehicle ? _activeVehicle.groundSpeed : _defaultGroundSpeed
property real _airSpeed: _activeVehicle ? _activeVehicle.airSpeed : _defaultAirSpeed property real _airSpeed: _activeVehicle ? _activeVehicle.airSpeed : _defaultAirSpeed
property real _climbRate: _activeVehicle ? _activeVehicle.climbRate : _defaultClimbRate property real _climbRate: _activeVehicle ? _activeVehicle.climbRate : _defaultClimbRate
property bool _showMap: getBool(QGroundControl.flightMapSettings.loadMapSetting(flightMap.mapName, _showMapBackgroundKey, "1")) property bool _showMap: getBool(QGroundControl.flightMapSettings.loadMapSetting(flightMap.mapName, _showMapBackgroundKey, "1"))
FlightDisplayViewController { id: _controller } FlightDisplayViewController { id: _controller }
MissionController { id: _missionController }
MissionController {
id: _missionController
Component.onCompleted: start(false /* editMode */)
}
ExclusiveGroup { ExclusiveGroup {
id: _dropButtonsExclusiveGroup id: _dropButtonsExclusiveGroup
......
...@@ -54,24 +54,27 @@ QGCView { ...@@ -54,24 +54,27 @@ QGCView {
readonly property string _autoSyncKey: "AutoSync" readonly property string _autoSyncKey: "AutoSync"
readonly property string _showHelpKey: "ShowHelp" readonly property string _showHelpKey: "ShowHelp"
readonly property int _addMissionItemsButtonAutoOffTimeout: 10000 readonly property int _addMissionItemsButtonAutoOffTimeout: 10000
readonly property var _defaultVehicleCoordinate: QtPositioning.coordinate(37.803784, -122.462276)
property var _missionItems: controller.missionItems property var _missionItems: controller.missionItems
property var _homePositionManager: QGroundControl.homePositionManager //property var _homePositionManager: QGroundControl.homePositionManager
property string _homePositionName: _homePositionManager.homePositions.get(0).name //property string _homePositionName: _homePositionManager.homePositions.get(0).name
//property var offlineHomePosition: _homePositionManager.homePositions.get(0).coordinate //property var offlineHomePosition: _homePositionManager.homePositions.get(0).coordinate
property var liveHomePosition: controller.liveHomePosition property var liveHomePosition: controller.liveHomePosition
property var liveHomePositionAvailable: controller.liveHomePositionAvailable property var liveHomePositionAvailable: controller.liveHomePositionAvailable
//property var homePosition: offlineHomePosition // live or offline depending on state property var homePosition: _defaultVehicleCoordinate
property bool _syncNeeded: controller.missionItems.dirty property bool _syncNeeded: controller.missionItems.dirty
property bool _syncInProgress: _activeVehicle ? _activeVehicle.missionManager.inProgress : false property bool _syncInProgress: _activeVehicle ? _activeVehicle.missionManager.inProgress : false
property bool _showHelp: QGroundControl.flightMapSettings.loadBoolMapSetting(editorMap.mapName, _showHelpKey, true) property bool _showHelp: QGroundControl.flightMapSettings.loadBoolMapSetting(editorMap.mapName, _showHelpKey, true)
MissionEditorController { MissionController {
id: controller id: controller
Component.onCompleted: start(true /* editMode */)
/* /*
FIXME: autoSync is temporarily disconnected since it's still buggy FIXME: autoSync is temporarily disconnected since it's still buggy
...@@ -104,6 +107,7 @@ QGCView { ...@@ -104,6 +107,7 @@ QGCView {
function updateHomePosition() { function updateHomePosition() {
if (liveHomePositionAvailable) { if (liveHomePositionAvailable) {
homePosition = liveHomePosition
_missionItems.get(0).coordinate = liveHomePosition _missionItems.get(0).coordinate = liveHomePosition
_missionItems.get(0).homePositionValid = true _missionItems.get(0).homePositionValid = true
} }
...@@ -282,8 +286,8 @@ QGCView { ...@@ -282,8 +286,8 @@ QGCView {
} }
} // Item - Mission Item editor } // Item - Mission Item editor
/* /*
Home Position Manager is commented out for now until a better implementation is completed Home Position Manager temporarily disbled till more work is done on it
// Home Position Manager // Home Position Manager
Rectangle { Rectangle {
...@@ -556,7 +560,7 @@ Home Position Manager is commented out for now until a better implementation is ...@@ -556,7 +560,7 @@ Home Position Manager is commented out for now until a better implementation is
} }
} // Column - Online view } // Column - Online view
} // Item - Home Position Manager } // Item - Home Position Manager
*/ */
// Help Panel // Help Panel
Rectangle { Rectangle {
...@@ -656,7 +660,7 @@ Home Position Manager is commented out for now until a better implementation is ...@@ -656,7 +660,7 @@ Home Position Manager is commented out for now until a better implementation is
} }
/* /*
Home Position Manager commented until more complete implementation is done Home Position Manager disabled
Image { Image {
id: homePositionManagerHelpIcon id: homePositionManagerHelpIcon
...@@ -680,7 +684,7 @@ Home Position Manager is commented out for now until a better implementation is ...@@ -680,7 +684,7 @@ Home Position Manager is commented out for now until a better implementation is
"When enabled, allows you to select/add/update flying field locations. " + "When enabled, allows you to select/add/update flying field locations. " +
"You can save multiple flying field locations for use while creating missions while you are not connected to your vehicle." "You can save multiple flying field locations for use while creating missions while you are not connected to your vehicle."
} }
*/ */
Image { Image {
id: mapCenterHelpIcon id: mapCenterHelpIcon
...@@ -803,8 +807,7 @@ Home Position Manager is commented out for now until a better implementation is ...@@ -803,8 +807,7 @@ Home Position Manager is commented out for now until a better implementation is
} }
/* /*
Home Position Manager commented until more complete implementation is done Home Position manager temporarily disable
RoundButton { RoundButton {
id: homePositionManagerButton id: homePositionManagerButton
anchors.margins: _margin anchors.margins: _margin
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 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 "MissionEditorController.h"
#include "ScreenToolsController.h"
#include "MultiVehicleManager.h"
#include "MissionManager.h"
#include "QGCFileDialog.h"
#include "CoordinateVector.h"
#include "QGCMessageBox.h"
#include <QQmlContext>
#include <QQmlEngine>
#include <QSettings>
const char* MissionEditorController::_settingsGroup = "MissionEditorController";
MissionEditorController::MissionEditorController(QObject *parent)
: QObject(parent)
, _missionItems(NULL)
, _canEdit(true)
, _activeVehicle(NULL)
, _liveHomePositionAvailable(false)
, _autoSync(false)
, _firstMissionItemSync(false)
, _missionItemsRequested(false)
{
MultiVehicleManager* multiVehicleMgr = MultiVehicleManager::instance();
connect(multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &MissionEditorController::_activeVehicleChanged);
Vehicle* activeVehicle = multiVehicleMgr->activeVehicle();
if (activeVehicle) {
_activeVehicleChanged(activeVehicle);
} else {
_missionItemsRequested = true;
_newMissionItemsAvailable();
}
}
MissionEditorController::~MissionEditorController()
{
}
void MissionEditorController::_newMissionItemsAvailable(void)
{
if (_firstMissionItemSync) {
// This is the first time the vehicle is seeing items. We have to be careful of transitioning from offline
// to online.
_firstMissionItemSync = false;
if (_missionItems && _missionItems->count() > 1) {
QGCMessageBox::StandardButton button = QGCMessageBox::warning("Mission Editing",
"The vehicle has sent a new set of Mission Items. "
"Do you want to discard your current set of unsaved items and use the ones from the vehicle instead?",
QGCMessageBox::Yes | QGCMessageBox::No,
QGCMessageBox::No);
if (button == QGCMessageBox::No) {
return;
}
}
} else if (!_missionItemsRequested) {
// We didn't specifically ask for new mission items. Disregard the new set since it is
// the most likely the set we just sent to the vehicle.
return;
}
_missionItemsRequested = false;
if (_missionItems) {
_deinitAllMissionItems();
_missionItems->deleteLater();
}
MissionManager* missionManager = NULL;
if (_activeVehicle) {
missionManager = _activeVehicle->missionManager();
}
if (!missionManager || missionManager->inProgress()) {
_canEdit = true;
_missionItems = new QmlObjectListModel(this);
} else {
_canEdit = missionManager->canEdit();
_missionItems = missionManager->copyMissionItems();
}
_initAllMissionItems();
}
void MissionEditorController::getMissionItems(void)
{
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (activeVehicle) {
_missionItemsRequested = true;
MissionManager* missionManager = activeVehicle->missionManager();
connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionEditorController::_newMissionItemsAvailable);
activeVehicle->missionManager()->requestMissionItems();
}
}
void MissionEditorController::sendMissionItems(void)
{
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (activeVehicle) {
activeVehicle->missionManager()->writeMissionItems(*_missionItems, true /* skipFirstItem */);
_missionItems->setDirty(false);
}
}
int MissionEditorController::addMissionItem(QGeoCoordinate coordinate)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
}
// Coordinate will come through without altitude
coordinate.setAltitude(MissionItem::defaultAltitude);
MissionItem * newItem = new MissionItem(this, _missionItems->count(), coordinate, MAV_CMD_NAV_WAYPOINT);
_initMissionItem(newItem);
if (_missionItems->count() == 1) {
newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF);
}
_missionItems->append(newItem);
_recalcAll();
return _missionItems->count() - 1;
}
void MissionEditorController::removeMissionItem(int index)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->removeAt(index));
_deinitMissionItem(item);
_recalcAll();
// Set the new current item
if (index >= _missionItems->count()) {
index--;
}
for (int i=0; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
item->setIsCurrentItem(i == index);
}
}
void MissionEditorController::loadMissionFromFile(void)
{
QString errorString;
QString filename = QGCFileDialog::getOpenFileName(NULL, "Select Mission File to load");
if (filename.isEmpty()) {
return;
}
if (_missionItems) {
_deinitAllMissionItems();
_missionItems->deleteLater();
}
_missionItems = new QmlObjectListModel(this);
_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();
}
_initAllMissionItems();
}
void MissionEditorController::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);
}
}
_missionItems->setDirty(false);
}
void MissionEditorController::_recalcWaypointLines(void)
{
bool firstCoordinateItem = true;
MissionItem* lastCoordinateItem = qobject_cast<MissionItem*>(_missionItems->get(0));
_waypointLines.clear();
for (int i=1; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
if (item->specifiesCoordinate()) {
if (firstCoordinateItem) {
if (item->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF) {
// The first coordinate we hit is a takeoff command so link back to home position
_waypointLines.append(new CoordinateVector(qobject_cast<MissionItem*>(_missionItems->get(0))->coordinate(), item->coordinate()));
} else {
// First coordiante is not a takeoff command, it does not link backwards to anything
}
firstCoordinateItem = false;
} else {
// Subsequent coordinate items link to last coordinate item
_waypointLines.append(new CoordinateVector(lastCoordinateItem->coordinate(), item->coordinate()));
}
lastCoordinateItem = item;
}
}
emit waypointLinesChanged();
}
// This will update the sequence numbers to be sequential starting from 0
void MissionEditorController::_recalcSequence(void)
{
for (int i=0; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
// Setup ascending sequence numbers
item->setSequenceNumber(i);
}
}
// This will update the child item hierarchy
void MissionEditorController::_recalcChildItems(void)
{
MissionItem* currentParentItem = qobject_cast<MissionItem*>(_missionItems->get(0));
currentParentItem->childItems()->clear();
for (int i=1; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
// Set up non-coordinate item child hierarchy
if (item->specifiesCoordinate()) {
item->childItems()->clear();
currentParentItem = item;
} else {
currentParentItem->childItems()->append(item);
}
}
}
void MissionEditorController::_recalcAll(void)
{
_recalcSequence();
_recalcChildItems();
_recalcWaypointLines();
}
/// Initializes a new set of mission items which may have come from the vehicle or have been loaded from a file
void MissionEditorController::_initAllMissionItems(void)
{
// Add the home position item to the front
MissionItem* homeItem = new MissionItem(this);
homeItem->setHomePositionSpecialCase(true);
homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT);
_missionItems->insert(0, homeItem);
for (int i=0; i<_missionItems->count(); i++) {
_initMissionItem(qobject_cast<MissionItem*>(_missionItems->get(i)));
}
_recalcAll();
emit missionItemsChanged();
emit canEditChanged(_canEdit);
_missionItems->setDirty(false);
connect(_missionItems, &QmlObjectListModel::dirtyChanged, this, &MissionEditorController::_dirtyChanged);
}
void MissionEditorController::_deinitAllMissionItems(void)
{
for (int i=0; i<_missionItems->count(); i++) {
_deinitMissionItem(qobject_cast<MissionItem*>(_missionItems->get(i)));
}
connect(_missionItems, &QmlObjectListModel::dirtyChanged, this, &MissionEditorController::_dirtyChanged);
}
void MissionEditorController::_initMissionItem(MissionItem* item)
{
_missionItems->setDirty(false);
connect(item, &MissionItem::commandChanged, this, &MissionEditorController::_itemCommandChanged);
connect(item, &MissionItem::coordinateChanged, this, &MissionEditorController::_itemCoordinateChanged);
}
void MissionEditorController::_deinitMissionItem(MissionItem* item)
{
disconnect(item, &MissionItem::commandChanged, this, &MissionEditorController::_itemCommandChanged);
disconnect(item, &MissionItem::coordinateChanged, this, &MissionEditorController::_itemCoordinateChanged);
}
void MissionEditorController::_itemCoordinateChanged(const QGeoCoordinate& coordinate)
{
Q_UNUSED(coordinate);
_recalcWaypointLines();
}
void MissionEditorController::_itemCommandChanged(MavlinkQmlSingleton::Qml_MAV_CMD command)
{
Q_UNUSED(command);;
_recalcChildItems();
_recalcWaypointLines();
}
void MissionEditorController::_activeVehicleChanged(Vehicle* activeVehicle)
{
if (_activeVehicle) {
MissionManager* missionManager = _activeVehicle->missionManager();
disconnect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionEditorController::_newMissionItemsAvailable);
disconnect(missionManager, &MissionManager::inProgressChanged, this, &MissionEditorController::_inProgressChanged);
disconnect(_activeVehicle, &Vehicle::homePositionAvailableChanged, this, &MissionEditorController::_activeVehicleHomePositionAvailableChanged);
disconnect(_activeVehicle, &Vehicle::homePositionChanged, this, &MissionEditorController::_activeVehicleHomePositionChanged);
_activeVehicle = NULL;
_newMissionItemsAvailable();
_activeVehicleHomePositionAvailableChanged(false);
}
_activeVehicle = activeVehicle;
if (_activeVehicle) {
MissionManager* missionManager = activeVehicle->missionManager();
connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionEditorController::_newMissionItemsAvailable);
connect(missionManager, &MissionManager::inProgressChanged, this, &MissionEditorController::_inProgressChanged);
connect(_activeVehicle, &Vehicle::homePositionAvailableChanged, this, &MissionEditorController::_activeVehicleHomePositionAvailableChanged);
connect(_activeVehicle, &Vehicle::homePositionChanged, this, &MissionEditorController::_activeVehicleHomePositionChanged);
if (missionManager->inProgress()) {
// Vehicle is still in process of requesting mission items
_firstMissionItemSync = true;
} else {
// Vehicle already has mission items
_firstMissionItemSync = false;
}
_missionItemsRequested = true;
_newMissionItemsAvailable();
_activeVehicleHomePositionChanged(_activeVehicle->homePosition());
_activeVehicleHomePositionAvailableChanged(_activeVehicle->homePositionAvailable());
}
}
void MissionEditorController::_activeVehicleHomePositionAvailableChanged(bool homePositionAvailable)
{
_liveHomePositionAvailable = homePositionAvailable;
emit liveHomePositionAvailableChanged(_liveHomePositionAvailable);
}
void MissionEditorController::_activeVehicleHomePositionChanged(const QGeoCoordinate& homePosition)
{
_liveHomePosition = homePosition;
emit liveHomePositionChanged(_liveHomePosition);
}
void MissionEditorController::deleteCurrentMissionItem(void)
{
for (int i=0; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
if (item->isCurrentItem() && i != 0) {
removeMissionItem(i);
return;
}
}
}
void MissionEditorController::setAutoSync(bool autoSync)
{
_autoSync = autoSync;
emit autoSyncChanged(_autoSync);
if (_autoSync) {
_dirtyChanged(true);
}
}
void MissionEditorController::_dirtyChanged(bool dirty)
{
if (dirty && _autoSync) {
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (activeVehicle && !activeVehicle->armed()) {
if (_activeVehicle->missionManager()->inProgress()) {
_queuedSend = true;
} else {
_autoSyncSend();
}
}
}
}
void MissionEditorController::_autoSyncSend(void)
{
qDebug() << "Auto-syncing with vehicle";
_queuedSend = false;
if (_missionItems) {
sendMissionItems();
_missionItems->setDirty(false);
}
}
void MissionEditorController::_inProgressChanged(bool inProgress)
{
if (!inProgress && _queuedSend) {
_autoSyncSend();
}
}
QmlObjectListModel* MissionEditorController::missionItems(void)
{
return _missionItems;
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2015 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 MissionEditorController_H
#define MissionEditorController_H
#include <QObject>
#include "QmlObjectListModel.h"
#include "Vehicle.h"
class MissionEditorController : public QObject
{
Q_OBJECT
public:
MissionEditorController(QObject* parent = NULL);
~MissionEditorController();
Q_PROPERTY(QmlObjectListModel* missionItems READ missionItems NOTIFY missionItemsChanged)
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(QGeoCoordinate liveHomePosition READ liveHomePosition NOTIFY liveHomePositionChanged)
Q_PROPERTY(bool autoSync READ autoSync WRITE setAutoSync NOTIFY autoSyncChanged)
Q_INVOKABLE int addMissionItem(QGeoCoordinate coordinate);
Q_INVOKABLE void getMissionItems(void);
Q_INVOKABLE void sendMissionItems(void);
Q_INVOKABLE void loadMissionFromFile(void);
Q_INVOKABLE void saveMissionToFile(void);
Q_INVOKABLE void removeMissionItem(int index);
Q_INVOKABLE void deleteCurrentMissionItem(void);
// Property accessors
QmlObjectListModel* missionItems(void);
QmlObjectListModel* waypointLines(void) { return &_waypointLines; }
bool canEdit(void) { return _canEdit; }
bool liveHomePositionAvailable(void) { return _liveHomePositionAvailable; }
QGeoCoordinate liveHomePosition(void) { return _liveHomePosition; }
bool autoSync(void) { return _autoSync; }
void setAutoSync(bool autoSync);
signals:
void missionItemsChanged(void);
void canEditChanged(bool canEdit);
void waypointLinesChanged(void);
void liveHomePositionAvailableChanged(bool homePositionAvailable);
void liveHomePositionChanged(const QGeoCoordinate& homePosition);
void autoSyncChanged(bool autoSync);
private slots:
void _newMissionItemsAvailable();
void _itemCoordinateChanged(const QGeoCoordinate& coordinate);
void _itemCommandChanged(MavlinkQmlSingleton::Qml_MAV_CMD command);
void _activeVehicleChanged(Vehicle* activeVehicle);
void _activeVehicleHomePositionAvailableChanged(bool homePositionAvailable);
void _activeVehicleHomePositionChanged(const QGeoCoordinate& homePosition);
void _dirtyChanged(bool dirty);
void _inProgressChanged(bool inProgress);
private:
void _recalcSequence(void);
void _recalcWaypointLines(void);
void _recalcChildItems(void);
void _recalcAll(void);
void _initAllMissionItems(void);
void _deinitAllMissionItems(void);
void _initMissionItem(MissionItem* item);
void _deinitMissionItem(MissionItem* item);
void _autoSyncSend(void);
private:
QmlObjectListModel* _missionItems;
QmlObjectListModel _waypointLines;
bool _canEdit; ///< true: UI can edit these items, false: can't edit, can only send to vehicle or save
Vehicle* _activeVehicle;
bool _liveHomePositionAvailable;
QGeoCoordinate _liveHomePosition;
bool _autoSync;
bool _firstMissionItemSync;
bool _missionItemsRequested;
bool _queuedSend;
static const char* _settingsGroup;
};
#endif
...@@ -86,7 +86,7 @@ public: ...@@ -86,7 +86,7 @@ public:
Q_PROPERTY(QmlObjectListModel* childItems READ childItems CONSTANT) Q_PROPERTY(QmlObjectListModel* childItems READ childItems CONSTANT)
/// true: this item is being used as a home position indicator /// true: this item is being used as a home position indicator
Q_PROPERTY(bool homePosition MEMBER _homePositionSpecialCase CONSTANT) Q_PROPERTY(bool homePosition READ homePosition CONSTANT)
/// true: home position should be shown /// true: home position should be shown
Q_PROPERTY(bool homePositionValid READ homePositionValid WRITE setHomePositionValid NOTIFY homePositionValidChanged) Q_PROPERTY(bool homePositionValid READ homePositionValid WRITE setHomePositionValid NOTIFY homePositionValidChanged)
...@@ -131,6 +131,7 @@ public: ...@@ -131,6 +131,7 @@ public:
QmlObjectListModel* childItems(void) { return &_childItems; } QmlObjectListModel* childItems(void) { return &_childItems; }
bool homePosition(void) { return _homePositionSpecialCase; }
bool homePositionValid(void) { return _homePositionValid; } bool homePositionValid(void) { return _homePositionValid; }
void setHomePositionValid(bool homePositionValid); void setHomePositionValid(bool homePositionValid);
......
...@@ -22,109 +22,281 @@ This file is part of the QGROUNDCONTROL project ...@@ -22,109 +22,281 @@ This file is part of the QGROUNDCONTROL project
======================================================================*/ ======================================================================*/
#include "MissionController.h" #include "MissionController.h"
#include "ScreenToolsController.h"
#include "MultiVehicleManager.h" #include "MultiVehicleManager.h"
#include "MissionManager.h" #include "MissionManager.h"
#include "QGCFileDialog.h"
#include "CoordinateVector.h" #include "CoordinateVector.h"
#include "QGCMessageBox.h"
#include "FirmwarePlugin.h"
const char* MissionController::_settingsGroup = "MissionController";
MissionController::MissionController(QObject *parent) MissionController::MissionController(QObject *parent)
: QObject(parent) : QObject(parent)
, _editMode(false)
, _missionItems(NULL) , _missionItems(NULL)
, _canEdit(true)
, _activeVehicle(NULL) , _activeVehicle(NULL)
, _liveHomePositionAvailable(false)
, _autoSync(false)
, _firstMissionItemSync(false)
, _missionItemsRequested(false)
, _queuedSend(false)
{ {
}
MissionController::~MissionController()
{
}
void MissionController::start(bool editMode)
{
_editMode = editMode;
MultiVehicleManager* multiVehicleMgr = MultiVehicleManager::instance(); MultiVehicleManager* multiVehicleMgr = MultiVehicleManager::instance();
connect(multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &MissionController::_activeVehicleChanged); connect(multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &MissionController::_activeVehicleChanged);
Vehicle* activeVehicle = multiVehicleMgr->activeVehicle(); Vehicle* activeVehicle = multiVehicleMgr->activeVehicle();
if (activeVehicle) { if (activeVehicle) {
_activeVehicleChanged(activeVehicle); _activeVehicleChanged(activeVehicle);
} else { } else {
_missionItemsRequested = true;
_newMissionItemsAvailable(); _newMissionItemsAvailable();
} }
} }
MissionController::~MissionController()
{
}
void MissionController::_newMissionItemsAvailable(void) void MissionController::_newMissionItemsAvailable(void)
{ {
if (_editMode) {
if (_firstMissionItemSync) {
// This is the first time the vehicle is seeing items. We have to be careful of transitioning from offline
// to online.
_firstMissionItemSync = false;
if (_missionItems && _missionItems->count() > 1) {
QGCMessageBox::StandardButton button = QGCMessageBox::warning("Mission Editing",
"The vehicle has sent a new set of Mission Items. "
"Do you want to discard your current set of unsaved items and use the ones from the vehicle instead?",
QGCMessageBox::Yes | QGCMessageBox::No,
QGCMessageBox::No);
if (button == QGCMessageBox::No) {
return;
}
}
} else if (!_missionItemsRequested) {
// We didn't specifically ask for new mission items. Disregard the new set since it is
// the most likely the set we just sent to the vehicle.
return;
}
}
_missionItemsRequested = false;
if (_missionItems) { if (_missionItems) {
_deinitAllMissionItems();
_missionItems->deleteLater(); _missionItems->deleteLater();
} }
MissionManager* missionManager = NULL; MissionManager* missionManager = NULL;
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle(); if (_activeVehicle) {
if (activeVehicle) { missionManager = _activeVehicle->missionManager();
missionManager = activeVehicle->missionManager();
} }
if (!missionManager || missionManager->inProgress()) { if (!missionManager || missionManager->inProgress()) {
_canEdit = true;
_missionItems = new QmlObjectListModel(this); _missionItems = new QmlObjectListModel(this);
} else { } else {
_canEdit = missionManager->canEdit();
_missionItems = missionManager->copyMissionItems(); _missionItems = missionManager->copyMissionItems();
} }
_initAllMissionItems(); _initAllMissionItems();
} }
void MissionController::_recalcWaypointLines(void) void MissionController::getMissionItems(void)
{ {
int firstIndex = _homePositionValid ? 0 : 1; Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
_waypointLines.clear(); if (activeVehicle) {
_missionItemsRequested = true;
MissionManager* missionManager = activeVehicle->missionManager();
connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionController::_newMissionItemsAvailable);
activeVehicle->missionManager()->requestMissionItems();
}
}
void MissionController::sendMissionItems(void)
{
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (firstIndex < _missionItems->count()) { if (activeVehicle) {
bool firstCoordinateItem = true; activeVehicle->missionManager()->writeMissionItems(*_missionItems);
MissionItem* lastCoordinateItem = qobject_cast<MissionItem*>(_missionItems->get(firstIndex)); _missionItems->setDirty(false);
}
}
for (int i=firstIndex; i<_missionItems->count(); i++) { int MissionController::addMissionItem(QGeoCoordinate coordinate)
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i)); {
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
}
if (item->specifiesCoordinate()) { // Coordinate will come through without altitude
if (firstCoordinateItem) { coordinate.setAltitude(MissionItem::defaultAltitude);
if (item->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF && _homePositionValid) {
// The first coordinate we hit is a takeoff command so link back to home position if we have one MissionItem * newItem = new MissionItem(this, _missionItems->count(), coordinate, MAV_CMD_NAV_WAYPOINT);
_waypointLines.append(new CoordinateVector(qobject_cast<MissionItem*>(_missionItems->get(0))->coordinate(), item->coordinate())); _initMissionItem(newItem);
} else { if (_missionItems->count() == 1) {
// First coordiante is not a takeoff command, it does not link backwards to anything newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF);
}
_missionItems->append(newItem);
_recalcAll();
return _missionItems->count() - 1;
}
void MissionController::removeMissionItem(int index)
{
if (!_canEdit) {
qWarning() << "addMissionItem called with _canEdit == false";
return;
}
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->removeAt(index));
_deinitMissionItem(item);
_recalcAll();
// Set the new current item
if (index >= _missionItems->count()) {
index--;
}
for (int i=0; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
item->setIsCurrentItem(i == index);
}
}
void MissionController::loadMissionFromFile(void)
{
QString errorString;
QString filename = QGCFileDialog::getOpenFileName(NULL, "Select Mission File to load");
if (filename.isEmpty()) {
return;
}
if (_missionItems) {
_deinitAllMissionItems();
_missionItems->deleteLater();
}
_missionItems = new QmlObjectListModel(this);
_canEdit = true;
// FIXME: This needs to handle APM files which have WP 0 in them
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;
} }
firstCoordinateItem = false;
} else { } else {
// Subsequent coordinate items link to last coordinate item errorString = "The mission file is corrupted.";
_waypointLines.append(new CoordinateVector(lastCoordinateItem->coordinate(), item->coordinate())); break;
} }
lastCoordinateItem = item;
} }
} }
} }
emit waypointLinesChanged(); if (!errorString.isEmpty()) {
_missionItems->clear();
}
_initAllMissionItems();
} }
// This will update the child item hierarchy void MissionController::saveMissionToFile(void)
void MissionController::_recalcChildItems(void)
{ {
int firstIndex = _homePositionValid ? 0 : 1; QString errorString;
QString filename = QGCFileDialog::getSaveFileName(NULL, "Select file to save mission to");
if (filename.isEmpty()) {
return;
}
if (_missionItems->count() > firstIndex) { QFile file(filename);
MissionItem* currentParentItem = qobject_cast<MissionItem*>(_missionItems->get(firstIndex));
currentParentItem->childItems()->clear(); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
errorString = file.errorString();
} else {
QTextStream out(&file);
for (int i=firstIndex+1; i<_missionItems->count(); i++) { out << "QGC WPL 120\r\n"; // Version string
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
// Set up non-coordinate item child hierarchy for (int i=1; i<_missionItems->count(); i++) {
if (item->specifiesCoordinate()) { qobject_cast<MissionItem*>(_missionItems->get(i))->save(out);
item->childItems()->clear(); }
currentParentItem = item; }
} else {
currentParentItem->childItems()->append(item); _missionItems->setDirty(false);
}
void MissionController::_recalcWaypointLines(void)
{
MissionItem* lastCoordinateItem = qobject_cast<MissionItem*>(_missionItems->get(0));
bool firstCoordinateItem = true;
_waypointLines.clear();
for (int i=1; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
if (item->specifiesCoordinate()) {
if (firstCoordinateItem) {
if (item->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF) {
MissionItem* homeItem = qobject_cast<MissionItem*>(_missionItems->get(0));
// The first coordinate we hit is a takeoff command so link back to home position if valid
if (homeItem->homePositionValid()) {
_waypointLines.append(new CoordinateVector(qobject_cast<MissionItem*>(_missionItems->get(0))->coordinate(), item->coordinate()));
}
} else {
// First coordiante is not a takeoff command, it does not link backwards to anything
}
firstCoordinateItem = false;
} else if (!lastCoordinateItem->homePosition() || lastCoordinateItem->homePositionValid()) {
// Subsequent coordinate items link to last coordinate item. If the last coordinate item
// is an invalid home position we skip the line
_waypointLines.append(new CoordinateVector(lastCoordinateItem->coordinate(), item->coordinate()));
} }
lastCoordinateItem = item;
} }
} }
emit waypointLinesChanged();
} }
// This will update the sequence numbers to be sequential starting from 0 // This will update the sequence numbers to be sequential starting from 0
...@@ -138,6 +310,26 @@ void MissionController::_recalcSequence(void) ...@@ -138,6 +310,26 @@ void MissionController::_recalcSequence(void)
} }
} }
// This will update the child item hierarchy
void MissionController::_recalcChildItems(void)
{
MissionItem* currentParentItem = qobject_cast<MissionItem*>(_missionItems->get(0));
currentParentItem->childItems()->clear();
for (int i=1; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
// Set up non-coordinate item child hierarchy
if (item->specifiesCoordinate()) {
item->childItems()->clear();
currentParentItem = item;
} else {
currentParentItem->childItems()->append(item);
}
}
}
void MissionController::_recalcAll(void) void MissionController::_recalcAll(void)
{ {
_recalcSequence(); _recalcSequence();
...@@ -148,41 +340,181 @@ void MissionController::_recalcAll(void) ...@@ -148,41 +340,181 @@ void MissionController::_recalcAll(void)
/// Initializes a new set of mission items which may have come from the vehicle or have been loaded from a file /// Initializes a new set of mission items which may have come from the vehicle or have been loaded from a file
void MissionController::_initAllMissionItems(void) void MissionController::_initAllMissionItems(void)
{ {
// Add the home position item to the front MissionItem* homeItem = NULL;
MissionItem* homeItem = new MissionItem(this);
homeItem->setHomePositionSpecialCase(true); if (_activeVehicle && _activeVehicle->firmwarePlugin()->sendHomePositionToVehicle() && _missionItems->count() != 0) {
homeItem = qobject_cast<MissionItem*>(_missionItems->get(0));
homeItem->setHomePositionSpecialCase(true);
} else {
// Add the home position item to the front
homeItem = new MissionItem(this);
homeItem->setHomePositionSpecialCase(true);
homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT);
_missionItems->insert(0, homeItem);
}
homeItem->setHomePositionValid(false); homeItem->setHomePositionValid(false);
homeItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT);
homeItem->setLatitude(47.3769); for (int i=0; i<_missionItems->count(); i++) {
homeItem->setLongitude(8.549444); _initMissionItem(qobject_cast<MissionItem*>(_missionItems->get(i)));
_missionItems->insert(0, homeItem); }
_recalcAll(); _recalcAll();
emit missionItemsChanged(); emit missionItemsChanged();
emit canEditChanged(_canEdit);
_missionItems->setDirty(false);
connect(_missionItems, &QmlObjectListModel::dirtyChanged, this, &MissionController::_dirtyChanged);
}
void MissionController::_deinitAllMissionItems(void)
{
for (int i=0; i<_missionItems->count(); i++) {
_deinitMissionItem(qobject_cast<MissionItem*>(_missionItems->get(i)));
}
connect(_missionItems, &QmlObjectListModel::dirtyChanged, this, &MissionController::_dirtyChanged);
}
void MissionController::_initMissionItem(MissionItem* item)
{
_missionItems->setDirty(false);
connect(item, &MissionItem::commandChanged, this, &MissionController::_itemCommandChanged);
connect(item, &MissionItem::coordinateChanged, this, &MissionController::_itemCoordinateChanged);
}
void MissionController::_deinitMissionItem(MissionItem* item)
{
disconnect(item, &MissionItem::commandChanged, this, &MissionController::_itemCommandChanged);
disconnect(item, &MissionItem::coordinateChanged, this, &MissionController::_itemCoordinateChanged);
}
void MissionController::_itemCoordinateChanged(const QGeoCoordinate& coordinate)
{
Q_UNUSED(coordinate);
_recalcWaypointLines();
}
void MissionController::_itemCommandChanged(MavlinkQmlSingleton::Qml_MAV_CMD command)
{
Q_UNUSED(command);;
_recalcChildItems();
_recalcWaypointLines();
} }
void MissionController::_activeVehicleChanged(Vehicle* activeVehicle) void MissionController::_activeVehicleChanged(Vehicle* activeVehicle)
{ {
if (_activeVehicle) { if (_activeVehicle) {
MissionManager* missionManager = _activeVehicle->missionManager(); MissionManager* missionManager = _activeVehicle->missionManager();
disconnect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionController::_newMissionItemsAvailable); disconnect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionController::_newMissionItemsAvailable);
disconnect(missionManager, &MissionManager::inProgressChanged, this, &MissionController::_inProgressChanged);
disconnect(_activeVehicle, &Vehicle::homePositionAvailableChanged, this, &MissionController::_activeVehicleHomePositionAvailableChanged);
disconnect(_activeVehicle, &Vehicle::homePositionChanged, this, &MissionController::_activeVehicleHomePositionChanged);
_activeVehicle = NULL; _activeVehicle = NULL;
_newMissionItemsAvailable();
_activeVehicleHomePositionAvailableChanged(false);
} }
_activeVehicle = activeVehicle; _activeVehicle = activeVehicle;
if (_activeVehicle) { if (_activeVehicle) {
MissionManager* missionManager = activeVehicle->missionManager(); MissionManager* missionManager = activeVehicle->missionManager();
connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionController::_newMissionItemsAvailable); connect(missionManager, &MissionManager::newMissionItemsAvailable, this, &MissionController::_newMissionItemsAvailable);
connect(missionManager, &MissionManager::inProgressChanged, this, &MissionController::_inProgressChanged);
connect(_activeVehicle, &Vehicle::homePositionAvailableChanged, this, &MissionController::_activeVehicleHomePositionAvailableChanged);
connect(_activeVehicle, &Vehicle::homePositionChanged, this, &MissionController::_activeVehicleHomePositionChanged);
if (missionManager->inProgress()) {
// Vehicle is still in process of requesting mission items
_firstMissionItemSync = true;
} else {
// Vehicle already has mission items
_firstMissionItemSync = false;
}
_missionItemsRequested = true;
_newMissionItemsAvailable(); _newMissionItemsAvailable();
_activeVehicleHomePositionChanged(_activeVehicle->homePosition());
_activeVehicleHomePositionAvailableChanged(_activeVehicle->homePositionAvailable());
}
}
void MissionController::_activeVehicleHomePositionAvailableChanged(bool homePositionAvailable)
{
_liveHomePositionAvailable = homePositionAvailable;
emit liveHomePositionAvailableChanged(_liveHomePositionAvailable);
}
void MissionController::_activeVehicleHomePositionChanged(const QGeoCoordinate& homePosition)
{
_liveHomePosition = homePosition;
emit liveHomePositionChanged(_liveHomePosition);
}
void MissionController::deleteCurrentMissionItem(void)
{
for (int i=0; i<_missionItems->count(); i++) {
MissionItem* item = qobject_cast<MissionItem*>(_missionItems->get(i));
if (item->isCurrentItem() && i != 0) {
removeMissionItem(i);
return;
}
}
}
void MissionController::setAutoSync(bool autoSync)
{
// FIXME: AutoSync temporarily turned off
#if 0
_autoSync = autoSync;
emit autoSyncChanged(_autoSync);
if (_autoSync) {
_dirtyChanged(true);
}
#else
Q_UNUSED(autoSync)
#endif
}
void MissionController::_dirtyChanged(bool dirty)
{
if (dirty && _autoSync) {
Vehicle* activeVehicle = MultiVehicleManager::instance()->activeVehicle();
if (activeVehicle && !activeVehicle->armed()) {
if (_activeVehicle->missionManager()->inProgress()) {
_queuedSend = true;
} else {
_autoSyncSend();
}
}
}
}
void MissionController::_autoSyncSend(void)
{
qDebug() << "Auto-syncing with vehicle";
_queuedSend = false;
if (_missionItems) {
sendMissionItems();
_missionItems->setDirty(false);
} }
} }
void MissionController::setHomePositionValid(bool homePositionValid) void MissionController::_inProgressChanged(bool inProgress)
{ {
_homePositionValid = homePositionValid; if (!inProgress && _queuedSend) {
qobject_cast<MissionItem*>(_missionItems->get(0))->setHomePositionValid(homePositionValid); _autoSyncSend();
}
}
emit homePositionValidChanged(_homePositionValid); QmlObjectListModel* MissionController::missionItems(void)
{
return _missionItems;
} }
...@@ -29,37 +29,57 @@ This file is part of the QGROUNDCONTROL project ...@@ -29,37 +29,57 @@ This file is part of the QGROUNDCONTROL project
#include "QmlObjectListModel.h" #include "QmlObjectListModel.h"
#include "Vehicle.h" #include "Vehicle.h"
/// MissionController is a read only controller for Mission Items
class MissionController : public QObject class MissionController : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
MissionController(QObject* parent = NULL); MissionController(QObject* parent = NULL);
~MissionController(); ~MissionController();
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(QGeoCoordinate liveHomePosition READ liveHomePosition NOTIFY liveHomePositionChanged)
Q_PROPERTY(bool autoSync READ autoSync WRITE setAutoSync NOTIFY autoSyncChanged)
Q_INVOKABLE void start(bool editMode) ;
Q_INVOKABLE int addMissionItem(QGeoCoordinate coordinate);
Q_INVOKABLE void getMissionItems(void);
Q_INVOKABLE void sendMissionItems(void);
Q_INVOKABLE void loadMissionFromFile(void);
Q_INVOKABLE void saveMissionToFile(void);
Q_INVOKABLE void removeMissionItem(int index);
Q_INVOKABLE void deleteCurrentMissionItem(void);
/// true: home position should be shown on map, false: home position not shown on map
Q_PROPERTY(bool homePositionValid READ homePositionValid WRITE setHomePositionValid NOTIFY homePositionValidChanged)
// Property accessors // Property accessors
QmlObjectListModel* missionItems(void) { return _missionItems; }
QmlObjectListModel* waypointLines(void) { return &_waypointLines; }
bool homePositionValid(void) { return _homePositionValid; } QmlObjectListModel* missionItems(void);
void setHomePositionValid(bool homPositionValid); QmlObjectListModel* waypointLines(void) { return &_waypointLines; }
bool canEdit(void) { return _canEdit; }
bool liveHomePositionAvailable(void) { return _liveHomePositionAvailable; }
QGeoCoordinate liveHomePosition(void) { return _liveHomePosition; }
bool autoSync(void) { return _autoSync; }
void setAutoSync(bool autoSync);
signals: signals:
void missionItemsChanged(void); void missionItemsChanged(void);
void canEditChanged(bool canEdit);
void waypointLinesChanged(void); void waypointLinesChanged(void);
void homePositionValidChanged(bool homePositionValid); void liveHomePositionAvailableChanged(bool homePositionAvailable);
void liveHomePositionChanged(const QGeoCoordinate& homePosition);
void autoSyncChanged(bool autoSync);
private slots: private slots:
void _newMissionItemsAvailable(); void _newMissionItemsAvailable();
void _itemCoordinateChanged(const QGeoCoordinate& coordinate);
void _itemCommandChanged(MavlinkQmlSingleton::Qml_MAV_CMD command);
void _activeVehicleChanged(Vehicle* activeVehicle); void _activeVehicleChanged(Vehicle* activeVehicle);
void _activeVehicleHomePositionAvailableChanged(bool homePositionAvailable);
void _activeVehicleHomePositionChanged(const QGeoCoordinate& homePosition);
void _dirtyChanged(bool dirty);
void _inProgressChanged(bool inProgress);
private: private:
void _recalcSequence(void); void _recalcSequence(void);
...@@ -67,12 +87,25 @@ private: ...@@ -67,12 +87,25 @@ private:
void _recalcChildItems(void); void _recalcChildItems(void);
void _recalcAll(void); void _recalcAll(void);
void _initAllMissionItems(void); void _initAllMissionItems(void);
void _deinitAllMissionItems(void);
void _initMissionItem(MissionItem* item);
void _deinitMissionItem(MissionItem* item);
void _autoSyncSend(void);
private: private:
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 _homePositionValid; bool _liveHomePositionAvailable;
QGeoCoordinate _liveHomePosition;
bool _autoSync;
bool _firstMissionItemSync;
bool _missionItemsRequested;
bool _queuedSend;
static const char* _settingsGroup;
}; };
#endif #endif
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "MissionManager.h" #include "MissionManager.h"
#include "Vehicle.h" #include "Vehicle.h"
#include "FirmwarePlugin.h"
#include "MAVLinkProtocol.h" #include "MAVLinkProtocol.h"
QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog") QGC_LOGGING_CATEGORY(MissionManagerLog, "MissionManagerLog")
...@@ -53,8 +54,10 @@ MissionManager::~MissionManager() ...@@ -53,8 +54,10 @@ MissionManager::~MissionManager()
} }
void MissionManager::writeMissionItems(const QmlObjectListModel& missionItems, bool skipFirstItem) void MissionManager::writeMissionItems(const QmlObjectListModel& missionItems)
{ {
bool skipFirstItem = !_vehicle->firmwarePlugin()->sendHomePositionToVehicle();
_retryCount = 0; _retryCount = 0;
_missionItems.clear(); _missionItems.clear();
...@@ -65,10 +68,12 @@ void MissionManager::writeMissionItems(const QmlObjectListModel& missionItems, b ...@@ -65,10 +68,12 @@ void MissionManager::writeMissionItems(const QmlObjectListModel& missionItems, b
MissionItem* item = qobject_cast<MissionItem*>(_missionItems.get(_missionItems.count() - 1)); MissionItem* item = qobject_cast<MissionItem*>(_missionItems.get(_missionItems.count() - 1));
// Editor uses 1-based sequence numbers, adjust them before going out if (skipFirstItem) {
item->setSequenceNumber(item->sequenceNumber() - 1); // Home is in sequence 1, remainder of items start at sequence 1
if (item->command() == MavlinkQmlSingleton::MAV_CMD_DO_JUMP) { item->setSequenceNumber(item->sequenceNumber() - 1);
item->setParam1((int)item->param1() - 1); if (item->command() == MavlinkQmlSingleton::MAV_CMD_DO_JUMP) {
item->setParam1((int)item->param1() - 1);
}
} }
} }
emit newMissionItemsAvailable(); emit newMissionItemsAvailable();
...@@ -200,9 +205,9 @@ bool MissionManager::_stopAckTimeout(AckType_t expectedAck) ...@@ -200,9 +205,9 @@ bool MissionManager::_stopAckTimeout(AckType_t expectedAck)
return success; return success;
} }
void MissionManager::_sendTransactionComplete(void) void MissionManager::_readTransactionComplete(void)
{ {
qCDebug(MissionManagerLog) << "_sendTransactionComplete read sequence complete"; qCDebug(MissionManagerLog) << "_readTransactionComplete read sequence complete";
mavlink_message_t message; mavlink_message_t message;
mavlink_mission_ack_t missionAck; mavlink_mission_ack_t missionAck;
...@@ -233,8 +238,7 @@ void MissionManager::_handleMissionCount(const mavlink_message_t& message) ...@@ -233,8 +238,7 @@ void MissionManager::_handleMissionCount(const mavlink_message_t& message)
qCDebug(MissionManagerLog) << "_handleMissionCount count:" << _cMissionItems; qCDebug(MissionManagerLog) << "_handleMissionCount count:" << _cMissionItems;
if (_cMissionItems == 0) { if (_cMissionItems == 0) {
emit newMissionItemsAvailable(); _readTransactionComplete();
emit inProgressChanged(false);
} else { } else {
_requestNextMissionItem(0); _requestNextMissionItem(0);
} }
...@@ -304,7 +308,7 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message) ...@@ -304,7 +308,7 @@ void MissionManager::_handleMissionItem(const mavlink_message_t& message)
int nextSequenceNumber = missionItem.seq + 1; int nextSequenceNumber = missionItem.seq + 1;
if (nextSequenceNumber == _cMissionItems) { if (nextSequenceNumber == _cMissionItems) {
_sendTransactionComplete(); _readTransactionComplete();
} else { } else {
_requestNextMissionItem(nextSequenceNumber); _requestNextMissionItem(nextSequenceNumber);
} }
......
...@@ -63,8 +63,7 @@ public: ...@@ -63,8 +63,7 @@ public:
/// Writes the specified set of mission items to the vehicle /// Writes the specified set of mission items to the vehicle
/// @oaram missionItems Items to send to vehicle /// @oaram missionItems Items to send to vehicle
/// @param skipFirstItem true: Don't send first item void writeMissionItems(const QmlObjectListModel& missionItems);
void writeMissionItems(const QmlObjectListModel& missionItems, bool skipFirstItem);
/// Returns a copy of the current set of mission items. Caller is responsible for /// Returns a copy of the current set of mission items. Caller is responsible for
/// freeing returned object. /// freeing returned object.
...@@ -106,7 +105,7 @@ private: ...@@ -106,7 +105,7 @@ private:
void _startAckTimeout(AckType_t ack); void _startAckTimeout(AckType_t ack);
bool _stopAckTimeout(AckType_t expectedAck); bool _stopAckTimeout(AckType_t expectedAck);
void _sendTransactionComplete(void); void _readTransactionComplete(void);
void _handleMissionCount(const mavlink_message_t& message); void _handleMissionCount(const mavlink_message_t& message);
void _handleMissionItem(const mavlink_message_t& message); void _handleMissionItem(const mavlink_message_t& message);
void _handleMissionRequest(const mavlink_message_t& message); void _handleMissionRequest(const mavlink_message_t& message);
...@@ -137,4 +136,4 @@ private: ...@@ -137,4 +136,4 @@ private:
QmlObjectListModel _missionItems; QmlObjectListModel _missionItems;
}; };
#endif #endif
\ No newline at end of file
...@@ -89,7 +89,6 @@ ...@@ -89,7 +89,6 @@
#include "CoordinateVector.h" #include "CoordinateVector.h"
#include "MainToolBarController.h" #include "MainToolBarController.h"
#include "MissionController.h" #include "MissionController.h"
#include "MissionEditorController.h"
#include "FlightDisplayViewController.h" #include "FlightDisplayViewController.h"
#include "VideoSurface.h" #include "VideoSurface.h"
#include "VideoReceiver.h" #include "VideoReceiver.h"
...@@ -355,7 +354,6 @@ void QGCApplication::_initCommon(void) ...@@ -355,7 +354,6 @@ void QGCApplication::_initCommon(void)
qmlRegisterType<ScreenToolsController> ("QGroundControl.Controllers", 1, 0, "ScreenToolsController"); qmlRegisterType<ScreenToolsController> ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
qmlRegisterType<MainToolBarController> ("QGroundControl.Controllers", 1, 0, "MainToolBarController"); qmlRegisterType<MainToolBarController> ("QGroundControl.Controllers", 1, 0, "MainToolBarController");
qmlRegisterType<MissionController> ("QGroundControl.Controllers", 1, 0, "MissionController"); qmlRegisterType<MissionController> ("QGroundControl.Controllers", 1, 0, "MissionController");
qmlRegisterType<MissionEditorController> ("QGroundControl.Controllers", 1, 0, "MissionEditorController");
qmlRegisterType<FlightDisplayViewController> ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController"); qmlRegisterType<FlightDisplayViewController> ("QGroundControl.Controllers", 1, 0, "FlightDisplayViewController");
#ifndef __mobile__ #ifndef __mobile__
......
...@@ -60,7 +60,7 @@ void SetupViewTest::_clickThrough_test(void) ...@@ -60,7 +60,7 @@ void SetupViewTest::_clickThrough_test(void)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
Q_CHECK_PTR(link); Q_CHECK_PTR(link);
link->setAutopilotType(MAV_AUTOPILOT_PX4); link->setFirmwareType(MAV_AUTOPILOT_PX4);
LinkManager::instance()->_addLink(link); LinkManager::instance()->_addLink(link);
linkMgr->connectLink(link); linkMgr->connectLink(link);
......
...@@ -63,7 +63,7 @@ void MainWindowTest::_connectWindowClose_test(MAV_AUTOPILOT autopilot) ...@@ -63,7 +63,7 @@ void MainWindowTest::_connectWindowClose_test(MAV_AUTOPILOT autopilot)
MockLink* link = new MockLink(); MockLink* link = new MockLink();
Q_CHECK_PTR(link); Q_CHECK_PTR(link);
link->setAutopilotType(autopilot); link->setFirmwareType(autopilot);
LinkManager::instance()->_addLink(link); LinkManager::instance()->_addLink(link);
linkMgr->connectLink(link); linkMgr->connectLink(link);
......
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