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

pixhawk's avatar
pixhawk committed
10 11 12

/**
 * @file
Don Gagne's avatar
Don Gagne committed
13
 *   @brief Implementation of class QGCApplication
pixhawk's avatar
pixhawk committed
14 15 16 17 18 19 20 21 22 23 24 25
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QFile>
#include <QFlags>
#include <QPixmap>
#include <QDesktopWidget>
#include <QPainter>
#include <QStyleFactory>
#include <QAction>
26
#include <QStringListModel>
Don Gagne's avatar
Don Gagne committed
27
#include <QRegularExpression>
Gus Grubba's avatar
Gus Grubba committed
28
#include <QFontDatabase>
29 30 31 32 33
#ifdef Q_OS_LINUX
#ifndef __mobile__
    #include <QMessageBox>
#endif
#endif
pixhawk's avatar
pixhawk committed
34

dogmaphobic's avatar
dogmaphobic committed
35 36 37 38
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

39 40
#include <QDebug>

41
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
42

43
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
44
#include "QGCApplication.h"
45
#include "AudioOutput.h"
46
#include "CmdLineOptParser.h"
47 48
#include "UDPLink.h"
#include "LinkManager.h"
49
#include "UASMessageHandler.h"
50
#include "QGCTemporaryFile.h"
51
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
52
#include "QGCMapPalette.h"
53
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
54 55
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
56
#include "CustomCommandWidgetController.h"
dogmaphobic's avatar
dogmaphobic committed
57
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
58
#include "ScreenToolsController.h"
59
#include "QGCFileDialogController.h"
Don Gagne's avatar
Don Gagne committed
60
#include "RCChannelMonitorController.h"
61
#include "SyslinkComponentController.h"
62 63
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
64
#include "FirmwarePluginManager.h"
65 66
#include "MultiVehicleManager.h"
#include "Vehicle.h"
67
#include "JoystickConfigController.h"
68
#include "JoystickManager.h"
69
#include "QmlObjectListModel.h"
70
#include "QGCGeoBoundingCube.h"
Don Gagne's avatar
Don Gagne committed
71
#include "MissionManager.h"
72
#include "QGroundControlQmlGlobal.h"
73
#include "FlightMapSettings.h"
74
#include "CoordinateVector.h"
75
#include "PlanMasterController.h"
76
#include "VideoManager.h"
77 78
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
79
#include "LogDownloadController.h"
80
#include "MAVLinkInspectorController.h"
81
#include "ValuesWidgetController.h"
82
#include "AppMessages.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
83 84 85
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
86
#include "MissionCommandTree.h"
87
#include "QGCMapPolygon.h"
88
#include "QGCMapCircle.h"
89
#include "ParameterManager.h"
90
#include "SettingsManager.h"
91
#include "QGCCorePlugin.h"
92
#include "QGCCameraManager.h"
93
#include "CameraCalc.h"
94
#include "VisualMissionItem.h"
95
#include "EditPositionDialogController.h"
96
#include "FactValueSliderListModel.h"
97
#include "ShapeFileHelper.h"
Don Gagne's avatar
Don Gagne committed
98
#include "QGCFileDownload.h"
Don Gagne's avatar
Don Gagne committed
99
#include "FirmwareImage.h"
100
#include "MavlinkConsoleController.h"
101
#include "MAVLinkInspectorController.h"
102
#include "GeoTagController.h"
103
#include "LogReplayLink.h"
104

Gus Grubba's avatar
Gus Grubba committed
105 106 107
#ifndef __mobile__
#include "FirmwareUpgradeController.h"
#endif
108

Gus Grubba's avatar
Gus Grubba committed
109
#ifndef NO_SERIAL_LINK
110
#include "SerialLink.h"
111 112 113
#endif

#ifndef __mobile__
114
#include "GPS/GPSManager.h"
115
#endif
116

117
#ifdef QGC_RTLAB_ENABLED
118
#include "OpalLink.h"
119
#endif
120

Don Gagne's avatar
Don Gagne committed
121 122 123 124 125 126
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
127

dogmaphobic's avatar
dogmaphobic committed
128 129
#include "QGCMapEngine.h"

130
QGCApplication* QGCApplication::_app = nullptr;
pixhawk's avatar
pixhawk committed
131

132 133
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
Don Gagne's avatar
Don Gagne committed
134

dogmaphobic's avatar
dogmaphobic committed
135 136
const char* QGCApplication::_darkStyleFile          = ":/res/styles/style-dark.css";
const char* QGCApplication::_lightStyleFile         = ":/res/styles/style-light.css";
137

138 139 140
// Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];

141
// Qml Singleton factories
142

Don Gagne's avatar
Don Gagne committed
143
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
144
{
Don Gagne's avatar
Don Gagne committed
145 146
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
147 148
}

149 150
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
151
    // We create this object as a QGCTool even though it isn't in the toolbox
152
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp(), qgcApp()->toolbox());
153 154 155
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
156 157
}

158
static QObject* shapeFileHelperSingletonFactory(QQmlEngine*, QJSEngine*)
159
{
160
    return new ShapeFileHelper;
161
}
pixhawk's avatar
pixhawk committed
162

163
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
164 165
    : QGuiApplication           (argc, argv)
    , _runningUnitTests         (unitTesting)
pixhawk's avatar
pixhawk committed
166
{
167
    _app = this;
168 169 170
    // Setup for network proxy support
    QNetworkProxyFactory::setUseSystemConfiguration(true);

171 172
#ifdef Q_OS_LINUX
#ifndef __mobile__
173 174 175
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
176
            msgBox.setInformativeText(tr("You are running %1 as root. "
DonLakeFlyer's avatar
DonLakeFlyer committed
177
                                         "You should not do this since it will cause other issues with %1. "
Don Gagne's avatar
Don Gagne committed
178
                                         "%1 will now exit. "
DonLakeFlyer's avatar
DonLakeFlyer committed
179 180 181
                                         "If you are having serial port issues on Ubuntu, execute the following commands to fix most issues:\n"
                                         "sudo usermod -a -G dialout $USER\n"
                                         "sudo apt-get remove modemmanager").arg(qgcApp()->applicationName()));
182 183 184 185 186
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.exec();
            _exit(0);
        }
187

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
        // Determine if we have the correct permissions to access USB serial devices
        QFile permFile("/etc/group");
        if(permFile.open(QIODevice::ReadOnly)) {
            while(!permFile.atEnd()) {
                QString line = permFile.readLine();
                if (line.contains("dialout") && !line.contains(getenv("USER"))) {
                    QMessageBox msgBox;
                    msgBox.setInformativeText("The current user does not have the correct permissions to access serial devices. "
                                              "You should also remove modemmanager since it also interferes. "
                                              "If you are using Ubuntu, execute the following commands to fix these issues:\n"
                                              "sudo usermod -a -G dialout $USER\n"
                                              "sudo apt-get remove modemmanager");
                    msgBox.setStandardButtons(QMessageBox::Ok);
                    msgBox.setDefaultButton(QMessageBox::Ok);
                    msgBox.exec();
                    break;
                }
205
            }
206
            permFile.close();
207 208 209 210 211
        }
    }
#endif
#endif

212
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
213

214
    bool fClearSettingsOptions = false; // Clear stored settings
Don Gagne's avatar
Don Gagne committed
215
    bool fClearCache = false;           // Clear parameter/airframe caches
216 217
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
218

219
    CmdLineOpt_t rgCmdLineOptions[] = {
220
        { "--clear-settings",   &fClearSettingsOptions, nullptr },
Don Gagne's avatar
Don Gagne committed
221
        { "--clear-cache",      &fClearCache,           nullptr },
222
        { "--logging",          &logging,               &loggingOptions },
223 224
        { "--fake-mobile",      &_fakeMobile,           nullptr },
        { "--log-output",       &_logOutput,            nullptr },
225 226
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
227

228
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
229

230
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
231 232 233
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
234

Don Gagne's avatar
Don Gagne committed
235
    // Set application information
236 237 238 239 240 241 242 243 244
    if (_runningUnitTests) {
        // We don't want unit tests to use the same QSettings space as the normal app. So we tweak the app
        // name. Also we want to run unit tests with clean settings every time.
        setApplicationName(QString("%1_unittest").arg(QGC_APPLICATION_NAME));
    } else {
        setApplicationName(QGC_APPLICATION_NAME);
    }
    setOrganizationName(QGC_ORG_NAME);
    setOrganizationDomain(QGC_ORG_DOMAIN);
245

Daniel Agar's avatar
Daniel Agar committed
246
    this->setApplicationVersion(QString(GIT_VERSION));
247

248 249
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
250
    QSettings settings;
251
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
252

253
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
254 255 256
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
257
#endif
258
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
259
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
260

Don Gagne's avatar
Don Gagne committed
261
    if (_runningUnitTests) {
262
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
263 264
        fClearSettingsOptions = true;
    }
265

266 267 268
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
269

270
        // Clear parameter cache
271
        QDir paramDir(ParameterManager::parameterCacheDir());
272 273
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
274
    } else {
275
        // Determine if upgrade message for settings version bump is required. Check and clear must happen before toolbox is started since
276 277 278
        // that will write some settings.
        if (settings.contains(_settingsVersionKey)) {
            if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
279
                settings.clear();
280 281 282 283
                _settingsUpgraded = true;
            }
        } else if (settings.allKeys().count()) {
            // Settings version key is missing and there are settings. This is an upgrade scenario.
284
            settings.clear();
285 286
            _settingsUpgraded = true;
        }
287
    }
288
    settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
Gus Grubba's avatar
Gus Grubba committed
289

Don Gagne's avatar
Don Gagne committed
290 291 292
    if (fClearCache) {
        QDir dir(ParameterManager::parameterCacheDir());
        dir.removeRecursively();
293
        QFile airframe(cachedAirframeMetaDataFile());
Don Gagne's avatar
Don Gagne committed
294
        airframe.remove();
295
        QFile parameter(cachedParameterMetaDataFile());
Don Gagne's avatar
Don Gagne committed
296 297 298
        parameter.remove();
    }

299 300 301
    // Set up our logging filters
    QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);

dogmaphobic's avatar
dogmaphobic committed
302 303 304 305 306 307 308 309 310
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

311 312 313 314 315
    // Gstreamer debug settings
#if defined(__ios__) || defined(__android__)
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv, nullptr, nullptr);
#else
316 317
    QString savePath, gstDebugLevel;
    if (settings.contains(AppSettings::savePathName)) {
318
        savePath = settings.value(AppSettings::savePathName).toString();
319
    }
320 321 322 323 324 325 326 327 328
    if(savePath.isEmpty()) {
        savePath = "/tmp";
    }
    savePath = savePath + "/Logs/gst";
    if (!QDir(savePath).exists()) {
        QDir().mkpath(savePath);
    }
    if (settings.contains(AppSettings::gstDebugLevelName)) {
        gstDebugLevel = "*:" + settings.value(AppSettings::gstDebugLevelName).toString();
329
    }
330
    // Initialize Video Streaming
331
    initializeVideoStreaming(argc, argv, savePath.toUtf8().data(), gstDebugLevel.toUtf8().data());
332
#endif
333 334

    _toolbox = new QGCToolbox(this);
335
    _toolbox->setChildToolboxes();
Don Gagne's avatar
Don Gagne committed
336

337 338 339 340 341 342 343 344 345 346 347
#ifndef __mobile__
    _gpsRtkFactGroup = new GPSRTKFactGroup(this);
   GPSManager *gpsManager = _toolbox->gpsManager();
   if (gpsManager) {
       connect(gpsManager, &GPSManager::onConnect,          this, &QGCApplication::_onGPSConnect);
       connect(gpsManager, &GPSManager::onDisconnect,       this, &QGCApplication::_onGPSDisconnect);
       connect(gpsManager, &GPSManager::surveyInStatus,     this, &QGCApplication::_gpsSurveyInStatus);
       connect(gpsManager, &GPSManager::satelliteUpdate,    this, &QGCApplication::_gpsNumSatellites);
   }
#endif /* __mobile__ */

348
   setLanguage();
Don Gagne's avatar
Don Gagne committed
349
    _checkForNewVersion();
350 351
}

352 353 354
void QGCApplication::setLanguage()
{
    QLocale locale = QLocale::system();
355
    qDebug() << "System reported locale:" << locale << locale.name();
356
    int langID = toolbox()->settingsManager()->appSettings()->language()->rawValue().toInt();
Gus Grubba's avatar
Gus Grubba committed
357
    //-- See App.SettinsGroup.json for index
358 359 360
    if(langID) {
        switch(langID) {
        case 1:
361
            locale = QLocale(QLocale::Bulgarian);
362 363
            break;
        case 2:
364
            locale = QLocale(QLocale::Chinese);
365 366
            break;
        case 3:
367
            locale = QLocale(QLocale::Dutch);
368 369
            break;
        case 4:
370
            locale = QLocale(QLocale::English);
371 372
            break;
        case 5:
373
            locale = QLocale(QLocale::Finnish);
374 375
            break;
        case 6:
376
            locale = QLocale(QLocale::French);
377 378
            break;
        case 7:
379
            locale = QLocale(QLocale::German);
380 381
            break;
        case 8:
382
            locale = QLocale(QLocale::Greek);
Gus Grubba's avatar
Gus Grubba committed
383 384
            break;
        case 9:
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
            locale = QLocale(QLocale::Hebrew);
            break;
        case 10:
            locale = QLocale(QLocale::Italian);
            break;
        case 11:
            locale = QLocale(QLocale::Japanese);
            break;
        case 12:
            locale = QLocale(QLocale::Korean);
            break;
        case 13:
            locale = QLocale(QLocale::Norwegian);
            break;
        case 14:
            locale = QLocale(QLocale::Polish);
            break;
        case 15:
            locale = QLocale(QLocale::Portuguese);
            break;
        case 16:
            locale = QLocale(QLocale::Russian);
            break;
        case 17:
            locale = QLocale(QLocale::Spanish);
            break;
        case 18:
            locale = QLocale(QLocale::Swedish);
            break;
        case 19:
            locale = QLocale(QLocale::Turkish);
416 417 418
            break;
        }
    }
419 420 421 422 423 424 425 426 427 428
    //-- We have specific fonts for Korean
    if(locale == QLocale::Korean) {
        qDebug() << "Loading Korean fonts" << locale.name();
        if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Regular") < 0) {
            qWarning() << "Could not load /fonts/NanumGothic-Regular font";
        }
        if(QFontDatabase::addApplicationFont(":/fonts/NanumGothic-Bold") < 0) {
            qWarning() << "Could not load /fonts/NanumGothic-Bold font";
        }
    }
429 430 431 432 433 434 435 436 437 438
    qDebug() << "Loading localization for" << locale.name();
    _app->removeTranslator(&_QGCTranslator);
    _app->removeTranslator(&_QGCTranslatorQt);
    if(_QGCTranslatorQt.load("qt_" + locale.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
        _app->installTranslator(&_QGCTranslatorQt);
    } else {
        qDebug() << "Error loading Qt localization for" << locale.name();
    }
    if(_QGCTranslator.load(locale, QLatin1String("qgc_"), "", ":/i18n")) {
        QLocale::setDefault(locale);
439
        _app->installTranslator(&_QGCTranslator);
440 441 442 443 444
    } else {
        qDebug() << "Error loading application localization for" << locale.name();
    }
    if(_qmlAppEngine)
        _qmlAppEngine->retranslate();
445 446
}

447
void QGCApplication::_shutdown()
Don Gagne's avatar
Don Gagne committed
448
{
449
    shutdownVideoStreaming();
450
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
451 452
}

Don Gagne's avatar
Don Gagne committed
453 454 455
QGCApplication::~QGCApplication()
{
    // Place shutdown code in _shutdown
456
    _app = nullptr;
Don Gagne's avatar
Don Gagne committed
457 458
}

459
void QGCApplication::_initCommon()
460
{
461 462 463 464
    static const char* kRefOnly         = "Reference only";
    static const char* kQGCControllers  = "QGroundControl.Controllers";
    static const char* kQGCVehicle      = "QGroundControl.Vehicle";

465
    QSettings settings;
466

Don Gagne's avatar
Don Gagne committed
467
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
468

Don Gagne's avatar
Don Gagne committed
469 470
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
471

472 473 474 475 476 477
    qmlRegisterUncreatableType<Vehicle>             (kQGCVehicle,                           1, 0, "Vehicle",                    kRefOnly);
    qmlRegisterUncreatableType<MissionItem>         (kQGCVehicle,                           1, 0, "MissionItem",                kRefOnly);
    qmlRegisterUncreatableType<MissionManager>      (kQGCVehicle,                           1, 0, "MissionManager",             kRefOnly);
    qmlRegisterUncreatableType<ParameterManager>    (kQGCVehicle,                           1, 0, "ParameterManager",           kRefOnly);
    qmlRegisterUncreatableType<QGCCameraManager>    (kQGCVehicle,                           1, 0, "QGCCameraManager",           kRefOnly);
    qmlRegisterUncreatableType<QGCCameraControl>    (kQGCVehicle,                           1, 0, "QGCCameraControl",           kRefOnly);
Gus Grubba's avatar
Gus Grubba committed
478
    qmlRegisterUncreatableType<QGCVideoStreamInfo>  (kQGCVehicle,                           1, 0, "QGCVideoStreamInfo",         kRefOnly);
479 480 481 482 483 484
    qmlRegisterUncreatableType<LinkInterface>       (kQGCVehicle,                           1, 0, "LinkInterface",              kRefOnly);
    qmlRegisterUncreatableType<MissionController>   (kQGCControllers,                       1, 0, "MissionController",          kRefOnly);
    qmlRegisterUncreatableType<GeoFenceController>  (kQGCControllers,                       1, 0, "GeoFenceController",         kRefOnly);
    qmlRegisterUncreatableType<RallyPointController>(kQGCControllers,                       1, 0, "RallyPointController",       kRefOnly);
    qmlRegisterUncreatableType<VisualMissionItem>   (kQGCControllers,                       1, 0, "VisualMissionItem",          kRefOnly);

485 486 487 488
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                      1, 0, "CoordinateVector",           kRefOnly);
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                      1, 0, "QmlObjectListModel",         kRefOnly);
    qmlRegisterUncreatableType<MissionCommandTree>  ("QGroundControl",                      1, 0, "MissionCommandTree",         kRefOnly);
    qmlRegisterUncreatableType<CameraCalc>          ("QGroundControl",                      1, 0, "CameraCalc",                 kRefOnly);
489 490 491
    qmlRegisterUncreatableType<LogReplayLink>       ("QGroundControl",                      1, 0, "LogReplayLink",              kRefOnly);

    qmlRegisterType<LogReplayLinkController>        ("QGroundControl",                      1, 0, "LogReplayLinkController");
492 493 494 495 496 497 498 499 500 501 502

    qmlRegisterUncreatableType<AutoPilotPlugin>     ("QGroundControl.AutoPilotPlugin",      1, 0, "AutoPilotPlugin",            kRefOnly);
    qmlRegisterUncreatableType<VehicleComponent>    ("QGroundControl.AutoPilotPlugin",      1, 0, "VehicleComponent",           kRefOnly);
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",      1, 0, "JoystickManager",            kRefOnly);
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",      1, 0, "Joystick",                   kRefOnly);
    qmlRegisterUncreatableType<QGCPositionManager>  ("QGroundControl.QGCPositionManager",   1, 0, "QGCPositionManager",         kRefOnly);
    qmlRegisterUncreatableType<FactValueSliderListModel>("QGroundControl.FactControls",     1, 0, "FactValueSliderListModel",   kRefOnly);

    qmlRegisterUncreatableType<QGCMapPolygon>       ("QGroundControl.FlightMap",            1, 0, "QGCMapPolygon",              kRefOnly);
    qmlRegisterUncreatableType<QGCGeoBoundingCube>  ("QGroundControl.FlightMap",            1, 0, "QGCGeoBoundingCube",         kRefOnly);

503
    qmlRegisterType<QGCMapCircle>                   ("QGroundControl.FlightMap",            1, 0, "QGCMapCircle");
504

505 506 507 508 509 510 511 512 513
    qmlRegisterType<ParameterEditorController>      (kQGCControllers,                       1, 0, "ParameterEditorController");
    qmlRegisterType<ESP8266ComponentController>     (kQGCControllers,                       1, 0, "ESP8266ComponentController");
    qmlRegisterType<ScreenToolsController>          (kQGCControllers,                       1, 0, "ScreenToolsController");
    qmlRegisterType<PlanMasterController>           (kQGCControllers,                       1, 0, "PlanMasterController");
    qmlRegisterType<ValuesWidgetController>         (kQGCControllers,                       1, 0, "ValuesWidgetController");
    qmlRegisterType<QGCFileDialogController>        (kQGCControllers,                       1, 0, "QGCFileDialogController");
    qmlRegisterType<RCChannelMonitorController>     (kQGCControllers,                       1, 0, "RCChannelMonitorController");
    qmlRegisterType<JoystickConfigController>       (kQGCControllers,                       1, 0, "JoystickConfigController");
    qmlRegisterType<LogDownloadController>          (kQGCControllers,                       1, 0, "LogDownloadController");
514
    qmlRegisterType<MAVLinkInspectorController>     (kQGCControllers,                       1, 0, "MAVLinkInspectorController");
515 516
    qmlRegisterType<SyslinkComponentController>     (kQGCControllers,                       1, 0, "SyslinkComponentController");
    qmlRegisterType<EditPositionDialogController>   (kQGCControllers,                       1, 0, "EditPositionDialogController");
517

Don Gagne's avatar
Don Gagne committed
518
#ifndef __mobile__
519
#ifndef NO_SERIAL_LINK
520
    qmlRegisterType<FirmwareUpgradeController>      (kQGCControllers,                       1, 0, "FirmwareUpgradeController");
521
#endif
Don Gagne's avatar
Don Gagne committed
522
#endif
523
    qmlRegisterType<GeoTagController>               (kQGCControllers,                       1, 0, "GeoTagController");
524
    qmlRegisterType<MavlinkConsoleController>       (kQGCControllers,                       1, 0, "MavlinkConsoleController");
525
    qmlRegisterType<MAVLinkInspectorController>     (kQGCControllers,                       1, 0, "MAVLinkInspectorController");
dogmaphobic's avatar
dogmaphobic committed
526

527
    // Register Qml Singletons
528 529
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
530
    qmlRegisterSingletonType<ShapeFileHelper>           ("QGroundControl.ShapeFileHelper",          1, 0, "ShapeFileHelper",        shapeFileHelperSingletonFactory);
531 532
}

533
bool QGCApplication::_initForNormalAppBoot()
534
{
535

536 537 538 539 540
    if(QFontDatabase::addApplicationFont(":/fonts/opensans") < 0) {
        qWarning() << "Could not load /fonts/opensans font";
    }
    if(QFontDatabase::addApplicationFont(":/fonts/opensans-demibold") < 0) {
        qWarning() << "Could not load /fonts/opensans-demibold font";
Gus Grubba's avatar
Gus Grubba committed
541 542
    }

543
    QSettings settings;
544

545
    // Exit main application when last window is closed
546
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
547

548
    _qmlAppEngine = toolbox()->corePlugin()->createRootWindow(this);
549

550
    // Now that main window is up check for lost log files
551
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
552
    emit checkForLostLogFiles();
553 554

    // Load known link configurations
555
    toolbox()->linkManager()->loadLinkConfigurationList();
556

Jacob Walser's avatar
Jacob Walser committed
557 558
    // Probe for joysticks
    toolbox()->joystickManager()->init();
559

560
    if (_settingsUpgraded) {
561 562
        showMessage(QString(tr("The format for %1 saved settings has been modified. "
                    "Your saved settings have been reset to defaults.")).arg(applicationName()));
Don Gagne's avatar
Don Gagne committed
563 564
    }

565 566 567
    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

dogmaphobic's avatar
dogmaphobic committed
568
    if (getQGCMapEngine()->wasCacheReset()) {
569 570
        showMessage(tr("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset."));
dogmaphobic's avatar
dogmaphobic committed
571 572
    }

Don Gagne's avatar
Don Gagne committed
573
    settings.sync();
574
    return true;
pixhawk's avatar
pixhawk committed
575 576
}

577
bool QGCApplication::_initForUnitTests()
pixhawk's avatar
pixhawk committed
578
{
579
    return true;
pixhawk's avatar
pixhawk committed
580 581
}

Don Gagne's avatar
Don Gagne committed
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
void QGCApplication::deleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.setValue(_deleteAllSettingsKey, true);
}

void QGCApplication::clearDeleteAllSettingsNextBoot(void)
{
    QSettings settings;
    settings.remove(_deleteAllSettingsKey);
}

/// @brief Returns the QGCApplication object singleton.
QGCApplication* qgcApp(void)
{
597 598 599
    return QGCApplication::_app;
}

600 601
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
602 603
    Q_UNUSED(title);
    showMessage(msg);
604 605 606 607
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
608 609
    Q_UNUSED(title)
    showMessage(msg);
610 611 612 613
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
614 615
    Q_UNUSED(title)
    showMessage(msg);
616 617
}

618
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
619
{
620 621 622 623 624 625 626 627
    // The vehicle is gone now and we are shutting down so we need to use a message box for errors to hold shutdown and show the error
    if (_checkTelemetrySavePath(true /* useMessageBox */)) {

        QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
        QDir saveDir(saveDirPath);

        QString nameFormat("%1%2.%3");
        QString dtFormat("yyyy-MM-dd hh-mm-ss");
628

629 630 631 632 633 634 635 636 637 638 639 640 641
        int tryIndex = 1;
        QString saveFileName = nameFormat.arg(
            QDateTime::currentDateTime().toString(dtFormat)).arg("").arg(toolbox()->settingsManager()->appSettings()->telemetryFileExtension);
        while (saveDir.exists(saveFileName)) {
            saveFileName = nameFormat.arg(
                QDateTime::currentDateTime().toString(dtFormat)).arg(QStringLiteral(".%1").arg(tryIndex++)).arg(toolbox()->settingsManager()->appSettings()->telemetryFileExtension);
        }
        QString saveFilePath = saveDir.absoluteFilePath(saveFileName);

        QFile tempFile(tempLogfile);
        if (!tempFile.copy(saveFilePath)) {
            QString error = tr("Unable to save telemetry log. Error copying telemetry to '%1': '%2'.").arg(saveFilePath).arg(tempFile.errorString());
            showMessage(error);
642
        }
643
    }
644 645
    QFile::remove(tempLogfile);
}
646

647
void QGCApplication::checkTelemetrySavePathOnMainThread()
648 649 650 651 652 653 654 655 656
{
    // This is called with an active vehicle so don't pop message boxes which holds ui thread
    _checkTelemetrySavePath(false /* useMessageBox */);
}

bool QGCApplication::_checkTelemetrySavePath(bool useMessageBox)
{
    QString errorTitle = tr("Telemetry save error");

657
    QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
658
    if (saveDirPath.isEmpty()) {
659
        QString error = tr("Unable to save telemetry log. Application save directory is not set.");
Gus Grubba's avatar
Gus Grubba committed
660 661
        Q_UNUSED(useMessageBox);
        showMessage(error);
662 663 664 665 666 667
        return false;
    }

    QDir saveDir(saveDirPath);
    if (!saveDir.exists()) {
        QString error = tr("Unable to save telemetry log. Telemetry save directory \"%1\" does not exist.").arg(saveDirPath);
Gus Grubba's avatar
Gus Grubba committed
668
        showMessage(error);
669 670 671 672 673
        return false;
    }

    return true;
}
674

Don Gagne's avatar
Don Gagne committed
675
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
676
{
Don Gagne's avatar
Don Gagne committed
677 678
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
679
}
680

Don Gagne's avatar
Don Gagne committed
681 682
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
683
{
DonLakeFlyer's avatar
DonLakeFlyer committed
684 685 686 687 688 689 690 691
    if (_missingParams.count()) {
        QString params;
        foreach (const QString &name, _missingParams) {
            if (params.isEmpty()) {
                params += name;
            } else {
                params += QString(", %1").arg(name);
            }
692
        }
DonLakeFlyer's avatar
DonLakeFlyer committed
693
        _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
694

695
        showMessage(tr("Parameters are missing from firmware. You may be running a version of firmware QGC does not work correctly with or your firmware has a bug in it. Missing params: %1").arg(params));
DonLakeFlyer's avatar
DonLakeFlyer committed
696
    }
697 698
}

Gus Grubba's avatar
Gus Grubba committed
699
QObject* QGCApplication::_rootQmlObject()
Don Gagne's avatar
Don Gagne committed
700
{
701 702 703
    if(_qmlAppEngine && _qmlAppEngine->rootObjects().size())
        return _qmlAppEngine->rootObjects()[0];
    return nullptr;
Don Gagne's avatar
Don Gagne committed
704 705 706
}


707
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
708
{
709
    // PreArm messages are handled by Vehicle and shown in Map
710
    if (message.startsWith(QStringLiteral("PreArm")) || message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
Don Gagne's avatar
Don Gagne committed
711 712
        return;
    }
713 714 715 716 717 718 719 720
    QObject* rootQmlObject = _rootQmlObject();
    if (rootQmlObject) {
        QVariant varReturn;
        QVariant varMessage = QVariant::fromValue(message);
        QMetaObject::invokeMethod(_rootQmlObject(), "showMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
    } else {
        qWarning() << "Internal error";
    }
Don Gagne's avatar
Don Gagne committed
721 722
}

723
QQuickItem* QGCApplication::mainRootWindow()
724
{
725 726
    if(_mainRootWindow) {
        _mainRootWindow = reinterpret_cast<QQuickItem*>(_rootQmlObject());
727
    }
728
    return _mainRootWindow;
729 730 731
}

void QGCApplication::showSetupView()
Don Gagne's avatar
Don Gagne committed
732
{
733 734 735
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
    }
Don Gagne's avatar
Don Gagne committed
736 737
}

738
void QGCApplication::qmlAttemptWindowClose()
Don Gagne's avatar
Don Gagne committed
739
{
740 741 742
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
    }
Don Gagne's avatar
Don Gagne committed
743
}
744 745 746 747 748

bool QGCApplication::isInternetAvailable()
{
    return getQGCMapEngine()->isInternetActive();
}
Don Gagne's avatar
Don Gagne committed
749

750
void QGCApplication::_checkForNewVersion()
Don Gagne's avatar
Don Gagne committed
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
{
#ifndef __mobile__
    if (!_runningUnitTests) {
        if (_parseVersionText(applicationVersion(), _majorVersion, _minorVersion, _buildVersion)) {
            QString versionCheckFile = toolbox()->corePlugin()->stableVersionCheckFileUrl();
            if (!versionCheckFile.isEmpty()) {
                _currentVersionDownload = new QGCFileDownload(this);
                connect(_currentVersionDownload, &QGCFileDownload::downloadFinished, this, &QGCApplication::_currentVersionDownloadFinished);
                connect(_currentVersionDownload, &QGCFileDownload::error, this, &QGCApplication::_currentVersionDownloadError);
                _currentVersionDownload->download(versionCheckFile);
            }
        }
    }
#endif
}

void QGCApplication::_currentVersionDownloadFinished(QString remoteFile, QString localFile)
{
    Q_UNUSED(remoteFile);

Don Gagne's avatar
Don Gagne committed
771 772 773
#ifdef __mobile__
    Q_UNUSED(localFile);
#else
Don Gagne's avatar
Don Gagne committed
774 775 776 777 778 779 780 781 782 783 784 785
    QFile versionFile(localFile);
    if (versionFile.open(QIODevice::ReadOnly)) {
        QTextStream textStream(&versionFile);
        QString version = textStream.readLine();

        qDebug() << version;

        int majorVersion, minorVersion, buildVersion;
        if (_parseVersionText(version, majorVersion, minorVersion, buildVersion)) {
            if (_majorVersion < majorVersion ||
                    (_majorVersion == majorVersion && _minorVersion < minorVersion) ||
                    (_majorVersion == majorVersion && _minorVersion == minorVersion && _buildVersion < buildVersion)) {
Gus Grubba's avatar
Gus Grubba committed
786 787
                //-- TODO
                ///QGCMessageBox::information(tr("New Version Available"), tr("There is a newer version of %1 available. You can download it from %2.").arg(applicationName()).arg(toolbox()->corePlugin()->stableDownloadLocation()));
Don Gagne's avatar
Don Gagne committed
788 789 790 791 792
            }
        }
    }

    _currentVersionDownload->deleteLater();
Don Gagne's avatar
Don Gagne committed
793
#endif
Don Gagne's avatar
Don Gagne committed
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
}

void QGCApplication::_currentVersionDownloadError(QString errorMsg)
{
    Q_UNUSED(errorMsg);
    _currentVersionDownload->deleteLater();
}

bool QGCApplication::_parseVersionText(const QString& versionString, int& majorVersion, int& minorVersion, int& buildVersion)
{
    QRegularExpression regExp("v(\\d+)\\.(\\d+)\\.(\\d+)");
    QRegularExpressionMatch match = regExp.match(versionString);
    if (match.hasMatch() && match.lastCapturedIndex() == 3) {
        majorVersion = match.captured(1).toInt();
        minorVersion = match.captured(2).toInt();
        buildVersion = match.captured(3).toInt();
        return true;
    }

    return false;
}
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829


void QGCApplication::_onGPSConnect()
{
    _gpsRtkFactGroup->connected()->setRawValue(true);
}

void QGCApplication::_onGPSDisconnect()
{
    _gpsRtkFactGroup->connected()->setRawValue(false);
}

void QGCApplication::_gpsSurveyInStatus(float duration, float accuracyMM,  double latitude, double longitude, float altitude, bool valid, bool active)
{
    _gpsRtkFactGroup->currentDuration()->setRawValue(duration);
830
    _gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast<double>(accuracyMM) / 1000.0);
831 832 833 834 835 836 837 838 839 840 841 842
    _gpsRtkFactGroup->currentLatitude()->setRawValue(latitude);
    _gpsRtkFactGroup->currentLongitude()->setRawValue(longitude);
    _gpsRtkFactGroup->currentAltitude()->setRawValue(altitude);
    _gpsRtkFactGroup->valid()->setRawValue(valid);
    _gpsRtkFactGroup->active()->setRawValue(active);
}

void QGCApplication::_gpsNumSatellites(int numSatellites)
{
    _gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites);
}

843 844 845 846 847 848 849 850 851 852 853 854 855
QString QGCApplication::cachedParameterMetaDataFile(void)
{
    QSettings settings;
    QDir parameterDir = QFileInfo(settings.fileName()).dir();
    return parameterDir.filePath(QStringLiteral("ParameterFactMetaData.xml"));
}

QString QGCApplication::cachedAirframeMetaDataFile(void)
{
    QSettings settings;
    QDir airframeDir = QFileInfo(settings.fileName()).dir();
    return airframeDir.filePath(QStringLiteral("PX4AirframeFactMetaData.xml"));
}