From d2923d8ee04d34636552c72880708b3adbccdf0b Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Wed, 26 Nov 2014 11:19:10 -0800 Subject: [PATCH] Track Singletons in QGCApplication - allow unit tests to use QGCApplication - clear global singletons for each unit test run --- qgroundcontrol.pro | 2 + .../AutoPilotPluginManager.cc | 23 ++- src/AutoPilotPlugins/AutoPilotPluginManager.h | 15 +- .../PX4/PX4AutoPilotPlugin.cc | 2 +- src/QGCApplication.cc | 171 +++++++++--------- src/QGCApplication.h | 48 +++-- src/QGCSingleton.cc | 36 ++++ src/QGCSingleton.h | 49 +++++ src/VehicleSetup/SetupView.cc | 4 +- src/comm/LinkManager.cc | 25 ++- src/comm/LinkManager.h | 22 ++- src/main.cc | 28 ++- src/qgcunittest/AutoTest.h | 14 +- src/qgcunittest/MockUASManager.h | 3 + src/uas/UAS.cc | 4 +- src/uas/UASManager.cc | 36 ++-- src/uas/UASManager.h | 13 +- src/uas/UASManagerInterface.h | 8 +- src/ui/MainWindow.cc | 6 + src/ui/MainWindow.h | 8 + src/ui/uas/UASControlWidget.cc | 4 +- 21 files changed, 352 insertions(+), 169 deletions(-) create mode 100644 src/QGCSingleton.cc create mode 100644 src/QGCSingleton.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 75b92da2c..1d5a9230c 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -335,6 +335,7 @@ FORMS += \ HEADERS += \ src/MG.h \ src/QGCApplication.h \ + src/QGCSingleton.h \ src/uas/UASInterface.h \ src/uas/UAS.h \ src/uas/UASManager.h \ @@ -493,6 +494,7 @@ HEADERS += \ SOURCES += \ src/main.cc \ src/QGCApplication.cc \ + src/QGCSingleton.cc \ src/uas/UASManager.cc \ src/uas/UAS.cc \ src/comm/LinkManager.cc \ diff --git a/src/AutoPilotPlugins/AutoPilotPluginManager.cc b/src/AutoPilotPlugins/AutoPilotPluginManager.cc index 3b4397475..de27c15ac 100644 --- a/src/AutoPilotPlugins/AutoPilotPluginManager.cc +++ b/src/AutoPilotPlugins/AutoPilotPluginManager.cc @@ -27,9 +27,30 @@ #include "AutoPilotPluginManager.h" #include "PX4/PX4AutoPilotPlugin.h" #include "Generic/GenericAutoPilotPlugin.h" +#include "QGCApplication.h" + +AutoPilotPluginManager* AutoPilotPluginManager::_instance = NULL; + +AutoPilotPluginManager* AutoPilotPluginManager::instance(void) +{ + if (_instance == NULL) { + _instance = new AutoPilotPluginManager(qgcApp()); + Q_CHECK_PTR(_instance); + } + + Q_ASSERT(_instance); + + return _instance; +} + +void AutoPilotPluginManager::deleteInstance(void) +{ + _instance = NULL; + delete this; +} AutoPilotPluginManager::AutoPilotPluginManager(QObject* parent) : - QObject(parent) + QGCSingleton(parent) { // All plugins are constructed here so that they end up on the correct thread _pluginMap[MAV_AUTOPILOT_PX4] = new PX4AutoPilotPlugin(this); diff --git a/src/AutoPilotPlugins/AutoPilotPluginManager.h b/src/AutoPilotPlugins/AutoPilotPluginManager.h index d27960137..073b166bc 100644 --- a/src/AutoPilotPlugins/AutoPilotPluginManager.h +++ b/src/AutoPilotPlugins/AutoPilotPluginManager.h @@ -30,32 +30,33 @@ #include "UASInterface.h" #include "VehicleComponent.h" -#include "QGCApplication.h" #include "AutoPilotPlugin.h" +#include "QGCSingleton.h" /// @file /// @brief The AutoPilotPlugin manager is a singleton which maintains the list of AutoPilotPlugin objects. /// /// @author Don Gagne -class AutoPilotPluginManager : public QObject +class AutoPilotPluginManager : public QGCSingleton { Q_OBJECT public: + /// @brief Returns the AutoPilotPluginManager singleton + static AutoPilotPluginManager* instance(void); + + virtual void deleteInstance(void); /// @brief Returns the singleton AutoPilot instance for the specified auto pilot type. /// @param autopilotType Specified using the MAV_AUTOPILOT_* values. AutoPilotPlugin* getInstanceForAutoPilotPlugin(int autopilotType); private: - /// @brief Only QGCQpplication is allowed to call constructor. All access to singleton is through - /// QGCApplication::singletonAutoPilotPluginManager. + /// All access to singleton is through AutoPilotPluginManager::instance AutoPilotPluginManager(QObject* parent = NULL); - /// @brief Only QGCQpplication is allowed to call constructor. All access to singleton is through - /// QGCApplication::singletonAutoPilotPluginManager. - friend class QGCApplication; + static AutoPilotPluginManager* _instance; QMap _pluginMap; }; diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index 14885b036..0cd536326 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -167,7 +167,7 @@ QString PX4AutoPilotPlugin::getShortModeText(uint8_t baseMode, uint32_t customMo mode = "|OFFBOARD"; } } else { - mode = qgcApp()->singletonAutoPilotPluginManager()->getInstanceForAutoPilotPlugin(MAV_AUTOPILOT_GENERIC)->getShortModeText(baseMode, customMode); + mode = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(MAV_AUTOPILOT_GENERIC)->getShortModeText(baseMode, customMode); } return mode; diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 0f6201b22..fd3dc4dbf 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -1,4 +1,4 @@ -/*===================================================================== + /*===================================================================== QGroundControl Open Source Ground Control Station @@ -48,13 +48,21 @@ #include "GAudioOutput.h" #include "CmdLineOptParser.h" #include "QGCMessageBox.h" +#include "MainWindow.h" +#include "UDPLink.h" +#include "MAVLinkSimulationLink.h" +#include "SerialLink.h" +#include "QGCSingleton.h" +#include "LinkManager.h" +#include "UASManager.h" +#include "AutoPilotPluginManager.h" #ifdef QGC_RTLAB_ENABLED #include "OpalLink.h" #endif -#include "UDPLink.h" -#include "MAVLinkSimulationLink.h" -#include "SerialLink.h" + + +QGCApplication* QGCApplication::_app = NULL; const char* QGCApplication::_deleteAllSettingsKey = "DeleteAllSettingsNextBoot"; const char* QGCApplication::_settingsVersionKey = "SettingsVersion"; @@ -77,9 +85,12 @@ const char* QGCApplication::_savedFileParameterDirectoryName = "SavedParameters" QGCApplication::QGCApplication(int &argc, char* argv[]) : -QApplication(argc, argv), -_mainWindow(NULL) + QApplication(argc, argv), + _mainWindow(NULL) { + Q_ASSERT(_app == NULL); + _app = this; + // Set application information this->setApplicationName(QGC_APPLICATION_NAME); this->setOrganizationName(QGC_ORG_NAME); @@ -119,7 +130,12 @@ _mainWindow(NULL) } -bool QGCApplication::init(void) +void QGCApplication::_initCommon(void) +{ + _createSingletons(); +} + +bool QGCApplication::_initForNormalAppBoot(void) { QSettings settings; @@ -169,16 +185,6 @@ bool QGCApplication::init(void) } } - // If we made it this far and we still don't have a location. Either the specfied location was invalid - // or we coudn't create a default location. Either way, we need to let the user know and prompt for a new - /// settings. - if (savedFilesLocation.isEmpty()) { - QMessageBox::warning(MainWindow::instance(), - tr("Bad save location"), - tr("The location to save files to is invalid, or cannot be written to. Please provide a new one.")); - MainWindow::instance()->showSettings(); - } - mode = (enum MainWindow::CUSTOM_MODE) settings.value("QGC_CUSTOM_MODE", (int)MainWindow::CUSTOM_MODE_PX4).toInt(); settings.sync(); @@ -202,17 +208,10 @@ bool QGCApplication::init(void) // "Warning: Do not use this function in conjunction with Qt Style Sheets." // setFont(fontDatabase.font(fontFamilyName, "Roman", 12)); - // Start the comm link manager - splashScreen->showMessage(tr("Starting communication links"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141)); - startLinkManager(); - - // Start the UAS Manager - splashScreen->showMessage(tr("Starting UAS manager"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141)); - startUASManager(); - // Start the user interface splashScreen->showMessage(tr("Starting user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141)); - _mainWindow = MainWindow::_create(splashScreen, mode); + _mainWindow = new MainWindow(splashScreen, mode); + Q_CHECK_PTR(_mainWindow); UDPLink* udpLink = NULL; @@ -232,21 +231,30 @@ bool QGCApplication::init(void) #ifdef QGC_RTLAB_ENABLED // Add OpalRT Link, but do not connect OpalLink* opalLink = new OpalLink(); - MainWindow::instance()->addLink(opalLink); + _mainWindow->addLink(opalLink); #endif // Remove splash screen splashScreen->finish(_mainWindow); _mainWindow->splashScreenFinished(); + // If we made it this far and we still don't have a location. Either the specfied location was invalid + // or we coudn't create a default location. Either way, we need to let the user know and prompt for a new + /// settings. + if (savedFilesLocation.isEmpty()) { + QGCMessageBox::warning(tr("Bad save location"), + tr("The location to save files to is invalid, or cannot be written to. Please provide a new one.")); + _mainWindow->showSettings(); + } + if (settingsUpgraded) { _mainWindow->showInfoMessage(tr("Settings Cleared"), - tr("The format for QGroundControl saved settings has been modified. " - "Your saved settings have been reset to defaults.")); + tr("The format for QGroundControl saved settings has been modified. " + "Your saved settings have been reset to defaults.")); } // Check if link could be connected - if (udpLink && !LinkManager::instance()->connectLink(udpLink)) + if (udpLink && LinkManager::instance()->connectLink(udpLink)) { QMessageBox::StandardButton button = QGCMessageBox::critical(tr("Could not connect UDP port. Is an instance of %1 already running?").arg(qAppName()), tr("It is recommended to close the application and stop all instances. Click Yes to close."), @@ -263,66 +271,18 @@ bool QGCApplication::init(void) return true; } -/** - * @brief Destructor for the groundstation. It destroys all loaded instances. - * - **/ -QGCApplication::~QGCApplication() +bool QGCApplication::_initForUnitTests(void) { - delete UASManager::instance(); - delete LinkManager::instance(); + return true; } /** - * @brief Start the link managing component. + * @brief Destructor for the groundstation. It destroys all loaded instances. * - * The link manager keeps track of all communication links and provides the global - * packet queue. It is the main communication hub **/ -void QGCApplication::startLinkManager() +QGCApplication::~QGCApplication() { - LinkManager::instance(); -} -/** - * @brief Start the Unmanned Air System Manager - * - **/ -void QGCApplication::startUASManager() -{ - // Load UAS plugins - QDir pluginsDir = QDir(qApp->applicationDirPath()); - -#if defined(Q_OS_WIN) - if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") - pluginsDir.cdUp(); -#elif defined(Q_OS_LINUX) - if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") - pluginsDir.cdUp(); -#elif defined(Q_OS_MAC) - if (pluginsDir.dirName() == "MacOS") { - pluginsDir.cdUp(); - pluginsDir.cdUp(); - pluginsDir.cdUp(); - } -#endif - pluginsDir.cd("plugins"); - - UASManager::instance(); - - // Load plugins - - QStringList pluginFileNames; - - foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { - QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); - QObject *plugin = loader.instance(); - if (plugin) { - //populateMenus(plugin); - pluginFileNames += fileName; - //printf(QString("Loaded plugin from " + fileName + "\n").toStdString().c_str()); - } - } } void QGCApplication::deleteAllSettingsNextBoot(void) @@ -415,7 +375,48 @@ void QGCApplication::setPromptFlightDataSave(bool promptForSave) /// @brief Returns the QGCApplication object singleton. QGCApplication* qgcApp(void) { - QGCApplication* app = dynamic_cast(qApp); - Q_ASSERT(app); - return app; + Q_ASSERT(QGCApplication::_app); + return QGCApplication::_app; +} + +/// @brief We create all the non-ui based singletons here instead of allowing them to be created randomly +/// by calls to instance. The reason being that depending on boot sequence the singleton may end +/// up being creating on something other than the main thread. +void QGCApplication::_createSingletons(void) +{ + + LinkManager* linkManager = LinkManager::instance(); + Q_UNUSED(linkManager); + Q_ASSERT(linkManager); + + UASManagerInterface* uasManager = UASManager::instance(); + Q_UNUSED(uasManager); + Q_ASSERT(uasManager); + + AutoPilotPluginManager* pluginManager = AutoPilotPluginManager::instance(); + Q_UNUSED(pluginManager); + Q_ASSERT(pluginManager); +} + +void QGCApplication::destroySingletonsForUnitTest(void) +{ + foreach(QGCSingleton* singleton, _singletons) { + Q_ASSERT(singleton); + singleton->deleteInstance(); + } + + if (_mainWindow) { + _mainWindow->deleteInstance(); + _mainWindow = NULL; + } + + _singletons.clear(); +} + +void QGCApplication::registerSingleton(QGCSingleton* singleton) +{ + Q_ASSERT(singleton); + Q_ASSERT(!_singletons.contains(singleton)); + + _singletons.append(singleton); } diff --git a/src/QGCApplication.h b/src/QGCApplication.h index 3cb00237b..74fd33631 100644 --- a/src/QGCApplication.h +++ b/src/QGCApplication.h @@ -29,19 +29,18 @@ * */ - #ifndef QGCAPPLICATION_H #define QGCAPPLICATION_H #include -#include "MainWindow.h" -#include "UASManager.h" -#include "LinkManager.h" #ifdef QGC_RTLAB_ENABLED #include "OpalLink.h" #endif +// Work around circular header includes +class QGCSingleton; +class MainWindow; /** * @brief The main application and management class. @@ -58,10 +57,6 @@ public: QGCApplication(int &argc, char* argv[]); ~QGCApplication(); - /// @brief Initialize the applicaation. - /// @return false: init failed, app should exit - bool init(void); - /// @brief Sets the persistent flag to delete all settings the next time QGroundControl is started. void deleteAllSettingsNextBoot(void); @@ -88,19 +83,34 @@ public: /// @brief Sets the flag to log all mavlink connections void setPromptFlightDataSave(bool promptForSave); + + /// @brief All global singletons must be registered such that QGCApplication::destorySingletonsForUnitTest + /// can work correctly. + void registerSingleton(QGCSingleton* singleton); + + /// @brief Creates non-ui based singletons for unit testing + void createSingletonsForUnitTest(void) { _createSingletons(); } + + /// @brief Destroys all singletons. Used by unit test code to reset global state. + void destroySingletonsForUnitTest(void); + +public: + /// @brief Perform initialize which is common to both normal application running and unit tests. + /// Although public should only be called by main. + void _initCommon(void); + + /// @brief Intialize the application for normal application boot. Or in other words we are not going to run + /// unit tests. Although public should only be called by main. + bool _initForNormalAppBoot(void); -protected: - void startLinkManager(); + /// @brief Intialize the application for normal application boot. Or in other words we are not going to run + /// unit tests. Although public should only be called by main. + bool _initForUnitTests(void); - /** - * @brief Start the robot managing system - * - * The robot manager keeps track of the configured robots. - **/ - void startUASManager(); + static QGCApplication* _app; ///< Our own singleton. Should be reference directly by qgcApp private: - MainWindow* _mainWindow; + void _createSingletons(void); static const char* _settingsVersionKey; ///< Settings key which hold settings version static const char* _deleteAllSettingsKey; ///< If this settings key is set on boot, all settings will be deleted @@ -110,6 +120,10 @@ private: static const char* _defaultSavedFileDirectoryName; ///< Default name for user visible save file directory static const char* _savedFileMavlinkLogDirectoryName; ///< Name of mavlink log subdirectory static const char* _savedFileParameterDirectoryName; ///< Name of parameter subdirectory + + MainWindow* _mainWindow; + + QList _singletons; ///< List of registered global singletons }; /// @brief Returns the QGCApplication object singleton. diff --git a/src/QGCSingleton.cc b/src/QGCSingleton.cc new file mode 100644 index 000000000..5cda7a69a --- /dev/null +++ b/src/QGCSingleton.cc @@ -0,0 +1,36 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @brief Base class for global singletons +/// +/// @author Don Gagne + +#include "QGCSingleton.h" +#include "QGCApplication.h" + +QGCSingleton::QGCSingleton(QObject* parent) : + QObject(parent) +{ + qgcApp()->registerSingleton(this); +} diff --git a/src/QGCSingleton.h b/src/QGCSingleton.h new file mode 100644 index 000000000..708f1bcdf --- /dev/null +++ b/src/QGCSingleton.h @@ -0,0 +1,49 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @brief Base class for global singletons +/// +/// @author Don Gagne + +#ifndef QGCSINGLETON_H +#define QGCSINGLETON_H + +#include + +#include "QGCApplication.h" + +class QGCSingleton : public QObject +{ + Q_OBJECT + +public: + /// @brief Contructor will register singleton to QGCApplication + QGCSingleton(QObject* parent = NULL); + + /// @brief Implementation should delete the singleton such that next call to instance + /// will create a new singleton. + virtual void deleteInstance(void) = 0; +}; + +#endif diff --git a/src/VehicleSetup/SetupView.cc b/src/VehicleSetup/SetupView.cc index 8c013a42d..48b10746f 100644 --- a/src/VehicleSetup/SetupView.cc +++ b/src/VehicleSetup/SetupView.cc @@ -28,7 +28,7 @@ #include "ui_SetupView.h" #include "UASManager.h" -#include "AutoPilotPlugin.h" +#include "AutoPilotPluginManager.h" #include "VehicleComponent.h" #include "VehicleComponentButton.h" #include "SummaryPage.h" @@ -208,7 +208,7 @@ void SetupView::_parametersReady(void) _ui->summaryButton->setVisible(true); - _components = AutoPilotPlugin::getInstanceForAutoPilotPlugin(_uasCurrent->getAutopilotType())->getVehicleComponents(_uasCurrent); + _components = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(_uasCurrent->getAutopilotType())->getVehicleComponents(_uasCurrent); foreach(VehicleComponent* component, _components) { VehicleComponentButton* button = new VehicleComponentButton(component, _ui->navBarWidget); diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 15aa623e9..57b97fe32 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -36,26 +36,35 @@ This file is part of the QGROUNDCONTROL project #include "LinkManager.h" #include "MainWindow.h" #include "QGCMessageBox.h" +#include "QGCApplication.h" -LinkManager* LinkManager::instance() +LinkManager* LinkManager::_instance = NULL; + +LinkManager* LinkManager::instance(void) { - static LinkManager* _instance = 0; if(_instance == 0) { - _instance = new LinkManager(); - - /* Set the application as parent to ensure that this object - * will be destroyed when the main application exits */ - _instance->setParent(qApp); + _instance = new LinkManager(qgcApp()); + Q_CHECK_PTR(_instance); } + + Q_ASSERT(_instance); + return _instance; } +void LinkManager::deleteInstance(void) +{ + _instance = NULL; + delete this; +} + /** * @brief Private singleton constructor * * This class implements the singleton design pattern and has therefore only a private constructor. **/ -LinkManager::LinkManager() : +LinkManager::LinkManager(QObject* parent) : + QGCSingleton(parent), _connectionsSuspended(false) { _links = QList(); diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 7f6df39d3..8dd4bc050 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -36,9 +36,11 @@ This file is part of the PIXHAWK project #include #include #include -#include -#include -#include + +#include "LinkInterface.h" +#include "SerialLink.h" +#include "ProtocolInterface.h" +#include "QGCSingleton.h" /** * The Link Manager organizes the physical Links. It can manage arbitrary @@ -46,12 +48,16 @@ This file is part of the PIXHAWK project * protocol instance to transport the link data into the application. * **/ -class LinkManager : public QObject +class LinkManager : public QGCSingleton { Q_OBJECT public: - static LinkManager* instance(); + /// @brief Returns the LinkManager singleton + static LinkManager* instance(void); + + virtual void deleteInstance(void); + ~LinkManager(); void run(); @@ -100,14 +106,16 @@ signals: void linkRemoved(LinkInterface* link); private: - LinkManager(void); + /// @brief All access to LinkManager is through LinkManager::instance + LinkManager(QObject* parent = NULL); + + static LinkManager* _instance; QList _links; QMultiMap _protocolLinks; QMutex _dataMutex; bool _connectionsSuspendedMsg(void); - static LinkManager* _instance; bool _connectionsSuspended; ///< true: all new connections should not be allowed QString _connectionsSuspendedReason; ///< User visible reason for suspension diff --git a/src/main.cc b/src/main.cc index 9f49afae6..573ee6edd 100644 --- a/src/main.cc +++ b/src/main.cc @@ -124,8 +124,19 @@ int main(int argc, char *argv[]) _CrtSetReportHook(WindowsCrtReportHook); #endif } +#endif + + QGCApplication* app = new QGCApplication(argc, argv); + Q_CHECK_PTR(app); + app->_initCommon(); + +#ifdef QT_DEBUG if (runUnitTests) { + if (!app->_initForUnitTests()) { + return -1; + } + // Run the test int failures = AutoTest::run(argc-1, argv); if (failures == 0) @@ -136,16 +147,13 @@ int main(int argc, char *argv[]) { qDebug() << failures << " TESTS FAILED!"; } - return failures; - } + return -failures; + } else #endif - - QGCApplication* app = new QGCApplication(argc, argv); - Q_CHECK_PTR(app); - - if (!app->init()) { - return -1; + { + if (!app->_initForNormalAppBoot()) { + return -1; + } + return app->exec(); } - - return app->exec(); } diff --git a/src/qgcunittest/AutoTest.h b/src/qgcunittest/AutoTest.h index b118d7fc2..d130b12c1 100755 --- a/src/qgcunittest/AutoTest.h +++ b/src/qgcunittest/AutoTest.h @@ -11,6 +11,7 @@ #include #include #include +#include "QGCApplication.h" namespace AutoTest { @@ -51,10 +52,11 @@ namespace AutoTest inline int run(int argc, char *argv[]) { int ret = 0; - QApplication t(argc, argv); foreach (QObject* test, testList()) - { - ret += QTest::qExec(test, argc, argv); + { + qgcApp()->destroySingletonsForUnitTest(); + qgcApp()->createSingletonsForUnitTest(); + ret += QTest::qExec(test, argc, argv); } return ret; @@ -76,10 +78,4 @@ public: #define DECLARE_TEST(className) static Test t(#className); -#define TEST_MAIN \ - int main(int argc, char *argv[]) \ - { \ - return AutoTest::run(argc, argv); \ - } - #endif // AUTOTEST_H diff --git a/src/qgcunittest/MockUASManager.h b/src/qgcunittest/MockUASManager.h index 0648f70a9..5607ea11c 100644 --- a/src/qgcunittest/MockUASManager.h +++ b/src/qgcunittest/MockUASManager.h @@ -53,6 +53,9 @@ public: // MockUASManager methods MockUASManager(void); + // Does not support singleton deletion + virtual void deleteInstance(void) { Q_ASSERT(false); } + /// Sets the currently active mock UAS /// @param mockUAS new mock uas, NULL for no active UAS void setMockActiveUAS(MockUAS* mockUAS); diff --git a/src/uas/UAS.cc b/src/uas/UAS.cc index 735032f7a..8dedbec47 100644 --- a/src/uas/UAS.cc +++ b/src/uas/UAS.cc @@ -29,7 +29,7 @@ #include "SerialLink.h" #include "UASParameterCommsMgr.h" #include -#include "AutoPilotPlugin.h" +#include "AutoPilotPluginManager.h" #include "QGCMessageBox.h" /** @@ -3304,7 +3304,7 @@ QString UAS::getAudioModeTextFor(int id) */ QString UAS::getShortModeTextFor(uint8_t base_mode, uint32_t custom_mode, int autopilot) { - QString mode = AutoPilotPlugin::getInstanceForAutoPilotPlugin(autopilot)->getShortModeText(base_mode, custom_mode); + QString mode = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(autopilot)->getShortModeText(base_mode, custom_mode); if (mode.length() == 0) { diff --git a/src/uas/UASManager.cc b/src/uas/UASManager.cc index e2e71fb5b..6007b4a69 100644 --- a/src/uas/UASManager.cc +++ b/src/uas/UASManager.cc @@ -18,11 +18,13 @@ #include "UASManager.h" #include "QGC.h" #include "QGCMessageBox.h" +#include "QGCApplication.h" #define PI 3.1415926535897932384626433832795 #define MEAN_EARTH_DIAMETER 12756274.0 #define UMR 0.017453292519943295769236907684886 +UASManager* UASManager::_instance = NULL; UASManagerInterface* UASManager::_mockUASManager = NULL; @@ -37,17 +39,22 @@ UASManagerInterface* UASManager::instance() return _mockUASManager; } - static UASManager* _instance = 0; - if(_instance == 0) { - _instance = new UASManager(); - - // Set the application as parent to ensure that this object - // will be destroyed when the main application exits - _instance->setParent(qApp); + if(_instance == NULL) { + _instance = new UASManager(qgcApp()); + Q_CHECK_PTR(_instance); } + + Q_ASSERT(_instance); + return _instance; } +void UASManager::deleteInstance(void) +{ + _instance = NULL; + delete this; +} + void UASManager::storeSettings() { QSettings settings; @@ -256,13 +263,14 @@ void UASManager::uavChangedHomePosition(int uav, double lat, double lon, double * * This class implements the singleton design pattern and has therefore only a private constructor. **/ -UASManager::UASManager() : - activeUAS(NULL), - offlineUASWaypointManager(NULL), - homeLat(47.3769), - homeLon(8.549444), - homeAlt(470.0), - homeFrame(MAV_FRAME_GLOBAL) +UASManager::UASManager(QObject* parent) : + UASManagerInterface(parent), + activeUAS(NULL), + offlineUASWaypointManager(NULL), + homeLat(47.3769), + homeLon(8.549444), + homeAlt(470.0), + homeFrame(MAV_FRAME_GLOBAL) { loadSettings(); setLocalNEDSafetyBorders(1, -1, 0, -1, 1, -1); diff --git a/src/uas/UASManager.h b/src/uas/UASManager.h index a0847cf5c..b79fb8d1a 100644 --- a/src/uas/UASManager.h +++ b/src/uas/UASManager.h @@ -38,6 +38,7 @@ This file is part of the QGROUNDCONTROL project #include #include "../../libs/eigen/Eigen/Eigen" #include "QGCGeo.h" +#include "QGCApplication.h" /** * @brief Central manager for all connected aerial vehicles @@ -50,7 +51,11 @@ class UASManager : public UASManagerInterface Q_OBJECT public: - static UASManagerInterface* instance(); + /// @brief Returns the UASManager singleton + static UASManagerInterface* instance(void); + + virtual void deleteInstance(void); + ~UASManager(); /** @@ -249,7 +254,6 @@ public slots: protected: - UASManager(); QList systems; UASInterface* activeUAS; UASWaypointManager *offlineUASWaypointManager; @@ -266,6 +270,11 @@ protected: void initReference(const double & latitude, const double & longitude, const double & altitude); private: + /// @brief All access to UASManager singleton is through UASManager::instance + UASManager(QObject* parent = NULL); + + static UASManager* _instance; + static UASManagerInterface* _mockUASManager; public: diff --git a/src/uas/UASManagerInterface.h b/src/uas/UASManagerInterface.h index a1b3e7d38..4bb5d4967 100644 --- a/src/uas/UASManagerInterface.h +++ b/src/uas/UASManagerInterface.h @@ -34,9 +34,11 @@ #include #include #include -#include + +#include "UASInterface.h" #include "../../libs/eigen/Eigen/Eigen" #include "QGCGeo.h" +#include "QGCSingleton.h" /** * @brief Central manager for all connected aerial vehicles @@ -50,11 +52,13 @@ * * See UASManager.h for method documentation **/ -class UASManagerInterface : public QObject +class UASManagerInterface : public QGCSingleton { Q_OBJECT public: + UASManagerInterface(QObject* parent = NULL) : QGCSingleton(parent) { } + virtual UASInterface* getActiveUAS() = 0; virtual UASWaypointManager *getActiveUASWaypointManager() = 0; virtual UASInterface* silentGetActiveUAS() = 0; diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 54a6ed6ca..83493a142 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -108,6 +108,12 @@ MainWindow* MainWindow::instance(void) return _instance; } +void MainWindow::deleteInstance(void) +{ + delete _instance; + _instance = NULL; +} + /// @brief Private constructor for MainWindow. MainWindow singleton is only ever created /// by MainWindow::_create method. Hence no other code should have access to /// constructor. diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 7de1a1110..3a970d94d 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -102,6 +102,9 @@ public: /// been created. static MainWindow* instance(void); + /// @brief Deletes the MainWindow singleton + void deleteInstance(void); + /// @brief Creates the MainWindow singleton. Should only be called once by QGCApplication. static MainWindow* _create(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode); @@ -469,6 +472,11 @@ private: /// Constructor is private since all creation should be through MainWindow::instance. MainWindow(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode); + /// @brief Two phase construction such that MainWindow::instance is available to code + void _init(void); + + friend class QGCApplication; + void _hideSplashScreen(void); void _openUrl(const QString& url, const QString& errorMessage); diff --git a/src/ui/uas/UASControlWidget.cc b/src/ui/uas/UASControlWidget.cc index ec58a82c2..7841be3d6 100644 --- a/src/ui/uas/UASControlWidget.cc +++ b/src/ui/uas/UASControlWidget.cc @@ -39,7 +39,7 @@ This file is part of the PIXHAWK project #include #include #include "QGC.h" -#include "AutoPilotPlugin.h" +#include "AutoPilotPluginManager.h" UASControlWidget::UASControlWidget(QWidget *parent) : QWidget(parent), uasID(-1), @@ -68,7 +68,7 @@ void UASControlWidget::updateModesList() } } - AutoPilotPlugin* autopilotPlugin = AutoPilotPlugin::getInstanceForAutoPilotPlugin(autopilot); + AutoPilotPlugin* autopilotPlugin = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(autopilot); _modeList = autopilotPlugin->getModes(); -- 2.22.0