QGCApplication.cc 25.3 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>
pixhawk's avatar
pixhawk committed
27

dogmaphobic's avatar
dogmaphobic committed
28 29 30 31
#ifdef QGC_ENABLE_BLUETOOTH
#include <QBluetoothLocalDevice>
#endif

32 33
#include <QDebug>

34
#include "VideoStreaming.h"
Gus Grubba's avatar
Gus Grubba committed
35

36
#include "QGC.h"
Don Gagne's avatar
Don Gagne committed
37
#include "QGCApplication.h"
pixhawk's avatar
pixhawk committed
38
#include "GAudioOutput.h"
39
#include "CmdLineOptParser.h"
40 41
#include "UDPLink.h"
#include "LinkManager.h"
42
#include "UASMessageHandler.h"
43
#include "QGCTemporaryFile.h"
44
#include "QGCPalette.h"
Don Gagne's avatar
Don Gagne committed
45
#include "QGCMapPalette.h"
46
#include "QGCLoggingCategory.h"
Don Gagne's avatar
Don Gagne committed
47 48
#include "ViewWidgetController.h"
#include "ParameterEditorController.h"
Don Gagne's avatar
Don Gagne committed
49
#include "CustomCommandWidgetController.h"
dogmaphobic's avatar
dogmaphobic committed
50
#include "ESP8266ComponentController.h"
Don Gagne's avatar
Don Gagne committed
51
#include "ScreenToolsController.h"
Don Gagne's avatar
Don Gagne committed
52
#include "QGCMobileFileDialogController.h"
Don Gagne's avatar
Don Gagne committed
53
#include "RCChannelMonitorController.h"
54 55
#include "AutoPilotPlugin.h"
#include "VehicleComponent.h"
Don Gagne's avatar
Don Gagne committed
56
#include "FirmwarePluginManager.h"
57 58
#include "MultiVehicleManager.h"
#include "Vehicle.h"
59
#include "MavlinkQmlSingleton.h"
60
#include "JoystickConfigController.h"
61
#include "JoystickManager.h"
62
#include "QmlObjectListModel.h"
Don Gagne's avatar
Don Gagne committed
63
#include "MissionManager.h"
64
#include "QGroundControlQmlGlobal.h"
65
#include "FlightMapSettings.h"
66
#include "CoordinateVector.h"
67
#include "MissionController.h"
68 69
#include "GeoFenceController.h"
#include "RallyPointController.h"
70
#include "VideoManager.h"
71 72
#include "VideoSurface.h"
#include "VideoReceiver.h"
dogmaphobic's avatar
dogmaphobic committed
73
#include "LogDownloadController.h"
74
#include "ValuesWidgetController.h"
75
#include "AppMessages.h"
Jimmy Johnson's avatar
Jimmy Johnson committed
76 77 78
#include "SimulatedPosition.h"
#include "PositionManager.h"
#include "FollowMe.h"
79
#include "MissionCommandTree.h"
80
#include "QGCMapPolygon.h"
81
#include "ParameterManager.h"
82
#include "SettingsManager.h"
83

Gus Grubba's avatar
Gus Grubba committed
84
#ifndef NO_SERIAL_LINK
85
#include "SerialLink.h"
86 87 88
#endif

#ifndef __mobile__
89 90 91 92 93
#include "QGCFileDialog.h"
#include "QGCMessageBox.h"
#include "FirmwareUpgradeController.h"
#include "MainWindow.h"
#include "GeoTagController.h"
94
#endif
95

96
#ifdef QGC_RTLAB_ENABLED
97
#include "OpalLink.h"
98
#endif
99

Don Gagne's avatar
Don Gagne committed
100 101 102 103 104 105
#ifdef Q_OS_LINUX
#ifndef __mobile__
#include <unistd.h>
#include <sys/types.h>
#endif
#endif
106

dogmaphobic's avatar
dogmaphobic committed
107 108
#include "QGCMapEngine.h"

109
QGCApplication* QGCApplication::_app = NULL;
pixhawk's avatar
pixhawk committed
110

Don Gagne's avatar
Don Gagne committed
111 112
const char* QGCApplication::parameterFileExtension =    "params";
const char* QGCApplication::missionFileExtension =      "mission";
113
const char* QGCApplication::fenceFileExtension =        "fence";
114
const char* QGCApplication::rallyPointFileExtension =   "rally";
Don Gagne's avatar
Don Gagne committed
115 116
const char* QGCApplication::telemetryFileExtension =     "tlog";

117 118
const char* QGCApplication::_deleteAllSettingsKey           = "DeleteAllSettingsNextBoot";
const char* QGCApplication::_settingsVersionKey             = "SettingsVersion";
119 120 121
const char* QGCApplication::_lastKnownHomePositionLatKey    = "LastKnownHomePositionLat";
const char* QGCApplication::_lastKnownHomePositionLonKey    = "LastKnownHomePositionLon";
const char* QGCApplication::_lastKnownHomePositionAltKey    = "LastKnownHomePositionAlt";
Don Gagne's avatar
Don Gagne committed
122

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

126 127 128
// Mavlink status structures for entire app
mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];

129
// Qml Singleton factories
130

Don Gagne's avatar
Don Gagne committed
131
static QObject* screenToolsControllerSingletonFactory(QQmlEngine*, QJSEngine*)
132
{
Don Gagne's avatar
Don Gagne committed
133 134
    ScreenToolsController* screenToolsController = new ScreenToolsController;
    return screenToolsController;
135 136
}

137 138 139 140 141
static QObject* mavlinkQmlSingletonFactory(QQmlEngine*, QJSEngine*)
{
    return new MavlinkQmlSingleton;
}

142 143
static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
{
144
    // We create this object as a QGCTool even though it isn't in the toolbox
145 146 147 148
    QGroundControlQmlGlobal* qmlGlobal = new QGroundControlQmlGlobal(qgcApp());
    qmlGlobal->setToolbox(qgcApp()->toolbox());

    return qmlGlobal;
149 150
}

pixhawk's avatar
pixhawk committed
151 152 153 154 155 156 157 158 159 160
/**
 * @brief Constructor for the main application.
 *
 * This constructor initializes and starts the whole application. It takes standard
 * command-line parameters
 *
 * @param argc The number of command-line parameters
 * @param argv The string array of parameters
 **/

161
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
Don Gagne's avatar
Don Gagne committed
162 163 164
#ifdef __mobile__
    : QGuiApplication(argc, argv)
    , _qmlAppEngine(NULL)
165
    #else
166
    : QApplication(argc, argv)
167
    #endif
168
    , _runningUnitTests(unitTesting)
dogmaphobic's avatar
dogmaphobic committed
169
    , _fakeMobile(false)
170
    , _settingsUpgraded(false)
171
    #ifdef QT_DEBUG
172
    , _testHighDPI(false)
173
    #endif
174
    , _toolbox(NULL)
dogmaphobic's avatar
dogmaphobic committed
175
    , _bluetoothAvailable(false)
176
    , _lastKnownHomePosition(37.803784, -122.462276, 0.0)
pixhawk's avatar
pixhawk committed
177
{
178 179
    Q_ASSERT(_app == NULL);
    _app = this;
180

181
    // This prevents usage of QQuickWidget to fail since it doesn't support native widget siblings
dogmaphobic's avatar
dogmaphobic committed
182
#ifndef __android__
183
    setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
dogmaphobic's avatar
dogmaphobic committed
184
#endif
dogmaphobic's avatar
dogmaphobic committed
185

186 187 188
    // Setup for network proxy support
    QNetworkProxyFactory::setUseSystemConfiguration(true);

189 190
#ifdef Q_OS_LINUX
#ifndef __mobile__
191 192 193
    if (!_runningUnitTests) {
        if (getuid() == 0) {
            QMessageBox msgBox;
Ricardo de Almeida Gonzaga's avatar
Ricardo de Almeida Gonzaga committed
194
            msgBox.setInformativeText("You are running QGroundControl as root. "
195 196 197 198 199 200 201 202 203 204
                                      "You should not do this since it will cause other issues with QGroundControl. "
                                      "QGroundControl will now exit. "
                                      "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");
            msgBox.setStandardButtons(QMessageBox::Ok);
            msgBox.setDefaultButton(QMessageBox::Ok);
            msgBox.exec();
            _exit(0);
        }
205

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
        // 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;
                }
223
            }
224
            permFile.close();
225 226 227 228 229
        }
    }
#endif
#endif

230
    // Parse command line options
dogmaphobic's avatar
dogmaphobic committed
231

232
    bool fClearSettingsOptions = false; // Clear stored settings
233 234
    bool logging = false;               // Turn on logging
    QString loggingOptions;
dogmaphobic's avatar
dogmaphobic committed
235

236
    CmdLineOpt_t rgCmdLineOptions[] = {
237 238
        { "--clear-settings",   &fClearSettingsOptions, NULL },
        { "--logging",          &logging,               &loggingOptions },
dogmaphobic's avatar
dogmaphobic committed
239
        { "--fake-mobile",      &_fakeMobile,           NULL },
240
    #ifdef QT_DEBUG
241
        { "--test-high-dpi",    &_testHighDPI,          NULL },
242
    #endif
243 244
        // Add additional command line option flags here
    };
dogmaphobic's avatar
dogmaphobic committed
245

246
    ParseCmdLineOptions(argc, argv, rgCmdLineOptions, sizeof(rgCmdLineOptions)/sizeof(rgCmdLineOptions[0]), false);
247

248
    // Set up timer for delayed missing fact display
Don Gagne's avatar
Don Gagne committed
249 250 251
    _missingParamsDelayedDisplayTimer.setSingleShot(true);
    _missingParamsDelayedDisplayTimer.setInterval(_missingParamsDelayedDisplayTimerTimeout);
    connect(&_missingParamsDelayedDisplayTimer, &QTimer::timeout, this, &QGCApplication::_missingParamsDisplay);
dogmaphobic's avatar
dogmaphobic committed
252

Don Gagne's avatar
Don Gagne committed
253
    // Set application information
254 255 256 257 258 259 260 261 262
    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);
263

Daniel Agar's avatar
Daniel Agar committed
264
    this->setApplicationVersion(QString(GIT_VERSION));
265

266 267
    // Set settings format
    QSettings::setDefaultFormat(QSettings::IniFormat);
268
    QSettings settings;
269
    qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
Don Gagne's avatar
Don Gagne committed
270

271
#ifdef UNITTEST_BUILD
Don Gagne's avatar
Don Gagne committed
272 273 274
    if (!settings.isWritable()) {
        qWarning() << "Setings location is not writable";
    }
275
#endif
276
    // The setting will delete all settings on this boot
Don Gagne's avatar
Don Gagne committed
277
    fClearSettingsOptions |= settings.contains(_deleteAllSettingsKey);
278

Don Gagne's avatar
Don Gagne committed
279
    if (_runningUnitTests) {
280
        // Unit tests run with clean settings
Don Gagne's avatar
Don Gagne committed
281 282
        fClearSettingsOptions = true;
    }
283

284 285 286
    if (fClearSettingsOptions) {
        // User requested settings to be cleared on command line
        settings.clear();
287

288
        // Clear parameter cache
289
        QDir paramDir(ParameterManager::parameterCacheDir());
290 291
        paramDir.removeRecursively();
        paramDir.mkpath(paramDir.absolutePath());
292
    } else {
293
        // Determine if upgrade message for settings version bump is required. Check and clear must happen before toolbox is started since
294 295 296
        // that will write some settings.
        if (settings.contains(_settingsVersionKey)) {
            if (settings.value(_settingsVersionKey).toInt() != QGC_SETTINGS_VERSION) {
297
                settings.clear();
298 299 300 301
                _settingsUpgraded = true;
            }
        } else if (settings.allKeys().count()) {
            // Settings version key is missing and there are settings. This is an upgrade scenario.
302
            settings.clear();
303 304
            _settingsUpgraded = true;
        }
305
    }
306
    settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
Gus Grubba's avatar
Gus Grubba committed
307

308 309 310
    // Set up our logging filters
    QGCLoggingCategoryRegister::instance()->setFilterRulesFromSettings(loggingOptions);

311 312 313
    _lastKnownHomePosition.setLatitude(settings.value(_lastKnownHomePositionLatKey, 37.803784).toDouble());
    _lastKnownHomePosition.setLongitude(settings.value(_lastKnownHomePositionLonKey, -122.462276).toDouble());
    _lastKnownHomePosition.setAltitude(settings.value(_lastKnownHomePositionAltKey, 0.0).toDouble());
Don Gagne's avatar
Don Gagne committed
314

dogmaphobic's avatar
dogmaphobic committed
315 316 317 318 319 320 321 322 323
    // Initialize Bluetooth
#ifdef QGC_ENABLE_BLUETOOTH
    QBluetoothLocalDevice localDevice;
    if (localDevice.isValid())
    {
        _bluetoothAvailable = true;
    }
#endif

324 325
    // Initialize Video Streaming
    initializeVideoStreaming(argc, argv);
326 327

    _toolbox = new QGCToolbox(this);
328
    _toolbox->setChildToolboxes();
329 330
}

Don Gagne's avatar
Don Gagne committed
331
void QGCApplication::_shutdown(void)
Don Gagne's avatar
Don Gagne committed
332
{
Don Gagne's avatar
Don Gagne committed
333 334 335 336
    // This code is specifically not in the destructor since the application object may not be available in the destructor.
    // This cause problems for deleting object like settings which are in the toolbox which may have qml references. By
    // moving them here and having main.cc call this prior to deleting the app object we make sure app object is still
    // around while these things are shutting down.
Don Gagne's avatar
Don Gagne committed
337
#ifndef __mobile__
Don Gagne's avatar
Don Gagne committed
338 339 340 341
    MainWindow* mainWindow = MainWindow::instance();
    if (mainWindow) {
        delete mainWindow;
    }
Don Gagne's avatar
Don Gagne committed
342
#endif
343
    shutdownVideoStreaming();
344
    delete _toolbox;
Don Gagne's avatar
Don Gagne committed
345 346
}

Don Gagne's avatar
Don Gagne committed
347 348 349 350 351
QGCApplication::~QGCApplication()
{
    // Place shutdown code in _shutdown
}

352
void QGCApplication::_initCommon(void)
353 354
{
    QSettings settings;
355

Don Gagne's avatar
Don Gagne committed
356
    // Register our Qml objects
dogmaphobic's avatar
dogmaphobic committed
357

Don Gagne's avatar
Don Gagne committed
358 359
    qmlRegisterType<QGCPalette>     ("QGroundControl.Palette", 1, 0, "QGCPalette");
    qmlRegisterType<QGCMapPalette>  ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
dogmaphobic's avatar
dogmaphobic committed
360

Don Gagne's avatar
Don Gagne committed
361 362 363 364
    qmlRegisterUncreatableType<CoordinateVector>    ("QGroundControl",                  1, 0, "CoordinateVector",       "Reference only");
    qmlRegisterUncreatableType<QmlObjectListModel>  ("QGroundControl",                  1, 0, "QmlObjectListModel",     "Reference only");
    qmlRegisterUncreatableType<VideoReceiver>       ("QGroundControl",                  1, 0, "VideoReceiver",          "Reference only");
    qmlRegisterUncreatableType<VideoSurface>        ("QGroundControl",                  1, 0, "VideoSurface",           "Reference only");
365
    qmlRegisterUncreatableType<MissionCommandTree>  ("QGroundControl",                  1, 0, "MissionCommandTree",     "Reference only");
Don Gagne's avatar
Don Gagne committed
366

367 368 369 370 371
    qmlRegisterUncreatableType<AutoPilotPlugin>     ("QGroundControl.AutoPilotPlugin",      1, 0, "AutoPilotPlugin",        "Reference only");
    qmlRegisterUncreatableType<VehicleComponent>    ("QGroundControl.AutoPilotPlugin",      1, 0, "VehicleComponent",       "Reference only");
    qmlRegisterUncreatableType<Vehicle>             ("QGroundControl.Vehicle",              1, 0, "Vehicle",                "Reference only");
    qmlRegisterUncreatableType<MissionItem>         ("QGroundControl.Vehicle",              1, 0, "MissionItem",            "Reference only");
    qmlRegisterUncreatableType<MissionManager>      ("QGroundControl.Vehicle",              1, 0, "MissionManager",         "Reference only");
372
    qmlRegisterUncreatableType<ParameterManager>    ("QGroundControl.Vehicle",              1, 0, "ParameterManager",       "Reference only");
373 374 375
    qmlRegisterUncreatableType<JoystickManager>     ("QGroundControl.JoystickManager",      1, 0, "JoystickManager",        "Reference only");
    qmlRegisterUncreatableType<Joystick>            ("QGroundControl.JoystickManager",      1, 0, "Joystick",               "Reference only");
    qmlRegisterUncreatableType<QGCPositionManager>  ("QGroundControl.QGCPositionManager",   1, 0, "QGCPositionManager",     "Reference only");
376
    qmlRegisterUncreatableType<QGCMapPolygon>       ("QGroundControl.FlightMap",            1, 0, "QGCMapPolygon",          "Reference only");
377

dogmaphobic's avatar
dogmaphobic committed
378
    qmlRegisterType<ParameterEditorController>          ("QGroundControl.Controllers", 1, 0, "ParameterEditorController");
dogmaphobic's avatar
dogmaphobic committed
379
    qmlRegisterType<ESP8266ComponentController>         ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController");
Don Gagne's avatar
Don Gagne committed
380 381
    qmlRegisterType<ScreenToolsController>              ("QGroundControl.Controllers", 1, 0, "ScreenToolsController");
    qmlRegisterType<MissionController>                  ("QGroundControl.Controllers", 1, 0, "MissionController");
382 383
    qmlRegisterType<GeoFenceController>                 ("QGroundControl.Controllers", 1, 0, "GeoFenceController");
    qmlRegisterType<RallyPointController>               ("QGroundControl.Controllers", 1, 0, "RallyPointController");
384
    qmlRegisterType<ValuesWidgetController>             ("QGroundControl.Controllers", 1, 0, "ValuesWidgetController");
Don Gagne's avatar
Don Gagne committed
385
    qmlRegisterType<QGCMobileFileDialogController>      ("QGroundControl.Controllers", 1, 0, "QGCMobileFileDialogController");
Don Gagne's avatar
Don Gagne committed
386
    qmlRegisterType<RCChannelMonitorController>         ("QGroundControl.Controllers", 1, 0, "RCChannelMonitorController");
387
    qmlRegisterType<JoystickConfigController>           ("QGroundControl.Controllers", 1, 0, "JoystickConfigController");
Don Gagne's avatar
Don Gagne committed
388
#ifndef __mobile__
389 390
    qmlRegisterType<ViewWidgetController>           ("QGroundControl.Controllers", 1, 0, "ViewWidgetController");
    qmlRegisterType<CustomCommandWidgetController>  ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController");
391
    qmlRegisterType<FirmwareUpgradeController>      ("QGroundControl.Controllers", 1, 0, "FirmwareUpgradeController");
dogmaphobic's avatar
dogmaphobic committed
392
    qmlRegisterType<LogDownloadController>          ("QGroundControl.Controllers", 1, 0, "LogDownloadController");
Don Gagne's avatar
Don Gagne committed
393
    qmlRegisterType<GeoTagController>               ("QGroundControl.Controllers", 1, 0, "GeoTagController");
Don Gagne's avatar
Don Gagne committed
394
#endif
dogmaphobic's avatar
dogmaphobic committed
395

396
    // Register Qml Singletons
397 398 399
    qmlRegisterSingletonType<QGroundControlQmlGlobal>   ("QGroundControl",                          1, 0, "QGroundControl",         qgroundcontrolQmlGlobalSingletonFactory);
    qmlRegisterSingletonType<ScreenToolsController>     ("QGroundControl.ScreenToolsController",    1, 0, "ScreenToolsController",  screenToolsControllerSingletonFactory);
    qmlRegisterSingletonType<MavlinkQmlSingleton>       ("QGroundControl.Mavlink",                  1, 0, "Mavlink",                mavlinkQmlSingletonFactory);
400 401 402 403 404
}

bool QGCApplication::_initForNormalAppBoot(void)
{
    QSettings settings;
405

406
    _loadCurrentStyleSheet();
Lorenz Meier's avatar
Lorenz Meier committed
407

408
    // Exit main application when last window is closed
409
    connect(this, &QGCApplication::lastWindowClosed, this, QGCApplication::quit);
410

Don Gagne's avatar
Don Gagne committed
411 412 413 414
#ifdef __mobile__
    _qmlAppEngine = new QQmlApplicationEngine(this);
    _qmlAppEngine->addImportPath("qrc:/qml");
    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
415
    _qmlAppEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel());
Don Gagne's avatar
Don Gagne committed
416 417
    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
#else
pixhawk's avatar
pixhawk committed
418
    // Start the user interface
Lorenz Meier's avatar
Lorenz Meier committed
419
    MainWindow* mainWindow = MainWindow::_create();
Don Gagne's avatar
Don Gagne committed
420
    Q_CHECK_PTR(mainWindow);
421

422
    // Now that main window is up check for lost log files
423
    connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
424
    emit checkForLostLogFiles();
Don Gagne's avatar
Don Gagne committed
425
#endif
426 427

    // Load known link configurations
428
    toolbox()->linkManager()->loadLinkConfigurationList();
429

Jacob Walser's avatar
Jacob Walser committed
430 431
    // Probe for joysticks
    toolbox()->joystickManager()->init();
432

433
    if (_settingsUpgraded) {
Don Gagne's avatar
Don Gagne committed
434 435 436 437
        showMessage("The format for QGroundControl saved settings has been modified. "
                    "Your saved settings have been reset to defaults.");
    }

438 439 440
    // Connect links with flag AutoconnectLink
    toolbox()->linkManager()->startAutoConnectedLinks();

dogmaphobic's avatar
dogmaphobic committed
441 442 443 444 445
    if (getQGCMapEngine()->wasCacheReset()) {
        showMessage("The Offline Map Cache database has been upgraded. "
                    "Your old map cache sets have been reset.");
    }

Don Gagne's avatar
Don Gagne committed
446
    settings.sync();
447
    return true;
pixhawk's avatar
pixhawk committed
448 449
}

450
bool QGCApplication::_initForUnitTests(void)
pixhawk's avatar
pixhawk committed
451
{
452
    return true;
pixhawk's avatar
pixhawk committed
453 454
}

Don Gagne's avatar
Don Gagne committed
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
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)
{
470 471 472 473
    Q_ASSERT(QGCApplication::_app);
    return QGCApplication::_app;
}

474 475
void QGCApplication::informationMessageBoxOnMainThread(const QString& title, const QString& msg)
{
476 477
    Q_UNUSED(title);
    showMessage(msg);
478 479 480 481
}

void QGCApplication::warningMessageBoxOnMainThread(const QString& title, const QString& msg)
{
482 483 484 485
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
486
    QGCMessageBox::warning(title, msg);
487
#endif
488 489 490 491
}

void QGCApplication::criticalMessageBoxOnMainThread(const QString& title, const QString& msg)
{
492 493 494 495
#ifdef __mobile__
    Q_UNUSED(title)
    showMessage(msg);
#else
496
    QGCMessageBox::critical(title, msg);
497
#endif
498 499
}

500
#ifndef __mobile__
501
void QGCApplication::saveTelemetryLogOnMainThread(QString tempLogfile)
502
{
503 504 505 506 507 508
    // 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()->rawValue().toString();
        QDir saveDir(saveDirPath);

509 510
        QString nameFormat("%1%2.tlog");
        QString dtFormat("yyyy-MM-dd hh-mm-ss");
511 512 513 514 515 516 517 518 519 520 521

        int tryIndex = 1;
        QString saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat)).arg("");
        while (saveDir.exists(saveFileName)) {
            saveFileName = nameFormat.arg(QDateTime::currentDateTime().toString(dtFormat)).arg(QStringLiteral(".%1").arg(tryIndex++));
        }
        QString saveFilePath = saveDir.absoluteFilePath(saveFileName);

        QFile tempFile(tempLogfile);
        if (!tempFile.copy(saveFilePath)) {
            QGCMessageBox::warning(tr("Telemetry save error"), tr("Unable to save telemetry log. Error copying telemetry to '%1': '%2'.").arg(saveFilePath).arg(tempFile.errorString()));
522
        }
523 524
    }

525 526
    QFile::remove(tempLogfile);
}
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561

void QGCApplication::checkTelemetrySavePathOnMainThread(void)
{
    // 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");

    QString saveDirPath = _toolbox->settingsManager()->appSettings()->telemetrySavePath()->rawValue().toString();
    if (saveDirPath.isEmpty()) {
        QString error = tr("Unable to save telemetry log. Telemetry save directory is not set.");
        if (useMessageBox) {
            QGCMessageBox::warning(errorTitle, error);
        } else {
            showMessage(error);
        }
        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);
        if (useMessageBox) {
            QGCMessageBox::warning(errorTitle, error);
        } else {
            showMessage(error);
        }
        return false;
    }

    return true;
}
562
#endif
563

564
void QGCApplication::_loadCurrentStyleSheet(void)
565
{
Don Gagne's avatar
Don Gagne committed
566
#ifndef __mobile__
567 568
    bool success = true;
    QString styles;
569

570 571 572 573 574 575 576 577 578
    // The dark style sheet is the master. Any other selected style sheet just overrides
    // the colors of the master sheet.
    QFile masterStyleSheet(_darkStyleFile);
    if (masterStyleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
        styles = masterStyleSheet.readAll();
    } else {
        qDebug() << "Unable to load master dark style sheet";
        success = false;
    }
579

580
    if (success && !_toolbox->settingsManager()->appSettings()->indoorPalette()->rawValue().toBool()) {
581 582 583 584 585
        // Load the slave light stylesheet.
        QFile styleSheet(_lightStyleFile);
        if (styleSheet.open(QIODevice::ReadOnly | QIODevice::Text)) {
            styles += styleSheet.readAll();
        } else {
586
            qWarning() << "Unable to load slave light sheet:";
587 588 589
            success = false;
        }
    }
dogmaphobic's avatar
dogmaphobic committed
590

591
    setStyleSheet(styles);
592

593 594 595 596
    if (!success) {
        // Fall back to plastique if we can't load our own
        setStyle("plastique");
    }
Don Gagne's avatar
Don Gagne committed
597
#endif
598
}
Don Gagne's avatar
Don Gagne committed
599

Don Gagne's avatar
Don Gagne committed
600
void QGCApplication::reportMissingParameter(int componentId, const QString& name)
601
{
Don Gagne's avatar
Don Gagne committed
602 603
    _missingParams += QString("%1:%2").arg(componentId).arg(name);
    _missingParamsDelayedDisplayTimer.start();
604
}
605

Don Gagne's avatar
Don Gagne committed
606 607
/// Called when the delay timer fires to show the missing parameters warning
void QGCApplication::_missingParamsDisplay(void)
608
{
Don Gagne's avatar
Don Gagne committed
609
    Q_ASSERT(_missingParams.count());
dogmaphobic's avatar
dogmaphobic committed
610

Don Gagne's avatar
Don Gagne committed
611
    QString params;
612
    foreach (const QString &name, _missingParams) {
Don Gagne's avatar
Don Gagne committed
613 614
        if (params.isEmpty()) {
            params += name;
615
        } else {
Don Gagne's avatar
Don Gagne committed
616
            params += QString(", %1").arg(name);
617 618
        }
    }
Don Gagne's avatar
Don Gagne committed
619
    _missingParams.clear();
dogmaphobic's avatar
dogmaphobic committed
620

621
    showMessage(QString("Parameters missing from firmware: %1. You may be running an older version of firmware QGC does not work correctly with or your firmware has a bug in it.").arg(params));
622 623
}

Gus Grubba's avatar
Gus Grubba committed
624
QObject* QGCApplication::_rootQmlObject()
Don Gagne's avatar
Don Gagne committed
625 626 627 628
{
#ifdef __mobile__
    return _qmlAppEngine->rootObjects()[0];
#else
629 630 631 632 633 634 635 636 637 638
    MainWindow * mainWindow = MainWindow::instance();
    if (mainWindow) {
        return mainWindow->rootQmlObject();
    } else if (runningUnitTests()){
        // Unit test can run without a main window
        return NULL;
    } else {
        qWarning() << "Why is MainWindow missing?";
        return NULL;
    }
Don Gagne's avatar
Don Gagne committed
639 640 641 642
#endif
}


643
void QGCApplication::showMessage(const QString& message)
Don Gagne's avatar
Don Gagne committed
644
{
Don Gagne's avatar
Don Gagne committed
645 646 647 648 649
    // Special case hack for ArduPilot prearm messages. These show up in the center of the map, so no need for popup.
    if (message.contains("PreArm:")) {
        return;
    }

650 651 652 653 654
    QObject* rootQmlObject = _rootQmlObject();

    if (rootQmlObject) {
        QVariant varReturn;
        QVariant varMessage = QVariant::fromValue(message);
Don Gagne's avatar
Don Gagne committed
655

656 657 658 659 660 661 662 663 664
        QMetaObject::invokeMethod(_rootQmlObject(), "showMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
#ifndef __mobile__
    } else if (runningUnitTests()){
        // Unit test can run without a main window which will lead to no root qml object. Use QGCMessageBox instead
        QGCMessageBox::information("Unit Test", message);
#endif
    } else {
        qWarning() << "Internal error";
    }
Don Gagne's avatar
Don Gagne committed
665 666 667 668 669 670 671
}

void QGCApplication::showSetupView(void)
{
    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
}

672
void QGCApplication::qmlAttemptWindowClose(void)
Don Gagne's avatar
Don Gagne committed
673
{
674
    QMetaObject::invokeMethod(_rootQmlObject(), "attemptWindowClose");
Don Gagne's avatar
Don Gagne committed
675 676 677
}


678
void QGCApplication::setLastKnownHomePosition(QGeoCoordinate& lastKnownHomePosition)
Don Gagne's avatar
Don Gagne committed
679 680 681
{
    QSettings settings;

682 683 684 685
    settings.setValue(_lastKnownHomePositionLatKey, lastKnownHomePosition.latitude());
    settings.setValue(_lastKnownHomePositionLonKey, lastKnownHomePosition.longitude());
    settings.setValue(_lastKnownHomePositionAltKey, lastKnownHomePosition.altitude());
    _lastKnownHomePosition = lastKnownHomePosition;
Don Gagne's avatar
Don Gagne committed
686
}