diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index b3ba1acf4d8fc9551c8fbcdd7156a19b46265439..5a866dc2ec2b9ced7a0d70321519b94306c40e84 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -270,7 +270,6 @@ HEADERS += \ src/QmlControls/QGCQGeoCoordinate.h \ src/QmlControls/QGroundControlQmlGlobal.h \ src/QmlControls/QmlObjectListModel.h \ - src/SerialPortIds.h \ src/uas/FileManager.h \ src/uas/UAS.h \ src/uas/UASInterface.h \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 176c47811750253476752f9905a47b43ad3e5ea3..dcaa8a389e5fe9c86874413a06fabb375b1be361 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -28,7 +28,6 @@ src/AutoPilotPlugins/PX4/PowerComponent.qml src/AutoPilotPlugins/PX4/PowerComponentSummary.qml src/VehicleSetup/PX4FlowSensor.qml - src/QmlControls/ClickableColor.qml src/QmlControls/DropButton.qml src/QmlControls/ExclusiveGroupItem.qml @@ -42,7 +41,6 @@ src/QmlControls/ParameterEditor.qml src/QmlControls/ParameterEditorDialog.qml src/QmlControls/QGCButton.qml - src/QmlControls/QGCCanvas.qml src/QmlControls/QGCCheckBox.qml src/QmlControls/QGCColoredImage.qml src/QmlControls/QGCComboBox.qml @@ -62,20 +60,17 @@ src/QmlControls/VehicleRotationCal.qml src/QmlControls/VehicleSummaryRow.qml src/ViewWidgets/ViewWidget.qml - src/FactSystem/FactControls/FactCheckBox.qml src/FactSystem/FactControls/FactComboBox.qml src/FactSystem/FactControls/FactLabel.qml src/FactSystem/FactControls/FactPanel.qml src/FactSystem/FactControls/FactTextField.qml src/FactSystem/FactControls/qmldir - src/FlightDisplay/FlightDisplayView.qml src/FlightDisplay/FlightDisplayViewMap.qml src/FlightDisplay/FlightDisplayViewVideo.qml src/FlightDisplay/FlightDisplayViewWidgets.qml src/FlightDisplay/qmldir - src/FlightMap/FlightMap.qml src/FlightMap/MapItems/MissionItemIndicator.qml src/FlightMap/MapItems/MissionItemView.qml @@ -90,10 +85,8 @@ src/FlightMap/QGCVideoBackground.qml src/FlightMap/qmldir src/FlightMap/MapItems/VehicleMapItem.qml - src/QmlControls/QGroundControl.ScreenTools.qmldir src/QmlControls/ScreenTools.qml - src/QmlControls/QmlTest.qml src/AutoPilotPlugins/PX4/RadioComponent.qml src/AutoPilotPlugins/PX4/RadioComponentSummary.qml diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index 757c64d9817cb928e65eb4159077687c73950256..8cc0f058aa477b6f12d5bcd5a79fc8f2d61c75cd 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -139,7 +139,7 @@ void AirframeComponentController::_rebootAfterStackUnwind(void) QGC::SLEEP::usleep(500); qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); } - qgcApp()->toolbox()->linkManager()->disconnectAll(); + qgcApp()->toolbox()->linkManager()->disconnectAll(false /* disconnectAutoconnectLink */); qgcApp()->restoreOverrideCursor(); } diff --git a/src/HomePositionManager.cc b/src/HomePositionManager.cc index 01f39b4ddcb20ce7d5289d32de9b9f6634728d56..f478c670b3173fa6d63dc1990cd9e9ee4d694b3e 100644 --- a/src/HomePositionManager.cc +++ b/src/HomePositionManager.cc @@ -50,14 +50,13 @@ HomePositionManager::HomePositionManager(QGCApplication* app) , homeLon(8.549444) , homeAlt(470.0) { - + qmlRegisterUncreatableType ("QGroundControl", 1, 0, "HomePositionManager", "Reference only"); } void HomePositionManager::setToolbox(QGCToolbox *toolbox) { QGCTool::setToolbox(toolbox); - qmlRegisterUncreatableType ("QGroundControl", 1, 0, "HomePositionManager", "Reference only"); _loadSettings(); } diff --git a/src/QmlControls/DropButton.qml b/src/QmlControls/DropButton.qml index b39cb29636706da2131ca2993aeaab8542b68553..281ff7ed8cc1f6a27e31e195a70a06ec406c6943 100644 --- a/src/QmlControls/DropButton.qml +++ b/src/QmlControls/DropButton.qml @@ -184,7 +184,7 @@ Item { id: dropDownItem visible: checked - QGCCanvas { + Canvas { id: arrowCanvas anchors.fill: parent diff --git a/src/QmlControls/QGCCanvas.qml b/src/QmlControls/QGCCanvas.qml deleted file mode 100644 index c7f0a12d935606ef803012452333d4b7448b1e46..0000000000000000000000000000000000000000 --- a/src/QmlControls/QGCCanvas.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick 2.2 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.2 - -import QGroundControl.Palette 1.0 -import QGroundControl.ScreenTools 1.0 - -/// Canvas has some sort of bug in it which can cause it to not paint when top level Views -/// are switched. In order to fix this we ahve a signal hacked into ScreenTools to force -/// a repaint. -Canvas { - id: _root - - Connections { - target: ScreenTools - - onRepaintRequested: _root.requestPaint() - } -} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 0b96e795252fe5c083c87f8862025826f6dae4af..adad5682f0c06d1b27c94cbef7545cd06c13b6fd 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -9,7 +9,6 @@ QGCComboBox 1.0 QGCComboBox.qml QGCColoredImage 1.0 QGCColoredImage.qml QGCToolBarButton 1.0 QGCToolBarButton.qml QGCMovableItem 1.0 QGCMovableItem.qml -QGCCanvas 1.0 QGCCanvas.qml SubMenuButton 1.0 SubMenuButton.qml IndicatorButton 1.0 IndicatorButton.qml diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index 97fc9f598c889c5e074b5e5ca73921e2e7dd1acd..566d64edbd9a9e88c38bceac8fa887fec4171f00 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -33,6 +33,8 @@ static const char* kQmlGlobalKeyName = "QGCQml"; QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCToolbox* toolbox, QObject* parent) : QObject(parent) + , _multiVehicleManager(toolbox->multiVehicleManager()) + , _linkManager(toolbox->linkManager()) , _homePositionManager(toolbox->homePositionManager()) , _flightMapSettings(toolbox->flightMapSettings()) { @@ -70,25 +72,25 @@ bool QGroundControlQmlGlobal::loadBoolGlobalSetting (const QString& key, bool de #ifdef QT_DEBUG void QGroundControlQmlGlobal::_startMockLink(MockConfiguration* mockConfig) { - MockLink* mockLink = new MockLink(mockConfig); - LinkManager* linkManager = qgcApp()->toolbox()->linkManager(); - linkManager->_addLink(mockLink); - linkManager->connectLink(mockLink); + mockConfig->setDynamic(true); + linkManager->linkConfigurations()->append(mockConfig); + + linkManager->createConnectedLink(mockConfig, false /* autoconnectLink */); } #endif void QGroundControlQmlGlobal::startPX4MockLink(bool sendStatusText) { #ifdef QT_DEBUG - MockConfiguration mockConfig("PX4 MockLink"); + MockConfiguration* mockConfig = new MockConfiguration("PX4 MockLink"); - mockConfig.setFirmwareType(MAV_AUTOPILOT_PX4); - mockConfig.setVehicleType(MAV_TYPE_QUADROTOR); - mockConfig.setSendStatusText(sendStatusText); + mockConfig->setFirmwareType(MAV_AUTOPILOT_PX4); + mockConfig->setVehicleType(MAV_TYPE_QUADROTOR); + mockConfig->setSendStatusText(sendStatusText); - _startMockLink(&mockConfig); + _startMockLink(mockConfig); #else Q_UNUSED(sendStatusText); #endif @@ -97,13 +99,13 @@ void QGroundControlQmlGlobal::startPX4MockLink(bool sendStatusText) void QGroundControlQmlGlobal::startGenericMockLink(bool sendStatusText) { #ifdef QT_DEBUG - MockConfiguration mockConfig("Generic MockLink"); + MockConfiguration* mockConfig = new MockConfiguration("Generic MockLink"); - mockConfig.setFirmwareType(MAV_AUTOPILOT_GENERIC); - mockConfig.setVehicleType(MAV_TYPE_QUADROTOR); - mockConfig.setSendStatusText(sendStatusText); + mockConfig->setFirmwareType(MAV_AUTOPILOT_GENERIC); + mockConfig->setVehicleType(MAV_TYPE_QUADROTOR); + mockConfig->setSendStatusText(sendStatusText); - _startMockLink(&mockConfig); + _startMockLink(mockConfig); #else Q_UNUSED(sendStatusText); #endif @@ -112,13 +114,13 @@ void QGroundControlQmlGlobal::startGenericMockLink(bool sendStatusText) void QGroundControlQmlGlobal::startAPMArduCopterMockLink(bool sendStatusText) { #ifdef QT_DEBUG - MockConfiguration mockConfig("APM ArduCopter MockLink"); + MockConfiguration* mockConfig = new MockConfiguration("APM ArduCopter MockLink"); - mockConfig.setFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); - mockConfig.setVehicleType(MAV_TYPE_QUADROTOR); - mockConfig.setSendStatusText(sendStatusText); + mockConfig->setFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); + mockConfig->setVehicleType(MAV_TYPE_QUADROTOR); + mockConfig->setSendStatusText(sendStatusText); - _startMockLink(&mockConfig); + _startMockLink(mockConfig); #else Q_UNUSED(sendStatusText); #endif @@ -127,13 +129,13 @@ void QGroundControlQmlGlobal::startAPMArduCopterMockLink(bool sendStatusText) void QGroundControlQmlGlobal::startAPMArduPlaneMockLink(bool sendStatusText) { #ifdef QT_DEBUG - MockConfiguration mockConfig("APM ArduPlane MockLink"); + MockConfiguration* mockConfig = new MockConfiguration("APM ArduPlane MockLink"); - mockConfig.setFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); - mockConfig.setVehicleType(MAV_TYPE_FIXED_WING); - mockConfig.setSendStatusText(sendStatusText); + mockConfig->setFirmwareType(MAV_AUTOPILOT_ARDUPILOTMEGA); + mockConfig->setVehicleType(MAV_TYPE_FIXED_WING); + mockConfig->setSendStatusText(sendStatusText); - _startMockLink(&mockConfig); + _startMockLink(mockConfig); #else Q_UNUSED(sendStatusText); #endif @@ -144,12 +146,12 @@ void QGroundControlQmlGlobal::stopAllMockLinks(void) #ifdef QT_DEBUG LinkManager* linkManager = qgcApp()->toolbox()->linkManager(); - QList links = linkManager->getLinks(); - for (int i=0; ilinks()->count(); i++) { + LinkInterface* link = linkManager->links()->value(i); MockLink* mockLink = qobject_cast(link); + if (mockLink) { - linkManager->disconnectLink(mockLink); + linkManager->disconnectLink(mockLink, false /* disconnectAutoconnectLink */); } } #endif diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 302439196909c892313cc6f4b8b41228641b3591..9d6a8a5a507f9b42e316acb6e4e4e5e602f665d0 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -31,6 +31,7 @@ #include "QGCApplication.h" #include "MainWindow.h" +#include "LinkManager.h" #include "HomePositionManager.h" #include "FlightMapSettings.h" @@ -47,6 +48,8 @@ class QGroundControlQmlGlobal : public QObject public: QGroundControlQmlGlobal(QGCToolbox* toolbox, QObject* parent = NULL); + Q_PROPERTY(LinkManager* linkManager READ linkManager CONSTANT) + Q_PROPERTY(MultiVehicleManager* multiVehicleManager READ multiVehicleManager CONSTANT) Q_PROPERTY(HomePositionManager* homePositionManager READ homePositionManager CONSTANT) Q_PROPERTY(FlightMapSettings* flightMapSettings READ flightMapSettings CONSTANT) @@ -70,6 +73,8 @@ public: // Property accesors + LinkManager* linkManager () { return _linkManager; } + MultiVehicleManager* multiVehicleManager () { return _multiVehicleManager; } HomePositionManager* homePositionManager () { return _homePositionManager; } FlightMapSettings* flightMapSettings () { return _flightMapSettings; } @@ -137,6 +142,8 @@ private: void _startMockLink(MockConfiguration* mockConfig); #endif + MultiVehicleManager* _multiVehicleManager; + LinkManager* _linkManager; HomePositionManager* _homePositionManager; FlightMapSettings* _flightMapSettings; }; diff --git a/src/QmlControls/QmlObjectListModel.cc b/src/QmlControls/QmlObjectListModel.cc index 5ab27884440b5bdcf559c1df0ee4753a26edcb96..1e510c81f353190298fcc6db6b8ca8367183e6ea 100644 --- a/src/QmlControls/QmlObjectListModel.cc +++ b/src/QmlControls/QmlObjectListModel.cc @@ -197,11 +197,6 @@ int QmlObjectListModel::count(void) const return rowCount(); } -QObject* QmlObjectListModel::get(int index) -{ - return _objectList[index]; -} - void QmlObjectListModel::setDirty(bool dirty) { _dirty = dirty; diff --git a/src/QmlControls/QmlObjectListModel.h b/src/QmlControls/QmlObjectListModel.h index 2e8d1bd8d8803f6a73894a6f2d41cc1e0cd58cf1..8e64d0f49b167ddf07254f3935f36721783c7576 100644 --- a/src/QmlControls/QmlObjectListModel.h +++ b/src/QmlControls/QmlObjectListModel.h @@ -34,14 +34,14 @@ public: QmlObjectListModel(QObject* parent = NULL); ~QmlObjectListModel(); - Q_INVOKABLE QObject* get(int index); - Q_PROPERTY(int count READ count NOTIFY countChanged) /// Returns true if any of the items in the list are dirty. Requires each object to have /// a dirty property and dirtyChanged signal. Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) + Q_INVOKABLE QObject* get(int index) { return _objectList[index]; } + // Property accessors int count(void) const; @@ -52,13 +52,14 @@ public: void append(QObject* object); void clear(void); QObject* removeAt(int i); + QObject* removeOne(QObject* object) { return removeAt(indexOf(object)); } void insert(int i, QObject* object); QObject* operator[](int i); const QObject* operator[](int i) const; - - template - const QList& list(void) { return *((QList*)((void*)(&_objectList))); } - + bool contains(QObject* object) { return _objectList.indexOf(object) != -1; } + int indexOf(QObject* object) { return _objectList.indexOf(object); } + template T value(int index) { return qobject_cast(_objectList[index]); } + signals: void countChanged(int count); void dirtyChanged(bool dirtyChanged); diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index 03863da4c667e2289ce35d988e81ee99bfeff091..ba0d959174f0bc74eb8c0378a850ca0029aa059b 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -47,9 +47,4 @@ Item { property real fontWidth: contentWidth * (ScreenToolsController.testHighDPI ? 2 : 1) property real fontHeight: contentHeight * (ScreenToolsController.testHighDPI ? 2 : 1) } - - Connections { - target: ScreenToolsController - onRepaintRequested: repaintRequested() - } } diff --git a/src/QmlControls/ScreenToolsController.cc b/src/QmlControls/ScreenToolsController.cc index a20c8dad4bc50e41b52f8c26686a2ee899e8a620..9380bf7cf0052f8a50c430cecaa520675022eef1 100644 --- a/src/QmlControls/ScreenToolsController.cc +++ b/src/QmlControls/ScreenToolsController.cc @@ -43,15 +43,5 @@ const double ScreenToolsController::_largeFontPixelSizeRatio = 1.66; ScreenToolsController::ScreenToolsController() { - MainWindow* mainWindow = MainWindow::instance(); - // Unit tests can run Qml without MainWindow - if (mainWindow) { - connect(mainWindow, &MainWindow::repaintCanvas, this, &ScreenToolsController::_updateCanvas); - } -} -void ScreenToolsController::_updateCanvas() -{ - emit repaintRequested(); } - diff --git a/src/QmlControls/ScreenToolsController.h b/src/QmlControls/ScreenToolsController.h index 004d1fd41ab09487115db2ef8654d63a7eed9949..6d8807f69f905918db0210de72a68467abb4b001 100644 --- a/src/QmlControls/ScreenToolsController.h +++ b/src/QmlControls/ScreenToolsController.h @@ -114,12 +114,6 @@ public: bool testHighDPI () { return false; } #endif -signals: - void repaintRequested(); - -private slots: - void _updateCanvas(); - private: static const double _defaultFontPixelSizeRatio; static const double _smallFontPixelSizeRatio; diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 0416ea6bafdd463e029f5955de969785cd606464..7e5cdbfa2406b9b1f19bffed3e22e07494e5fe83 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -57,33 +57,26 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.MultiVehicleManager", 1, 0, "MultiVehicleManager", "Reference only"); + connect(_toolbox->linkManager(), &LinkManager::linkActive, this, &MultiVehicleManager::_linkActive); } -bool MultiVehicleManager::notifyHeartbeatInfo(LinkInterface* link, int vehicleId, mavlink_heartbeat_t& heartbeat) +void MultiVehicleManager::_linkActive(LinkInterface* link, int vehicleId, int vehicleFirmwareType, int vehicleType) { - if (!getVehicleById(vehicleId) && !_ignoreVehicleIds.contains(vehicleId)) { - if (vehicleId == _mavlinkProtocol->getSystemId()) { - _app->showToolBarMessage(QString("Warning: A vehicle is using the same system id as QGroundControl: %1").arg(vehicleId)); - } - - QSettings settings; - bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool(); - if (mavlinkVersionCheck && heartbeat.mavlink_version != MAVLINK_VERSION) { - _ignoreVehicleIds += vehicleId; - _app->showToolBarMessage(QString("The MAVLink protocol version on vehicle #%1 and QGroundControl differ! " - "It is unsafe to use different MAVLink versions. " - "QGroundControl therefore refuses to connect to vehicle #%1, which sends MAVLink version %2 (QGroundControl uses version %3).").arg(vehicleId).arg(heartbeat.mavlink_version).arg(MAVLINK_VERSION)); - return false; - } + if (!getVehicleById(vehicleId)) { + qCDebug(MultiVehicleManagerLog) << "Adding new vehicle linkName:vehicleId:vehicleFirmwareType:vehicleType" + << link->getName() + << vehicleId + << vehicleFirmwareType + << vehicleType; - Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)heartbeat.autopilot, (MAV_TYPE)heartbeat.type, _firmwarePluginManager, _autopilotPluginManager, _joystickManager); + Vehicle* vehicle = new Vehicle(link, vehicleId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _autopilotPluginManager, _joystickManager); if (!vehicle) { qWarning() << "New Vehicle allocation failed"; - return false; + return; } - connect(vehicle, &Vehicle::allLinksDisconnected, this, &MultiVehicleManager::_deleteVehiclePhase1); + connect(vehicle, &Vehicle::allLinksInactive, this, &MultiVehicleManager::_deleteVehiclePhase1); connect(vehicle->autopilotPlugin(), &AutoPilotPlugin::parametersReadyChanged, this, &MultiVehicleManager::_autopilotParametersReadyChanged); _vehicles.append(vehicle); @@ -92,22 +85,20 @@ bool MultiVehicleManager::notifyHeartbeatInfo(LinkInterface* link, int vehicleId setActiveVehicle(vehicle); } - - return true; } /// This slot is connected to the Vehicle::allLinksDestroyed signal such that the Vehicle is deleted /// and all other right things happen when the Vehicle goes away. void MultiVehicleManager::_deleteVehiclePhase1(Vehicle* vehicle) { - qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase1"; + qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase1" << vehicle; - _vehicleBeingDeleted = vehicle; + _vehiclesBeingDeleted << vehicle; // Remove from map bool found = false; for (int i=0; i<_vehicles.count(); i++) { - if (_vehicles[i] == _vehicleBeingDeleted) { + if (_vehicles[i] == vehicle) { _vehicles.removeAt(i); found = true; break; @@ -136,9 +127,9 @@ void MultiVehicleManager::_deleteVehiclePhase1(Vehicle* vehicle) QTimer::singleShot(20, this, &MultiVehicleManager::_deleteVehiclePhase2); } -void MultiVehicleManager::_deleteVehiclePhase2 (void) +void MultiVehicleManager::_deleteVehiclePhase2(void) { - qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase2"; + qCDebug(MultiVehicleManagerLog) << "_deleteVehiclePhase2" << _vehiclesBeingDeleted[0]; /// Qml has been notified of vehicle about to go away and should be disconnected from it by now. /// This means we can now clear the active vehicle property and delete the Vehicle for real. @@ -159,7 +150,8 @@ void MultiVehicleManager::_deleteVehiclePhase2 (void) } } - _vehicleBeingDeleted->deleteLater(); + delete _vehiclesBeingDeleted[0]; + _vehiclesBeingDeleted.removeAt(0); } void MultiVehicleManager::setActiveVehicle(Vehicle* vehicle) @@ -189,7 +181,7 @@ void MultiVehicleManager::setActiveVehicle(Vehicle* vehicle) void MultiVehicleManager::_setActiveVehiclePhase2(void) { - qCDebug(MultiVehicleManagerLog) << "_setActiveVehiclePhase2"; + qCDebug(MultiVehicleManagerLog) << "_setActiveVehiclePhase2 _vehicleBeingSetActive" << _vehicleBeingSetActive; // Now we signal the new active vehicle _activeVehicle = _vehicleBeingSetActive; diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 0b85ee3a4bf88c2e1dab0df9985525177e6c9c8e..f5101df9daa70830057301412e439beb4bbfe243 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -58,14 +58,6 @@ public: // Methods - /// Called to notify that a heartbeat was received with the specified information. MultiVehicleManager - /// will create/update Vehicles as necessary. - /// @param link Heartbeat came through on this link - /// @param vehicleId Mavlink system id for vehicle - /// @param heartbeat Mavlink heartbeat message - /// @return true: continue further processing of this message, false: disregard this message - bool notifyHeartbeatInfo(LinkInterface* link, int vehicleId, mavlink_heartbeat_t& heartbeat); - Q_INVOKABLE Vehicle* getVehicleById(int vehicleId); UAS* activeUas(void) { return _activeVehicle ? _activeVehicle->uas() : NULL; } @@ -98,6 +90,7 @@ private slots: void _deleteVehiclePhase2(void); void _setActiveVehiclePhase2(void); void _autopilotParametersReadyChanged(bool parametersReady); + void _linkActive(LinkInterface* link, int vehicleId, int vehicleFirmwareType, int vehicleType); private: bool _vehicleExists(int vehicleId); @@ -106,8 +99,8 @@ private: bool _parameterReadyVehicleAvailable; ///< true: An active vehicle with ready parameters is available Vehicle* _activeVehicle; ///< Currently active vehicle from a ui perspective - Vehicle* _vehicleBeingDeleted; ///< Vehicle being deleted in queued phases - Vehicle* _vehicleBeingSetActive; ///< Vehicle being set active in queued phases + QList _vehiclesBeingDeleted; ///< List of Vehicles being deleted in queued phases + Vehicle* _vehicleBeingSetActive; ///< Vehicle being set active in queued phases QList _ignoreVehicleIds; ///< List of vehicle id for which we ignore further communication diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 339006dadac774bb75004ec8ed06d1b6070b35aa..938a90045b3d9a514d89251a79e6d6ba4afefd98 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -94,6 +94,8 @@ Vehicle::Vehicle(LinkInterface* link, , _satelliteCount(-1) , _satelliteLock(0) , _updateCount(0) + , _rcRSSI(0) + , _rcRSSIstore(100.0) , _missionManager(NULL) , _missionManagerInitialRequestComplete(false) , _parameterLoader(NULL) @@ -106,6 +108,7 @@ Vehicle::Vehicle(LinkInterface* link, , _autopilotPluginManager(autopilotPluginManager) , _joystickManager(joystickManager) , _flowImageIndex(0) + , _allLinksInactiveSent(false) { _addLink(link); @@ -119,9 +122,10 @@ Vehicle::Vehicle(LinkInterface* link, setLatitude(_uas->getLatitude()); setLongitude(_uas->getLongitude()); - connect(_uas, &UAS::latitudeChanged, this, &Vehicle::setLatitude); - connect(_uas, &UAS::longitudeChanged, this, &Vehicle::setLongitude); - connect(_uas, &UAS::imageReady, this, &Vehicle::_imageReady); + connect(_uas, &UAS::latitudeChanged, this, &Vehicle::setLatitude); + connect(_uas, &UAS::longitudeChanged, this, &Vehicle::setLongitude); + connect(_uas, &UAS::imageReady, this, &Vehicle::_imageReady); + connect(_uas, &UAS::remoteControlRSSIChanged, this, &Vehicle::_remoteControlRSSIChanged); _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); _autopilotPlugin = _autopilotPluginManager->newAutopilotPluginForVehicle(this); @@ -186,6 +190,8 @@ Vehicle::Vehicle(LinkInterface* link, Vehicle::~Vehicle() { + qCDebug(VehicleLog) << "~Vehicle" << this; + delete _missionManager; _missionManager = NULL; @@ -285,38 +291,29 @@ void Vehicle::_handleHeartbeat(mavlink_message_t& message) bool Vehicle::_containsLink(LinkInterface* link) { - foreach (SharedLinkInterface sharedLink, _links) { - if (sharedLink.data() == link) { - return true; - } - } - - return false; + return _links.contains(link); } void Vehicle::_addLink(LinkInterface* link) { if (!_containsLink(link)) { - _links += qgcApp()->toolbox()->linkManager()->sharedPointerForLink(link); + _links += link; qCDebug(VehicleLog) << "_addLink:" << QString("%1").arg((ulong)link, 0, 16); - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDisconnected, this, &Vehicle::_linkDisconnected); + connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkInactive, this, &Vehicle::_linkInactiveOrDeleted); + connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDeleted, this, &Vehicle::_linkInactiveOrDeleted); } } -void Vehicle::_linkDisconnected(LinkInterface* link) +void Vehicle::_linkInactiveOrDeleted(LinkInterface* link) { - qCDebug(VehicleLog) << "_linkDisconnected:" << link->getName(); - qCDebug(VehicleLog) << "link count:" << _links.count(); + qCDebug(VehicleLog) << "_linkInactiveOrDeleted linkCount" << _links.count(); - for (int i=0; i<_links.count(); i++) { - if (_links[i].data() == link) { - _links.removeAt(i); - break; - } - } + _links.removeOne(link); - if (_links.count() == 0) { - emit allLinksDisconnected(this); + if (_links.count() == 0 && !_allLinksInactiveSent) { + // Make sure to not send this more than one time + _allLinksInactiveSent = true; + emit allLinksInactive(this); } } @@ -328,10 +325,7 @@ void Vehicle::sendMessage(mavlink_message_t message) void Vehicle::_sendMessage(mavlink_message_t message) { // Emit message on all links that are currently connected - foreach (SharedLinkInterface sharedLink, _links) { - LinkInterface* link = sharedLink.data(); - Q_ASSERT(link); - + foreach (LinkInterface* link, _links) { if (link->isConnected()) { MAVLinkProtocol* mavlink = _mavlink; @@ -350,17 +344,6 @@ void Vehicle::_sendMessage(mavlink_message_t message) } } -QList Vehicle::links(void) -{ - QList list; - - foreach (SharedLinkInterface sharedLink, _links) { - list += sharedLink.data(); - } - - return list; -} - void Vehicle::setLatitude(double latitude) { _coordinate.setLatitude(latitude); @@ -1035,11 +1018,11 @@ void Vehicle::_parametersReady(bool parametersReady) void Vehicle::_communicationInactivityTimedOut(void) { - // Vechile is no longer communicating with us, disconnect all links + // Vehicle is no longer communicating with us, disconnect all links inactive LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); for (int i=0; i<_links.count(); i++) { - linkMgr->disconnectLink(_links[i].data()); + linkMgr->disconnectLink(_links[i], false /* disconnectAutoconnectLink */); } } @@ -1058,3 +1041,17 @@ void Vehicle::_imageReady(UASInterface*) emit flowImageIndexChanged(); } } + +void Vehicle::_remoteControlRSSIChanged(uint8_t rssi) +{ + // Low pass to git rid of jitter + _rcRSSIstore = (_rcRSSIstore * 0.9f) + ((float)rssi * 0.1); + uint8_t filteredRSSI = (uint8_t)ceil(_rcRSSIstore); + if(_rcRSSIstore < 0.1) { + filteredRSSI = 0; + } + if(_rcRSSI != filteredRSSI) { + _rcRSSI = filteredRSSI; + emit rcRSSIChanged(_rcRSSI); + } +} diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index b41f86855868f4a02e9b1749f63a57542a0f8f56..9a844669e09a8c6ff5a8abfae0e118a25e4eb2a9 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -107,6 +107,7 @@ public: Q_PROPERTY(bool joystickEnabled READ joystickEnabled WRITE setJoystickEnabled NOTIFY joystickEnabledChanged) Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) Q_PROPERTY(int flowImageIndex READ flowImageIndex NOTIFY flowImageIndexChanged) + Q_PROPERTY(int rcRSSI READ rcRSSI NOTIFY rcRSSIChanged) /// Returns the number of buttons which are reserved for firmware use in the MANUAL_CONTROL mavlink /// message. For example PX4 Flight Stack reserves the first 8 buttons to simulate rc switches. @@ -166,8 +167,6 @@ public: /// Provides access to the Firmware Plugin for this Vehicle FirmwarePlugin* firmwarePlugin(void) { return _firmwarePlugin; } - QList links(void); - int manualControlReservedButtonCount(void); MissionManager* missionManager(void) { return _missionManager; } @@ -245,6 +244,7 @@ public: QString currentState () { return _currentState; } int satelliteLock () { return _satelliteLock; } unsigned int heartbeatTimeout () { return _currentHeartbeatTimeout; } + int rcRSSI () { return _rcRSSI; } ParameterLoader* getParameterLoader(void); @@ -253,7 +253,7 @@ public slots: void setLongitude(double longitude); signals: - void allLinksDisconnected(Vehicle* vehicle); + void allLinksInactive(Vehicle* vehicle); void coordinateChanged(QGeoCoordinate coordinate); void coordinateValidChanged(bool coordinateValid); void joystickModeChanged(int mode); @@ -293,15 +293,17 @@ signals: void currentStateChanged (); void satelliteLockChanged (); void flowImageIndexChanged (); + void rcRSSIChanged (int rcRSSI); private slots: void _mavlinkMessageReceived(LinkInterface* link, mavlink_message_t message); - void _linkDisconnected(LinkInterface* link); + void _linkInactiveOrDeleted(LinkInterface* link); void _sendMessage(mavlink_message_t message); void _sendMessageMultipleNext(void); void _addNewMapTrajectoryPoint(void); void _parametersReady(bool parametersReady); void _communicationInactivityTimedOut(void); + void _remoteControlRSSIChanged(uint8_t rssi); void _handleTextMessage (int newCount); /** @brief Attitude from main autopilot / system state */ @@ -347,10 +349,7 @@ private: AutoPilotPlugin* _autopilotPlugin; MAVLinkProtocol* _mavlink; - /// List of all links associated with this vehicle. We keep SharedLinkInterface objects - /// which are QSharedPointer's in order to maintain reference counts across threads. - /// This way Link deletion works correctly. - QList _links; + QList _links; JoystickMode_t _joystickMode; bool _joystickEnabled; @@ -394,6 +393,8 @@ private: int _satelliteCount; int _satelliteLock; int _updateCount; + int _rcRSSI; + double _rcRSSIstore; MissionManager* _missionManager; bool _missionManagerInitialRequestComplete; @@ -434,6 +435,8 @@ private: int _flowImageIndex; + bool _allLinksInactiveSent; ///< true: allLinkInactive signal already sent one time + // Settings keys static const char* _settingsGroup; static const char* _joystickModeSettingsKey; diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 617254df6ff2766c8f67f268949fb3d56abb8897..96a01a4719a10ee61ade9d6bff3ffcb4aeba91b7 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -26,12 +26,13 @@ import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 import QtQuick.Dialogs 1.2 -import QGroundControl.Controls 1.0 -import QGroundControl.FactSystem 1.0 -import QGroundControl.FactControls 1.0 -import QGroundControl.Palette 1.0 -import QGroundControl.Controllers 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.ScreenTools 1.0 QGCView { id: qgcView @@ -88,7 +89,7 @@ QGCView { onBoardFound: { if (initialBoardSearch) { // Board was found right away, so something is already plugged in before we've started upgrade - if (controller.qgcConnections) { + if (QGroundControl.linkManager.anyActiveLinks) { statusTextArea.append(qgcDisconnectText) } else { statusTextArea.append(usbUnplugText.replace('{0}', controller.boardType)) diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index eb1eb35bb5f14fbcc2536b1c897ee4c23537de5e..48b10e0d79b55eda03a45a30820c0d60025c4a45 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -68,13 +68,17 @@ FirmwareUpgradeController::FirmwareUpgradeController(void) : connect(_threadController, &PX4FirmwareUpgradeThreadController::flashComplete, this, &FirmwareUpgradeController::_flashComplete); connect(_threadController, &PX4FirmwareUpgradeThreadController::updateProgress, this, &FirmwareUpgradeController::_updateProgress); - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDisconnected, this, &FirmwareUpgradeController::_linkDisconnected); - connect(&_eraseTimer, &QTimer::timeout, this, &FirmwareUpgradeController::_eraseProgressTick); } +FirmwareUpgradeController::~FirmwareUpgradeController() +{ + qgcApp()->toolbox()->linkManager()->setConnectionsAllowed(); +} + void FirmwareUpgradeController::startBoardSearch(void) { + qgcApp()->toolbox()->linkManager()->setConnectionsSuspended(tr("Connect not allowed during Firmware Upgrade.")); _bootloaderFound = false; _startFlashWhenBootloaderFound = false; _threadController->startFindBoardLoop(); @@ -556,17 +560,6 @@ void FirmwareUpgradeController::_appendStatusLog(const QString& text, bool criti Q_ARG(QVariant, varText)); } -bool FirmwareUpgradeController::qgcConnections(void) -{ - return qgcApp()->toolbox()->linkManager()->anyConnectedLinks(); -} - -void FirmwareUpgradeController::_linkDisconnected(LinkInterface* link) -{ - Q_UNUSED(link); - emit qgcConnectionsChanged(qgcConnections()); -} - void FirmwareUpgradeController::_errorCancel(const QString& msg) { _appendStatusLog(msg, false); diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index aec35f44d52ce6d84fddf22739531c0d2d53f2c3..d7f7c9d22d20d4fd6f7a4b08a8ea0d4d9730b362 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -104,7 +104,8 @@ public: }; FirmwareUpgradeController(void); - + ~FirmwareUpgradeController(); + Q_PROPERTY(QString boardPort READ boardPort NOTIFY boardFound) Q_PROPERTY(QString boardDescription READ boardDescription NOTIFY boardFound) Q_PROPERTY(QString boardType MEMBER _foundBoardType NOTIFY boardFound) @@ -115,9 +116,6 @@ public: /// Progress bar for you know what Q_PROPERTY(QQuickItem* progressBar READ progressBar WRITE setProgressBar) - /// Returns true if there are active QGC connections - Q_PROPERTY(bool qgcConnections READ qgcConnections NOTIFY qgcConnectionsChanged) - /// Starts searching for boards on the background thread Q_INVOKABLE void startBoardSearch(void); @@ -140,8 +138,6 @@ public: QQuickItem* statusLog(void) { return _statusLog; } void setStatusLog(QQuickItem* statusLog) { _statusLog = statusLog; } - bool qgcConnections(void); - QString boardPort(void) { return _foundBoardInfo.portName(); } QString boardDescription(void) { return _foundBoardInfo.description(); } @@ -151,7 +147,6 @@ signals: void boardGone(void); void flashComplete(void); void flashCancelled(void); - void qgcConnectionsChanged(bool connections); void error(void); private slots: @@ -170,7 +165,6 @@ private slots: void _eraseStarted(void); void _eraseComplete(void); void _eraseProgressTick(void); - void _linkDisconnected(LinkInterface* link); private: void _getFirmwareFile(FirmwareIdentifier firmwareId); diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc index bf40204ca7f8a0ea1fa60eb6bf8a7040be1d4572..a3213d8a1173b6a0843ee4ce94c1a8e80e109289 100644 --- a/src/comm/LinkConfiguration.cc +++ b/src/comm/LinkConfiguration.cc @@ -42,19 +42,20 @@ This file is part of the QGROUNDCONTROL project #define LINK_SETTING_ROOT "LinkConfigurations" LinkConfiguration::LinkConfiguration(const QString& name) - : _preferred(false) + : _link(NULL) + , _name(name) , _dynamic(false) { - _link = NULL; _name = name; - Q_ASSERT(!_name.isEmpty()); + if (_name.isEmpty()) { + qWarning() << "Internal error"; + } } LinkConfiguration::LinkConfiguration(LinkConfiguration* copy) { - _link = copy->getLink(); + _link = copy->link(); _name = copy->name(); - _preferred = copy->isPreferred(); _dynamic = copy->isDynamic(); Q_ASSERT(!_name.isEmpty()); } @@ -62,9 +63,8 @@ LinkConfiguration::LinkConfiguration(LinkConfiguration* copy) void LinkConfiguration::copyFrom(LinkConfiguration* source) { Q_ASSERT(source != NULL); - _link = source->getLink(); + _link = source->link(); _name = source->name(); - _preferred = source->isPreferred(); _dynamic = source->isDynamic(); } @@ -138,3 +138,15 @@ LinkConfiguration* LinkConfiguration::duplicateSettings(LinkConfiguration* sourc } return dupe; } + +void LinkConfiguration::setName(const QString name) +{ + _name = name; + emit nameChanged(name); +} + +void LinkConfiguration::setLink(LinkInterface* link) +{ + _link = link; + emit linkChanged(link); +} diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 8e59bad93834ea846ed9baba87355dddb5301f93..61018605207e61d1df67277ebb2b352e82b7f050 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -30,13 +30,26 @@ class LinkInterface; /// Interface holding link specific settings. -class LinkConfiguration +class LinkConfiguration : public QObject { + Q_OBJECT + public: LinkConfiguration(const QString& name); LinkConfiguration(LinkConfiguration* copy); virtual ~LinkConfiguration() {} + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(LinkInterface* link READ link WRITE setLink NOTIFY linkChanged) + + // Property accessors + + const QString name(void) { return _name; } + LinkInterface* link(void) { return _link; } + + void setName(const QString name); + void setLink(LinkInterface* link); + /// The link types supported by QGC enum { #ifndef __ios__ @@ -55,48 +68,6 @@ public: TypeLast // Last type value (type >= TypeLast == invalid) }; - /*! - * @brief Get configuration name - * - * This is the user friendly name shown in the connection drop down box and the name used to save the configuration in the settings. - * @return The name of this link. - */ - const QString name() { return _name; } - - /*! - * @brief Set the name of this link configuration. - * - * This is the user friendly name shown in the connection drop down box and the name used to save the configuration in the settings. - * @param[in] name The configuration name - */ - void setName(const QString name) {_name = name; } - - /*! - * @brief Set the link this configuration is currently attched to. - * - * @param[in] link The pointer to the current LinkInterface instance (if any) - */ - void setLink(LinkInterface* link) { _link = link; } - - /*! - * @brief Get the link this configuration is currently attched to. - * - * @return The pointer to the current LinkInterface instance (if any) - */ - LinkInterface* getLink() { return _link; } - - /*! - * - * Is this a preferred configuration? (decided at runtime) - * @return True if this is a known configuration (PX4, etc.) - */ - bool isPreferred() { return _preferred; } - - /*! - * Set if this is this a preferred configuration. (decided at runtime) - */ - void setPreferred(bool preferred = true) { _preferred = preferred; } - /*! * * Is this a dynamic configuration? (non persistent) @@ -178,11 +149,14 @@ public: */ static LinkConfiguration* duplicateSettings(LinkConfiguration *source); +signals: + void nameChanged(const QString& name); + void linkChanged(LinkInterface* link); + protected: LinkInterface* _link; ///< Link currently using this configuration (if any) private: QString _name; - bool _preferred; ///< Determined internally if this is a preferred connection. It comes up first in the drop down box. bool _dynamic; ///< A connection added automatically and not persistent (unless it's edited). }; diff --git a/src/comm/LinkInterface.h b/src/comm/LinkInterface.h index 63ce51d784a697d7e233966e63939ae2f178ba2c..ffc6e615017146b08b486a9023ae9699c64d3068 100644 --- a/src/comm/LinkInterface.h +++ b/src/comm/LinkInterface.h @@ -57,6 +57,15 @@ class LinkInterface : public QThread friend class LinkManager; public: + Q_PROPERTY(bool autoconnect READ autoconnect WRITE setAutoconnect NOTIFY autoconnectChanged) + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + + // Property accessors + bool autoconnect(void) { return _autoconnect; } + void setAutoconnect(bool autoconnect) { _autoconnect = autoconnect; emit autoconnectChanged(autoconnect); } + bool active(void) { return _active; } + void setActive(bool active) { _active = active; emit activeChanged(active); } + /** * @brief Get link configuration (if used) * @return A pointer to the instance of LinkConfiguration if supported. NULL otherwise. @@ -128,6 +137,7 @@ public: /// @return true: "sh /etc/init.d/rc.usb" must be sent on link to start mavlink virtual bool requiresUSBMavlinkStart(void) const { return false; } + // These are left unimplemented in order to cause linker errors which indicate incorrect usage of // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. bool connect(void); @@ -148,6 +158,8 @@ public slots: virtual void writeBytes(const char *bytes, qint64 length) = 0; signals: + void autoconnectChanged(bool autoconnect); + void activeChanged(bool active); /** * @brief New data arrived @@ -184,8 +196,10 @@ signals: protected: // Links are only created by LinkManager so constructor is not public LinkInterface() : - QThread(0), - _mavlinkChannelSet(false) + QThread(0) + , _mavlinkChannelSet(false) + , _autoconnect(false) + , _active(false) { // Initialize everything for the data rate calculation buffers. _inDataIndex = 0; @@ -321,12 +335,7 @@ private: **/ virtual bool _connect(void) = 0; - /** - * @brief Disconnect this interface logically - * - * @return True if connection could be terminated, false otherwise - **/ - virtual bool _disconnect(void) = 0; + virtual void _disconnect(void) = 0; /// Sets the mavlink channel to use for this link void _setMavlinkChannel(uint8_t channel) { Q_ASSERT(!_mavlinkChannelSet); _mavlinkChannelSet = true; _mavlinkChannel = channel; } @@ -351,6 +360,9 @@ private: qint64 _outDataWriteTimes[_dataRateBufferSize]; // in ms mutable QMutex _dataRateMutex; // Mutex for accessing the data rate member variables + + bool _autoconnect; ///< true: Link is an autoconnect connection and should never be disconnected + bool _active; ///< true: link is actively receiving mavlink messages }; typedef QSharedPointer SharedLinkInterface; diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index 9a8ef0ede6cfb524d2b3bb785042866b6b6ea477..5b43a7ab65a4b7fb7a9d60b2ae62b173da60082a 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -42,8 +42,17 @@ This file is part of the QGROUNDCONTROL project #include "QGCMessageBox.h" #include "QGCApplication.h" #include "QGCApplication.h" +#include "UDPLink.h" +#include "TCPLink.h" QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog") +QGC_LOGGING_CATEGORY(LinkManagerVerboseLog, "LinkManagerVerboseLog") + +const char* LinkManager::_settingsGroup = "LinkManager"; +const char* LinkManager::_autoconnectUDPKey = "AutoconnectUDP"; +const char* LinkManager::_autoconnectPixhawkKey = "AutoconnectPixhawk"; +const char* LinkManager::_autoconnect3DRRadioKey = "Autoconnect3DRRadio"; +const char* LinkManager::_autoconnectPX4FlowKey = "AutoconnectPX4Flow"; LinkManager::LinkManager(QGCApplication* app) : QGCTool(app) @@ -51,21 +60,32 @@ LinkManager::LinkManager(QGCApplication* app) , _configurationsLoaded(false) , _connectionsSuspended(false) , _mavlinkChannelsUsedBitMask(0) - , _nullSharedLink(NULL) , _mavlinkProtocol(NULL) + , _autoconnectUDPConfig(NULL) + , _autoconnectUDP(true) + , _autoconnectPixhawk(true) + , _autoconnect3DRRadio(true) + , _autoconnectPX4Flow(true) + { + qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkManager", "Reference only"); + qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkConfiguration", "Reference only"); + qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkInterface", "Reference only"); + + QSettings settings; + settings.beginGroup(_settingsGroup); + _autoconnectUDP = settings.value(_autoconnectUDPKey, true).toBool(); + _autoconnectPixhawk = settings.value(_autoconnectPixhawkKey, true).toBool(); + _autoconnect3DRRadio = settings.value(_autoconnect3DRRadioKey, true).toBool(); + _autoconnectPX4Flow = settings.value(_autoconnectPX4FlowKey, true).toBool(); } LinkManager::~LinkManager() { - // Clear configuration list - while(_linkConfigurations.count()) { - LinkConfiguration* pLink = _linkConfigurations.at(0); - if(pLink) delete pLink; - _linkConfigurations.removeAt(0); + if (anyActiveLinks()) { + qWarning() << "Why are there still active links?"; } - Q_ASSERT_X(_links.count() == 0, "LinkManager", "LinkManager::_shutdown should have been called previously"); } void LinkManager::setToolbox(QGCToolbox *toolbox) @@ -73,14 +93,15 @@ void LinkManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); _mavlinkProtocol = _toolbox->mavlinkProtocol(); + connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &LinkManager::_vehicleHeartbeatInfo); #ifndef __ios__ - connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateConfigurationList); + connect(&_portListTimer, &QTimer::timeout, this, &LinkManager::_updateAutoConnectLinks); _portListTimer.start(1000); #endif } -LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) +LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config, bool autoconnectLink) { Q_ASSERT(config); LinkInterface* pLink = NULL; @@ -106,30 +127,36 @@ LinkInterface* LinkManager::createConnectedLink(LinkConfiguration* config) #endif } if(pLink) { + pLink->setAutoconnect(autoconnectLink); _addLink(pLink); connectLink(pLink); } return pLink; } -LinkInterface* LinkManager::createConnectedLink(const QString& name) +LinkInterface* LinkManager::createConnectedLink(const QString& name, bool autoconnectLink) { Q_ASSERT(name.isEmpty() == false); for(int i = 0; i < _linkConfigurations.count(); i++) { - LinkConfiguration* conf = _linkConfigurations.at(i); + LinkConfiguration* conf = _linkConfigurations.value(i); if(conf && conf->name() == name) - return createConnectedLink(conf); + return createConnectedLink(conf, autoconnectLink); } return NULL; } void LinkManager::_addLink(LinkInterface* link) { - Q_ASSERT(link); + if (thread() != QThread::currentThread()) { + qWarning() << "_deleteLink called from incorrect thread"; + return; + } - _linkListMutex.lock(); + if (!link) { + return; + } - if (!containsLink(link)) { + if (!_links.contains(link)) { // Find a mavlink channel to use for this link for (int i=0; i<32; i++) { if (!(_mavlinkChannelsUsedBitMask && 1 << i)) { @@ -140,11 +167,8 @@ void LinkManager::_addLink(LinkInterface* link) } } - _links.append(QSharedPointer(link)); - _linkListMutex.unlock(); + _links.append(link); emit newLink(link); - } else { - _linkListMutex.unlock(); } // MainWindow may be around when doing things like running unit tests @@ -161,39 +185,12 @@ void LinkManager::_addLink(LinkInterface* link) connect(link, &LinkInterface::disconnected, this, &LinkManager::_linkDisconnected); } -bool LinkManager::connectAll() +void LinkManager::disconnectAll(bool disconnectAutoconnectLink) { - if (_connectionsSuspendedMsg()) { - return false; - } - - bool allConnected = true; - - foreach (SharedLinkInterface sharedLink, _links) { - Q_ASSERT(sharedLink.data()); - if (!sharedLink.data()->_connect()) { - allConnected = false; - } + // Walk list in reverse order to preserve indices during delete + for (int i=_links.count()-1; i>=0; i--) { + disconnectLink(_links.value(i), disconnectAutoconnectLink); } - - return allConnected; -} - -bool LinkManager::disconnectAll() -{ - bool allDisconnected = true; - - // Make a copy so the list is modified out from under us - QList links = _links; - - foreach (SharedLinkInterface sharedLink, links) { - Q_ASSERT(sharedLink.data()); - if (!disconnectLink(sharedLink.data())) { - allDisconnected = false; - } - } - - return allDisconnected; } bool LinkManager::connectLink(LinkInterface* link) @@ -204,68 +201,60 @@ bool LinkManager::connectLink(LinkInterface* link) return false; } + bool previousAnyConnectedLinks = anyConnectedLinks(); + bool previousAnyNonAutoconnectConnectedLinks = anyNonAutoconnectConnectedLinks(); + if (link->_connect()) { + if (!previousAnyConnectedLinks) { + emit anyConnectedLinksChanged(true); + } + if (!previousAnyNonAutoconnectConnectedLinks && anyNonAutoconnectConnectedLinks()) { + emit anyNonAutoconnectConnectedLinksChanged(true); + } return true; } else { return false; } } -bool LinkManager::disconnectLink(LinkInterface* link) +bool LinkManager::disconnectLink(LinkInterface* link, bool disconnectAutoconnectLink) { Q_ASSERT(link); - if (link->_disconnect()) { + + if (disconnectAutoconnectLink || !link->autoconnect()) { + link->_disconnect(); LinkConfiguration* config = link->getLinkConfiguration(); if(config) { config->setLink(NULL); } _deleteLink(link); return true; - } else { - return false; } + + return false; } void LinkManager::_deleteLink(LinkInterface* link) { - Q_ASSERT(link); + if (thread() != QThread::currentThread()) { + qWarning() << "_deleteLink called from incorrect thread"; + return; + } + + if (!link) { + return; + } - _linkListMutex.lock(); - // Free up the mavlink channel associated with this link _mavlinkChannelsUsedBitMask &= ~(1 << link->getMavlinkChannel()); - bool found = false; - for (int i=0; i<_links.count(); i++) { - if (_links[i].data() == link) { - _links.removeAt(i); - found = true; - break; - } - } - Q_UNUSED(found); - Q_ASSERT(found); - - _linkListMutex.unlock(); + _links.removeOne(link); + delete link; // Emit removal of link emit linkDeleted(link); } -/** - * - */ -const QList LinkManager::getLinks() -{ - QList list; - - foreach (SharedLinkInterface sharedLink, _links) { - list << sharedLink.data(); - } - - return list; -} - /// @brief If all new connections should be suspended a message is displayed to the user and true /// is returned; bool LinkManager::_connectionsSuspendedMsg(void) @@ -286,13 +275,6 @@ void LinkManager::setConnectionsSuspended(QString reason) Q_ASSERT(!reason.isEmpty()); } -void LinkManager::_shutdown(void) -{ - while (_links.count() != 0) { - disconnectLink(_links[0].data()); - } -} - void LinkManager::_linkConnected(void) { emit linkConnected((LinkInterface*)sender()); @@ -303,33 +285,6 @@ void LinkManager::_linkDisconnected(void) emit linkDisconnected((LinkInterface*)sender()); } -void LinkManager::addLinkConfiguration(LinkConfiguration* link) -{ - Q_ASSERT(link != NULL); - //-- If not there already, add it - int idx = _linkConfigurations.indexOf(link); - if(idx < 0) - { - _linkConfigurations.append(link); - } -} - -void LinkManager::removeLinkConfiguration(LinkConfiguration *link) -{ - Q_ASSERT(link != NULL); - int idx = _linkConfigurations.indexOf(link); - if(idx >= 0) - { - _linkConfigurations.removeAt(idx); - delete link; - } -} - -const QList LinkManager::getLinkConfigurationList() -{ - return _linkConfigurations; -} - void LinkManager::suspendConfigurationUpdates(bool suspend) { _configUpdateSuspended = suspend; @@ -339,30 +294,35 @@ void LinkManager::saveLinkConfigurationList() { QSettings settings; settings.remove(LinkConfiguration::settingsRoot()); - int index = 0; - foreach (LinkConfiguration* pLink, _linkConfigurations) { - Q_ASSERT(pLink != NULL); - if(!pLink->isDynamic()) - { - QString root = LinkConfiguration::settingsRoot(); - root += QString("/Link%1").arg(index++); - settings.setValue(root + "/name", pLink->name()); - settings.setValue(root + "/type", pLink->type()); - settings.setValue(root + "/preferred", pLink->isPreferred()); - // Have the instance save its own values - pLink->saveSettings(settings, root); + + for (int i=0; i<_linkConfigurations.count(); i++) { + LinkConfiguration* linkConfig = _linkConfigurations.value(i); + + if (linkConfig) { + if(!linkConfig->isDynamic()) + { + QString root = LinkConfiguration::settingsRoot(); + root += QString("/Link%1").arg(i); + settings.setValue(root + "/name", linkConfig->name()); + settings.setValue(root + "/type", linkConfig->type()); + // Have the instance save its own values + linkConfig->saveSettings(settings, root); + } + } else { + qWarning() << "Internal error"; } } + QString root(LinkConfiguration::settingsRoot()); - settings.setValue(root + "/count", index); + settings.setValue(root + "/count", _linkConfigurations.count()); emit linkConfigurationChanged(); } void LinkManager::loadLinkConfigurationList() { - bool udpExists = false; bool linksChanged = false; QSettings settings; + // Is the group even there? if(settings.contains(LinkConfiguration::settingsRoot() + "/count")) { // Find out how many configurations we have @@ -376,49 +336,33 @@ void LinkManager::loadLinkConfigurationList() if(settings.contains(root + "/name")) { QString name = settings.value(root + "/name").toString(); if(!name.isEmpty()) { - bool preferred = false; - if(settings.contains(root + "/preferred")) { - preferred = settings.value(root + "/preferred").toBool(); - } LinkConfiguration* pLink = NULL; switch(type) { #ifndef __ios__ case LinkConfiguration::TypeSerial: pLink = (LinkConfiguration*)new SerialConfiguration(name); - pLink->setPreferred(preferred); break; #endif case LinkConfiguration::TypeUdp: pLink = (LinkConfiguration*)new UDPConfiguration(name); - pLink->setPreferred(preferred); break; case LinkConfiguration::TypeTcp: pLink = (LinkConfiguration*)new TCPConfiguration(name); - pLink->setPreferred(preferred); break; case LinkConfiguration::TypeLogReplay: pLink = (LinkConfiguration*)new LogReplayLinkConfiguration(name); - pLink->setPreferred(preferred); break; #ifdef QT_DEBUG case LinkConfiguration::TypeMock: pLink = (LinkConfiguration*)new MockConfiguration(name); - pLink->setPreferred(false); break; #endif } if(pLink) { // Have the instance load its own values pLink->loadSettings(settings, root); - addLinkConfiguration(pLink); + _linkConfigurations.append(pLink); linksChanged = true; - // Check for UDP links - if(pLink->type() == LinkConfiguration::TypeUdp) { - UDPConfiguration* uLink = dynamic_cast(pLink); - if(uLink && uLink->localPort() == QGC_UDP_LOCAL_PORT) { - udpExists = true; - } - } } } else { qWarning() << "Link Configuration " << root << " has an empty name." ; @@ -439,37 +383,32 @@ void LinkManager::loadLinkConfigurationList() #ifdef QT_DEBUG MockConfiguration* pMock = new MockConfiguration("Mock Link PX4"); pMock->setDynamic(true); - addLinkConfiguration(pMock); + _linkConfigurations.append(pMock); linksChanged = true; #endif - //-- If we don't have a configured UDP link, create a default one - if(!udpExists) { - UDPConfiguration* uLink = new UDPConfiguration("Default UDP Link"); - uLink->setLocalPort(QGC_UDP_LOCAL_PORT); - uLink->setDynamic(); - addLinkConfiguration(uLink); - linksChanged = true; - } - if(linksChanged) { emit linkConfigurationChanged(); } + // Enable automatic Serial PX4/3DR Radio hunting _configurationsLoaded = true; } #ifndef __ios__ -SerialConfiguration* LinkManager::_findSerialConfiguration(const QString& portName) +SerialConfiguration* LinkManager::_autoconnectConfigurationsContainsPort(const QString& portName) { QString searchPort = portName.trimmed(); - foreach (LinkConfiguration* pLink, _linkConfigurations) { - Q_ASSERT(pLink != NULL); - if(pLink->type() == LinkConfiguration::TypeSerial) { - SerialConfiguration* pSerial = dynamic_cast(pLink); - if(pSerial->portName() == searchPort) { - return pSerial; + + for (int i=0; i<_autoconnectConfigurations.count(); i++) { + SerialConfiguration* linkConfig = _autoconnectConfigurations.value(i); + + if (linkConfig) { + if (linkConfig->portName() == searchPort) { + return linkConfig; } + } else { + qWarning() << "Internal error"; } } return NULL; @@ -477,26 +416,34 @@ SerialConfiguration* LinkManager::_findSerialConfiguration(const QString& portNa #endif #ifndef __ios__ -void LinkManager::_updateConfigurationList(void) +void LinkManager::_updateAutoConnectLinks(void) { - if (_configUpdateSuspended || !_configurationsLoaded) { + if (_connectionsSuspended || qgcApp()->runningUnitTests()) { return; } - bool saveList = false; + + if (_autoconnectUDP && !_autoconnectUDPConfig) { + _autoconnectUDPConfig = new UDPConfiguration("Default UDP Link"); + _autoconnectUDPConfig->setLocalPort(QGC_UDP_LOCAL_PORT); + _autoconnectUDPConfig->setDynamic(true); + createConnectedLink(_autoconnectUDPConfig, true /* persistenLink */); + } + + QStringList currentPorts; QList portList = QGCSerialPortInfo::availablePorts(); + // Iterate Comm Ports foreach (QGCSerialPortInfo portInfo, portList) { -#if 0 - // Too noisy for most logging, so turn on as needed - qCDebug(LinkManagerLog) << "-----------------------------------------------------"; - qCDebug(LinkManagerLog) << "portName: " << portInfo.portName(); - qCDebug(LinkManagerLog) << "systemLocation: " << portInfo.systemLocation(); - qCDebug(LinkManagerLog) << "description: " << portInfo.description(); - qCDebug(LinkManagerLog) << "manufacturer: " << portInfo.manufacturer(); - qCDebug(LinkManagerLog) << "serialNumber: " << portInfo.serialNumber(); - qCDebug(LinkManagerLog) << "vendorIdentifier: " << portInfo.vendorIdentifier(); -#endif + qCDebug(LinkManagerVerboseLog) << "-----------------------------------------------------"; + qCDebug(LinkManagerVerboseLog) << "portName: " << portInfo.portName(); + qCDebug(LinkManagerVerboseLog) << "systemLocation: " << portInfo.systemLocation(); + qCDebug(LinkManagerVerboseLog) << "description: " << portInfo.description(); + qCDebug(LinkManagerVerboseLog) << "manufacturer: " << portInfo.manufacturer(); + qCDebug(LinkManagerVerboseLog) << "serialNumber: " << portInfo.serialNumber(); + qCDebug(LinkManagerVerboseLog) << "vendorIdentifier: " << portInfo.vendorIdentifier(); + qCDebug(LinkManagerVerboseLog) << "productIdentifier: " << portInfo.productIdentifier(); + // Save port name currentPorts << portInfo.systemLocation(); @@ -508,76 +455,98 @@ void LinkManager::_updateConfigurationList(void) continue; } - SerialConfiguration* pSerial = _findSerialConfiguration(portInfo.systemLocation()); - if (pSerial) { - //-- If this port is configured make sure it has the preferred flag set - if(!pSerial->isPreferred()) { - pSerial->setPreferred(true); - saveList = true; - } - } else { + if (!_autoconnectConfigurationsContainsPort(portInfo.systemLocation())) { + SerialConfiguration* pSerialConfig = NULL; + switch (boardType) { case QGCSerialPortInfo::BoardTypePX4FMUV1: case QGCSerialPortInfo::BoardTypePX4FMUV2: - pSerial = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed())); + if (_autoconnectPixhawk) { + pSerialConfig = new SerialConfiguration(QString("Pixhawk on %1").arg(portInfo.portName().trimmed())); + } break; case QGCSerialPortInfo::BoardTypeAeroCore: - pSerial = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed())); + if (_autoconnectPixhawk) { + pSerialConfig = new SerialConfiguration(QString("AeroCore on %1").arg(portInfo.portName().trimmed())); + } break; case QGCSerialPortInfo::BoardTypePX4Flow: - pSerial = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed())); + if (_autoconnectPX4Flow) { + pSerialConfig = new SerialConfiguration(QString("PX4Flow on %1").arg(portInfo.portName().trimmed())); + } break; case QGCSerialPortInfo::BoardType3drRadio: - pSerial = new SerialConfiguration(QString("3DR Radio on %1").arg(portInfo.portName().trimmed())); + if (_autoconnect3DRRadio) { + pSerialConfig = new SerialConfiguration(QString("3DR Radio on %1").arg(portInfo.portName().trimmed())); + } + break; default: qWarning() << "Internal error"; - break; + continue; } - pSerial->setBaud(boardType == QGCSerialPortInfo::BoardType3drRadio ? 57600 : 115200); - pSerial->setDynamic(true); - pSerial->setPreferred(true); - pSerial->setPortName(portInfo.systemLocation()); - addLinkConfiguration(pSerial); - saveList = true; + if (pSerialConfig) { + qCDebug(LinkManagerLog) << "New auto-connect port added: " << pSerialConfig->name() << portInfo.systemLocation(); + pSerialConfig->setBaud(boardType == QGCSerialPortInfo::BoardType3drRadio ? 57600 : 115200); + pSerialConfig->setDynamic(true); + pSerialConfig->setPortName(portInfo.systemLocation()); + + _autoconnectConfigurations.append(pSerialConfig); + + createConnectedLink(pSerialConfig, true /* persistenLink */); + } } } } // Now we go through the current configuration list and make sure any dynamic config has gone away QList _confToDelete; - foreach (LinkConfiguration* pLink, _linkConfigurations) { - Q_ASSERT(pLink != NULL); - // We only care about dynamic links - if(pLink->isDynamic()) { - if(pLink->type() == LinkConfiguration::TypeSerial) { - // Don't mess with connected link. Let it deal with the disapearing device. - if(pLink->getLink() == NULL) { - SerialConfiguration* pSerial = dynamic_cast(pLink); - if(!currentPorts.contains(pSerial->portName())) { - _confToDelete.append(pSerial); - } - } + for (int i=0; i<_autoconnectConfigurations.count(); i++) { + SerialConfiguration* linkConfig = _autoconnectConfigurations.value(i); + + if (linkConfig) { + if (!currentPorts.contains(linkConfig->portName())) { + _confToDelete.append(linkConfig); } + } else { + qWarning() << "Internal error"; } } - // Now remove all links that are gone - foreach (LinkConfiguration* pDelete, _confToDelete) { - removeLinkConfiguration(pDelete); - saveList = true; - } - // Save configuration list, which will also trigger a signal for the UI - if(saveList) { - saveLinkConfigurationList(); + + // Now disconnect/remove all links that are gone + foreach (LinkConfiguration* pDeleteConfig, _confToDelete) { + LinkInterface* link = pDeleteConfig->link(); + if (link) { + disconnectLink(link, true /* disconnectAutoconnectLink */); + } + _autoconnectConfigurations.removeOne(pDeleteConfig); + delete pDeleteConfig; } } #endif -bool LinkManager::containsLink(LinkInterface* link) +bool LinkManager::anyConnectedLinks(void) +{ + bool found = false; + for (int i=0; i<_links.count(); i++) { + LinkInterface* link = _links.value(i); + + if (link && link->isConnected()) { + found = true; + break; + } + } + return found; +} + +bool LinkManager::anyNonAutoconnectConnectedLinks(void) { + // FIXME: Should remove this duplication with anyConnectedLinks bool found = false; - foreach (SharedLinkInterface sharedLink, _links) { - if (sharedLink.data() == link) { + for (int i=0; i<_links.count(); i++) { + LinkInterface* link = _links.value(i); + + if (link && !link->autoconnect() && link->isConnected()) { found = true; break; } @@ -585,11 +554,13 @@ bool LinkManager::containsLink(LinkInterface* link) return found; } -bool LinkManager::anyConnectedLinks(void) +bool LinkManager::anyActiveLinks(void) { bool found = false; - foreach (SharedLinkInterface sharedLink, _links) { - if (sharedLink.data()->isConnected()) { + for (int i=0; i<_links.count(); i++) { + LinkInterface* link = _links.value(i); + + if (link && link->active()) { found = true; break; } @@ -597,14 +568,117 @@ bool LinkManager::anyConnectedLinks(void) return found; } -SharedLinkInterface& LinkManager::sharedPointerForLink(LinkInterface* link) +bool LinkManager::anyNonAutoconnectActiveLinks(void) { + // FIXME: Should remove this duplication with anyActiveLinks + bool found = false; for (int i=0; i<_links.count(); i++) { - if (_links[i].data() == link) { - return _links[i]; + LinkInterface* link = _links.value(i); + + if (link && !link->autoconnect() && link->active()) { + found = true; + break; + } + } + return found; +} + +void LinkManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType) +{ + if (!link->active() && !_ignoreVehicleIds.contains(vehicleId)) { + qCDebug(LinkManagerLog) << "New heartbeat on link:vehicleId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType " + << link->getName() + << vehicleId + << vehicleMavlinkVersion + << vehicleFirmwareType + << vehicleType; + + if (vehicleId == _mavlinkProtocol->getSystemId()) { + _app->showToolBarMessage(QString("Warning: A vehicle is using the same system id as QGroundControl: %1").arg(vehicleId)); + } + + QSettings settings; + bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool(); + if (mavlinkVersionCheck && vehicleMavlinkVersion != MAVLINK_VERSION) { + _ignoreVehicleIds += vehicleId; + _app->showToolBarMessage(QString("The MAVLink protocol version on vehicle #%1 and QGroundControl differ! " + "It is unsafe to use different MAVLink versions. " + "QGroundControl therefore refuses to connect to vehicle #%1, which sends MAVLink version %2 (QGroundControl uses version %3).").arg(vehicleId).arg(vehicleMavlinkVersion).arg(MAVLINK_VERSION)); + return; + } + + bool previousAnyActiveLinks = anyActiveLinks(); + bool previousAnyNonAutoconnectActiveLinks = anyNonAutoconnectActiveLinks(); + + link->setActive(true); + emit linkActive(link, vehicleId, vehicleFirmwareType, vehicleType); + + if (!previousAnyActiveLinks) { + emit anyActiveLinksChanged(true); } + if (!previousAnyNonAutoconnectActiveLinks && anyNonAutoconnectActiveLinks()) { + emit anyNonAutoconnectActiveLinksChanged(true); + } + } +} + +void LinkManager::shutdown(void) +{ + setConnectionsSuspended("Shutdown"); + disconnectAll(true /* disconnectAutoconnectLink */); +} + +void LinkManager::setAutoconnectUDP(bool autoconnect) +{ + if (_autoconnectUDP != autoconnect) { + QSettings settings; + + settings.beginGroup(_settingsGroup); + settings.setValue(_autoconnectUDPKey, autoconnect); + + _autoconnectUDP = autoconnect; + emit autoconnectUDPChanged(autoconnect); } - // This should never happen - Q_ASSERT(false); - return _nullSharedLink; +} + +void LinkManager::setAutoconnectPixhawk(bool autoconnect) +{ + if (_autoconnectPixhawk != autoconnect) { + QSettings settings; + + settings.beginGroup(_settingsGroup); + settings.setValue(_autoconnectPixhawkKey, autoconnect); + + _autoconnectPixhawk = autoconnect; + emit autoconnectPixhawkChanged(autoconnect); + } + +} + +void LinkManager::setAutoconnect3DRRadio(bool autoconnect) +{ + if (_autoconnect3DRRadio != autoconnect) { + QSettings settings; + + settings.beginGroup(_settingsGroup); + settings.setValue(_autoconnect3DRRadioKey, autoconnect); + + _autoconnect3DRRadio = autoconnect; + emit autoconnect3DRRadioChanged(autoconnect); + } + +} + +void LinkManager::setAutoconnectPX4Flow(bool autoconnect) +{ + if (_autoconnectPX4Flow != autoconnect) { + QSettings settings; + + settings.beginGroup(_settingsGroup); + settings.setValue(_autoconnectPX4FlowKey, autoconnect); + + _autoconnectPX4Flow = autoconnect; + emit autoconnectPX4FlowChanged(autoconnect); + } + } diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index 3063f50eb23ef7c86c458b6eca0444efd03bfb28..521efa6a95d0f608471af406ae2aafe3440de03c 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -35,23 +35,23 @@ This file is part of the PIXHAWK project #include "LinkInterface.h" #include "QGCLoggingCategory.h" #include "QGCToolbox.h" +#include "ProtocolInterface.h" +#include "MAVLinkProtocol.h" +#include "LogReplayLink.h" +#include "QmlObjectListModel.h" -// Links #ifndef __ios__ -#include "SerialLink.h" + #include "SerialLink.h" #endif -#include "UDPLink.h" -#include "TCPLink.h" -#include "LogReplayLink.h" #ifdef QT_DEBUG -#include "MockLink.h" + #include "MockLink.h" #endif -#include "ProtocolInterface.h" -#include "MAVLinkProtocol.h" +class UDPConfiguration; Q_DECLARE_LOGGING_CATEGORY(LinkManagerLog) +Q_DECLARE_LOGGING_CATEGORY(LinkManagerVerboseLog) class QGCApplication; @@ -72,17 +72,36 @@ public: LinkManager(QGCApplication* app); ~LinkManager(); - /*! - Add a new link configuration setting to the list - @param[in] link An instance of the link setting. - */ - void addLinkConfiguration(LinkConfiguration* link); + Q_PROPERTY(bool anyActiveLinks READ anyActiveLinks NOTIFY anyActiveLinksChanged) + Q_PROPERTY(bool anyNonAutoconnectActiveLinks READ anyNonAutoconnectActiveLinks NOTIFY anyNonAutoconnectActiveLinksChanged) + Q_PROPERTY(bool anyConnectedLinks READ anyConnectedLinks NOTIFY anyConnectedLinksChanged) + Q_PROPERTY(bool anyNonAutoconnectConnectedLinks READ anyNonAutoconnectConnectedLinks NOTIFY anyNonAutoconnectConnectedLinksChanged) + Q_PROPERTY(bool autoconnectUDP READ autoconnectUDP WRITE setAutoconnectUDP NOTIFY autoconnectUDPChanged) + Q_PROPERTY(bool autoconnectPixhawk READ autoconnectPixhawk WRITE setAutoconnectPixhawk NOTIFY autoconnectPixhawkChanged) + Q_PROPERTY(bool autoconnect3DRRadio READ autoconnect3DRRadio WRITE setAutoconnect3DRRadio NOTIFY autoconnect3DRRadioChanged) + Q_PROPERTY(bool autoconnectPX4Flow READ autoconnectPX4Flow WRITE setAutoconnectPX4Flow NOTIFY autoconnectPX4FlowChanged) + Q_PROPERTY(QmlObjectListModel* links READ links CONSTANT) + Q_PROPERTY(QmlObjectListModel* linkConfigurations READ linkConfigurations CONSTANT) + + // Property accessors + + bool anyConnectedLinks(void); + bool anyNonAutoconnectConnectedLinks(void); + bool anyActiveLinks(void); + bool anyNonAutoconnectActiveLinks(void); + bool autoconnectUDP(void) { return _autoconnectUDP; } + bool autoconnectPixhawk(void) { return _autoconnectPixhawk; } + bool autoconnect3DRRadio(void) { return _autoconnect3DRRadio; } + bool autoconnectPX4Flow(void) { return _autoconnectPX4Flow; } + + QmlObjectListModel* links(void) { return &_links; } + QmlObjectListModel* linkConfigurations(void) { return &_linkConfigurations; } + + void setAutoconnectUDP(bool autoconnect); + void setAutoconnectPixhawk(bool autoconnect); + void setAutoconnect3DRRadio(bool autoconnect); + void setAutoconnectPX4Flow(bool autoconnect); - /*! - Removes (and deletes) an existing link configuration setting from the list - @param[in] link An instance of the link setting. - */ - void removeLinkConfiguration(LinkConfiguration* link); /// Load list of link configurations from disk void loadLinkConfigurationList(); @@ -90,19 +109,9 @@ public: /// Save list of link configurations from disk void saveLinkConfigurationList(); - /// Get a list of the configured links. This is the list of configured links that can be used by QGC. - const QList getLinkConfigurationList(); - /// Suspend automatic confguration updates (during link maintenance for instance) void suspendConfigurationUpdates(bool suspend); - /// Returns list of all links - const QList getLinks(); - - // Returns list of all serial links -#ifndef __ios__ - const QList getSerialLinks(); -#endif /// Sets the flag to suspend the all new connections /// @param reason User visible reason to suspend connections void setConnectionsSuspended(QString reason); @@ -112,68 +121,85 @@ public: /// Creates, connects (and adds) a link based on the given configuration instance. /// Link takes ownership of config. - LinkInterface* createConnectedLink(LinkConfiguration* config); + Q_INVOKABLE LinkInterface* createConnectedLink(LinkConfiguration* config, bool persistenLink = false); /// Creates, connects (and adds) a link based on the given configuration name. - LinkInterface* createConnectedLink(const QString& name); - - /// Returns true if the link manager is holding this link - bool containsLink(LinkInterface* link); - - /// Returns the QSharedPointer for this link. You must use SharedLinkInterface if you are going to - /// keep references to a link in a thread other than the main ui thread. - SharedLinkInterface& sharedPointerForLink(LinkInterface* link); + LinkInterface* createConnectedLink(const QString& name, bool persistenLink = false); - /// Re-connects all existing links - bool connectAll(); - - /// Disconnects all existing links - bool disconnectAll(); + /// Disconnects all existing links, including persistent links. + /// @param disconnectAutoconnectLink See disconnectLink + void disconnectAll(bool disconnectAutoconnectLink); /// Connect the specified link bool connectLink(LinkInterface* link); - /// Disconnect the specified link - bool disconnectLink(LinkInterface* link); - - /// Returns true if there are any connected links - bool anyConnectedLinks(void); + /// Disconnect the specified link. + /// @param disconnectAutoconnectLink + /// true: link is disconnected no matter what type + /// false: if autoconnect link, link is marked as inactive and linkInactive is signalled + /// false: if not autoconnect link, link is disconnected + Q_INVOKABLE bool disconnectLink(LinkInterface* link, bool disconnectAutoconnectLink); + /// Called to notify that a heartbeat was received with the specified information. Will transition + /// a link to active as needed. + /// @param link Heartbeat came through on this link + /// @param vehicleId Mavlink system id for vehicle + /// @param heartbeat Mavlink heartbeat message + /// @return true: continue further processing of this message, false: disregard this message + bool notifyHeartbeatInfo(LinkInterface* link, int vehicleId, mavlink_heartbeat_t& heartbeat); + // The following APIs are public but should not be called in normal use. The are mainly exposed // here for unit test code. void _deleteLink(LinkInterface* link); void _addLink(LinkInterface* link); + // Called to signal app shutdown. Disconnects all links while turning off auto-connect. + void shutdown(void); + // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); signals: + void anyActiveLinksChanged(bool anyActiveLinks); + void anyNonAutoconnectActiveLinksChanged(bool anyNonAutoconnectActiveLinks); + void anyConnectedLinksChanged(bool anyConnectedLinks); + void anyNonAutoconnectConnectedLinksChanged(bool anyNoAutoconnectConnectedLinks); + void autoconnectUDPChanged(bool autoconnect); + void autoconnectPixhawkChanged(bool autoconnect); + void autoconnect3DRRadioChanged(bool autoconnect); + void autoconnectPX4FlowChanged(bool autoconnect); + void newLink(LinkInterface* link); + + // Link has been deleted. You may not necessarily get a linkInactive before the link is deleted. void linkDeleted(LinkInterface* link); + + // Link has been connected, but no Vehicle seen on link yet. void linkConnected(LinkInterface* link); + + // Link disconnected, all vehicles on link should be gone as well. void linkDisconnected(LinkInterface* link); + + // New vehicle has been seen on the link. + void linkActive(LinkInterface* link, int vehicleId, int vehicleFirmwareType, int vehicleType); + + // No longer hearing from any vehicles on this link. + void linkInactive(LinkInterface* link); + void linkConfigurationChanged(); private slots: void _linkConnected(void); void _linkDisconnected(void); + void _vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); private: - virtual void _shutdown(void); - bool _connectionsSuspendedMsg(void); - void _updateConfigurationList(void); + void _updateAutoConnectLinks(void); + #ifndef __ios__ - SerialConfiguration* _findSerialConfiguration(const QString& portName); + SerialConfiguration* _autoconnectConfigurationsContainsPort(const QString& portName); #endif - QList _linkConfigurations; ///< List of configured links - - /// List of available links kept as QSharedPointers. We use QSharedPointer since - /// there are other objects that maintain copies of these links in other threads. - /// The reference counting allows for orderly deletion. - QList _links; - - QMutex _linkListMutex; ///< Mutex for thread safe access to _links list bool _configUpdateSuspended; ///< true: stop updating configuration list bool _configurationsLoaded; ///< true: Link configurations have been loaded @@ -183,10 +209,25 @@ private: QTimer _portListTimer; #endif uint32_t _mavlinkChannelsUsedBitMask; - - SharedLinkInterface _nullSharedLink; MAVLinkProtocol* _mavlinkProtocol; + QList _ignoreVehicleIds; ///< List of vehicle id for which we ignore further communication + + QmlObjectListModel _links; + QmlObjectListModel _linkConfigurations; + QmlObjectListModel _autoconnectConfigurations; + UDPConfiguration* _autoconnectUDPConfig; + + bool _autoconnectUDP; + bool _autoconnectPixhawk; + bool _autoconnect3DRRadio; + bool _autoconnectPX4Flow; + + static const char* _settingsGroup; + static const char* _autoconnectUDPKey; + static const char* _autoconnectPixhawkKey; + static const char* _autoconnect3DRRadioKey; + static const char* _autoconnectPX4FlowKey; }; #endif diff --git a/src/comm/LogReplayLink.cc b/src/comm/LogReplayLink.cc index d5ac20cebffc2e36a45e1a89c6543bade06128cc..1ab9214f406fe69953d3407344555f59c2a7bda6 100644 --- a/src/comm/LogReplayLink.cc +++ b/src/comm/LogReplayLink.cc @@ -102,7 +102,7 @@ LogReplayLink::~LogReplayLink(void) bool LogReplayLink::_connect(void) { // Disallow replay when any links are connected - if (qgcApp()->toolbox()->linkManager()->anyConnectedLinks()) { + if (qgcApp()->toolbox()->linkManager()->anyActiveLinks()) { emit communicationError(_errorTitle, "You must close all connections prior to replaying a log."); return false; } @@ -115,7 +115,7 @@ bool LogReplayLink::_connect(void) return true; } -bool LogReplayLink::_disconnect(void) +void LogReplayLink::_disconnect(void) { if (_connected) { quit(); @@ -123,7 +123,6 @@ bool LogReplayLink::_disconnect(void) _connected = false; emit disconnected(); } - return true; } void LogReplayLink::run(void) @@ -366,7 +365,6 @@ void LogReplayLink::_readNextLogEntry(void) void LogReplayLink::_play(void) { - // FIXME: With move to link I don't think this is needed any more? Except for the replay widget handling multi-uas? qgcApp()->toolbox()->linkManager()->setConnectionsSuspended(tr("Connect not allowed during Flight Data replay.")); #ifndef __mobile__ qgcApp()->toolbox()->mavlinkProtocol()->suspendLogForReplay(true); diff --git a/src/comm/LogReplayLink.h b/src/comm/LogReplayLink.h index 556c95c534ccab6c83fd40a84dc54b28f9ff0f44..b1cd2a9ed84d0d99fdc8f73a6f84866afe82b73f 100644 --- a/src/comm/LogReplayLink.h +++ b/src/comm/LogReplayLink.h @@ -33,6 +33,8 @@ class LogReplayLinkConfiguration : public LinkConfiguration { + Q_OBJECT + public: LogReplayLinkConfiguration(const QString& name); LogReplayLinkConfiguration(LogReplayLinkConfiguration* copy); @@ -130,7 +132,7 @@ private: // Virtuals from LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); // Virtuals from QThread virtual void run(void); diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index af6b42128508fdc6f354cfcff86fca3611302ac0..19c673075a6e156dceb33b3c11fbfb495c79174b 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -193,13 +193,6 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected) Q_ASSERT(link); if (connected) { - foreach (SharedLinkInterface sharedLink, _connectedLinks) { - Q_ASSERT(sharedLink.data() != link); - } - - // Use the same shared pointer as LinkManager - _connectedLinks.append(_linkMgr->sharedPointerForLink(link)); - if (link->requiresUSBMavlinkStart()) { // Send command to start MAVLink // XXX hacky but safe @@ -210,17 +203,6 @@ void MAVLinkProtocol::_linkStatusChanged(LinkInterface* link, bool connected) link->writeBytes(cmd, strlen(cmd)); link->writeBytes(init, 4); } - } else { - bool found = false; - for (int i=0; i<_connectedLinks.count(); i++) { - if (_connectedLinks[i].data() == link) { - found = true; - _connectedLinks.removeAt(i); - break; - } - } - Q_UNUSED(found); - Q_ASSERT(found); } } @@ -236,7 +218,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) // Since receiveBytes signals cross threads we can end up with signals in the queue // that come through after the link is disconnected. For these we just drop the data // since the link is closed. - if (!_linkMgr->containsLink(link)) { + if (!_linkMgr->links()->contains(link)) { return; } @@ -300,7 +282,7 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) { mavlink_message_t msg; mavlink_msg_ping_pack(getSystemId(), getComponentId(), &msg, ping.time_usec, ping.seq, message.sysid, message.compid); - sendMessage(msg); + _sendMessage(msg); } } @@ -359,12 +341,9 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) _startLogging(); #endif - // Notify the vehicle manager of the heartbeat. This will create/update vehicles as needed. mavlink_heartbeat_t heartbeat; mavlink_msg_heartbeat_decode(&message, &heartbeat); - if (!_multiVehicleManager->notifyHeartbeatInfo(link, message.sysid, heartbeat)) { - continue; - } + emit vehicleHeartbeatInfo(link, message.sysid, heartbeat.mavlink_version, heartbeat.autopilot, heartbeat.type); } // Increase receive counter @@ -417,15 +396,13 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) // Multiplex message if enabled if (m_multiplexingEnabled) { - // Get all links connected to this unit - QList links = _linkMgr->getLinks(); - // Emit message on all links that are currently connected - foreach (LinkInterface* currLink, links) - { + for (int i=0; i<_linkMgr->links()->count(); i++) { + LinkInterface* currLink = _linkMgr->links()->value(i); + // Only forward this message to the other links, // not the link the message was received on - if (currLink != link) sendMessage(currLink, message, message.sysid, message.compid); + if (currLink && currLink != link) _sendMessage(currLink, message, message.sysid, message.compid); } } } @@ -460,17 +437,11 @@ int MAVLinkProtocol::getComponentId() /** * @param message message to send */ -void MAVLinkProtocol::sendMessage(mavlink_message_t message) +void MAVLinkProtocol::_sendMessage(mavlink_message_t message) { - // Get all links connected to this unit - QList links = _linkMgr->getLinks(); - - // Emit message on all links that are currently connected - QList::iterator i; - for (i = links.begin(); i != links.end(); ++i) - { - sendMessage(*i, message); -// qDebug() << __FILE__ << __LINE__ << "SENT MESSAGE OVER" << ((LinkInterface*)*i)->getName() << "LIST SIZE:" << links.size(); + for (int i=0; i<_linkMgr->links()->count(); i++) { + LinkInterface* link = _linkMgr->links()->value(i); + _sendMessage(link, message); } } @@ -478,7 +449,7 @@ void MAVLinkProtocol::sendMessage(mavlink_message_t message) * @param link the link to send the message over * @param message message to send */ -void MAVLinkProtocol::sendMessage(LinkInterface* link, mavlink_message_t message) +void MAVLinkProtocol::_sendMessage(LinkInterface* link, mavlink_message_t message) { // Create buffer static uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; @@ -501,7 +472,7 @@ void MAVLinkProtocol::sendMessage(LinkInterface* link, mavlink_message_t message * @param systemid id of the system the message is originating from * @param componentid id of the component the message is originating from */ -void MAVLinkProtocol::sendMessage(LinkInterface* link, mavlink_message_t message, quint8 systemid, quint8 componentid) +void MAVLinkProtocol::_sendMessage(LinkInterface* link, mavlink_message_t message, quint8 systemid, quint8 componentid) { // Create buffer static uint8_t buffer[MAVLINK_MAX_PACKET_LEN]; @@ -529,7 +500,7 @@ void MAVLinkProtocol::sendHeartbeat() { mavlink_message_t beat; mavlink_msg_heartbeat_pack(getSystemId(), getComponentId(),&beat, MAV_TYPE_GCS, MAV_AUTOPILOT_INVALID, MAV_MODE_MANUAL_ARMED, 0, MAV_STATE_ACTIVE); - sendMessage(beat); + _sendMessage(beat); } if (m_authEnabled) { @@ -538,7 +509,7 @@ void MAVLinkProtocol::sendHeartbeat() memset(&auth, 0, sizeof(auth)); memcpy(auth.key, m_authKey.toStdString().c_str(), qMin(m_authKey.length(), MAVLINK_MSG_AUTH_KEY_FIELD_KEY_LEN)); mavlink_msg_auth_key_encode(getSystemId(), getComponentId(), &msg, &auth); - sendMessage(msg); + _sendMessage(msg); } } diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 02964a994b916cd938c5d933d857ec548fb9a6ce..fe29a5f112aba6ba44de5e2867ea5f9d0444e8f9 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -158,12 +158,6 @@ public slots: void linkConnected(void); void linkDisconnected(void); - /** @brief Send MAVLink message through serial interface */ - void sendMessage(mavlink_message_t message); - /** @brief Send MAVLink message */ - void sendMessage(LinkInterface* link, mavlink_message_t message); - /** @brief Send MAVLink message with correct system / component ID */ - void sendMessage(LinkInterface* link, mavlink_message_t message, quint8 systemid, quint8 componentid); /** @brief Set the rate at which heartbeats are emitted */ void setHeartbeatRate(int rate); /** @brief Set the system id of this application */ @@ -238,6 +232,9 @@ protected: int systemId; signals: + /// Heartbeat received on link + void vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); + /** @brief Message received and directly copied via signal */ void messageReceived(LinkInterface* link, mavlink_message_t message); /** @brief Emitted if heartbeat emission mode is changed */ @@ -289,6 +286,9 @@ private slots: private: void _linkStatusChanged(LinkInterface* link, bool connected); + void _sendMessage(mavlink_message_t message); + void _sendMessage(LinkInterface* link, mavlink_message_t message); + void _sendMessage(LinkInterface* link, mavlink_message_t message, quint8 systemid, quint8 componentid); #ifndef __mobile__ bool _closeLogFile(void); @@ -303,12 +303,7 @@ private: static const char* _tempLogFileTemplate; ///< Template for temporary log file static const char* _logFileExtension; ///< Extension for log files #endif - - /// List of all links connected to protocol. We keep SharedLinkInterface objects - /// which are QSharedPointer's in order to maintain reference counts across threads. - /// This way Link deletion works correctly. - QList _connectedLinks; - + QTimer _heartbeatTimer; ///< Timer to emit heartbeats int _heartbeatRate; ///< Heartbeat rate, controls the timer interval bool _heartbeatsEnabled; ///< Enabled/disable heartbeat emission diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index d265ee2615bd108c320c873fe3276ccc650ef63a..4ca7c4a50eac915a53db1046f5b1d4292f553307 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -98,6 +98,7 @@ MockLink::MockLink(MockConfiguration* config) _firmwareType = config->firmwareType(); _vehicleType = config->vehicleType(); _sendStatusText = config->sendStatusText(); + _config->setLink(this); } union px4_custom_mode px4_cm; @@ -136,7 +137,7 @@ bool MockLink::_connect(void) return true; } -bool MockLink::_disconnect(void) +void MockLink::_disconnect(void) { if (_connected) { _connected = false; @@ -144,8 +145,6 @@ bool MockLink::_disconnect(void) wait(); emit disconnected(); } - - return true; } void MockLink::run(void) diff --git a/src/comm/MockLink.h b/src/comm/MockLink.h index 25fac11fb7cb24eb48960836ce5c031f04331619..e643320eaa67a8416a7bac7600ebb83dfd34c2cc 100644 --- a/src/comm/MockLink.h +++ b/src/comm/MockLink.h @@ -37,6 +37,8 @@ Q_DECLARE_LOGGING_CATEGORY(MockLinkVerboseLog) class MockConfiguration : public LinkConfiguration { + Q_OBJECT + public: MockConfiguration(const QString& name); MockConfiguration(MockConfiguration* source); @@ -146,7 +148,7 @@ private slots: private: // From LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); // QThread override virtual void run(void); diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index a19296ac1685b1e2cac2dfa126070295e6f23da5..b8f49107ef1a397e9d9b37d8b5b834ac06ed7ce7 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -124,7 +124,7 @@ void SerialLink::readBytes() * * @return True if connection has been disconnected, false if connection couldn't be disconnected. **/ -bool SerialLink::_disconnect(void) +void SerialLink::_disconnect(void) { if (_port) { _port->close(); @@ -135,7 +135,6 @@ bool SerialLink::_disconnect(void) #ifdef __android__ qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(false); #endif - return true; } /** diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index 49baceaadd56478106a35316868f98011a735dc6..078fccf6980c35714d791f2419b84fdee25058f1 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -59,6 +59,8 @@ Q_DECLARE_LOGGING_CATEGORY(SerialLinkLog) class SerialConfiguration : public LinkConfiguration { + Q_OBJECT + public: SerialConfiguration(const QString& name); @@ -158,7 +160,7 @@ private: // From LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); // Internal methods void _emitLinkError(const QString& errorMsg); diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index c2d891926394c7a627a827019e06d002a23364a0..be821f19ed75ed3792f898ce4e1f428853247db6 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -124,18 +124,16 @@ void TCPLink::readBytes() * * @return True if connection has been disconnected, false if connection couldn't be disconnected. **/ -bool TCPLink::_disconnect(void) +void TCPLink::_disconnect(void) { quit(); wait(); - if (_socket) - { + if (_socket) { _socketIsConnected = false; _socket->deleteLater(); // Make sure delete happens on correct thread _socket = NULL; emit disconnected(); } - return true; } /** diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h index 2d0e5a52eef1ee511a36f5290671048187a2de03..87404c2f0000c5fb8e468798f4bf666b7b5a3601 100644 --- a/src/comm/TCPLink.h +++ b/src/comm/TCPLink.h @@ -52,6 +52,8 @@ class TCPLinkUnitTest; class TCPConfiguration : public LinkConfiguration { + Q_OBJECT + public: /*! @@ -164,7 +166,7 @@ private: // From LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); bool _hardwareConnect(); void _restartConnection(); diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index a393337e6e48843a0947e58390afa1b6d3ffdcfa..90d6140630b095879e5d06c2e6378222e59c4296 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -281,7 +281,7 @@ void UDPLink::readBytes() * * @return True if connection has been disconnected, false if connection couldn't be disconnected. **/ -bool UDPLink::_disconnect(void) +void UDPLink::_disconnect(void) { _running = false; quit(); @@ -293,7 +293,6 @@ bool UDPLink::_disconnect(void) emit disconnected(); } _connectState = false; - return true; } /** diff --git a/src/comm/UDPLink.h b/src/comm/UDPLink.h index d83058a91f79ced00fbcf9bd2c9b24038420f0e1..a934b9fd0a2001b14525a0ec24ebb8d3765a5592 100644 --- a/src/comm/UDPLink.h +++ b/src/comm/UDPLink.h @@ -52,6 +52,8 @@ This file is part of the QGROUNDCONTROL project class UDPConfiguration : public LinkConfiguration { + Q_OBJECT + public: /*! @@ -203,7 +205,7 @@ private: // From LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); bool _hardwareConnect(); void _restartConnection(); diff --git a/src/comm/XbeeLink.cpp b/src/comm/XbeeLink.cpp index de1f31a1bdaea47a0c9a81f672888f9a94fe7026..d365eb85d65f0b1bf60aeccf06c0ba63b9ad1eca 100644 --- a/src/comm/XbeeLink.cpp +++ b/src/comm/XbeeLink.cpp @@ -158,7 +158,7 @@ bool XbeeLink::_connect(void) return true; } -bool XbeeLink::_disconnect(void) +void XbeeLink::_disconnect(void) { if(this->isRunning()) this->terminate(); //stop running the thread, restart it upon connect @@ -170,7 +170,6 @@ bool XbeeLink::_disconnect(void) this->m_connected = false; emit disconnected(); - return true; } void XbeeLink::writeBytes(const char *bytes, qint64 length) // TO DO: delete the data array diff --git a/src/comm/XbeeLink.h b/src/comm/XbeeLink.h index 04bb62aa3504ef5cc9c54a764f0ab71585dbff3c..78048be7ddac2a10953497727eaed157b17797e3 100644 --- a/src/comm/XbeeLink.h +++ b/src/comm/XbeeLink.h @@ -67,7 +67,7 @@ protected: private: // From LinkInterface virtual bool _connect(void); - virtual bool _disconnect(void); + virtual void _disconnect(void); bool hardwareConnect(); //void CALLTYPE portCallback(xbee_con *XbeeCon, xbee_pkt *XbeePkt); diff --git a/src/qgcunittest/LinkManagerTest.cc b/src/qgcunittest/LinkManagerTest.cc index 114dab7774f35475d545c73743ca561b2897236c..ad8861b4d5e11f2e8ef1fbedc7fd36ea80615b53 100644 --- a/src/qgcunittest/LinkManagerTest.cc +++ b/src/qgcunittest/LinkManagerTest.cc @@ -71,30 +71,29 @@ void LinkManagerTest::cleanup(void) void LinkManagerTest::_add_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->getLinks().count() == 0); + Q_ASSERT(_linkMgr->links()->count() == 0); _connectMockLink(); - QList links = _linkMgr->getLinks(); - QCOMPARE(links.count(), 1); - QCOMPARE(dynamic_cast(links[0]), _mockLink); + QCOMPARE(_linkMgr->links()->count(), 1); + QCOMPARE(_linkMgr->links()->value(0), _mockLink); } void LinkManagerTest::_delete_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->getLinks().count() == 0); + Q_ASSERT(_linkMgr->links()->count() == 0); _connectMockLink(); _disconnectMockLink(); - QCOMPARE(_linkMgr->getLinks().count(), 0); + QCOMPARE(_linkMgr->links()->count(), 0); } void LinkManagerTest::_addSignals_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->getLinks().count() == 0); + Q_ASSERT(_linkMgr->links()->count() == 0); Q_ASSERT(_multiSpy->checkNoSignals() == true); _connectMockLink(); @@ -114,7 +113,7 @@ void LinkManagerTest::_addSignals_test(void) void LinkManagerTest::_deleteSignals_test(void) { Q_ASSERT(_linkMgr); - Q_ASSERT(_linkMgr->getLinks().count() == 0); + Q_ASSERT(_linkMgr->links()->count() == 0); Q_ASSERT(_multiSpy->checkNoSignals() == true); _connectMockLink(); diff --git a/src/qgcunittest/UnitTest.cc b/src/qgcunittest/UnitTest.cc index b3f5b404c4cd9846ae96212c8eee14a9f04afd60..6b29e3f36741657645e894f4925e46fd2031f245 100644 --- a/src/qgcunittest/UnitTest.cc +++ b/src/qgcunittest/UnitTest.cc @@ -387,7 +387,7 @@ void UnitTest::_disconnectMockLink(void) if (_mockLink) { QSignalSpy linkSpy(_linkManager, SIGNAL(linkDeleted(LinkInterface*))); - _linkManager->disconnectLink(_mockLink); + _linkManager->disconnectLink(_mockLink, false /* disconnectAutoconnectLink */); // Wait for link to go away linkSpy.wait(1000); diff --git a/src/ui/MAVLinkSettingsWidget.cc b/src/ui/MAVLinkSettingsWidget.cc index e1d2f3305992f386f157028051880f0b227fd1eb..5faa01f5e9ae2faaa5d0088cd130e22f12b37e45 100644 --- a/src/ui/MAVLinkSettingsWidget.cc +++ b/src/ui/MAVLinkSettingsWidget.cc @@ -141,12 +141,14 @@ void MAVLinkSettingsWidget::enableDroneOS(bool enable) QString hostString = m_ui->droneOSComboBox->currentText(); //QString host = hostString.split(":").first(); + LinkManager* linkMgr = qgcApp()->toolbox()->linkManager(); + UDPLink* firstUdp = NULL; + // Delete from all lists first - UDPLink* firstUdp = NULL; - QList links = qgcApp()->toolbox()->linkManager()->getLinks(); - foreach (LinkInterface* link, links) - { - UDPLink* udp = dynamic_cast(link); + for (int i=0; ilinks()->count(); i++) { + LinkInterface* link = linkMgr->links()->value(i); + UDPLink* udp = qobject_cast(link); + if (udp) { if (!firstUdp) firstUdp = udp; diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index db4a8509cfbd61e18c6522c9c9626cf04d0de8df..de4799f6469f7ff2dfac9925ce2d784fbe69cc21 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -208,10 +208,6 @@ MainWindow::MainWindow() connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*))); #endif //QGC_MOUSE_ENABLED_LINUX - // These also cause the screen to redraw so we need to update any OpenGL canvases in QML controls - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkConnected, this, &MainWindow::_linkStateChange); - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDisconnected, this, &MainWindow::_linkStateChange); - // Connect link if (_autoReconnect) { @@ -445,7 +441,7 @@ void MainWindow::closeEvent(QCloseEvent *event) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if (button == QMessageBox::Yes) { - qgcApp()->toolbox()->linkManager()->disconnectAll(); + qgcApp()->toolbox()->linkManager()->shutdown(); // The above disconnect causes a flurry of activity as the vehicle components are removed. This in turn // causes the Windows Version of Qt to crash if you allow the close event to be accepted. In order to prevent // the crash, we ignore the close event and setup a delayed timer to close the window after things settle down. @@ -456,11 +452,17 @@ void MainWindow::closeEvent(QCloseEvent *event) return; } + // We still need to shutdown LinkManager even though no active connections so that we don't get any + // more auto-connect links during shutdown. + qgcApp()->toolbox()->linkManager()->shutdown(); + // This will process any remaining flight log save dialogs qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); // Should not be any active connections - Q_ASSERT(!qgcApp()->toolbox()->linkManager()->anyConnectedLinks()); + if (qgcApp()->toolbox()->linkManager()->anyActiveLinks()) { + qWarning() << "All links should be disconnected by now"; + } // We have to pull out the QmlWidget from the main window and delete it here, before // the MainWindow ends up getting deleted. Otherwise the Qml has a reference to MainWindow @@ -649,11 +651,6 @@ void MainWindow::restoreLastUsedConnection() } } -void MainWindow::_linkStateChange(LinkInterface*) -{ - emit repaintCanvas(); -} - #ifdef QGC_MOUSE_ENABLED_LINUX bool MainWindow::x11Event(XEvent *event) { diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 3ea0b300680c4e0d61497f761b5e19547ddcbefa..0772df024a91feb4770d0bc3a733253ff95e5329 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -159,8 +159,6 @@ signals: void initStatusChanged(const QString& message, int alignment, const QColor &color); /** Emitted when any value changes from any source */ void valueChanged(const int uasId, const QString& name, const QString& unit, const QVariant& value, const quint64 msec); - /** Emitted when any the Canvas elements within QML wudgets need updating */ - void repaintCanvas(); // Used for unit tests to know when the main window closes void mainWindowClosed(void); @@ -213,7 +211,6 @@ protected: QTimer windowNameUpdateTimer; private slots: - void _linkStateChange(LinkInterface*); void _closeWindow(void) { close(); } void _vehicleAdded(Vehicle* vehicle); diff --git a/src/ui/QGCLinkConfiguration.cc b/src/ui/QGCLinkConfiguration.cc index 80ae6e9ec168da61c2499efc2f862519cd63e6f0..71c564a5dc73cd27587549a23027c04a39f79ab8 100644 --- a/src/ui/QGCLinkConfiguration.cc +++ b/src/ui/QGCLinkConfiguration.cc @@ -32,6 +32,8 @@ This file is part of the QGROUNDCONTROL project #include "ui_QGCLinkConfiguration.h" #include "QGCCommConfiguration.h" #include "QGCMessageBox.h" +#include "UDPLink.h" +#include "TCPLink.h" QGCLinkConfiguration::QGCLinkConfiguration(QWidget *parent) : QWidget(parent), @@ -69,14 +71,17 @@ void QGCLinkConfiguration::on_delLinkButton_clicked() QMessageBox::Cancel); if (button == QMessageBox::Yes) { // Get link attached to this configuration (if any) - LinkInterface* iface = config->getLink(); + LinkInterface* iface = config->link(); if(iface) { - // Disconnect it (if connected) - qgcApp()->toolbox()->linkManager()->disconnectLink(iface); + qgcApp()->toolbox()->linkManager()->disconnectLink(iface, false /* disconnectAutoconnectLink */); } _viewModel->beginChange(); + // Remove configuration - qgcApp()->toolbox()->linkManager()->removeLinkConfiguration(config); + QmlObjectListModel* linkConfigurations = qgcApp()->toolbox()->linkManager()->linkConfigurations(); + linkConfigurations->removeOne(config); + delete config; + // Save list qgcApp()->toolbox()->linkManager()->saveLinkConfigurationList(); _viewModel->endChange(); @@ -97,11 +102,11 @@ void QGCLinkConfiguration::on_connectLinkButton_clicked() if(index.row() >= 0) { LinkConfiguration* config = _viewModel->getConfiguration(index.row()); if(config) { - LinkInterface* link = config->getLink(); + LinkInterface* link = config->link(); if(link) { // Disconnect Link if (link->isConnected()) { - qgcApp()->toolbox()->linkManager()->disconnectLink(link); + qgcApp()->toolbox()->linkManager()->disconnectLink(link, false /* disconnectAutoconnectLink */); } } else { LinkInterface* link = qgcApp()->toolbox()->linkManager()->createConnectedLink(config); @@ -186,7 +191,7 @@ void QGCLinkConfiguration::on_addLinkButton_clicked() if(config) { _fixUnnamed(config); _viewModel->beginChange(); - qgcApp()->toolbox()->linkManager()->addLinkConfiguration(commDialog->getConfig()); + qgcApp()->toolbox()->linkManager()->linkConfigurations()->append(commDialog->getConfig()); qgcApp()->toolbox()->linkManager()->saveLinkConfigurationList(); _viewModel->endChange(); } @@ -240,7 +245,7 @@ void QGCLinkConfiguration::_updateButtons() if(config->isDynamic()) { deleteEnabled = false; } - LinkInterface* link = config->getLink(); + LinkInterface* link = config->link(); if(link) { _ui->connectLinkButton->setText("Disconnect"); } else { @@ -261,16 +266,13 @@ LinkViewModel::LinkViewModel(QObject *parent) : QAbstractListModel(parent) int LinkViewModel::rowCount( const QModelIndex & parent) const { Q_UNUSED(parent); - QList cfgList = qgcApp()->toolbox()->linkManager()->getLinkConfigurationList(); - int count = cfgList.count(); - return count; + return qgcApp()->toolbox()->linkManager()->linkConfigurations()->count(); } QVariant LinkViewModel::data( const QModelIndex & index, int role) const { - QList cfgList = qgcApp()->toolbox()->linkManager()->getLinkConfigurationList(); - if (role == Qt::DisplayRole && index.row() < cfgList.count()) { - QString name(cfgList.at(index.row())->name()); + if (role == Qt::DisplayRole && index.row() < rowCount()) { + QString name(qgcApp()->toolbox()->linkManager()->linkConfigurations()->value(index.row())->name()); return name; } return QVariant(); @@ -278,9 +280,8 @@ QVariant LinkViewModel::data( const QModelIndex & index, int role) const LinkConfiguration* LinkViewModel::getConfiguration(int row) { - QList cfgList = qgcApp()->toolbox()->linkManager()->getLinkConfigurationList(); - if(row < cfgList.count()) { - return cfgList.at(row); + if(row < rowCount()) { + return qgcApp()->toolbox()->linkManager()->linkConfigurations()->value(row); } return NULL; } diff --git a/src/ui/QGCMAVLinkInspector.cc b/src/ui/QGCMAVLinkInspector.cc index 0ab169cd36aa50d8b86f1142098ee69a6f62f70b..56f4d2093e437a3d2ef839e897f9f7170fed2823 100644 --- a/src/ui/QGCMAVLinkInspector.cc +++ b/src/ui/QGCMAVLinkInspector.cc @@ -490,7 +490,10 @@ void QGCMAVLinkInspector::changeStreamInterval(int msgid, int interval) mavlink_message_t msg; mavlink_msg_request_data_stream_encode(_protocol->getSystemId(), _protocol->getComponentId(), &msg, &stream); +#if 0 + // FIXME: Is this really used? _protocol->sendMessage(msg); +#endif } void QGCMAVLinkInspector::rateTreeItemChanged(QTreeWidgetItem* paramItem, int column) diff --git a/src/ui/QGCMAVLinkLogPlayer.cc b/src/ui/QGCMAVLinkLogPlayer.cc index 957a2e363ffb30e099b163fe9c7bbd45d393c13b..78332fcb59fdb781b8931be3423ec2a813c72f58 100644 --- a/src/ui/QGCMAVLinkLogPlayer.cc +++ b/src/ui/QGCMAVLinkLogPlayer.cc @@ -60,7 +60,7 @@ void QGCMAVLinkLogPlayer::_pause(void) void QGCMAVLinkLogPlayer::_selectLogFileForPlayback(void) { // Disallow replay when any links are connected - if (qgcApp()->toolbox()->linkManager()->anyConnectedLinks()) { + if (qgcApp()->toolbox()->linkManager()->anyActiveLinks()) { QGCMessageBox::information(tr("Log Replay"), tr("You must close all connections prior to replaying a log.")); return; } @@ -188,4 +188,4 @@ void QGCMAVLinkLogPlayer::_replayLinkDisconnected(void) { _enablePlaybackControls(false); _replayLink = NULL; -} \ No newline at end of file +} diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index dd3aef9d0f9e8bfb0faa2d37df6f70d52eec23ed..204bbf09a48446d6491b6fc0c527c5f2229525eb 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -169,6 +169,34 @@ Rectangle { } } } + + //----------------------------------------------------------------- + //-- Autoconnect settings + QGCLabel { text: "Autoconnect to the following devices:" } + + QGCCheckBox { + text: "Pixhawk" + checked: QGroundControl.linkManager.autoconnectPixhawk + onClicked: QGroundControl.linkManager.autoconnectPixhawk = checked + } + + QGCCheckBox { + text: "3DR Radio" + checked: QGroundControl.linkManager.autoconnect3DRRadio + onClicked: QGroundControl.linkManager.autoconnect3DRRadio = checked + } + + QGCCheckBox { + text: "PX4 Flow" + checked: QGroundControl.linkManager.autoconnectPX4Flow + onClicked: QGroundControl.linkManager.autoconnectPX4Flow = checked + } + + QGCCheckBox { + text: "UDP" + checked: QGroundControl.linkManager.autoconnectUDP + onClicked: QGroundControl.linkManager.autoconnectUDP = checked + } } } } diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 577c5fba07dffb3ac71b7f2dd87bb76ea966c365..0b341755bc58b6185ad287a6b57fe16c940e4d02 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -31,6 +31,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 +import QGroundControl 1.0 import QGroundControl.Controls 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 @@ -152,7 +153,7 @@ Rectangle { } function showMavStatus() { - return (multiVehicleManager.activeVehicleAvailable && activeVehicle.heartbeatTimeout === 0 && _controller.connectionCount > 0); + return (multiVehicleManager.activeVehicleAvailable && activeVehicle.heartbeatTimeout === 0); } Component.onCompleted: { @@ -250,7 +251,7 @@ Rectangle { Item { visible: showMavStatus() && !connectionStatus.visible height: mainWindow.tbCellHeight - width: (toolBar.width - viewRow.width - connectRow.width) + width: (toolBar.width - viewRow.width) anchors.left: viewRow.right anchors.leftMargin: mainWindow.tbSpacing * 2 anchors.verticalCenter: parent.verticalCenter @@ -273,125 +274,6 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter } - Row { - id: connectRow - height: mainWindow.tbCellHeight - spacing: mainWindow.tbSpacing - anchors.rightMargin: mainWindow.tbSpacing - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - - Menu { - id: connectMenu - Component.onCompleted: { - _controller.configListChanged.connect(connectMenu.updateConnectionList); - connectMenu.updateConnectionList(); - } - function addMenuEntry(name) { - var label = "Add Connection" - if(name !== "") - label = name; - var mItem = connectMenu.addItem(label); - var menuSlot = function() {_controller.onConnect(name)}; - mItem.triggered.connect(menuSlot); - } - function updateConnectionList() { - connectMenu.clear(); - for(var i = 0; i < _controller.configList.length; i++) { - connectMenu.addMenuEntry(_controller.configList[i]); - } - if(_controller.configList.length > 0) { - connectMenu.addSeparator(); - } - // Add "Add Connection" to the list - connectMenu.addMenuEntry(""); - } - } - - Rectangle { - height: mainWindow.tbCellHeight - width: 1 - color: Qt.rgba(1,1,1,0.45) - } - - QGCToolBarButton { - id: connectButton - width: mainWindow.tbButtonWidth - height: mainWindow.tbCellHeight - visible: _controller.connectionCount === 0 - source: "/qmlimages/Connect.svg" - checked: false - onClicked: { - checked = false - connectMenu.popup() - /* - console.log("Main Window Width: " + mainWindow.width) - console.log("Toolbar height: " + toolBar.height) - console.log("Default font: " + ScreenTools.defaultFontPixelSize) - console.log("Font (.75): " + ScreenTools.defaultFontPixelSize * 0.75) - console.log("Font (.85): " + ScreenTools.defaultFontPixelSize * 0.85) - console.log("Font 1.5): " + ScreenTools.defaultFontPixelSize * 1.5) - console.log("Default Font Width: " + ScreenTools.defaultFontPixelWidth) - console.log("Default Font Height: " + ScreenTools.defaultFontPixelHeight) - console.log("--") - console.log("Real Font Height: " + ScreenTools.realFontHeight) - console.log("fontHRatio: " + ScreenTools.fontHRatio) - console.log("--") - console.log("cellHeight: " + cellHeight) - console.log("tbFontSmall: " + tbFontSmall); - console.log("tbFontNormal: " + tbFontNormal); - console.log("tbFontLarge: " + tbFontLarge); - console.log("mainWindow.tbSpacing: " + tbSpacing); - */ - } - } - - QGCToolBarButton { - id: disconnectButton - width: mainWindow.tbButtonWidth - height: mainWindow.tbCellHeight - visible: _controller.connectionCount === 1 - source: "/qmlimages/Disconnect.svg" - checked: false - onClicked: { - checked = false - _controller.onDisconnect(""); - } - } - - Menu { - id: disconnectMenu - Component.onCompleted: { - _controller.connectedListChanged.connect(disconnectMenu.onConnectedListChanged) - } - function addMenuEntry(name) { - var mItem = disconnectMenu.addItem(name); - var menuSlot = function() {_controller.onDisconnect(name)}; - mItem.triggered.connect(menuSlot); - } - function onConnectedListChanged(conList) { - disconnectMenu.clear(); - for(var i = 0; i < conList.length; i++) { - disconnectMenu.addMenuEntry(conList[i]); - } - } - } - - QGCToolBarButton { - id: multidisconnectButton - width: mainWindow.tbButtonWidth - height: mainWindow.tbCellHeight - visible: _controller.connectionCount > 1 - source: "/qmlimages/Disconnect.svg" - checked: false - onClicked: { - checked = false - disconnectMenu.popup() - } - } - - } - // Progress bar Rectangle { id: progressBar diff --git a/src/ui/toolbar/MainToolBarController.cc b/src/ui/toolbar/MainToolBarController.cc index 900e490670e5c3ddd74d65d98be7be76dafba20a..e7fa3e151b3ffb2e12fd3fb7430e7906fa5d9afd 100644 --- a/src/ui/toolbar/MainToolBarController.cc +++ b/src/ui/toolbar/MainToolBarController.cc @@ -43,24 +43,14 @@ MainToolBarController::MainToolBarController(QObject* parent) : QObject(parent) , _vehicle(NULL) , _mav(NULL) - , _connectionCount(0) , _progressBarValue(0.0f) - , _remoteRSSI(0) - , _remoteRSSIstore(100.0) , _telemetryRRSSI(0) , _telemetryLRSSI(0) , _rollDownMessages(0) , _toolbarMessageVisible(false) { - emit configListChanged(); - emit connectionCountChanged(_connectionCount); _activeVehicleChanged(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()); - // Link signals - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkConfigurationChanged, this, &MainToolBarController::_updateConfigurations); - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkConnected, this, &MainToolBarController::_linkConnected); - connect(qgcApp()->toolbox()->linkManager(), &LinkManager::linkDisconnected, this, &MainToolBarController::_linkDisconnected); - // RSSI (didn't like standard connection) connect(qgcApp()->toolbox()->mavlinkProtocol(), SIGNAL(radioStatusChanged(LinkInterface*, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned)), this, @@ -89,54 +79,6 @@ void MainToolBarController::onFlyView() MainWindow::instance()->showFlyView(); } -void MainToolBarController::onDisconnect(QString conf) -{ - if(conf.isEmpty()) { - // Disconnect Only Connected Link - int connectedCount = 0; - LinkInterface* connectedLink = NULL; - QList links = qgcApp()->toolbox()->linkManager()->getLinks(); - foreach(LinkInterface* link, links) { - if (link->isConnected()) { - connectedCount++; - connectedLink = link; - } - } - Q_ASSERT(connectedCount == 1); - Q_ASSERT(_connectionCount == 1); - Q_ASSERT(connectedLink); - qgcApp()->toolbox()->linkManager()->disconnectLink(connectedLink); - } else { - // Disconnect Named Connected Link - QList links = qgcApp()->toolbox()->linkManager()->getLinks(); - foreach(LinkInterface* link, links) { - if (link->isConnected()) { - if(link->getLinkConfiguration() && link->getLinkConfiguration()->name() == conf) { - qgcApp()->toolbox()->linkManager()->disconnectLink(link); - } - } - } - } -} - -void MainToolBarController::onConnect(QString conf) -{ - // Connect Link - if(conf.isEmpty()) { - MainWindow::instance()->manageLinks(); - } else { - // We don't want the list updating under our feet - qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(true); - // Create a link - LinkInterface* link = qgcApp()->toolbox()->linkManager()->createConnectedLink(conf); - if(link) { - // Save last used connection - MainWindow::instance()->saveLastUsedConnection(conf); - } - qgcApp()->toolbox()->linkManager()->suspendConfigurationUpdates(false); - } -} - void MainToolBarController::onEnterMessageArea(int x, int y) { Q_UNUSED(x); @@ -177,7 +119,6 @@ void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle) { // Disconnect the previous one (if any) if (_vehicle) { - disconnect(_mav, &UASInterface::remoteControlRSSIChanged, this, &MainToolBarController::_remoteControlRSSIChanged); disconnect(_vehicle->autopilotPlugin(), &AutoPilotPlugin::parameterListProgress, this, &MainToolBarController::_setProgressBarValue); _mav = NULL; _vehicle = NULL; @@ -188,109 +129,21 @@ void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle) { _vehicle = vehicle; _mav = vehicle->uas(); - connect(_mav, &UASInterface::remoteControlRSSIChanged, this, &MainToolBarController::_remoteControlRSSIChanged); connect(_vehicle->autopilotPlugin(), &AutoPilotPlugin::parameterListProgress, this, &MainToolBarController::_setProgressBarValue); } } -void MainToolBarController::_updateConfigurations() -{ - QStringList tmpList; - QList configs = qgcApp()->toolbox()->linkManager()->getLinkConfigurationList(); - foreach(LinkConfiguration* conf, configs) { - if(conf) { - if(conf->isPreferred()) { - tmpList.insert(0,conf->name()); - } else { - tmpList << conf->name(); - } - } - } - // Any changes? - if(tmpList != _linkConfigurations) { - _linkConfigurations = tmpList; - emit configListChanged(); - } -} - void MainToolBarController::_telemetryChanged(LinkInterface*, unsigned, unsigned, unsigned rssi, unsigned remrssi, unsigned, unsigned, unsigned) { - // We only care if we haveone single connection - if(_connectionCount == 1) { - if((unsigned)_telemetryLRSSI != rssi) { - // According to the Silabs data sheet, the RSSI value is 0.5db per bit - _telemetryLRSSI = rssi >> 1; - emit telemetryLRSSIChanged(_telemetryLRSSI); - } - if((unsigned)_telemetryRRSSI != remrssi) { - // According to the Silabs data sheet, the RSSI value is 0.5db per bit - _telemetryRRSSI = remrssi >> 1; - emit telemetryRRSSIChanged(_telemetryRRSSI); - } - } -} - -void MainToolBarController::_remoteControlRSSIChanged(uint8_t rssi) -{ - // We only care if we have one single connection - if(_connectionCount == 1) { - // Low pass to git rid of jitter - _remoteRSSIstore = (_remoteRSSIstore * 0.9f) + ((float)rssi * 0.1); - uint8_t filteredRSSI = (uint8_t)ceil(_remoteRSSIstore); - if(_remoteRSSIstore < 0.1) { - filteredRSSI = 0; - } - if(_remoteRSSI != filteredRSSI) { - _remoteRSSI = filteredRSSI; - emit remoteRSSIChanged(_remoteRSSI); - } - } -} - -void MainToolBarController::_linkConnected(LinkInterface*) -{ - _updateConnection(); -} - -void MainToolBarController::_linkDisconnected(LinkInterface* link) -{ - _updateConnection(link); -} - -void MainToolBarController::_updateConnection(LinkInterface *disconnectedLink) -{ - QStringList connList; - int oldCount = _connectionCount; - // If there are multiple connected links add/update the connect button menu - _connectionCount = 0; - QList links = qgcApp()->toolbox()->linkManager()->getLinks(); - foreach(LinkInterface* link, links) { - if (disconnectedLink != link && link->isConnected()) { - _connectionCount++; - if(link->getLinkConfiguration()) { - connList << link->getLinkConfiguration()->name(); - } - } - } - if(oldCount != _connectionCount) { - emit connectionCountChanged(_connectionCount); - } - if(connList != _connectedList) { - _connectedList = connList; - emit connectedListChanged(_connectedList); - } - // Update telemetry RSSI display - if(_connectionCount != 1 && _telemetryRRSSI > 0) { - _telemetryRRSSI = 0; - emit telemetryRRSSIChanged(_telemetryRRSSI); - } - if(_connectionCount != 1 && _telemetryLRSSI > 0) { - _telemetryLRSSI = 0; + if((unsigned)_telemetryLRSSI != rssi) { + // According to the Silabs data sheet, the RSSI value is 0.5db per bit + _telemetryLRSSI = rssi >> 1; emit telemetryLRSSIChanged(_telemetryLRSSI); } - if(_connectionCount != 1 && _remoteRSSI > 0) { - _remoteRSSI = 0; - emit remoteRSSIChanged(_remoteRSSI); + if((unsigned)_telemetryRRSSI != remrssi) { + // According to the Silabs data sheet, the RSSI value is 0.5db per bit + _telemetryRRSSI = remrssi >> 1; + emit telemetryRRSSIChanged(_telemetryRRSSI); } } @@ -344,3 +197,8 @@ void MainToolBarController::showSettings(void) { MainWindow::instance()->showSettings(); } + +void MainToolBarController::manageLinks(void) +{ + MainWindow::instance()->manageLinks(); +} diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h index eaf5d14a8396bc285bcf451221cc3fd98ed3401f..25e37f3af260fa5ae87fa8ec3ef16dfd43116e66 100644 --- a/src/ui/toolbar/MainToolBarController.h +++ b/src/ui/toolbar/MainToolBarController.h @@ -53,35 +53,24 @@ public: Q_INVOKABLE void onSetupView(); Q_INVOKABLE void onPlanView(); Q_INVOKABLE void onFlyView(); - Q_INVOKABLE void onConnect(QString conf); - Q_INVOKABLE void onDisconnect(QString conf); Q_INVOKABLE void onEnterMessageArea(int x, int y); Q_INVOKABLE void onToolBarMessageClosed(void); Q_INVOKABLE void showSettings(void); + Q_INVOKABLE void manageLinks(void); Q_PROPERTY(double height MEMBER _toolbarHeight NOTIFY heightChanged) - Q_PROPERTY(QStringList configList MEMBER _linkConfigurations NOTIFY configListChanged) - Q_PROPERTY(int connectionCount READ connectionCount NOTIFY connectionCountChanged) - Q_PROPERTY(QStringList connectedList MEMBER _connectedList NOTIFY connectedListChanged) Q_PROPERTY(float progressBarValue MEMBER _progressBarValue NOTIFY progressBarValueChanged) - Q_PROPERTY(int remoteRSSI READ remoteRSSI NOTIFY remoteRSSIChanged) Q_PROPERTY(int telemetryRRSSI READ telemetryRRSSI NOTIFY telemetryRRSSIChanged) Q_PROPERTY(int telemetryLRSSI READ telemetryLRSSI NOTIFY telemetryLRSSIChanged) void viewStateChanged (const QString& key, bool value); - int remoteRSSI () { return _remoteRSSI; } int telemetryRRSSI () { return _telemetryRRSSI; } int telemetryLRSSI () { return _telemetryLRSSI; } - int connectionCount () { return _connectionCount; } void showToolBarMessage(const QString& message); signals: - void connectionCountChanged (int count); - void configListChanged (); - void connectedListChanged (QStringList connectedList); void progressBarValueChanged (float value); - void remoteRSSIChanged (int value); void telemetryRRSSIChanged (int value); void telemetryLRSSIChanged (int value); void heightChanged (double height); @@ -91,26 +80,15 @@ signals: private slots: void _activeVehicleChanged (Vehicle* vehicle); - void _updateConfigurations (); - void _linkConnected (LinkInterface* link); - void _linkDisconnected (LinkInterface* link); void _leaveMessageView (); void _setProgressBarValue (float value); - void _remoteControlRSSIChanged (uint8_t rssi); void _telemetryChanged (LinkInterface* link, unsigned rxerrors, unsigned fixed, unsigned rssi, unsigned remrssi, unsigned txbuf, unsigned noise, unsigned remnoise); void _delayedShowToolBarMessage (void); -private: - void _updateConnection (LinkInterface *disconnectedLink = NULL); - private: Vehicle* _vehicle; UASInterface* _mav; - QStringList _linkConfigurations; - int _connectionCount; - QStringList _connectedList; float _progressBarValue; - int _remoteRSSI; double _remoteRSSIstore; int _telemetryRRSSI; int _telemetryLRSSI; diff --git a/src/ui/toolbar/MainToolBarIndicators.qml b/src/ui/toolbar/MainToolBarIndicators.qml index b819afbe36e9b6e0f15bea071c00cf4fa0ddb29c..18ea7045473aeb10f5d0827b1e77a9073017fab7 100644 --- a/src/ui/toolbar/MainToolBarIndicators.qml +++ b/src/ui/toolbar/MainToolBarIndicators.qml @@ -219,12 +219,12 @@ Row { smooth: true width: mainWindow.tbCellHeight * 0.65 height: mainWindow.tbCellHeight * 0.5 - opacity: _controller.remoteRSSI < 1 ? 0.5 : 1 + opacity: activeVehicle.rcRSSI < 1 ? 0.5 : 1 anchors.verticalCenter: parent.verticalCenter } SignalStrength { size: mainWindow.tbCellHeight * 0.5 - percent: _controller.remoteRSSI + percent: activeVehicle.rcRSSI anchors.verticalCenter: parent.verticalCenter } } @@ -284,10 +284,11 @@ Row { //------------------------------------------------------------------------- //-- Vehicle Selector QGCButton { - width: ScreenTools.defaultFontPixelSize * 12 - height: mainWindow.tbButtonWidth - text: "Vehicle " + activeVehicle.id - visible: vehicleMenuItems.length > 0 + id: vehicleSelectorButton + width: ScreenTools.defaultFontPixelSize * 12 + height: mainWindow.tbButtonWidth + text: "Vehicle " + activeVehicle.id + visible: QGroundControl.multiVehicleManager.vehicles.count > 1 anchors.verticalCenter: parent.verticalCenter menu: vehicleMenu @@ -331,7 +332,7 @@ Row { Connections { target: multiVehicleManager.vehicles - onCountChanged: parent.updateVehicleMenu + onCountChanged: vehicleSelectorButton.updateVehicleMenu } }