QGCApplication.h 12.3 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

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

#include <QApplication>
13
#include <QTimer>
Don Gagne's avatar
Don Gagne committed
14
#include <QQmlApplicationEngine>
15
#include <QElapsedTimer>
16 17 18 19 20 21 22
#include <QMap>
#include <QSet>
#include <QMetaMethod>
#include <QMetaObject>
#include <private/qcoreapplication_p.h>
#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"
31
#include "AudioOutput.h"
32 33
#include "UASMessageHandler.h"
#include "FactSystem.h"
34
#include "GPSRTKFactGroup.h"
Don Gagne's avatar
Don Gagne committed
35

36
#ifdef QGC_RTLAB_ENABLED
37 38
#include "OpalLink.h"
#endif
39

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

45 46 47 48 49 50 51 52 53 54 55 56
/**
 * @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
**/
57
class QGCApplication : public QApplication
pixhawk's avatar
pixhawk committed
58
{
59
    Q_OBJECT
pixhawk's avatar
pixhawk committed
60
public:
DonLakeFlyer's avatar
DonLakeFlyer committed
61
    QGCApplication(int &argc, char* argv[], bool unitTesting);
Don Gagne's avatar
Don Gagne committed
62
    ~QGCApplication();
dogmaphobic's avatar
dogmaphobic committed
63

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

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

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

73 74 75
    /// @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
76
    /// Used to report a missing Parameter. Warning will be displayed to user. Method may be called
77
    /// multiple times.
Don Gagne's avatar
Don Gagne committed
78
    void reportMissingParameter(int componentId, const QString& name);
79

80 81 82 83 84
    /// 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());
85

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

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

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

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

98 99
    FactGroup* gpsRtkFactGroup(void)  { return _gpsRtkFactGroup; }

100 101 102
    static QString cachedParameterMetaDataFile(void);
    static QString cachedAirframeMetaDataFile(void);

103
    void            setLanguage();
104
    QQuickItem*     mainRootWindow();
105

106 107
    uint64_t        msecsSinceBoot(void) { return _msecsElapsedTime.elapsed(); }

108 109 110
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
111

112 113
    /// 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
114

115 116
    /// 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
117

118
    void showSetupView();
Don Gagne's avatar
Don Gagne committed
119

120
    void qmlAttemptWindowClose();
Don Gagne's avatar
Don Gagne committed
121

122 123 124 125
    /// Save the specified telemetry Log
    void saveTelemetryLogOnMainThread(QString tempLogfile);

    /// Check that the telemetry save path is set correctly
126
    void checkTelemetrySavePathOnMainThread();
127

128 129 130
    /// Get current language
    const QLocale getCurrentLanguage() { return _locale; }

131 132 133
signals:
    /// This is connected to MAVLinkProtocol::checkForLostLogFiles. We signal this to ourselves to call the slot
    /// on the MAVLinkProtocol thread;
134 135 136
    void checkForLostLogFiles   ();

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

138
public:
139
    // Although public, these methods are internal and should only be called by UnitTest code
dogmaphobic's avatar
dogmaphobic committed
140

141 142
    /// @brief Perform initialize which is common to both normal application running and unit tests.
    ///         Although public should only be called by main.
143
    void _initCommon();
144

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

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

153
    static QGCApplication*  _app;   ///< Our own singleton. Should be reference directly by qgcApp
dogmaphobic's avatar
dogmaphobic committed
154

155 156
    bool    isErrorState()  { return _error; }

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

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

163 164
    bool _checkTelemetrySavePath(bool useMessageBox);

Don Gagne's avatar
Don Gagne committed
165
private slots:
166
    void _missingParamsDisplay          (void);
Don Gagne's avatar
Don Gagne committed
167
    void _currentVersionDownloadFinished(QString remoteFile, QString localFile);
168 169 170 171 172 173 174
    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
175

pixhawk's avatar
pixhawk committed
176
private:
177 178
    QObject*    _rootQmlObject          ();
    void        _checkForNewVersion     ();
179
    void        _exitWithError          (QString errorMessage);
Don Gagne's avatar
Don Gagne committed
180

dogmaphobic's avatar
dogmaphobic committed
181

182 183 184 185
    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
186

187 188 189 190 191 192 193 194 195 196
    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;
197
    QQuickItem*         _mainRootWindow         = nullptr;
198 199
    bool                _bluetoothAvailable     = false;
    QTranslator         _QGCTranslator;
200
    QTranslator         _QGCTranslatorQt;
201
    QLocale             _locale;
202
    bool                _error                  = false;
203
    QElapsedTimer       _msecsElapsedTime;
dogmaphobic's avatar
dogmaphobic committed
204

205 206
    QList<QPair<QString /* title */, QString /* message */>> _delayedAppMessages;

Don Gagne's avatar
Don Gagne committed
207 208 209
    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
210 211
    /// Unit Test have access to creating and destroying singletons
    friend class UnitTest;
Gus Grubba's avatar
Gus Grubba committed
212

213 214 215 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
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
306 307
};

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