Browse Source

AirMapManager: change backend to use airmapd

You need to adjust the 'AIRMAPD_PATH = path_to_airmapd' in
qgroundcontrol.pro to make it compile.

And sorry for the huge commit :/
QGC4.4
Beat Küng 8 years ago
parent
commit
44a05ab69d
  1. 6
      .gitmodules
  2. 25
      qgroundcontrol.pro
  3. 1445
      src/MissionManager/AirMapManager.cc
  4. 279
      src/MissionManager/AirMapManager.h
  5. 2
      src/protobuf/.gitignore
  6. 68
      src/protobuf/airmap_telemetry.proto
  7. 39
      src/protobuf/proto_compile.pri

6
.gitmodules vendored

@ -4,9 +4,3 @@
[submodule "libs/mavlink/include/mavlink/v2.0"] [submodule "libs/mavlink/include/mavlink/v2.0"]
path = libs/mavlink/include/mavlink/v2.0 path = libs/mavlink/include/mavlink/v2.0
url = https://github.com/mavlink/c_library_v2.git url = https://github.com/mavlink/c_library_v2.git
[submodule "libs/thirdParty/tiny-AES128-C"]
path = libs/thirdParty/tiny-AES128-C
url = https://github.com/bkueng/tiny-AES128-C.git
[submodule "libs/thirdParty/qmqtt"]
path = libs/thirdParty/qmqtt
url = https://github.com/emqtt/qmqtt.git

25
qgroundcontrol.pro

@ -131,10 +131,6 @@ WindowsBuild {
QMAKE_TARGET_PRODUCT = "$${QGC_APP_NAME}" QMAKE_TARGET_PRODUCT = "$${QGC_APP_NAME}"
} }
include($$PWD/libs/thirdParty/qmqtt/src/mqtt/mqtt.pri)
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
# #
# Plugin configuration # Plugin configuration
# #
@ -592,7 +588,9 @@ HEADERS += \
src/uas/UASInterface.h \ src/uas/UASInterface.h \
src/uas/UASMessageHandler.h \ src/uas/UASMessageHandler.h \
src/AnalyzeView/LogDownloadController.h \ src/AnalyzeView/LogDownloadController.h \
libs/thirdParty/tiny-AES128-C/aes.h \
AIRMAPD_PATH = path_to_airmapd
# Protobuf (AirMap) # Protobuf (AirMap)
# This should be optional. As is, QGC now requires protobuf to be installed. # This should be optional. As is, QGC now requires protobuf to be installed.
@ -602,10 +600,19 @@ MacBuild {
LIBS += \ LIBS += \
-L/usr/local/opt/protobuf/lib -L/usr/local/opt/protobuf/lib
} }
LIBS += -lprotobuf LIBS += -lprotobuf
PROTOS = src/protobuf/airmap_telemetry.proto
include(src/protobuf/proto_compile.pri) # airmapd
INCLUDEPATH += $${AIRMAPD_PATH}/include
LIBS += -L$${AIRMAPD_PATH}/build/src/airmap -lairmap-qt \
-lairmap-mavlink \
-lairmap-client -lssl -lcrypto \
-L$${AIRMAPD_PATH}/build/vendor/fmt/fmt -lfmt \
-lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options \
-lboost_unit_test_framework \
-L$${AIRMAPD_PATH}/build/vendor/xdg -lxdg \
-L$${AIRMAPD_PATH}/build/vendor/uri/src -lnetwork-uri \
AndroidBuild { AndroidBuild {
HEADERS += \ HEADERS += \
@ -797,7 +804,6 @@ SOURCES += \
src/uas/UAS.cc \ src/uas/UAS.cc \
src/uas/UASMessageHandler.cc \ src/uas/UASMessageHandler.cc \
src/AnalyzeView/LogDownloadController.cc \ src/AnalyzeView/LogDownloadController.cc \
libs/thirdParty/tiny-AES128-C/aes.c \
DebugBuild { DebugBuild {
SOURCES += \ SOURCES += \
@ -875,7 +881,6 @@ INCLUDEPATH += \
src/FirmwarePlugin \ src/FirmwarePlugin \
src/Vehicle \ src/Vehicle \
src/VehicleSetup \ src/VehicleSetup \
libs/thirdParty/tiny-AES128-C \
HEADERS+= \ HEADERS+= \
src/AutoPilotPlugins/AutoPilotPlugin.h \ src/AutoPilotPlugins/AutoPilotPlugin.h \

1445
src/MissionManager/AirMapManager.cc

File diff suppressed because it is too large Load Diff

279
src/MissionManager/AirMapManager.h

@ -7,8 +7,7 @@
* *
****************************************************************************/ ****************************************************************************/
#ifndef AirMapManager_H #pragma once
#define AirMapManager_H
#include "QGCToolbox.h" #include "QGCToolbox.h"
#include "QGCLoggingCategory.h" #include "QGCLoggingCategory.h"
@ -17,146 +16,82 @@
#include "MultiVehicleManager.h" #include "MultiVehicleManager.h"
#include "AirspaceManagement.h" #include "AirspaceManagement.h"
#include <qmqtt.h>
#include <QGeoCoordinate> #include <QGeoCoordinate>
#include <QList> #include <QList>
#include <QNetworkAccessManager> #include <QQueue>
#include <QNetworkReply>
#include <QTimer> #include <QTimer>
#include <QUdpSocket>
#include <QHostInfo>
#include <QHostAddress>
#include <stdint.h>
Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)
class AirMapLogin : public QObject
{
Q_OBJECT
public:
/**
* @param networkManager
* @param APIKey AirMap API key: this is stored as a reference, and thus must live as long as this object does
*/
AirMapLogin(QNetworkAccessManager& networkManager, const QString& APIKey);
void setCredentials(const QString& clientID, const QString& userName, const QString& password);
/**
* check if the credentials are set (not necessarily valid)
*/
bool hasCredentials() const { return _userName != "" && _password != ""; }
void login();
void logout() { _JWTToken = ""; }
/** get the JWT token. Empty if user not logged in */
const QString& JWTToken() const { return _JWTToken; }
bool isLoggedIn() const { return _JWTToken != ""; } #include <cstdint>
#include <functional>
#include <memory>
signals: #include <airmap/qt/client.h>
void loginSuccess(); #include <airmap/qt/logger.h>
void loginFailure(QNetworkReply::NetworkError error, const QString& errorString, const QString& serverErrorMessage); #include <airmap/qt/types.h>
#include <airmap/traffic.h>
private slots:
void _requestFinished(void);
void _requestError(QNetworkReply::NetworkError code);
private:
void _post(QUrl url, const QByteArray& postData);
QNetworkAccessManager& _networkManager; Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)
bool _isLoginInProgress = false;
QString _JWTToken = ""; ///< JWT login token: empty when not logged in
const QString& _APIKey;
// login credentials
QString _clientID;
QString _userName;
QString _password;
};
/** /**
* @class AirMapNetworking * @class AirMapSharedState
* Handles networking requests (GET & POST), with login if required. * contains state & settings that need to be shared (such as login)
* There can only be one active request per object instance.
*/ */
class AirMapNetworking : public QObject class AirMapSharedState : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
struct Settings {
QString apiKey;
struct SharedData { // login credentials
SharedData() : login(networkManager, airmapAPIKey) {} QString clientID;
QString userName; ///< use anonymous login if empty
QNetworkAccessManager networkManager; QString password;
QString airmapAPIKey;
AirMapLogin login;
}; };
AirMapNetworking(SharedData& networkingData); void setSettings(const Settings& settings);
const Settings& settings() const { return _settings; }
/** void setClient(airmap::qt::Client* client) { _client = client; }
* send a GET request
* @param url
* @param requiresLogin set to true if the user needs to be logged in for the request
*/
void get(QUrl url, bool requiresLogin = false);
/** /**
* send a POST request * Get the current client instance. It can be NULL. If not NULL, it implies
* @param url * there's an API key set.
* @param postData
* @param isJsonData if true, content type is set to JSON, form data otherwise
* @param requiresLogin set to true if the user needs to be logged in for the request
*/ */
void post(QUrl url, const QByteArray& postData, bool isJsonData = false, bool requiresLogin = false); airmap::qt::Client* client() const { return _client; }
bool hasAPIKey() const { return _settings.apiKey != ""; }
const QString& JWTLoginToken() const { return _networkingData.login.JWTToken(); } bool isLoggedIn() const { return _loginToken != ""; }
const AirMapLogin& getLogin() const { return _networkingData.login; } using Callback = std::function<void(const QString& /* login_token */)>;
/** /**
* abort the current request (_requestFinished() or _requestError() will not be emitted) * Do a request that requires user login: if not yet logged in, the request is queued and
* processed after successful login, otherwise it's executed directly.
*/ */
void abort(); void doRequestWithLogin(const Callback& callback);
bool hasAPIKey() const { return _networkingData.airmapAPIKey != ""; } void login();
void logout();
const QString& loginToken() const { return _loginToken; }
signals: signals:
/// signal when the request finished (get or post). All requests are assumed to return JSON. void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
void finished(QJsonParseError parseError, QJsonDocument document);
void error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
private slots:
void _loginSuccess();
void _loginFailure(QNetworkReply::NetworkError networkError, const QString& errorString, const QString& serverErrorMessage);
void _requestFinished(void);
private: private:
SharedData& _networkingData; void _processPendingRequests();
enum class RequestType { bool _isLoginInProgress = false;
None, QString _loginToken; ///< login token: empty when not logged in
GET,
POST airmap::qt::Client* _client = nullptr;
};
struct PendingRequest { Settings _settings;
RequestType type = RequestType::None;
QUrl url;
QByteArray postData;
bool isJsonData;
bool requiresLogin;
};
PendingRequest _pendingRequest;
QNetworkReply* _currentNetworkReply = nullptr; QQueue<Callback> _pendingRequests; ///< pending requests that are processed after a successful login
}; };
@ -165,26 +100,22 @@ class AirMapRestrictionManager : public AirspaceRestrictionProvider
{ {
Q_OBJECT Q_OBJECT
public: public:
AirMapRestrictionManager(AirMapNetworking::SharedData& sharedData); AirMapRestrictionManager(AirMapSharedState& shared);
void setROI(const QGeoCoordinate& center, double radiusMeters) override; void setROI(const QGeoCoordinate& center, double radiusMeters) override;
signals: signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage); void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
private slots:
void _parseAirspaceJson(QJsonParseError parseError, QJsonDocument airspaceDoc);
void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
private: private:
enum class State { enum class State {
Idle, Idle,
RetrieveList,
RetrieveItems, RetrieveItems,
}; };
State _state = State::Idle; State _state = State::Idle;
int _numAwaitingItems = 0; AirMapSharedState& _shared;
AirMapNetworking _networking;
}; };
@ -193,7 +124,7 @@ class AirMapFlightManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AirMapFlightManager(AirMapNetworking::SharedData& sharedData); AirMapFlightManager(AirMapSharedState& shared);
/// Send flight path to AirMap /// Send flight path to AirMap
void createFlight(const QList<MissionItem*>& missionItems); void createFlight(const QList<MissionItem*>& missionItems);
@ -202,30 +133,16 @@ public:
const QString& flightID() const { return _currentFlightId; } const QString& flightID() const { return _currentFlightId; }
void setSitaUavRegistrationId(const QString& sitaUavRegistrationId) {
_sitaUavRegistrationId = sitaUavRegistrationId;
}
void setSitaPilotRegistrationId(const QString& sitaPilotRegistrationId) {
_sitaPilotRegistrationId = sitaPilotRegistrationId;
}
/**
* abort the current operation
*/
void abort();
public slots: public slots:
void endFlight(); void endFlight();
signals: signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage); void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
void flightPermitStatusChanged(); void flightPermitStatusChanged();
private slots: private slots:
void _parseJson(QJsonParseError parseError, QJsonDocument doc); void _pollBriefing();
void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void _sendBriefingRequest();
private: private:
/** /**
@ -234,10 +151,22 @@ private:
void _uploadFlight(); void _uploadFlight();
/** /**
* query the active flights and end the first one (because only a single flight can be active at a time).
*/
void _endFirstFlight();
/**
* implementation of endFlight() * implementation of endFlight()
*/ */
void _endFlight(const QString& flightID); void _endFlight(const QString& flightID);
/**
* check if the briefing response is valid and call _submitPendingFlightPlan() if it is.
*/
void _checkForValidBriefing();
void _submitPendingFlightPlan();
enum class State { enum class State {
Idle, Idle,
GetPilotID, GetPilotID,
@ -261,7 +190,7 @@ private:
Flight _flight; ///< flight pending to be uploaded Flight _flight; ///< flight pending to be uploaded
State _state = State::Idle; State _state = State::Idle;
AirMapNetworking _networking; AirMapSharedState& _shared;
QString _currentFlightId; ///< Flight ID, empty if there is none QString _currentFlightId; ///< Flight ID, empty if there is none
QString _pendingFlightId; ///< current flight ID, not necessarily accepted yet (once accepted, it's equal to _currentFlightId) QString _pendingFlightId; ///< current flight ID, not necessarily accepted yet (once accepted, it's equal to _currentFlightId)
QString _pendingFlightPlan; ///< current flight plan, waiting to be submitted QString _pendingFlightPlan; ///< current flight plan, waiting to be submitted
@ -269,9 +198,6 @@ private:
QString _pilotID; ///< Pilot ID in the form "auth0|abc123" QString _pilotID; ///< Pilot ID in the form "auth0|abc123"
bool _noFlightCreatedYet = true; bool _noFlightCreatedYet = true;
QTimer _pollTimer; ///< timer to poll for approval check QTimer _pollTimer; ///< timer to poll for approval check
QString _sitaUavRegistrationId;
QString _sitaPilotRegistrationId;
}; };
/// class to send telemetry data to AirMap /// class to send telemetry data to AirMap
@ -279,8 +205,8 @@ class AirMapTelemetry : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AirMapTelemetry(AirMapNetworking::SharedData& sharedData); AirMapTelemetry(AirMapSharedState& shared);
virtual ~AirMapTelemetry(); virtual ~AirMapTelemetry() = default;
/** /**
* Setup the connection to start sending telemetry * Setup the connection to start sending telemetry
@ -292,16 +218,11 @@ public:
bool isTelemetryStreaming() const; bool isTelemetryStreaming() const;
signals: signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage); void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
public slots: public slots:
void vehicleMavlinkMessageReceived(const mavlink_message_t& message); void vehicleMavlinkMessageReceived(const mavlink_message_t& message);
private slots:
void _parseJson(QJsonParseError parseError, QJsonDocument doc);
void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void _udpTelemetryHostLookup(QHostInfo info);
private: private:
void _handleGlobalPositionInt(const mavlink_message_t& message); void _handleGlobalPositionInt(const mavlink_message_t& message);
@ -316,49 +237,39 @@ private:
State _state = State::Idle; State _state = State::Idle;
AirMapNetworking _networking; AirMapSharedState& _shared;
QByteArray _key; ///< key for AES encryption, 16 bytes std::string _key; ///< key for AES encryption (16 bytes)
QString _flightID; QString _flightID;
uint32_t _seqNum = 1;
QUdpSocket* _socket = nullptr;
QHostAddress _udpHost;
static constexpr int _udpPort = 32003;
float _lastHdop = 1.f; float _lastHdop = 1.f;
}; };
class AirMapTrafficAlertClient : public QMQTT::Client class AirMapTrafficMonitor : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AirMapTrafficAlertClient(const QString& host, const quint16 port, QObject* parent = NULL) AirMapTrafficMonitor(AirMapSharedState& shared)
: QMQTT::Client(host, port, QSslConfiguration::defaultConfiguration(), true, parent) : _shared(shared)
{ {
connect(this, &AirMapTrafficAlertClient::connected, this, &AirMapTrafficAlertClient::onConnected);
connect(this, &AirMapTrafficAlertClient::subscribed, this, &AirMapTrafficAlertClient::onSubscribed);
connect(this, &AirMapTrafficAlertClient::received, this, &AirMapTrafficAlertClient::onReceived);
connect(this, &AirMapTrafficAlertClient::error, this, &AirMapTrafficAlertClient::onError);
} }
virtual ~AirMapTrafficAlertClient() = default; virtual ~AirMapTrafficMonitor();
void startConnection(const QString& flightID);
void startConnection(const QString& flightID, const QString& password); void stop();
signals: signals:
void trafficUpdate(QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading); void trafficUpdate(QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading);
private slots: private:
void _update(airmap::Traffic::Update::Type type, const std::vector<airmap::Traffic::Update>& update);
void onError(const QMQTT::ClientError error);
void onConnected();
void onSubscribed(const QString& topic);
void onReceived(const QMQTT::Message& message);
private: private:
QString _flightID; QString _flightID;
AirMapSharedState& _shared;
std::shared_ptr<airmap::Traffic::Monitor> _monitor;
std::shared_ptr<airmap::Traffic::Monitor::Subscriber> _subscriber;
}; };
@ -368,7 +279,7 @@ class AirMapManagerPerVehicle : public AirspaceManagerPerVehicle
{ {
Q_OBJECT Q_OBJECT
public: public:
AirMapManagerPerVehicle(AirMapNetworking::SharedData& sharedData, const Vehicle& vehicle, QGCToolbox& toolbox); AirMapManagerPerVehicle(AirMapSharedState& shared, const Vehicle& vehicle, QGCToolbox& toolbox);
virtual ~AirMapManagerPerVehicle() = default; virtual ~AirMapManagerPerVehicle() = default;
@ -388,18 +299,16 @@ signals:
public slots: public slots:
void endFlight() override; void endFlight() override;
void settingsChanged();
protected slots: protected slots:
virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override; virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override;
private slots: private slots:
void _flightPermitStatusChanged(); void _flightPermitStatusChanged();
private: private:
AirMapNetworking _networking; AirMapSharedState& _shared;
AirMapFlightManager _flightManager; AirMapFlightManager _flightManager;
AirMapTelemetry _telemetry; AirMapTelemetry _telemetry;
AirMapTrafficAlertClient _trafficAlerts; AirMapTrafficMonitor _trafficMonitor;
QGCToolbox& _toolbox; QGCToolbox& _toolbox;
}; };
@ -411,7 +320,7 @@ class AirMapManager : public AirspaceManager
public: public:
AirMapManager(QGCApplication* app, QGCToolbox* toolbox); AirMapManager(QGCApplication* app, QGCToolbox* toolbox);
virtual ~AirMapManager() = default; virtual ~AirMapManager();
void setToolbox(QGCToolbox* toolbox) override; void setToolbox(QGCToolbox* toolbox) override;
@ -421,16 +330,16 @@ public:
QString name() const override { return "AirMap"; } QString name() const override { return "AirMap"; }
signals:
void settingsChanged();
private slots: private slots:
void _networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage); void _error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
void _settingsChanged(); void _settingsChanged();
private: private:
AirMapNetworking::SharedData _networkingData; AirMapSharedState _shared;
std::shared_ptr<airmap::qt::Logger> _logger;
std::shared_ptr<airmap::qt::DispatchingLogger> _dispatchingLogger;
}; };
#endif

2
src/protobuf/.gitignore vendored

@ -1,2 +0,0 @@
/airmap_telemetry.pb.*

68
src/protobuf/airmap_telemetry.proto

@ -1,68 +0,0 @@
// protoc -I=. --cpp_out=. airmap_telemetry.proto
syntax = "proto2";
package airmap.telemetry;
message Position {
// UNIX time in Milliseconds
required uint64 timestamp = 1;
// The recorded latitude
// Decimal place requirement: 7 decimal places.
required double latitude = 2;
// The recorded longitude
// Decimal place requirement: 7 decimal places.
required double longitude = 3;
//Altitude above mean sea level (ie. GPS), meters
required float altitude_agl = 4;
// Altitude above ground level, meters
required float altitude_msl = 5;
// Horizontal Dilution of Precision, in meters
required float horizontal_accuracy = 6;
}
message Attitude {
// UNIX time in Milliseconds
required uint64 timestamp = 1;
// Yaw angle measured from True North, { 0 <= x < 360 } degrees
required float yaw = 2;
// Pitch angle, { -180 < x <= 180 } degrees
required float pitch = 3;
// Roll angle, { -180 < x <= 180 } degrees
required float roll = 4;
}
message Speed {
// UNIX time in Milliseconds
required uint64 timestamp = 1;
// Aircraft Speed in the x direction in meters per second using the North-East-Down (N-E-D) coordinate system
required float velocity_x = 2;
// Aircraft Speed in the y direction in meters per second using the North-East-Down (N-E-D) coordinate system
required float velocity_y = 3;
// Aircraft Speed in the z direction in meters per second using the North-East-Down (N-E-D) coordinate system
required float velocity_z = 4;
}
message Barometer {
// UNIX time in Milliseconds
required uint64 timestamp = 1;
// Barometric pressure in hPa
required float pressure = 2;
}

39
src/protobuf/proto_compile.pri

@ -1,39 +0,0 @@
# Qt qmake integration with Google Protocol Buffers compiler protoc
#
# To compile protocol buffers with qt qmake, specify PROTOS variable and
# include this file
#
# Example:
# BITSIZE = $$system(getconf LONG_BIT)
# if (contains(BITSIZE, 64)) {
# LIBS += /usr/lib/x86_64-linux-gnu/libprotobuf.so
# }
# if (contains(BITSIZE, 32)) {
# LIBS += /usr/lib/libprotobuf.so
# }
# PROTOS = a.proto b.proto
# include(protobuf.pri)
#
# By default protoc looks for .proto files (including the imported ones) in
# the current directory where protoc is run. If you need to include additional
# paths specify the PROTOPATH variable
message("Generating protocol buffer classes from .proto files.")
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
protobuf_decl.name = protobuf headers
protobuf_decl.input = PROTOS
protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.pb.h
protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME}
protobuf_decl.variable_out = HEADERS
QMAKE_EXTRA_COMPILERS += protobuf_decl
protobuf_impl.name = protobuf sources
protobuf_impl.input = PROTOS
protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.pb.cc
protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.pb.h
protobuf_impl.commands = $$escape_expand(\n)
protobuf_impl.variable_out = SOURCES
QMAKE_EXTRA_COMPILERS += protobuf_impl
Loading…
Cancel
Save