QGCApplication.h 12.4 KB
Newer Older
1
2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4
5
6
7
8
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/
dogmaphobic's avatar
dogmaphobic committed
9

DonLakeFlyer's avatar
   
DonLakeFlyer committed
10
#pragma once
pixhawk's avatar
pixhawk committed
11
12

#include <QApplication>
13
#include <QTimer>
Don Gagne's avatar
   
Don Gagne committed
14
#include <QElapsedTimer>
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
15
16
17
18
#include <QMap>
#include <QSet>
#include <QMetaMethod>
#include <QMetaObject>
Don Gagne's avatar
   
Don Gagne committed
19
20

// These private headers are require to implement the signal compress support below
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
21
22
#include <private/qthread_p.h>
#include <private/qobject_p.h>
pixhawk's avatar
pixhawk committed
23

Don Gagne's avatar
Don Gagne committed
24
#include "LinkConfiguration.h"
25
26
27
28
29
30
#include "LinkManager.h"
#include "MAVLinkProtocol.h"
#include "FlightMapSettings.h"
#include "FirmwarePluginManager.h"
#include "MultiVehicleManager.h"
#include "JoystickManager.h"
DonLakeFlyer's avatar
DonLakeFlyer committed
31
#include "AudioOutput.h"
32
33
#include "UASMessageHandler.h"
#include "FactSystem.h"
Don Gagne's avatar
   
Don Gagne committed
34
#include "GPSRTKFactGroup.h"
Don Gagne's avatar
Don Gagne committed
35

36
#ifdef QGC_RTLAB_ENABLED
37
38
#include "OpalLink.h"
#endif
Don Gagne's avatar
Don Gagne committed
39

Don Gagne's avatar
Don Gagne committed
40
// Work around circular header includes
41
class QQmlApplicationEngine;
Don Gagne's avatar
Don Gagne committed
42
class QGCSingleton;
43
class QGCToolbox;
Don Gagne's avatar
   
Don Gagne committed
44
class QGCFileDownload;
Don Gagne's avatar
Don Gagne committed
45

46
47
48
49
50
51
52
53
54
55
56
57
/**
 * @brief The main application and management class.
 *
 * This class is started by the main method and provides
 * the central management unit of the groundstation application.
 *
 * Needs QApplication base to support QtCharts module. This way
 * we avoid application crashing on 5.12 when using the module.
 *
 * Note: `lastWindowClosed` will be sent by MessageBox popups and other
 * dialogs, that are spawned in QML, when they are closed
**/
Gus Grubba's avatar
Gus Grubba committed
58
class QGCApplication : public QApplication
pixhawk's avatar
pixhawk committed
59
{
60
    Q_OBJECT
pixhawk's avatar
pixhawk committed
61
public:
DonLakeFlyer's avatar
DonLakeFlyer committed
62
    QGCApplication(int &argc, char* argv[], bool unitTesting);
Don Gagne's avatar
Don Gagne committed
63
    ~QGCApplication();
dogmaphobic's avatar
dogmaphobic committed
64

Don Gagne's avatar
Don Gagne committed
65
66
    /// @brief Sets the persistent flag to delete all settings the next time QGroundControl is started.
    void deleteAllSettingsNextBoot(void);
dogmaphobic's avatar
dogmaphobic committed
67

Don Gagne's avatar
Don Gagne committed
68
69
    /// @brief Clears the persistent flag to delete all settings the next time QGroundControl is started.
    void clearDeleteAllSettingsNextBoot(void);
dogmaphobic's avatar
dogmaphobic committed
70

71
    /// @brief Returns true if unit tests are being run
Don Gagne's avatar
Don Gagne committed
72
    bool runningUnitTests(void) { return _runningUnitTests; }
dogmaphobic's avatar
dogmaphobic committed
73

74
75
76
    /// @brief Returns true if Qt debug output should be logged to a file
    bool logOutput(void) { return _logOutput; }

Don Gagne's avatar
Don Gagne committed
77
    /// Used to report a missing Parameter. Warning will be displayed to user. Method may be called
78
    /// multiple times.
Don Gagne's avatar
Don Gagne committed
79
    void reportMissingParameter(int componentId, const QString& name);
80

DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
81
82
83
84
85
    /// Show non-modal vehicle message to the user
    Q_SLOT void showVehicleMessage(const QString& message);

    /// Show modal application message to the user
    Q_SLOT void showAppMessage(const QString& message, const QString& title = QString());
Don Gagne's avatar
Don Gagne committed
86

dogmaphobic's avatar
dogmaphobic committed
87
    /// @return true: Fake ui into showing mobile interface
88
    bool fakeMobile(void) const { return _fakeMobile; }
dogmaphobic's avatar
dogmaphobic committed
89

90
91
    // Still working on getting rid of this and using dependency injection instead for everything
    QGCToolbox* toolbox(void) { return _toolbox; }
dogmaphobic's avatar
dogmaphobic committed
92
93
94
95

    /// Do we have Bluetooth Support?
    bool isBluetoothAvailable() { return _bluetoothAvailable; }

96
97
98
    /// Is Internet available?
    bool isInternetAvailable();

Don Gagne's avatar
   
Don Gagne committed
99
100
    FactGroup* gpsRtkFactGroup(void)  { return _gpsRtkFactGroup; }

DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
101
102
    QTranslator& qgcTranslator(void) { return _QGCTranslator; }

Don Gagne's avatar
   
Don Gagne committed
103
104
105
    static QString cachedParameterMetaDataFile(void);
    static QString cachedAirframeMetaDataFile(void);

106
    void            setLanguage();
107
    QQuickItem*     mainRootWindow();
108

Don Gagne's avatar
   
Don Gagne committed
109
110
    uint64_t        msecsSinceBoot(void) { return _msecsElapsedTime.elapsed(); }

111
112
113
public slots:
    /// You can connect to this slot to show an information message box from a different thread.
    void informationMessageBoxOnMainThread(const QString& title, const QString& msg);
dogmaphobic's avatar
dogmaphobic committed
114

115
116
    /// You can connect to this slot to show a warning message box from a different thread.
    void warningMessageBoxOnMainThread(const QString& title, const QString& msg);
dogmaphobic's avatar
dogmaphobic committed
117

118
119
    /// You can connect to this slot to show a critical message box from a different thread.
    void criticalMessageBoxOnMainThread(const QString& title, const QString& msg);
dogmaphobic's avatar
dogmaphobic committed
120

121
    void showSetupView();
Don Gagne's avatar
Don Gagne committed
122

123
    void qmlAttemptWindowClose();
Don Gagne's avatar
Don Gagne committed
124

125
126
127
128
    /// Save the specified telemetry Log
    void saveTelemetryLogOnMainThread(QString tempLogfile);

    /// Check that the telemetry save path is set correctly
129
    void checkTelemetrySavePathOnMainThread();
130

Gus Grubba's avatar
Gus Grubba committed
131
132
133
    /// Get current language
    const QLocale getCurrentLanguage() { return _locale; }

134
135
136
signals:
    /// This is connected to MAVLinkProtocol::checkForLostLogFiles. We signal this to ourselves to call the slot
    /// on the MAVLinkProtocol thread;
Gus Grubba's avatar
Gus Grubba committed
137
138
139
    void checkForLostLogFiles   ();

    void languageChanged        (const QLocale locale);
dogmaphobic's avatar
dogmaphobic committed
140

Don Gagne's avatar
Don Gagne committed
141
public:
142
    // Although public, these methods are internal and should only be called by UnitTest code
dogmaphobic's avatar
dogmaphobic committed
143

Don Gagne's avatar
Don Gagne committed
144
145
    /// @brief Perform initialize which is common to both normal application running and unit tests.
    ///         Although public should only be called by main.
146
    void _initCommon();
Don Gagne's avatar
Don Gagne committed
147

Patrick José Pereira's avatar
Patrick José Pereira committed
148
    /// @brief Initialize the application for normal application boot. Or in other words we are not going to run
Don Gagne's avatar
Don Gagne committed
149
    ///         unit tests. Although public should only be called by main.
150
    bool _initForNormalAppBoot();
dogmaphobic's avatar
dogmaphobic committed
151

Patrick José Pereira's avatar
Patrick José Pereira committed
152
    /// @brief Initialize the application for normal application boot. Or in other words we are not going to run
Don Gagne's avatar
Don Gagne committed
153
    ///         unit tests. Although public should only be called by main.
154
    bool _initForUnitTests();
Don Gagne's avatar
Don Gagne committed
155

Don Gagne's avatar
Don Gagne committed
156
    static QGCApplication*  _app;   ///< Our own singleton. Should be reference directly by qgcApp
dogmaphobic's avatar
dogmaphobic committed
157

158
159
    bool    isErrorState()  { return _error; }

Don Gagne's avatar
Don Gagne committed
160
161
162
163
public:
    // Although public, these methods are internal and should only be called by UnitTest code

    /// Shutdown the application object
164
    void _shutdown();
Don Gagne's avatar
Don Gagne committed
165

166
167
    bool _checkTelemetrySavePath(bool useMessageBox);

Don Gagne's avatar
Don Gagne committed
168
private slots:
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
169
    void _missingParamsDisplay          (void);
Don Gagne's avatar
   
Don Gagne committed
170
    void _currentVersionDownloadFinished(QString remoteFile, QString localFile);
DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
171
172
173
174
175
176
177
    void _currentVersionDownloadError   (QString errorMsg);
    bool _parseVersionText              (const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion);
    void _onGPSConnect                  (void);
    void _onGPSDisconnect               (void);
    void _gpsSurveyInStatus             (float duration, float accuracyMM,  double latitude, double longitude, float altitude, bool valid, bool active);
    void _gpsNumSatellites              (int numSatellites);
    void _showDelayedAppMessages        (void);
dogmaphobic's avatar
dogmaphobic committed
178

pixhawk's avatar
pixhawk committed
179
private:
180
181
    QObject*    _rootQmlObject          ();
    void        _checkForNewVersion     ();
182
    void        _exitWithError          (QString errorMessage);
Don Gagne's avatar
Don Gagne committed
183

dogmaphobic's avatar
dogmaphobic committed
184

Don Gagne's avatar
   
Don Gagne committed
185
186
187
188
    bool                        _runningUnitTests;                                  ///< true: running unit tests, false: normal app
    static const int            _missingParamsDelayedDisplayTimerTimeout = 1000;    ///< Timeout to wait for next missing fact to come in before display
    QTimer                      _missingParamsDelayedDisplayTimer;                  ///< Timer use to delay missing fact display
    QList<QPair<int,QString>>   _missingParams;                                     ///< List of missing parameter component id:name
189

190
191
192
193
194
195
196
197
198
199
    QQmlApplicationEngine* _qmlAppEngine        = nullptr;
    bool                _logOutput              = false;                    ///< true: Log Qt debug output to file
    bool				_fakeMobile             = false;                    ///< true: Fake ui into displaying mobile interface
    bool                _settingsUpgraded       = false;                    ///< true: Settings format has been upgrade to new version
    int                 _majorVersion           = 0;
    int                 _minorVersion           = 0;
    int                 _buildVersion           = 0;
    QGCFileDownload*    _currentVersionDownload = nullptr;
    GPSRTKFactGroup*    _gpsRtkFactGroup        = nullptr;
    QGCToolbox*         _toolbox                = nullptr;
200
    QQuickItem*         _mainRootWindow         = nullptr;
201
202
    bool                _bluetoothAvailable     = false;
    QTranslator         _QGCTranslator;
203
    QTranslator         _QGCTranslatorQt;
Gus Grubba's avatar
Gus Grubba committed
204
    QLocale             _locale;
205
    bool                _error                  = false;
Don Gagne's avatar
   
Don Gagne committed
206
    QElapsedTimer       _msecsElapsedTime;
dogmaphobic's avatar
dogmaphobic committed
207

DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
208
209
    QList<QPair<QString /* title */, QString /* message */>> _delayedAppMessages;

Don Gagne's avatar
Don Gagne committed
210
211
212
    static const char* _settingsVersionKey;             ///< Settings key which hold settings version
    static const char* _deleteAllSettingsKey;           ///< If this settings key is set on boot, all settings will be deleted

Don Gagne's avatar
Don Gagne committed
213
214
    /// Unit Test have access to creating and destroying singletons
    friend class UnitTest;
Gus Grubba's avatar
Gus Grubba committed
215

DoinLakeFlyer's avatar
   
DoinLakeFlyer committed
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
private:
    /*! Keeps a list of singal indices for one or more meatobject classes.
     * The indices are signal indices as given by QMetaCallEvent.signalId.
     * On Qt 5, those do *not* match QMetaObject::methodIndex since they
     * exclude non-signal methods. */
    class SignalList {
        Q_DISABLE_COPY(SignalList)
        typedef QMap<const QMetaObject *, QSet<int> > T;
        T m_data;
        /*! Returns a signal index that is can be compared to QMetaCallEvent.signalId. */
        static int signalIndex(const QMetaMethod & method) {
            Q_ASSERT(method.methodType() == QMetaMethod::Signal);
    #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
            int index = -1;
            const QMetaObject * mobj = method.enclosingMetaObject();
            for (int i = 0; i <= method.methodIndex(); ++i) {
                if (mobj->method(i).methodType() != QMetaMethod::Signal) continue;
                ++ index;
            }
            return index;
    #else
            return method.methodIndex();
    #endif
        }
    public:
        SignalList() {}
        void add(const QMetaMethod & method) {
            m_data[method.enclosingMetaObject()].insert(signalIndex(method));
        }
        void remove(const QMetaMethod & method) {
            T::iterator it = m_data.find(method.enclosingMetaObject());
            if (it != m_data.end()) {
                it->remove(signalIndex(method));
                if (it->empty()) m_data.erase(it);
            }
        }
        bool contains(const QMetaObject * metaObject, int signalId) {
            T::const_iterator it = m_data.find(metaObject);
            return it != m_data.end() && it.value().contains(signalId);
        }
    };

    SignalList m_compressedSignals;

public:
    void addCompressedSignal(const QMetaMethod & method) { m_compressedSignals.add(method); }
    void removeCompressedSignal(const QMetaMethod & method) { m_compressedSignals.remove(method); }

private:
    struct EventHelper : private QEvent {
        static void clearPostedFlag(QEvent * ev) {
            (&static_cast<EventHelper*>(ev)->t)[1] &= ~0x8001; // Hack to clear QEvent::posted
        }
    };

    bool compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents) {
        if (event->type() != QEvent::MetaCall)
            return QApplication::compressEvent(event, receiver, postedEvents);

        QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(event);

        if (mce->sender() && !m_compressedSignals.contains(mce->sender()->metaObject(), mce->signalId())) {
            return false;
        }

        for (QPostEventList::iterator it = postedEvents->begin(); it != postedEvents->end(); ++it) {
            QPostEvent &cur = *it;
            if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type())
                continue;
            QMetaCallEvent *cur_mce = static_cast<QMetaCallEvent*>(cur.event);
            if (cur_mce->sender() != mce->sender() || cur_mce->signalId() != mce->signalId() ||
                    cur_mce->id() != mce->id())
                continue;
            if (true) {
              /* Keep The Newest Call */
              // We can't merely qSwap the existing posted event with the new one, since QEvent
              // keeps track of whether it has been posted. Deletion of a formerly posted event
              // takes the posted event list mutex and does a useless search of the posted event
              // list upon deletion. We thus clear the QEvent::posted flag before deletion.
              EventHelper::clearPostedFlag(cur.event);
              delete cur.event;
              cur.event = event;
            } else {
              /* Keep the Oldest Call */
              delete event;
            }
            return true;
        }
        return false;
    }



pixhawk's avatar
pixhawk committed
309
310
};

Don Gagne's avatar
Don Gagne committed
311
312
/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void);