QGCApplication.cc 32.1 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
            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);
415 416 417
            break;
        }
    }
418 419 420 421 422 423 424 425 426 427
    //-- 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";
        }
    }
428 429 430 431 432 433 434 435 436 437
    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);
438
        _app->installTranslator(&_QGCTranslator);
439 440 441 442 443
    } else {
        qDebug() << "Error loading application localization for" << locale.name();
    }
    if(_qmlAppEngine)
        _qmlAppEngine->retranslate();
444 445
}

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

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

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

464
    QSettings settings;
465

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

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

471 472 473 474 475 476
    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
477
    qmlRegisterUncreatableType<QGCVideoStreamInfo>  (kQGCVehicle,                           1, 0, "QGCVideoStreamInfo",         kRefOnly);
478 479 480 481 482 483
    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);

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
    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);

499
    qmlRegisterType<QGCMapCircle>                   ("QGroundControl.FlightMap",            1, 0, "QGCMapCircle");
500

501 502 503 504 505 506 507 508 509
    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");
510
    qmlRegisterType<MAVLinkInspectorController>     (kQGCControllers,                       1, 0, "MAVLinkInspectorController");
511 512
    qmlRegisterType<SyslinkComponentController>     (kQGCControllers,                       1, 0, "SyslinkComponentController");
    qmlRegisterType<EditPositionDialogController>   (kQGCControllers,                       1, 0, "EditPositionDialogController");
513

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

523
    // Register Qml Singletons
524 525
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
526
    qmlRegisterSingletonType<ShapeFileHelper>           ("QGroundControl.ShapeFileHelper",          1, 0, "ShapeFileHelper",        shapeFileHelperSingletonFactory);
527 528
}

529
bool QGCApplication::_initForNormalAppBoot()
530
{
531

532 533 534 535 536
    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
537 538
    }

539
    QSettings settings;
540

541
    // Exit main application when last window is closed
542
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
543

544
    _qmlAppEngine = toolbox()->corePlugin()->createRootWindow(this);
545

546
    // Now that main window is up check for lost log files
547
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
548
    emit checkForLostLogFiles();
549 550

    // Load known link configurations
551
    toolbox()->linkManager()->loadLinkConfigurationList();
552

Jacob Walser's avatar
Jacob Walser committed
553 554
    // Probe for joysticks
    toolbox()->joystickManager()->init();
555

556
    if (_settingsUpgraded) {
557 558
        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
559 560
    }

561 562 563
    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

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

Don Gagne's avatar
Don Gagne committed
569
    settings.sync();
570
    return true;
pixhawk's avatar
pixhawk committed
571 572
}

573
bool QGCApplication::_initForUnitTests()
pixhawk's avatar
pixhawk committed
574
{
575
    return true;
pixhawk's avatar
pixhawk committed
576 577
}

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

596 597
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
598 599
    Q_UNUSED(title);
    showMessage(msg);
600 601 602 603
}

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

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

614
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
615
{
616 617 618 619 620 621 622 623
    // 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");
624

625 626 627 628 629 630 631 632 633 634 635 636 637
        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);
638
        }
639
    }
640 641
    QFile::remove(tempLogfile);
}
642

643
void QGCApplication::checkTelemetrySavePathOnMainThread()
644 645 646 647 648 649 650 651 652
{
    // 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");

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

    return true;
}
670

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

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

691
        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
692
    }
693 694
}

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


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

719
QQuickItem* QGCApplication::mainRootWindow()
720
{
721 722
    if(_mainRootWindow) {
        _mainRootWindow = reinterpret_cast<QQuickItem*>(_rootQmlObject());
723
    }
724
    return _mainRootWindow;
725 726 727
}

void QGCApplication::showSetupView()
Don Gagne's avatar
Don Gagne committed
728
{
729 730 731
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
    }
Don Gagne's avatar
Don Gagne committed
732 733
}

734
void QGCApplication::qmlAttemptWindowClose()
Don Gagne's avatar
Don Gagne committed
735
{
736 737 738
    if(_rootQmlObject()) {
        QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
    }
Don Gagne's avatar
Don Gagne committed
739
}
740 741 742 743 744

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

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

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

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;
}
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825


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);
826
    _gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast<double>(accuracyMM) / 1000.0);
827 828 829 830 831 832 833 834 835 836 837 838
    _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);
}

839 840 841 842 843 844 845 846 847 848 849 850 851
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"));
}