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

10
#pragma once
11 12 13 14

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

#include <QGeoCoordinate>
#include <QList>
21
#include <QQueue>
22
#include <QTimer>
23

24 25 26
#include <cstdint>
#include <functional>
#include <memory>
27

28 29 30 31
#include <airmap/qt/client.h>
#include <airmap/qt/logger.h>
#include <airmap/qt/types.h>
#include <airmap/traffic.h>
32

33
Q_DECLARE_LOGGING_CATEGORY(AirMapManagerLog)
34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

/**
 * @class LifetimeChecker
 * Base class which helps to check if an object instance still exists.
 * A subclass can take a weak pointer from _instance and then check if the object was deleted.
 * This is used in callbacks that access 'this', but the instance might already be deleted (e.g. vehicle disconnect).
 */
class LifetimeChecker
{
public:
    LifetimeChecker() : _instance(this, [](void*){}) { }
    virtual ~LifetimeChecker() = default;

protected:
    std::shared_ptr<LifetimeChecker> _instance;
};

52
/**
53 54
 * @class AirMapSharedState
 * contains state & settings that need to be shared (such as login)
55
 */
56
class AirMapSharedState : public QObject
57 58 59
{
    Q_OBJECT
public:
60 61
    struct Settings {
        QString apiKey;
62

63 64 65 66
        // login credentials
        QString clientID;
        QString userName; ///< use anonymous login if empty
        QString password;
67 68
    };

69 70
    void setSettings(const Settings& settings);
    const Settings& settings() const { return _settings; }
71

72
    void setClient(airmap::qt::Client* client) { _client = client; }
73 74

    /**
75 76
     * Get the current client instance. It can be NULL. If not NULL, it implies
     * there's an API key set.
77
     */
78 79 80
    airmap::qt::Client* client() const { return _client; }

    bool hasAPIKey() const { return _settings.apiKey != ""; }
81

82
    bool isLoggedIn() const { return _loginToken != ""; }
83

84
    using Callback = std::function<void(const QString& /* login_token */)>;
85

86
    /**
87 88
     * 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.
89
     */
90
    void doRequestWithLogin(const Callback& callback);
91

92 93 94 95 96
    void login();

    void logout();

    const QString& loginToken() const { return _loginToken; }
97

98
signals:
99
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
100 101

private:
102
    void _processPendingRequests();
103

104 105 106 107 108 109
    bool _isLoginInProgress = false;
    QString _loginToken; ///< login token: empty when not logged in

    airmap::qt::Client* _client = nullptr;

    Settings _settings;
110

111
    QQueue<Callback> _pendingRequests; ///< pending requests that are processed after a successful login
112 113 114 115
};


/// class to download polygons from AirMap
116
class AirMapRestrictionManager : public AirspaceRestrictionProvider, public LifetimeChecker
117 118 119
{
    Q_OBJECT
public:
120
    AirMapRestrictionManager(AirMapSharedState& shared);
121

122
    void setROI(const QGeoCoordinate& center, double radiusMeters) override;
123 124

signals:
125 126
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);

127 128
private:

129 130
    static void _addPolygonToList(const airmap::Geometry::Polygon& polygon, QList<PolygonAirspaceRestriction*>& list);

131 132 133 134 135 136
    enum class State {
        Idle,
        RetrieveItems,
    };

    State                   _state = State::Idle;
137
    AirMapSharedState&      _shared;
138 139
};

140

141
/// class to upload a flight
142
class AirMapFlightManager : public QObject, public LifetimeChecker
143 144 145
{
    Q_OBJECT
public:
146
    AirMapFlightManager(AirMapSharedState& shared);
147 148 149 150

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

151 152 153 154
    AirspaceAuthorization::PermitStatus flightPermitStatus() const { return _flightPermitStatus; }

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

155 156 157
public slots:
    void endFlight();

158
signals:
159
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
160 161
    void flightPermitStatusChanged();

162
private slots:
163
    void _pollBriefing();
164

165
private:
166 167 168 169 170 171

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

172 173 174 175 176
    /**
     * query the active flights and end the first one (because only a single flight can be active at a time).
     */
    void _endFirstFlight();

177 178 179 180 181
    /**
     * implementation of endFlight()
     */
    void _endFlight(const QString& flightID);

182 183 184 185 186 187 188
    /**
     * check if the briefing response is valid and call _submitPendingFlightPlan() if it is.
     */
    void _checkForValidBriefing();

    void _submitPendingFlightPlan();

189 190
    enum class State {
        Idle,
191
        GetPilotID,
192
        FlightUpload,
193 194 195
        FlightBrief,
        FlightSubmit,
        FlightPolling, // poll & check for approval
196
        FlightEnd,
197 198 199 200 201 202 203 204 205 206 207
        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;
        }
208
    };
209
    Flight                              _flight; ///< flight pending to be uploaded
210

211
    State                               _state = State::Idle;
212
    AirMapSharedState&                  _shared;
213
    QString                             _currentFlightId; ///< Flight ID, empty if there is none
214 215
    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
216
    AirspaceAuthorization::PermitStatus _flightPermitStatus = AirspaceAuthorization::PermitUnknown;
217 218 219
    QString                             _pilotID; ///< Pilot ID in the form "auth0|abc123"
    bool                                _noFlightCreatedYet = true;
    QTimer                              _pollTimer; ///< timer to poll for approval check
220 221 222
};

/// class to send telemetry data to AirMap
223
class AirMapTelemetry : public QObject, public LifetimeChecker
224 225 226
{
    Q_OBJECT
public:
227 228
    AirMapTelemetry(AirMapSharedState& shared);
    virtual ~AirMapTelemetry() = default;
229 230 231 232 233 234 235 236

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

    void stopTelemetryStream();

237 238
    bool isTelemetryStreaming() const;

239
signals:
240
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
241 242 243 244 245 246 247

public slots:
    void vehicleMavlinkMessageReceived(const mavlink_message_t& message);

private:

    void _handleGlobalPositionInt(const mavlink_message_t& message);
248 249
    void _handleGPSRawInt(const mavlink_message_t& message);

250 251 252 253 254 255 256
    enum class State {
        Idle,
        StartCommunication,
        EndCommunication,
        Streaming,
    };

257
    State                   _state = State::Idle;
258

259 260
    AirMapSharedState&      _shared;
    std::string              _key; ///< key for AES encryption (16 bytes)
261
    QString                 _flightID;
262 263

    float                   _lastHdop = 1.f;
264 265
};

266

267
class AirMapTrafficMonitor : public QObject, public LifetimeChecker
268 269 270
{
    Q_OBJECT
public:
271 272
    AirMapTrafficMonitor(AirMapSharedState& shared)
    : _shared(shared)
273 274
    {
    }
275 276 277
    virtual ~AirMapTrafficMonitor();

    void startConnection(const QString& flightID);
278

279
    void stop();
280 281

signals:
282
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
283 284
    void trafficUpdate(QString traffic_id, QString vehicle_id, QGeoCoordinate location, float heading);

285 286
private:
    void _update(airmap::Traffic::Update::Type type, const std::vector<airmap::Traffic::Update>& update);
287 288

private:
289 290 291 292
    QString                                               _flightID;
    AirMapSharedState&                                    _shared;
    std::shared_ptr<airmap::Traffic::Monitor>             _monitor;
    std::shared_ptr<airmap::Traffic::Monitor::Subscriber> _subscriber;
293 294 295 296
};



297 298
/// AirMap per vehicle management class.
class AirMapManagerPerVehicle : public AirspaceManagerPerVehicle
299 300 301
{
    Q_OBJECT
public:
302
    AirMapManagerPerVehicle(AirMapSharedState& shared, const Vehicle& vehicle, QGCToolbox& toolbox);
303
    virtual ~AirMapManagerPerVehicle() = default;
304 305


306
    void createFlight(const QList<MissionItem*>& missionItems) override;
307

308
    AirspaceAuthorization::PermitStatus flightPermitStatus() const override;
309

310
    void startTelemetryStream() override;
311

312
    void stopTelemetryStream() override;
313

314
    bool isTelemetryStreaming() const override;
315 316

signals:
317
    void error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
318

319 320
public slots:
    void endFlight() override;
321

322 323 324
protected slots:
    virtual void vehicleMavlinkMessageReceived(const mavlink_message_t& message) override;
private slots:
325
    void _flightPermitStatusChanged();
326
private:
327
    AirMapSharedState&           _shared;
328

329
    AirMapFlightManager          _flightManager;
330
    AirMapTelemetry              _telemetry;
331
    AirMapTrafficMonitor         _trafficMonitor;
332

333 334 335
    QGCToolbox&                  _toolbox;
};

336

337 338 339 340 341 342
class AirMapManager : public AirspaceManager
{
    Q_OBJECT
    
public:
    AirMapManager(QGCApplication* app, QGCToolbox* toolbox);
343
    virtual ~AirMapManager();
344 345 346 347 348 349 350 351

    void setToolbox(QGCToolbox* toolbox) override;

    AirspaceManagerPerVehicle* instantiateVehicle(const Vehicle& vehicle) override;

    AirspaceRestrictionProvider* instantiateRestrictionProvider() override;

    QString name() const override { return "AirMap"; }
352

353 354
    void requestWeatherUpdate(const QGeoCoordinate& coordinate) override;

355
private slots:
356
    void _error(const QString& what, const QString& airmapdMessage, const QString& airmapdDetails);
357 358 359 360

    void _settingsChanged();
private:

361 362 363 364
    AirMapSharedState _shared;

    std::shared_ptr<airmap::qt::Logger> _logger;
    std::shared_ptr<airmap::qt::DispatchingLogger> _dispatchingLogger;
365 366
};

367