QGCApplication.cc 31.9 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 103
#include "GeoTagController.h"

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

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

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

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

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

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

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

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

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

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

140
// Qml Singleton factories
141

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

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

    return qmlGlobal;
155 156
}

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

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

170 171
#ifdef Q_OS_LINUX
#ifndef __mobile__
172 173 174
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
175
            msgBox.setInformativeText(tr("You are running %1 as root. "
DonLakeFlyer's avatar
DonLakeFlyer committed
176
                                         "You should not do this since it will cause other issues with %1. "
Don Gagne's avatar
Don Gagne committed
177
                                         "%1 will now exit. "
DonLakeFlyer's avatar
DonLakeFlyer committed
178 179 180
                                         "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()));
181 182 183 184 185
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.exec();
            _exit(0);
        }
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
        // 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;
                }
204
            }
205
            permFile.close();
206 207 208 209 210
        }
    }
#endif
#endif

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

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

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

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

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

Don Gagne's avatar
Don Gagne committed
234
    // Set application information
235 236 237 238 239 240 241 242 243
    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);
244

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

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

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

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

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

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

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

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

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

310 311 312 313 314
    // Gstreamer debug settings
#if defined(__ios__) || defined(__android__)
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv, nullptr, nullptr);
#else
315 316
    QString savePath, gstDebugLevel;
    if (settings.contains(AppSettings::savePathName)) {
317
        savePath = settings.value(AppSettings::savePathName).toString();
318
    }
319 320 321 322 323 324 325 326 327
    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();
328
    }
329
    // Initialize Video Streaming
330
    initializeVideoStreaming(argc, argv, savePath.toUtf8().data(), gstDebugLevel.toUtf8().data());
331
#endif
332 333

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

336 337 338 339 340 341 342 343 344 345 346
#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__ */

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

351 352 353
void QGCApplication::setLanguage()
{
    QLocale locale = QLocale::system();
354
    qDebug() << "System reported locale:" << locale << locale.name();
355
    int langID = toolbox()->settingsManager()->appSettings()->language()->rawValue().toInt();
Gus Grubba's avatar
Gus Grubba committed
356
    //-- See App.SettinsGroup.json for index
357 358 359
    if(langID) {
        switch(langID) {
        case 1:
360
            locale = QLocale(QLocale::Bulgarian);
361 362
            break;
        case 2:
363
            locale = QLocale(QLocale::Chinese);
364 365
            break;
        case 3:
366
            locale = QLocale(QLocale::Dutch);
367 368
            break;
        case 4:
369
            locale = QLocale(QLocale::English);
370 371
            break;
        case 5:
372
            locale = QLocale(QLocale::Finnish);
373 374
            break;
        case 6:
375
            locale = QLocale(QLocale::French);
376 377
            break;
        case 7:
378
            locale = QLocale(QLocale::German);
379 380
            break;
        case 8:
381
            locale = QLocale(QLocale::Greek);
Gus Grubba's avatar
Gus Grubba committed
382 383
            break;
        case 9:
384 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 416 417 418 419 420
            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);
            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";
            }
            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);
421 422 423
            break;
        }
    }
424 425 426 427 428 429 430 431 432 433
    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);
434
        _app->installTranslator(&_QGCTranslator);
435 436 437 438 439
    } else {
        qDebug() << "Error loading application localization for" << locale.name();
    }
    if(_qmlAppEngine)
        _qmlAppEngine->retranslate();
440 441
}

442
void QGCApplication::_shutdown()
Don Gagne's avatar
Don Gagne committed
443
{
444
    shutdownVideoStreaming();
445
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
446 447
}

Don Gagne's avatar
Don Gagne committed
448 449 450
QGCApplication::~QGCApplication()
{
    // Place shutdown code in _shutdown
451
    _app = nullptr;
Don Gagne's avatar
Don Gagne committed
452 453
}

454
void QGCApplication::_initCommon()
455
{
456 457 458 459
    static const char* kRefOnly         = "Reference only";
    static const char* kQGCControllers  = "QGroundControl.Controllers";
    static const char* kQGCVehicle      = "QGroundControl.Vehicle";

460
    QSettings settings;
461

Don Gagne's avatar
Don Gagne committed
462
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
463

Don Gagne's avatar
Don Gagne committed
464 465
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
466

467 468 469 470 471 472
    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
473
    qmlRegisterUncreatableType<QGCVideoStreamInfo>  (kQGCVehicle,                           1, 0, "QGCVideoStreamInfo",         kRefOnly);
474 475 476 477 478 479
    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);

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    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);

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

495
    qmlRegisterType<QGCMapCircle>                   ("QGroundControl.FlightMap",            1, 0, "QGCMapCircle");
496

497 498 499 500 501 502 503 504 505
    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");
506
    qmlRegisterType<MAVLinkInspectorController>     (kQGCControllers,                       1, 0, "MAVLinkInspectorController");
507 508
    qmlRegisterType<SyslinkComponentController>     (kQGCControllers,                       1, 0, "SyslinkComponentController");
    qmlRegisterType<EditPositionDialogController>   (kQGCControllers,                       1, 0, "EditPositionDialogController");
509

Don Gagne's avatar
Don Gagne committed
510
#ifndef __mobile__
511
#ifndef NO_SERIAL_LINK
512
    qmlRegisterType<FirmwareUpgradeController>      (kQGCControllers,                       1, 0, "FirmwareUpgradeController");
513
#endif
Don Gagne's avatar
Don Gagne committed
514
#endif
515
    qmlRegisterType<GeoTagController>               (kQGCControllers,                       1, 0, "GeoTagController");
516
    qmlRegisterType<MavlinkConsoleController>       (kQGCControllers,                       1, 0, "MavlinkConsoleController");
517
    qmlRegisterType<MAVLinkInspectorController>     (kQGCControllers,                       1, 0, "MAVLinkInspectorController");
dogmaphobic's avatar
dogmaphobic committed
518

519
    // Register Qml Singletons
520 521
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
522
    qmlRegisterSingletonType<ShapeFileHelper>           ("QGroundControl.ShapeFileHelper",          1, 0, "ShapeFileHelper",        shapeFileHelperSingletonFactory);
523 524
}

525
bool QGCApplication::_initForNormalAppBoot()
526
{
527

528 529 530 531 532
    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
533 534
    }

535
    QSettings settings;
536

537
    // Exit main application when last window is closed
538
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
539

540
    _qmlAppEngine = toolbox()->corePlugin()->createRootWindow(this);
541

542
    // Now that main window is up check for lost log files
543
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
544
    emit checkForLostLogFiles();
545 546

    // Load known link configurations
547
    toolbox()->linkManager()->loadLinkConfigurationList();
548

Jacob Walser's avatar
Jacob Walser committed
549 550
    // Probe for joysticks
    toolbox()->joystickManager()->init();
551

552
    if (_settingsUpgraded) {
553 554
        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
555 556
    }

557 558 559
    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

dogmaphobic's avatar
dogmaphobic committed
560
    if (getQGCMapEngine()->wasCacheReset()) {
561 562
        showMessage(tr("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset."));
dogmaphobic's avatar
dogmaphobic committed
563 564
    }

Don Gagne's avatar
Don Gagne committed
565
    settings.sync();
566
    return true;
pixhawk's avatar
pixhawk committed
567 568
}

569
bool QGCApplication::_initForUnitTests()
pixhawk's avatar
pixhawk committed
570
{
571
    return true;
pixhawk's avatar
pixhawk committed
572 573
}

Don Gagne's avatar
Don Gagne committed
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
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)
{
589 590 591
    return QGCApplication::_app;
}

592 593
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
594 595
    Q_UNUSED(title);
    showMessage(msg);
596 597 598 599
}

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

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
606 607
    Q_UNUSED(title)
    showMessage(msg);
608 609
}

610
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
611
{
612 613 614 615 616 617 618 619
    // 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");
620

621 622 623 624 625 626 627 628 629 630 631 632 633
        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);
634
        }
635
    }
636 637
    QFile::remove(tempLogfile);
}
638

639
void QGCApplication::checkTelemetrySavePathOnMainThread()
640 641 642 643 644 645 646 647 648
{
    // 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");

649
    QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath();
650
    if (saveDirPath.isEmpty()) {
651
        QString error = tr("Unable to save telemetry log. Application save directory is not set.");
Gus Grubba's avatar
Gus Grubba committed
652 653
        Q_UNUSED(useMessageBox);
        showMessage(error);
654 655 656 657 658 659
        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
660
        showMessage(error);
661 662 663 664 665
        return false;
    }

    return true;
}
666

Don Gagne's avatar
Don Gagne committed
667
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
668
{
Don Gagne's avatar
Don Gagne committed
669 670
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
671
}
672

Don Gagne's avatar
Don Gagne committed
673 674
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
675
{
DonLakeFlyer's avatar
DonLakeFlyer committed
676 677 678 679 680 681 682 683
    if (_missingParams.count()) {
        QString params;
        foreach (const QString &name, _missingParams) {
            if (params.isEmpty()) {
                params += name;
            } else {
                params += QString(", %1").arg(name);
            }
684
        }
DonLakeFlyer's avatar
DonLakeFlyer committed
685
        _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
686

687
        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
688
    }
689 690
}

Gus Grubba's avatar
Gus Grubba committed
691
QObject* QGCApplication::_rootQmlObject()
Don Gagne's avatar
Don Gagne committed
692
{
693 694 695
    if(_qmlAppEngine && _qmlAppEngine->rootObjects().size())
        return _qmlAppEngine->rootObjects()[0];
    return nullptr;
Don Gagne's avatar
Don Gagne committed
696 697 698
}


699
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
700
{
701
    // PreArm messages are handled by Vehicle and shown in Map
702
    if (message.startsWith(QStringLiteral("PreArm")) || message.startsWith(QStringLiteral("preflight"), Qt::CaseInsensitive)) {
Don Gagne's avatar
Don Gagne committed
703 704
        return;
    }
705 706 707 708 709 710 711 712
    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
713 714
}

715
QQuickItem* QGCApplication::mainRootWindow()
716
{
717 718
    if(_mainRootWindow) {
        _mainRootWindow = reinterpret_cast<QQuickItem*>(_rootQmlObject());
719
    }
720
    return _mainRootWindow;
721 722 723
}

void QGCApplication::showSetupView()
Don Gagne's avatar
Don Gagne committed
724
{
725 726 727
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
    }
Don Gagne's avatar
Don Gagne committed
728 729
}

730
void QGCApplication::qmlAttemptWindowClose()
Don Gagne's avatar
Don Gagne committed
731
{
732 733 734
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
    }
Don Gagne's avatar
Don Gagne committed
735
}
736 737 738 739 740

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

742
void QGCApplication::_checkForNewVersion()
Don Gagne's avatar
Don Gagne committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
{
#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
763 764 765
#ifdef __mobile__
    Q_UNUSED(localFile);
#else
Don Gagne's avatar
Don Gagne committed
766 767 768 769 770 771 772 773 774 775 776 777
    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
778 779
                //-- 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
780 781 782 783 784
            }
        }
    }

    _currentVersionDownload->deleteLater();
Don Gagne's avatar
Don Gagne committed
785
#endif
Don Gagne's avatar
Don Gagne committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
}

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;
}
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821


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);
822
    _gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast<double>(accuracyMM) / 1000.0);
823 824 825 826 827 828 829 830 831 832 833 834
    _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);
}

835 836 837 838 839 840 841 842 843 844 845 846 847
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"));
}