Commit d17f10d2 authored by Beat Küng's avatar Beat Küng

AirMap: refactor for multi-vehicle support & base class API

- add multi-vehicle support: each vehicle has now a per-vehicle AirMap
  object that takes care of flight creation, traffic & telemetry
- add a base class API layer so that the interface between QGC and the
  AirMap implementation is clear, and easier to change
- removes polygon upload to vehicle (needs a better solution)
parent 3f64f28b
......@@ -505,7 +505,9 @@ HEADERS += \
src/MG.h \
src/MissionManager/CameraCalc.h \
src/MissionManager/AirMapController.h \
src/MissionManager/AirspaceController.h \
src/MissionManager/AirMapManager.h \
src/MissionManager/AirspaceManagement.h \
src/MissionManager/CameraSection.h \
src/MissionManager/CameraSpec.h \
src/MissionManager/ComplexMissionItem.h \
......@@ -712,7 +714,9 @@ SOURCES += \
src/LogCompressor.cc \
src/MissionManager/CameraCalc.cc \
src/MissionManager/AirMapController.cc \
src/MissionManager/AirspaceController.cc \
src/MissionManager/AirMapManager.cc \
src/MissionManager/AirspaceManagement.cc \
src/MissionManager/CameraSection.cc \
src/MissionManager/CameraSpec.cc \
src/MissionManager/ComplexMissionItem.cc \
......
......@@ -615,11 +615,11 @@ QGCView {
}
}
//-- AirMap Indicator
//-- Airspace Indicator
Rectangle {
id: airMapIndicator
width: airMapRow.width + (ScreenTools.defaultFontPixelWidth * 3)
height: airMapRow.height * 1.25
id: airspaceIndicator
width: airspaceRow.width + (ScreenTools.defaultFontPixelWidth * 3)
height: airspaceRow.height * 1.25
color: qgcPal.globalTheme === QGCPalette.Light ? Qt.rgba(1,1,1,0.95) : Qt.rgba(0,0,0,0.75)
visible: _mainIsMap && flightPermit && flightPermit !== AirspaceAuthorization.PermitUnknown && !messageArea.visible && !criticalMmessageArea.visible
radius: 3
......@@ -629,27 +629,27 @@ QGCView {
anchors.topMargin: ScreenTools.toolbarHeight + (ScreenTools.defaultFontPixelHeight * 0.25)
anchors.horizontalCenter: parent.horizontalCenter
Row {
id: airMapRow
id: airspaceRow
spacing: ScreenTools.defaultFontPixelWidth
anchors.centerIn: parent
QGCLabel { text: qsTr("AirMap:"); anchors.verticalCenter: parent.verticalCenter; }
QGCLabel { text: airspaceIndicator.providerName+":"; anchors.verticalCenter: parent.verticalCenter; }
QGCLabel {
text: {
if(airMapIndicator.flightPermit) {
if(airMapIndicator.flightPermit === AirspaceAuthorization.PermitPending)
if(airspaceIndicator.flightPermit) {
if(airspaceIndicator.flightPermit === AirspaceAuthorization.PermitPending)
return qsTr("Approval Pending")
if(airMapIndicator.flightPermit === AirspaceAuthorization.PermitAccepted)
if(airspaceIndicator.flightPermit === AirspaceAuthorization.PermitAccepted)
return qsTr("Flight Approved")
if(airMapIndicator.flightPermit === AirspaceAuthorization.PermitRejected)
return qsTr("Flight Denied")
if(airspaceIndicator.flightPermit === AirspaceAuthorization.PermitRejected)
return qsTr("Flight Rejected")
}
return ""
}
color: {
if(airMapIndicator.flightPermit) {
if(airMapIndicator.flightPermit === AirspaceAuthorization.PermitPending)
if(airspaceIndicator.flightPermit) {
if(airspaceIndicator.flightPermit === AirspaceAuthorization.PermitPending)
return qgcPal.colorOrange
if(airMapIndicator.flightPermit === AirspaceAuthorization.PermitAccepted)
if(airspaceIndicator.flightPermit === AirspaceAuthorization.PermitAccepted)
return qgcPal.colorGreen
}
return qgcPal.colorRed
......@@ -657,7 +657,8 @@ QGCView {
anchors.verticalCenter: parent.verticalCenter;
}
}
property var flightPermit: _activeVehicle ? _activeVehicle.airMapController.flightPermitStatus : null
property var flightPermit: _activeVehicle ? _activeVehicle.flightPermitStatus : null
property var providerName: _activeVehicle ? _activeVehicle.airspaceController.providerName : ""
}
}
......@@ -59,7 +59,7 @@ FlightMap {
onZoomLevelChanged: QGroundControl.flightMapZoom = zoomLevel
onCenterChanged: {
if(_activeVehicle) {
_activeVehicle.airMapController.setROI(center, 5000)
_activeVehicle.airspaceController.setROI(center, 5000)
}
QGroundControl.flightMapPosition = center
}
......@@ -329,9 +329,9 @@ FlightMap {
]
}
// AirMap overlap support
// Airspace overlap support
MapItemView {
model: _activeVehicle ? _activeVehicle.airMapController.circles : []
model: _activeVehicle ? _activeVehicle.airspaceController.circles : []
delegate: MapCircle {
center: object.center
radius: object.radius
......@@ -342,7 +342,7 @@ FlightMap {
}
MapItemView {
model: _activeVehicle ? _activeVehicle.airMapController.polygons : []
model: _activeVehicle ? _activeVehicle.airspaceController.polygons : []
delegate: MapPolygon {
border.color: "white"
color: "yellow"
......
This diff is collapsed.
......@@ -15,6 +15,7 @@
#include "QmlObjectListModel.h"
#include "MissionItem.h"
#include "MultiVehicleManager.h"
#include "AirspaceManagement.h"
#include <qmqtt.h>
......@@ -32,44 +33,6 @@
Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)
class AirspaceRestriction : public QObject
{
Q_OBJECT
public:
AirspaceRestriction(QObject* parent = NULL);
};
class PolygonAirspaceRestriction : public AirspaceRestriction
{
Q_OBJECT
public:
PolygonAirspaceRestriction(const QVariantList& polygon, QObject* parent = NULL);
Q_PROPERTY(QVariantList polygon MEMBER _polygon CONSTANT)
const QVariantList& getPolygon() const { return _polygon; }
private:
QVariantList _polygon;
};
class CircularAirspaceRestriction : public AirspaceRestriction
{
Q_OBJECT
public:
CircularAirspaceRestriction(const QGeoCoordinate& center, double radius, QObject* parent = NULL);
Q_PROPERTY(QGeoCoordinate center MEMBER _center CONSTANT)
Q_PROPERTY(double radius MEMBER _radius CONSTANT)
private:
QGeoCoordinate _center;
double _radius;
};
class AirMapLogin : public QObject
{
......@@ -165,6 +128,8 @@ public:
*/
void abort();
bool hasAPIKey() const { return _networkingData.airmapAPIKey != ""; }
signals:
/// signal when the request finished (get or post). All requests are assumed to return JSON.
void finished(QJsonParseError parseError, QJsonDocument document);
......@@ -196,16 +161,13 @@ private:
/// class to download polygons from AirMap
class AirspaceRestrictionManager : public QObject
class AirMapRestrictionManager : public AirspaceRestrictionProvider
{
Q_OBJECT
public:
AirspaceRestrictionManager(AirMapNetworking::SharedData& sharedData);
void updateROI(const QGeoCoordinate& center, double radiusMeters);
AirMapRestrictionManager(AirMapNetworking::SharedData& sharedData);
QmlObjectListModel* polygonRestrictions(void) { return &_polygonList; }
QmlObjectListModel* circularRestrictions(void) { return &_circleList; }
void setROI(const QGeoCoordinate& center, double radiusMeters) override;
signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
......@@ -223,26 +185,9 @@ private:
State _state = State::Idle;
int _numAwaitingItems = 0;
AirMapNetworking _networking;
QmlObjectListModel _polygonList;
QmlObjectListModel _circleList;
QList<PolygonAirspaceRestriction*> _nextPolygonList;
QList<CircularAirspaceRestriction*> _nextcircleList;
};
class AirspaceAuthorization : public QObject {
Q_OBJECT
public:
enum PermitStatus {
PermitUnknown = 0,
PermitPending,
PermitAccepted,
PermitRejected,
};
Q_ENUMS(PermitStatus);
};
/// class to upload a flight
class AirMapFlightManager : public QObject
{
......@@ -344,6 +289,8 @@ public:
void stopTelemetryStream();
bool isTelemetryStreaming() const;
signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
......@@ -360,8 +307,6 @@ private:
void _handleGlobalPositionInt(const mavlink_message_t& message);
void _handleGPSRawInt(const mavlink_message_t& message);
bool _isTelemetryStreaming() const;
enum class State {
Idle,
StartCommunication,
......@@ -418,68 +363,74 @@ private:
/// AirMap server communication support.
class AirMapManager : public QGCTool
/// AirMap per vehicle management class.
class AirMapManagerPerVehicle : public AirspaceManagerPerVehicle
{
Q_OBJECT
public:
AirMapManager(QGCApplication* app, QGCToolbox* toolbox);
~AirMapManager();
AirMapManagerPerVehicle(AirMapNetworking::SharedData& sharedData, const Vehicle& vehicle, QGCToolbox& toolbox);
virtual ~AirMapManagerPerVehicle() = default;
/// Set the ROI for airspace information
/// @param center Center coordinate for ROI
/// @param radiusMeters Radius in meters around center which is of interest
void setROI(QGeoCoordinate& center, double radiusMeters);
/// Send flight path to AirMap
void createFlight(const QList<MissionItem*>& missionItems);
void createFlight(const QList<MissionItem*>& missionItems) override;
AirspaceAuthorization::PermitStatus flightPermitStatus() const override;
QmlObjectListModel* polygonRestrictions(void) { return _airspaceRestrictionManager.polygonRestrictions(); }
QmlObjectListModel* circularRestrictions(void) { return _airspaceRestrictionManager.circularRestrictions(); }
void startTelemetryStream() override;
void setToolbox(QGCToolbox* toolbox) override;
void stopTelemetryStream() override;
AirspaceAuthorization::PermitStatus flightPermitStatus() const { return _flightManager.flightPermitStatus(); }
bool isTelemetryStreaming() const override;
signals:
void flightPermitStatusChanged();
void trafficUpdate(QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading);
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
private slots:
void _updateToROI(void);
void _networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
public slots:
void endFlight() override;
void _activeVehicleChanged(Vehicle* activeVehicle);
void _vehicleArmedChanged(bool armed);
void settingsChanged();
protected slots:
virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override;
private slots:
void _flightPermitStatusChanged();
void _settingsChanged();
private:
bool _hasAPIKey() const { return _networkingData.airmapAPIKey != ""; }
AirMapNetworking _networking;
/**
* A new vehicle got connected, listen to its state
*/
void _connectVehicle(Vehicle* vehicle);
AirMapNetworking::SharedData _networkingData;
AirspaceRestrictionManager _airspaceRestrictionManager;
AirMapFlightManager _flightManager;
AirMapTelemetry _telemetry;
AirMapTrafficAlertClient _trafficAlerts;
QGeoCoordinate _roiCenter;
double _roiRadius;
QGCToolbox& _toolbox;
};
QTimer _updateTimer;
class AirMapManager : public AirspaceManager
{
Q_OBJECT
public:
AirMapManager(QGCApplication* app, QGCToolbox* toolbox);
virtual ~AirMapManager() = default;
void setToolbox(QGCToolbox* toolbox) override;
AirspaceManagerPerVehicle* instantiateVehicle(const Vehicle& vehicle) override;
AirspaceRestrictionProvider* instantiateRestrictionProvider() override;
QString name() const override { return "AirMap"; }
Vehicle* _vehicle = nullptr; ///< current vehicle
bool _vehicleWasInMissionMode = false; ///< true if the vehicle was in mission mode when arming
signals:
void settingsChanged();
private slots:
void _networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void _settingsChanged();
private:
AirMapNetworking::SharedData _networkingData;
};
#endif
......@@ -7,21 +7,14 @@
*
****************************************************************************/
#include "AirMapController.h"
#include "AirMapManager.h"
#include "AirspaceController.h"
#include "AirspaceManagement.h"
#include "QGCApplication.h"
#include "QGCQGeoCoordinate.h"
QGC_LOGGING_CATEGORY(AirMapControllerLog, "AirMapControllerLog")
AirMapController::AirMapController(QObject* parent)
AirspaceController::AirspaceController(QObject* parent)
: QObject(parent)
, _manager(qgcApp()->toolbox()->airMapManager())
, _manager(qgcApp()->toolbox()->airspaceManager())
{
connect(_manager, &AirMapManager::flightPermitStatusChanged, this, &AirMapController::flightPermitStatusChanged);
}
AirMapController::~AirMapController()
{
}
......@@ -7,44 +7,30 @@
*
****************************************************************************/
#ifndef AirMapController_H
#define AirMapController_H
#pragma once
#include "AirMapManager.h"
#include "AirspaceManagement.h"
#include "QGCMapPolygon.h"
#include "QGCLoggingCategory.h"
Q_DECLARE_LOGGING_CATEGORY(AirMapControllerLog)
class AirMapManager;
class AirMapController : public QObject
class AirspaceController : public QObject
{
Q_OBJECT
public:
AirMapController(QObject* parent = NULL);
~AirMapController();
AirspaceController(QObject* parent = NULL);
~AirspaceController() = default;
Q_PROPERTY(QmlObjectListModel* polygons READ polygons CONSTANT) ///< List of PolygonAirspaceRestriction objects
Q_PROPERTY(QmlObjectListModel* circles READ circles CONSTANT) ///< List of CircularAirspaceRestriction objects
Q_PROPERTY(AirspaceAuthorization::PermitStatus flightPermitStatus READ flightPermitStatus NOTIFY flightPermitStatusChanged) ///< state of flight permission
Q_INVOKABLE void setROI(QGeoCoordinate center, double radius) { _manager->setROI(center, radius); }
QmlObjectListModel* polygons(void) { return _manager->polygonRestrictions(); }
QmlObjectListModel* circles(void) { return _manager->circularRestrictions(); }
QmlObjectListModel* polygons() { return _manager->polygonRestrictions(); }
QmlObjectListModel* circles() { return _manager->circularRestrictions(); }
AirspaceAuthorization::PermitStatus flightPermitStatus() const { return _manager->flightPermitStatus(); }
signals:
void flightPermitStatusChanged();
Q_PROPERTY(QString providerName READ providerName CONSTANT)
QString providerName() { return _manager->name(); }
private:
AirMapManager* _manager;
QmlObjectListModel _polygonList;
QmlObjectListModel _circleList;
AirspaceManager* _manager;
};
#endif
/****************************************************************************
*
* (c) 2017 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "AirspaceManagement.h"
#include <Vehicle.h>
QGC_LOGGING_CATEGORY(AirspaceManagementLog, "AirspaceManagementLog")
AirspaceRestriction::AirspaceRestriction(QObject* parent)
: QObject(parent)
{
}
PolygonAirspaceRestriction::PolygonAirspaceRestriction(const QVariantList& polygon, QObject* parent)
: AirspaceRestriction(parent)
, _polygon(polygon)
{
}
CircularAirspaceRestriction::CircularAirspaceRestriction(const QGeoCoordinate& center, double radius, QObject* parent)
: AirspaceRestriction(parent)
, _center(center)
, _radius(radius)
{
}
AirspaceManager::AirspaceManager(QGCApplication* app, QGCToolbox* toolbox)
: QGCTool(app, toolbox)
{
_roiUpdateTimer.setInterval(2000);
_roiUpdateTimer.setSingleShot(true);
connect(&_roiUpdateTimer, &QTimer::timeout, this, &AirspaceManager::_updateToROI);
qmlRegisterUncreatableType<AirspaceAuthorization>("QGroundControl", 1, 0, "AirspaceAuthorization", "Reference only");
}
AirspaceManager::~AirspaceManager()
{
if (_restrictionsProvider) {
delete _restrictionsProvider;
}
_polygonRestrictions.clearAndDeleteContents();
_circleRestrictions.clearAndDeleteContents();
}
void AirspaceManager::setToolbox(QGCToolbox* toolbox)
{
QGCTool::setToolbox(toolbox);
// we should not call virtual methods in the constructor, so we instantiate the restriction provider here
_restrictionsProvider = instantiateRestrictionProvider();
if (_restrictionsProvider) {
connect(_restrictionsProvider, &AirspaceRestrictionProvider::requestDone, this,
&AirspaceManager::_restrictionsUpdated);
}
}
void AirspaceManager::setROI(const QGeoCoordinate& center, double radiusMeters)
{
_roiCenter = center;
_roiRadius = radiusMeters;
_roiUpdateTimer.start();
}
void AirspaceManager::_updateToROI()
{
if (_restrictionsProvider) {
_restrictionsProvider->setROI(_roiCenter, _roiRadius);
}
}
void AirspaceManager::_restrictionsUpdated(bool success)
{
_polygonRestrictions.clearAndDeleteContents();
_circleRestrictions.clearAndDeleteContents();
if (success) {
for (const auto& circle : _restrictionsProvider->circles()) {
_circleRestrictions.append(circle);
}
for (const auto& polygon : _restrictionsProvider->polygons()) {
_polygonRestrictions.append(polygon);
}
} else {
// TODO: show error?
}
}
AirspaceManagerPerVehicle::AirspaceManagerPerVehicle(const Vehicle& vehicle)
: _vehicle(vehicle)
{
connect(&_vehicle, &Vehicle::armedChanged, this, &AirspaceManagerPerVehicle::_vehicleArmedChanged);
connect(&_vehicle, &Vehicle::mavlinkMessageReceived, this, &AirspaceManagerPerVehicle::vehicleMavlinkMessageReceived);
}
void AirspaceManagerPerVehicle::_vehicleArmedChanged(bool armed)
{
if (armed) {
startTelemetryStream();
_vehicleWasInMissionMode = _vehicle.flightMode() == _vehicle.missionFlightMode();
} else {
stopTelemetryStream();
// end the flight if we were in mission mode during arming or disarming
// TODO: needs to be improved. for instance if we do RTL and then want to continue the mission...
if (_vehicleWasInMissionMode || _vehicle.flightMode() == _vehicle.missionFlightMode()) {
endFlight();
}
}
}
/****************************************************************************
*
* (c) 2017 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
/**
* @file AirspaceManagement.h
* This file contains the interface definitions used by an airspace management implementation (AirMap).
* There are 3 base classes that must be subclassed:
* - AirspaceManager
* main manager that contains the restrictions for display. It acts as a factory to create instances of the other
* classes.
* - AirspaceManagerPerVehicle
* this provides the multi-vehicle support - each vehicle has an instance
* - AirspaceRestrictionProvider
* provides airspace restrictions. Currently only used by AirspaceManager, but
* each vehicle could have its own restrictions.
*/
#include "QGCToolbox.h"
#include "MissionItem.h"
#include <QObject>
#include <QString>
#include <QList>
#include <QGCLoggingCategory.h>
Q_DECLARE_LOGGING_CATEGORY(AirspaceManagementLog)
/**
* Contains the status of the Airspace authorization
*/
class AirspaceAuthorization : public QObject {
Q_OBJECT
public:
enum PermitStatus {
PermitUnknown = 0,
PermitPending,
PermitAccepted,
PermitRejected,
};
Q_ENUMS(PermitStatus);
};
/**
* Base class for an airspace restriction
*/
class AirspaceRestriction : public QObject
{
Q_OBJECT
public:
AirspaceRestriction(QObject* parent = NULL);
};
class PolygonAirspaceRestriction : public AirspaceRestriction
{
Q_OBJECT
public:
PolygonAirspaceRestriction(const QVariantList& polygon, QObject* parent = NULL);
Q_PROPERTY(QVariantList polygon MEMBER _polygon CONSTANT)
const QVariantList& getPolygon() const { return _polygon; }
private:
QVariantList _polygon;
};
class CircularAirspaceRestriction : public AirspaceRestriction
{
Q_OBJECT
public:
CircularAirspaceRestriction(const QGeoCoordinate& center, double radius, QObject* parent = NULL);
Q_PROPERTY(QGeoCoordinate center MEMBER _center CONSTANT)
Q_PROPERTY(double radius MEMBER _radius CONSTANT)
private:
QGeoCoordinate _center;
double _radius;
};
/**
* @class AirspaceRestrictionProvider
* Base class that queries for airspace restrictions
*/
class AirspaceRestrictionProvider : public QObject {
Q_OBJECT
public:
AirspaceRestrictionProvider() = default;
~AirspaceRestrictionProvider() = default;
/**
* Set region of interest that should be queried. When finished, the requestDone() signal will be emmited.
* @param center Center coordinate for ROI
* @param radiusMeters Radius in meters around center which is of interest
*/
virtual void setROI(const QGeoCoordinate& center, double radiusMeters) = 0;
const QList<PolygonAirspaceRestriction*>& polygons() const { return _polygonList; }
const QList<CircularAirspaceRestriction*>& circles() const { return _circleList; }
signals:
void requestDone(bool success);
protected:
QList<PolygonAirspaceRestriction*> _polygonList;
QList<CircularAirspaceRestriction*> _circleList;
};
class AirspaceManagerPerVehicle;
class Vehicle;
/**
* @class AirspaceManager
* Base class for airspace management. There is one (global) instantiation of this
*/
class AirspaceManager : public QGCTool {
Q_OBJECT
public:
AirspaceManager(QGCApplication* app, QGCToolbox* toolbox);
virtual ~AirspaceManager();
/**
* Factory method to create an AirspaceManagerPerVehicle object
*/
virtual AirspaceManagerPerVehicle* instantiateVehicle(const Vehicle& vehicle) = 0;
/**
*
* Factory method to create an AirspaceRestrictionProvider object
*/
virtual AirspaceRestrictionProvider* instantiateRestrictionProvider() = 0;
/**
* Set the ROI for airspace information (restrictions shown in UI)
* @param center Center coordinate for ROI
* @param radiusMeters Radius in meters around center which is of interest
*/
void setROI(const QGeoCoordinate& center, double radiusMeters);
QmlObjectListModel* polygonRestrictions(void) { return &_polygonRestrictions; }
QmlObjectListModel* circularRestrictions(void) { return &_circleRestrictions; }
void setToolbox(QGCToolbox* toolbox) override;
/**
* Name of the airspace management provider (used in the UI)
*/
virtual QString name() const = 0;
private slots: