AirMapManager.h 12.8 KB
Newer Older
1 2
/****************************************************************************
 *
3
 *   (c) 2017 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9 10 11 12 13 14 15
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

#ifndef AirMapManager_H
#define AirMapManager_H

#include "QGCToolbox.h"
#include "QGCLoggingCategory.h"
#include "QmlObjectListModel.h"
16
#include "MissionItem.h"
17
#include "MultiVehicleManager.h"
18
#include "AirspaceManagement.h"
19

20 21
#include <qmqtt.h>

22 23 24 25 26
#include <QGeoCoordinate>
#include <QList>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTimer>
27 28 29 30 31
#include <QUdpSocket>
#include <QHostInfo>
#include <QHostAddress>

#include <stdint.h>
32 33 34 35

Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)


36

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
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 != ""; }

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);

    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;
};

85 86 87 88 89
/**
 * @class AirMapNetworking
 * Handles networking requests (GET & POST), with login if required.
 * There can only be one active request per object instance.
 */
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
class AirMapNetworking : public QObject
{
    Q_OBJECT
public:

    struct SharedData {
        SharedData() : login(networkManager, airmapAPIKey) {}

        QNetworkAccessManager networkManager;
        QString airmapAPIKey;

        AirMapLogin login;
    };

    AirMapNetworking(SharedData& networkingData);

    /**
     * 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
     * @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
     */
    void post(QUrl url, const QByteArray& postData, bool isJsonData = false, bool requiresLogin = false);

    const QString& JWTLoginToken() const { return _networkingData.login.JWTToken(); }

    const AirMapLogin& getLogin() const { return _networkingData.login; }

126 127 128 129 130
    /**
     * abort the current request (_requestFinished() or _requestError() will not be emitted)
     */
    void abort();

131 132
    bool hasAPIKey() const { return _networkingData.airmapAPIKey != ""; }

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
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);

private slots:
    void _loginSuccess();
    void _loginFailure(QNetworkReply::NetworkError networkError, const QString& errorString, const QString& serverErrorMessage);
    void _requestFinished(void);
private:
    SharedData& _networkingData;

    enum class RequestType {
        None,
        GET,
        POST
    };
    struct PendingRequest {
        RequestType type = RequestType::None;
        QUrl url;
        QByteArray postData;
        bool isJsonData;
        bool requiresLogin;
    };
    PendingRequest _pendingRequest;
158 159

    QNetworkReply* _currentNetworkReply = nullptr;
160 161 162 163
};


/// class to download polygons from AirMap
164
class AirMapRestrictionManager : public AirspaceRestrictionProvider
165 166 167
{
    Q_OBJECT
public:
168
    AirMapRestrictionManager(AirMapNetworking::SharedData& sharedData);
169

170
    void setROI(const QGeoCoordinate& center, double radiusMeters) override;
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

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);
private:

    enum class State {
        Idle,
        RetrieveList,
        RetrieveItems,
    };

    State                   _state = State::Idle;
    int                     _numAwaitingItems = 0;
    AirMapNetworking        _networking;
};

190

191 192 193 194 195 196 197 198 199 200
/// class to upload a flight
class AirMapFlightManager : public QObject
{
    Q_OBJECT
public:
    AirMapFlightManager(AirMapNetworking::SharedData& sharedData);

    /// Send flight path to AirMap
    void createFlight(const QList<MissionItem*>& missionItems);

201 202 203 204
    AirspaceAuthorization::PermitStatus flightPermitStatus() const { return _flightPermitStatus; }

    const QString& flightID() const { return _currentFlightId; }

205 206 207 208 209 210 211
    void setSitaUavRegistrationId(const QString& sitaUavRegistrationId) {
        _sitaUavRegistrationId = sitaUavRegistrationId;
    }
    void setSitaPilotRegistrationId(const QString& sitaPilotRegistrationId) {
        _sitaPilotRegistrationId = sitaPilotRegistrationId;
    }

212 213 214 215 216
    /**
     * abort the current operation
     */
    void abort();

217 218 219
public slots:
    void endFlight();

220 221
signals:
    void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
222 223
    void flightPermitStatusChanged();

224 225 226
private slots:
    void _parseJson(QJsonParseError parseError, QJsonDocument doc);
    void _error(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
227 228

    void _sendBriefingRequest();
229
private:
230 231 232 233 234 235 236 237 238 239 240

    /**
     * upload flight stored in _flight
     */
    void _uploadFlight();

    /**
     * implementation of endFlight()
     */
    void _endFlight(const QString& flightID);

241 242
    enum class State {
        Idle,
243
        GetPilotID,
244
        FlightUpload,
245 246 247
        FlightBrief,
        FlightSubmit,
        FlightPolling, // poll & check for approval
248
        FlightEnd,
249 250 251 252 253 254 255 256 257 258 259
        EndFirstFlight, // get a list of open flights & end the first one (because there can only be 1 active at a time)
    };
    struct Flight {
        QList<QGeoCoordinate> coords;
        QGeoCoordinate takeoffCoord;
        float maxAltitude = 0;

        void reset() {
            coords.clear();
            maxAltitude = 0;
        }
260
    };
261
    Flight                              _flight; ///< flight pending to be uploaded
262

263 264 265
    State                               _state = State::Idle;
    AirMapNetworking                    _networking;
    QString                             _currentFlightId; ///< Flight ID, empty if there is none
266 267
    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
268
    AirspaceAuthorization::PermitStatus _flightPermitStatus = AirspaceAuthorization::PermitUnknown;
269 270 271
    QString                             _pilotID; ///< Pilot ID in the form "auth0|abc123"
    bool                                _noFlightCreatedYet = true;
    QTimer                              _pollTimer; ///< timer to poll for approval check
272 273 274

    QString                             _sitaUavRegistrationId;
    QString                             _sitaPilotRegistrationId;
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
};

/// class to send telemetry data to AirMap
class AirMapTelemetry : public QObject
{
    Q_OBJECT
public:
    AirMapTelemetry(AirMapNetworking::SharedData& sharedData);
    virtual ~AirMapTelemetry();

    /**
     * Setup the connection to start sending telemetry
     */
    void startTelemetryStream(const QString& flightID);

    void stopTelemetryStream();

292 293
    bool isTelemetryStreaming() const;

294 295 296 297 298 299 300 301 302 303 304 305 306 307
signals:
    void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);

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);
308 309
    void _handleGPSRawInt(const mavlink_message_t& message);

310 311 312 313 314 315 316
    enum class State {
        Idle,
        StartCommunication,
        EndCommunication,
        Streaming,
    };

317
    State                   _state = State::Idle;
318

319
    AirMapNetworking        _networking;
320 321 322 323 324
    QByteArray              _key; ///< key for AES encryption, 16 bytes
    QString                 _flightID;
    uint32_t                _seqNum = 1;
    QUdpSocket*             _socket = nullptr;
    QHostAddress            _udpHost;
325
    static constexpr int    _udpPort = 32003;
326 327

    float                   _lastHdop = 1.f;
328 329
};

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

class AirMapTrafficAlertClient : public QMQTT::Client
{
    Q_OBJECT
public:
    AirMapTrafficAlertClient(const QString& host, const quint16 port, QObject* parent = NULL)
        : QMQTT::Client(host, port, QSslConfiguration::defaultConfiguration(), true, parent)
    {
        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;

    void startConnection(const QString& flightID, const QString& password);

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:
    QString _flightID;
};



366 367
/// AirMap per vehicle management class.
class AirMapManagerPerVehicle : public AirspaceManagerPerVehicle
368 369 370
{
    Q_OBJECT
public:
371 372
    AirMapManagerPerVehicle(AirMapNetworking::SharedData& sharedData, const Vehicle& vehicle, QGCToolbox& toolbox);
    virtual ~AirMapManagerPerVehicle() = default;
373 374


375
    void createFlight(const QList<MissionItem*>& missionItems) override;
376

377
    AirspaceAuthorization::PermitStatus flightPermitStatus() const override;
378

379
    void startTelemetryStream() override;
380

381
    void stopTelemetryStream() override;
382

383
    bool isTelemetryStreaming() const override;
384 385

signals:
386
    void networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);
387

388 389
public slots:
    void endFlight() override;
390

391
    void settingsChanged();
392

393 394 395
protected slots:
    virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override;
private slots:
396
    void _flightPermitStatusChanged();
397
private:
398
    AirMapNetworking             _networking;
399

400
    AirMapFlightManager          _flightManager;
401
    AirMapTelemetry              _telemetry;
402
    AirMapTrafficAlertClient     _trafficAlerts;
403

404 405 406
    QGCToolbox&                  _toolbox;
};

407

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
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"; }
423

424 425 426 427 428 429 430 431 432 433
signals:
    void settingsChanged();

private slots:
    void _networkError(QNetworkReply::NetworkError code, const QString& errorString, const QString& serverErrorMessage);

    void _settingsChanged();
private:

    AirMapNetworking::SharedData _networkingData;
434 435
};

436
#endif