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 7 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 @@ @@ -4,9 +4,3 @@
[submodule "libs/mavlink/include/mavlink/v2.0"]
path = libs/mavlink/include/mavlink/v2.0
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 { @@ -131,10 +131,6 @@ WindowsBuild {
QMAKE_TARGET_PRODUCT = "$${QGC_APP_NAME}"
}
include($$PWD/libs/thirdParty/qmqtt/src/mqtt/mqtt.pri)
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
#
# Plugin configuration
#
@ -592,7 +588,9 @@ HEADERS += \ @@ -592,7 +588,9 @@ HEADERS += \
src/uas/UASInterface.h \
src/uas/UASMessageHandler.h \
src/AnalyzeView/LogDownloadController.h \
libs/thirdParty/tiny-AES128-C/aes.h \
AIRMAPD_PATH = path_to_airmapd
# Protobuf (AirMap)
# This should be optional. As is, QGC now requires protobuf to be installed.
@ -602,10 +600,19 @@ MacBuild { @@ -602,10 +600,19 @@ MacBuild {
LIBS += \
-L/usr/local/opt/protobuf/lib
}
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 {
HEADERS += \
@ -797,7 +804,6 @@ SOURCES += \ @@ -797,7 +804,6 @@ SOURCES += \
src/uas/UAS.cc \
src/uas/UASMessageHandler.cc \
src/AnalyzeView/LogDownloadController.cc \
libs/thirdParty/tiny-AES128-C/aes.c \
DebugBuild {
SOURCES += \
@ -875,7 +881,6 @@ INCLUDEPATH += \ @@ -875,7 +881,6 @@ INCLUDEPATH += \
src/FirmwarePlugin \
src/Vehicle \
src/VehicleSetup \
libs/thirdParty/tiny-AES128-C \
HEADERS+= \
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 @@ @@ -7,8 +7,7 @@
*
****************************************************************************/
#ifndef AirMapManager_H
#define AirMapManager_H
#pragma once
#include "QGCToolbox.h"
#include "QGCLoggingCategory.h"
@ -17,146 +16,82 @@ @@ -17,146 +16,82 @@
#include "MultiVehicleManager.h"
#include "AirspaceManagement.h"
#include <qmqtt.h>
#include <QGeoCoordinate>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QQueue>
#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:
void loginSuccess();
void loginFailure(QNetworkReply::NetworkError error, const QString& errorString, const QString& serverErrorMessage);
private slots:
void _requestFinished(void);
void _requestError(QNetworkReply::NetworkError code);
private:
void _post(QUrl url, const QByteArray& postData);
#include <airmap/qt/client.h>
#include <airmap/qt/logger.h>
#include <airmap/qt/types.h>
#include <airmap/traffic.h>
QNetworkAccessManager& _networkManager;
bool _isLoginInProgress = false;
QString _JWTToken = ""; ///< JWT login token: empty when not logged in
const QString& _APIKey;
// login credentials
QString _clientID;
QString _userName;
QString _password;
};
Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)
/**
* @class AirMapNetworking
* Handles networking requests (GET & POST), with login if required.
* There can only be one active request per object instance.
* @class AirMapSharedState
* contains state & settings that need to be shared (such as login)
*/
class AirMapNetworking : public QObject
class AirMapSharedState : public QObject
{
Q_OBJECT
public:
struct Settings {
QString apiKey;
struct SharedData {
SharedData() : login(networkManager, airmapAPIKey) {}
QNetworkAccessManager networkManager;
QString airmapAPIKey;
AirMapLogin login;
// login credentials
QString clientID;
QString userName; ///< use anonymous login if empty
QString password;
};
AirMapNetworking(SharedData& networkingData);
void setSettings(const Settings& settings);
const Settings& settings() const { return _settings; }
/**
* 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);
void setClient(airmap::qt::Client* client) { _client = client; }
/**
* send a POST request
* @param url
* @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
* Get the current client instance. It can be NULL. If not NULL, it implies
* there's an API key set.
*/
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:
/// signal when the request finished (get or post). All requests are assumed to return JSON.
void finished(QJsonParseError parseError, QJsonDocument document);
void error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
private slots:
void _loginSuccess();
void _loginFailure(QNetworkReply::NetworkError networkError, const QString& errorString, const QString& serverErrorMessage);
void _requestFinished(void);
private:
SharedData& _networkingData;
void _processPendingRequests();
enum class RequestType {
None,
GET,
POST
};
struct PendingRequest {
RequestType type = RequestType::None;
QUrl url;
QByteArray postData;
bool isJsonData;
bool requiresLogin;
};
PendingRequest _pendingRequest;
bool _isLoginInProgress = false;
QString _loginToken; ///< login token: empty when not logged in
airmap::qt::Client* _client = nullptr;
Settings _settings;
QNetworkReply* _currentNetworkReply = nullptr;
QQueue<Callback> _pendingRequests; ///< pending requests that are processed after a successful login
};
@ -165,26 +100,22 @@ class AirMapRestrictionManager : public AirspaceRestrictionProvider @@ -165,26 +100,22 @@ class AirMapRestrictionManager : public AirspaceRestrictionProvider
{
Q_OBJECT
public:
AirMapRestrictionManager(AirMapNetworking::SharedData& sharedData);
AirMapRestrictionManager(AirMapSharedState& shared);
void setROI(const QGeoCoordinate& center, double radiusMeters) override;
signals:
void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
private slots:
void _parseAirspaceJson(QJsonParseError parseError, QJsonDocument airspaceDoc);
void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
private:
enum class State {
Idle,
RetrieveList,
RetrieveItems,
};
State _state = State::Idle;
int _numAwaitingItems = 0;
AirMapNetworking _networking;
AirMapSharedState& _shared;
};
@ -193,7 +124,7 @@ class AirMapFlightManager : public QObject @@ -193,7 +124,7 @@ class AirMapFlightManager : public QObject
{
Q_OBJECT
public:
AirMapFlightManager(AirMapNetworking::SharedData& sharedData);
AirMapFlightManager(AirMapSharedState& shared);
/// Send flight path to AirMap
void createFlight(const QList<MissionItem*>& missionItems);
@ -202,30 +133,16 @@ public: @@ -202,30 +133,16 @@ public:
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:
void endFlight();
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();
private slots:
void _parseJson(QJsonParseError parseError, QJsonDocument doc);
void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
void _pollBriefing();
void _sendBriefingRequest();
private:
/**
@ -234,10 +151,22 @@ private: @@ -234,10 +151,22 @@ private:
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()
*/
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 {
Idle,
GetPilotID,
@ -261,7 +190,7 @@ private: @@ -261,7 +190,7 @@ private:
Flight _flight; ///< flight pending to be uploaded
State _state = State::Idle;
AirMapNetworking _networking;
AirMapSharedState& _shared;
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 _pendingFlightPlan; ///< current flight plan, waiting to be submitted
@ -269,9 +198,6 @@ private: @@ -269,9 +198,6 @@ private:
QString _pilotID; ///< Pilot ID in the form "auth0|abc123"
bool _noFlightCreatedYet = true;
QTimer _pollTimer; ///< timer to poll for approval check
QString _sitaUavRegistrationId;
QString _sitaPilotRegistrationId;
};
/// class to send telemetry data to AirMap
@ -279,8 +205,8 @@ class AirMapTelemetry : public QObject @@ -279,8 +205,8 @@ class AirMapTelemetry : public QObject
{
Q_OBJECT
public:
AirMapTelemetry(AirMapNetworking::SharedData& sharedData);
virtual ~AirMapTelemetry();
AirMapTelemetry(AirMapSharedState& shared);
virtual ~AirMapTelemetry() = default;
/**
* Setup the connection to start sending telemetry
@ -292,16 +218,11 @@ public: @@ -292,16 +218,11 @@ public:
bool isTelemetryStreaming() const;
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:
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:
void _handleGlobalPositionInt(const mavlink_message_t& message);
@ -316,49 +237,39 @@ private: @@ -316,49 +237,39 @@ private:
State _state = State::Idle;
AirMapNetworking _networking;
QByteArray _key; ///< key for AES encryption, 16 bytes
AirMapSharedState& _shared;
std::string _key; ///< key for AES encryption (16 bytes)
QString _flightID;
uint32_t _seqNum = 1;
QUdpSocket* _socket = nullptr;
QHostAddress _udpHost;
static constexpr int _udpPort = 32003;
float _lastHdop = 1.f;
};
class AirMapTrafficAlertClient : public QMQTT::Client
class AirMapTrafficMonitor : public QObject
{
Q_OBJECT
public:
AirMapTrafficAlertClient(const QString& host, const quint16 port, QObject* parent = NULL)
: QMQTT::Client(host, port, QSslConfiguration::defaultConfiguration(), true, parent)
AirMapTrafficMonitor(AirMapSharedState& shared)
: _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:
void trafficUpdate(QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading);
private slots:
void onError(const QMQTT::ClientError error);
void onConnected();
void onSubscribed(const QString& topic);
void onReceived(const QMQTT::Message& message);
private:
void _update(airmap::Traffic::Update::Type type, const std::vector<airmap::Traffic::Update>& update);
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 @@ -368,7 +279,7 @@ class AirMapManagerPerVehicle : public AirspaceManagerPerVehicle
{
Q_OBJECT
public:
AirMapManagerPerVehicle(AirMapNetworking::SharedData& sharedData, const Vehicle& vehicle, QGCToolbox& toolbox);
AirMapManagerPerVehicle(AirMapSharedState& shared, const Vehicle& vehicle, QGCToolbox& toolbox);
virtual ~AirMapManagerPerVehicle() = default;
@ -388,18 +299,16 @@ signals: @@ -388,18 +299,16 @@ signals:
public slots:
void endFlight() override;
void settingsChanged();
protected slots:
virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override;
private slots:
void _flightPermitStatusChanged();
private:
AirMapNetworking _networking;
AirMapSharedState& _shared;
AirMapFlightManager _flightManager;
AirMapTelemetry _telemetry;
AirMapTrafficAlertClient _trafficAlerts;
AirMapTrafficMonitor _trafficMonitor;
QGCToolbox& _toolbox;
};
@ -411,7 +320,7 @@ class AirMapManager : public AirspaceManager @@ -411,7 +320,7 @@ class AirMapManager : public AirspaceManager
public:
AirMapManager(QGCApplication* app, QGCToolbox* toolbox);
virtual ~AirMapManager() = default;
virtual ~AirMapManager();
void setToolbox(QGCToolbox* toolbox) override;
@ -421,16 +330,16 @@ public: @@ -421,16 +330,16 @@ public:
QString name() const override { return "AirMap"; }
signals:
void settingsChanged();
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();
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 @@ @@ -1,2 +0,0 @@
/airmap_telemetry.pb.*

68
src/protobuf/airmap_telemetry.proto

@ -1,68 +0,0 @@ @@ -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 @@ @@ -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