diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 8116a80e233887354f7532759b2ff087bccf3534..75d4c3f56759d0300a91c688e71ae10c99765e08 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -18,6 +18,7 @@ src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml src/ui/preferences/GeneralSettings.qml src/ui/preferences/LinkSettings.qml + src/ui/preferences/UdpSettings.qml src/VehicleSetup/JoystickConfig.qml src/ui/toolbar/MainToolBar.qml src/ui/MainWindow.qml diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 70761ff7370ed5e27d8b55ee33af5f7472329cc0..f2f6aaee13783559a00efc5eca790666ef8ee5c1 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -40,11 +40,12 @@ public: 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) - Q_PROPERTY(LinkType linkType READ type CONSTANT) - Q_PROPERTY(bool dynamic READ isDynamic WRITE setDynamic NOTIFY dynamicChanged) - Q_PROPERTY(bool autoConnect READ isAutoConnect WRITE setAutoConnect NOTIFY autoConnectChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(LinkInterface* link READ link WRITE setLink NOTIFY linkChanged) + Q_PROPERTY(LinkType linkType READ type CONSTANT) + Q_PROPERTY(bool dynamic READ isDynamic WRITE setDynamic NOTIFY dynamicChanged) + Q_PROPERTY(bool autoConnect READ isAutoConnect WRITE setAutoConnect NOTIFY autoConnectChanged) + Q_PROPERTY(bool autoConnectAllowed READ isAutoConnectAllowed CONSTANT) // Property accessors @@ -98,6 +99,13 @@ public: /// Virtual Methods + /*! + * + * Is Auto Connect allowed for this type? + * @return True if this type can be set as an Auto Connect configuration + */ + virtual bool isAutoConnectAllowed() { return true; } + /*! * @brief Connection type * diff --git a/src/comm/LogReplayLink.h b/src/comm/LogReplayLink.h index d26f28bb2788d3a41922bf2a436d640fbde8e74d..e586e25d4a7d02bcefb6f3ae4b9de1faa306b180 100644 --- a/src/comm/LogReplayLink.h +++ b/src/comm/LogReplayLink.h @@ -36,20 +36,26 @@ class LogReplayLinkConfiguration : public LinkConfiguration Q_OBJECT public: + + Q_PROPERTY(QString fileName READ logFilename WRITE setLogFilename NOTIFY fileNameChanged) + LogReplayLinkConfiguration(const QString& name); LogReplayLinkConfiguration(LogReplayLinkConfiguration* copy); QString logFilename(void) { return _logFilename; } - void setLogFilename(const QString& logFilename) { _logFilename = logFilename; } + void setLogFilename(const QString& logFilename) { _logFilename = logFilename; emit fileNameChanged(); } QString logFilenameShort(void); // Virtuals from LinkConfiguration - virtual LinkType type() { return LinkConfiguration::TypeLogReplay; } - virtual void copyFrom(LinkConfiguration* source); - virtual void loadSettings(QSettings& settings, const QString& root); - virtual void saveSettings(QSettings& settings, const QString& root); - virtual void updateSettings(); + LinkType type () { return LinkConfiguration::TypeLogReplay; } + void copyFrom (LinkConfiguration* source); + void loadSettings (QSettings& settings, const QString& root); + void saveSettings (QSettings& settings, const QString& root); + void updateSettings (); + bool isAutoConnectAllowed () { return false; } +signals: + void fileNameChanged(); private: static const char* _logFilenameKey; diff --git a/src/comm/TCPLink.cc b/src/comm/TCPLink.cc index be821f19ed75ed3792f898ce4e1f428853247db6..a7acc341598690eeb7c66e7ae9e19f0ec552150a 100644 --- a/src/comm/TCPLink.cc +++ b/src/comm/TCPLink.cc @@ -1,24 +1,24 @@ /*===================================================================== - + QGroundControl Open Source Ground Control Station - + (c) 2009 - 2015 QGROUNDCONTROL PROJECT - + This file is part of the QGROUNDCONTROL project - + QGROUNDCONTROL is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - + QGROUNDCONTROL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with QGROUNDCONTROL. If not, see . - + ======================================================================*/ #include @@ -61,7 +61,7 @@ TCPLink::~TCPLink() void TCPLink::run() { _hardwareConnect(); - exec(); + exec(); } #ifdef TCPLINK_READWRITE_DEBUG @@ -126,14 +126,14 @@ void TCPLink::readBytes() **/ void TCPLink::_disconnect(void) { - quit(); - wait(); + quit(); + wait(); if (_socket) { _socketIsConnected = false; - _socket->deleteLater(); // Make sure delete happens on correct thread - _socket = NULL; + _socket->deleteLater(); // Make sure delete happens on correct thread + _socket = NULL; emit disconnected(); - } + } } /** @@ -143,11 +143,11 @@ void TCPLink::_disconnect(void) **/ bool TCPLink::_connect(void) { - if (isRunning()) - { - quit(); - wait(); - } + if (isRunning()) + { + quit(); + wait(); + } start(HighPriority); return true; } @@ -155,7 +155,7 @@ bool TCPLink::_connect(void) bool TCPLink::_hardwareConnect() { Q_ASSERT(_socket == NULL); - _socket = new QTcpSocket(); + _socket = new QTcpSocket(); QSignalSpy errorSpy(_socket, SIGNAL(error(QAbstractSocket::SocketError))); _socket->connectToHost(_config->address(), _config->port()); QObject::connect(_socket, SIGNAL(readyRead()), this, SLOT(readBytes())); @@ -268,6 +268,11 @@ void TCPConfiguration::setAddress(const QHostAddress& address) _address = address; } +void TCPConfiguration::setHost(const QString host) +{ + _address = host; +} + void TCPConfiguration::saveSettings(QSettings& settings, const QString& root) { settings.beginGroup(root); diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h index 5c88a889e33c5d426a5091717728aae21b3d4e48..2771824fc0aaeb1ad10e652e902d24df86ab8aa2 100644 --- a/src/comm/TCPLink.h +++ b/src/comm/TCPLink.h @@ -56,6 +56,9 @@ class TCPConfiguration : public LinkConfiguration public: + Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged) + Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged) + /*! * @brief Regular constructor * @@ -94,6 +97,7 @@ public: * @return Host address */ const QHostAddress& address () { return _address; } + const QString host () { return _address.toString(); } /*! * @brief Set the host address @@ -101,6 +105,7 @@ public: * @param[in] address Host address */ void setAddress (const QHostAddress& address); + void setHost (const QString host); /// From LinkConfiguration LinkType type() { return LinkConfiguration::TypeTcp; } @@ -109,6 +114,10 @@ public: void saveSettings(QSettings& settings, const QString& root); void updateSettings(); +signals: + void portChanged(); + void hostChanged(); + private: QHostAddress _address; quint16 _port; diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index 90d6140630b095879e5d06c2e6378222e59c4296..a7ff432aab7efd417cd08ed854e77cf8932307c7 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -406,9 +406,9 @@ UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source) { _localPort = source->localPort(); - _hosts.clear(); QString host; int port; + _hostList.clear(); if(source->firstHost(host, port)) { do { addHost(host, port); @@ -435,7 +435,7 @@ void UDPConfiguration::copyFrom(LinkConfiguration *source) /** * @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551 */ -void UDPConfiguration::addHost(const QString& host) +void UDPConfiguration::addHost(const QString host) { // Handle x.x.x.x:p if (host.contains(":")) @@ -495,9 +495,10 @@ void UDPConfiguration::addHost(const QString& host, int port) } } } + _updateHostList(); } -void UDPConfiguration::removeHost(const QString& host) +void UDPConfiguration::removeHost(const QString host) { QMutexLocker locker(&_confMutex); QString tHost = host; @@ -512,6 +513,7 @@ void UDPConfiguration::removeHost(const QString& host) } else { qWarning() << "UDP:" << "Could not remove unknown host:" << host; } + _updateHostList(); } bool UDPConfiguration::firstHost(QString& host, int& port) @@ -579,6 +581,7 @@ void UDPConfiguration::loadSettings(QSettings& settings, const QString& root) } } settings.endGroup(); + _updateHostList(); } void UDPConfiguration::updateSettings() @@ -590,3 +593,15 @@ void UDPConfiguration::updateSettings() } } } + +void UDPConfiguration::_updateHostList() +{ + _hostList.clear(); + QMap::const_iterator it = _hosts.begin(); + while(it != _hosts.end()) { + QString host = QString("%1").arg(it.key()) + ":" + QString("%1").arg(it.value()); + _hostList += host; + it++; + } + emit hostListChanged(); +} diff --git a/src/comm/UDPLink.h b/src/comm/UDPLink.h index cc2d09339ec5035f38b921a52b39bd2b4346cf82..9065eaf8f4ee5e7753d72ea3ff90148c49dfabe6 100644 --- a/src/comm/UDPLink.h +++ b/src/comm/UDPLink.h @@ -56,6 +56,9 @@ class UDPConfiguration : public LinkConfiguration public: + Q_PROPERTY(quint16 localPort READ localPort WRITE setLocalPort NOTIFY localPortChanged) + Q_PROPERTY(QStringList hostList READ hostList NOTIFY hostListChanged) + /*! * @brief Regular constructor * @@ -111,7 +114,7 @@ public: * * @param[in] host Host name in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551 */ - void addHost (const QString& host); + Q_INVOKABLE void addHost (const QString host); /*! * @brief Add a target host @@ -126,7 +129,7 @@ public: * * @param[in] host Host name, e.g. localhost or 192.168.1.1 */ - void removeHost (const QString& host); + Q_INVOKABLE void removeHost (const QString host); /*! * @brief Set the UDP port we bind to @@ -135,6 +138,11 @@ public: */ void setLocalPort (quint16 port); + /*! + * @brief QML Interface + */ + QStringList hostList () { return _hostList; } + /// From LinkConfiguration LinkType type() { return LinkConfiguration::TypeUdp; } void copyFrom(LinkConfiguration* source); @@ -142,10 +150,18 @@ public: void saveSettings(QSettings& settings, const QString& root); void updateSettings(); +signals: + void localPortChanged (); + void hostListChanged (); + +private: + void _updateHostList (); + private: QMutex _confMutex; QMap::iterator _it; QMap _hosts; ///< ("host", port) + QStringList _hostList; ///< Exposed to QML quint16 _localPort; }; diff --git a/src/ui/preferences/LinkSettings.qml b/src/ui/preferences/LinkSettings.qml index c2b297cdb53749a72a5291e1972ae23b86cd6c62..25a71a56db78ba64d0e7b4e4ff76c3fec8ea9b14 100644 --- a/src/ui/preferences/LinkSettings.qml +++ b/src/ui/preferences/LinkSettings.qml @@ -263,7 +263,7 @@ Rectangle { if(index === LinkConfiguration.TypeSerial) linkSettingLoader.sourceComponent = serialLinkSettings if(index === LinkConfiguration.TypeUdp) - linkSettingLoader.sourceComponent = udpLinkSettings + linkSettingLoader.source = "UdpSettings.qml" if(index === LinkConfiguration.TypeTcp) linkSettingLoader.sourceComponent = tcpLinkSettings if(index === LinkConfiguration.TypeMock) @@ -284,6 +284,10 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter onActivated: { if (index != -1 && index !== editConfig.linkType) { + // Destroy current panel + linkSettingLoader.sourceComponent = null + linkSettingLoader.source = "" + linkSettingLoader.visible = false // Save current name var name = editConfig.name // Discard link configuration (old type) @@ -291,17 +295,17 @@ Rectangle { // Create new link configuration editConfig = QGroundControl.linkManager.createConfiguration(index, name) // Load appropriate configuration panel - linkSettingLoader.sourceComponent = null if(index === LinkConfiguration.TypeSerial) linkSettingLoader.sourceComponent = serialLinkSettings if(index === LinkConfiguration.TypeUdp) - linkSettingLoader.sourceComponent = udpLinkSettings + linkSettingLoader.source = "UdpSettings.qml" if(index === LinkConfiguration.TypeTcp) linkSettingLoader.sourceComponent = tcpLinkSettings if(index === LinkConfiguration.TypeMock) linkSettingLoader.sourceComponent = mockLinkSettings if(index === LinkConfiguration.TypeLogReplay) linkSettingLoader.sourceComponent = logLinkSettings + linkSettingLoader.visible = true } } Component.onCompleted: { @@ -311,7 +315,7 @@ Rectangle { if(index === LinkConfiguration.TypeSerial) linkSettingLoader.sourceComponent = serialLinkSettings if(index === LinkConfiguration.TypeUdp) - linkSettingLoader.sourceComponent = udpLinkSettings + linkSettingLoader.source = "UdpSettings.qml" if(index === LinkConfiguration.TypeTcp) linkSettingLoader.sourceComponent = tcpLinkSettings if(index === LinkConfiguration.TypeMock) @@ -331,6 +335,7 @@ Rectangle { QGCCheckBox { text: "Automatically Connect on Start" checked: false + enabled: editConfig ? editConfig.autoConnectAllowed : false onCheckedChanged: { if(editConfig) { editConfig.autoConnect = checked @@ -362,8 +367,7 @@ Rectangle { QGCButton { width: ScreenTools.defaultFontPixelWidth * 10 text: "OK" - //-- TODO: For now, only allow Serial (the only one completed) - enabled: editConfig && editConfig.linkType === LinkConfiguration.TypeSerial + enabled: nameField.text !== "" onClicked: { // Save editting editConfig.name = nameField.text @@ -599,42 +603,6 @@ Rectangle { } } //--------------------------------------------- - // UDP Link Settings - Component { - id: udpLinkSettings - Column { - width: udpLinkSettings.width - spacing: ScreenTools.defaultFontPixelHeight / 2 - QGCLabel { - id: udpLabel - text: "UDP Link Settings" - } - Rectangle { - height: 1 - width: udpLabel.width - color: qgcPal.button - } - Item { - height: ScreenTools.defaultFontPixelHeight / 2 - width: parent.width - } - Row { - spacing: ScreenTools.defaultFontPixelWidth - QGCLabel { - text: "Listening Port:" - width: _firstColumn - } - QGCLabel { - text: "14550" - width: _secondColumn - } - } - QGCLabel { - text: "Target Hosts:" - } - } - } - //--------------------------------------------- // TCP Link Settings Component { id: tcpLinkSettings @@ -657,23 +625,40 @@ Rectangle { Row { spacing: ScreenTools.defaultFontPixelWidth QGCLabel { - text: "TCP Port:" + text: "Host Address:" width: _firstColumn + anchors.verticalCenter: parent.verticalCenter } - QGCLabel { - text: "5760" + QGCTextField { + id: hostField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeTcp ? subEditConfig.host : "" width: _secondColumn + anchors.verticalCenter: parent.verticalCenter + onTextChanged: { + if(subEditConfig) { + subEditConfig.host = hostField.text + } + } } } Row { spacing: ScreenTools.defaultFontPixelWidth QGCLabel { - text: "Host Address:" + text: "TCP Port:" width: _firstColumn + anchors.verticalCenter: parent.verticalCenter } - QGCLabel { - text: "0.0.0.0" - width: _secondColumn + QGCTextField { + id: portField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeTcp ? subEditConfig.port.toString() : "" + width: _firstColumn + inputMethodHints: Qt.ImhFormattedNumbersOnly + anchors.verticalCenter: parent.verticalCenter + onTextChanged: { + if(subEditConfig) { + subEditConfig.port = parseInt(portField.text) + } + } } } } @@ -692,8 +677,46 @@ Rectangle { height: ScreenTools.defaultFontPixelHeight / 2 width: parent.width } - QGCButton { - text: "Select Log File" + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: "Log File:" + width: _firstColumn + anchors.verticalCenter: parent.verticalCenter + } + QGCTextField { + id: logField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeMock ? subEditConfig.fileName : "" + width: _secondColumn + anchors.verticalCenter: parent.verticalCenter + onTextChanged: { + if(subEditConfig) { + subEditConfig.filename = logField.text + } + } + } + QGCButton { + text: "Browse" + onClicked: { + fileDialog.visible = true + } + } + } + FileDialog { + id: fileDialog + title: "Please choose a file" + folder: shortcuts.home + visible: false + selectExisting: true + onAccepted: { + if(subEditConfig) { + subEditConfig.fileName = fileDialog.fileUrl.toString().replace("file://", "") + } + fileDialog.visible = false + } + onRejected: { + fileDialog.visible = false + } } } } diff --git a/src/ui/preferences/UdpSettings.qml b/src/ui/preferences/UdpSettings.qml new file mode 100644 index 0000000000000000000000000000000000000000..928a09ce396712b345e0387274811416ddc1804d --- /dev/null +++ b/src/ui/preferences/UdpSettings.qml @@ -0,0 +1,188 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.1 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +Item { + id: _udpSetting + width: parent ? parent.width : 0 + height: udpColumn.height + + property var _currentHost: "" + + Column { + id: udpColumn + spacing: ScreenTools.defaultFontPixelHeight / 2 + + ExclusiveGroup { id: linkGroup } + + QGCPalette { + id: qgcPal + colorGroupEnabled: enabled + } + + QGCLabel { + id: udpLabel + text: "UDP Link Settings" + } + Rectangle { + height: 1 + width: udpLabel.width + color: qgcPal.button + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + Row { + spacing: ScreenTools.defaultFontPixelWidth + QGCLabel { + text: "Listening Port:" + width: _firstColumn + anchors.verticalCenter: parent.verticalCenter + } + QGCTextField { + id: portField + text: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeUdp ? subEditConfig.localPort.toString() : "" + focus: true + width: _firstColumn + inputMethodHints: Qt.ImhFormattedNumbersOnly + anchors.verticalCenter: parent.verticalCenter + onTextChanged: { + if(subEditConfig) { + subEditConfig.localPort = parseInt(portField.text) + } + } + } + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + QGCLabel { + text: "Target Hosts:" + } + Item { + width: hostRow.width + height: hostRow.height + Row { + id: hostRow + spacing: ScreenTools.defaultFontPixelWidth + Item { + height: 1 + width: _firstColumn + } + Column { + id: hostColumn + spacing: ScreenTools.defaultFontPixelHeight / 2 + Rectangle { + height: 1 + width: _secondColumn + color: qgcPal.button + visible: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeUdp && subEditConfig.hostList.length > 0 + } + Repeater { + model: subEditConfig && subEditConfig.linkType === LinkConfiguration.TypeUdp ? subEditConfig.hostList : "" + delegate: + QGCButton { + text: modelData + width: _secondColumn + anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 + exclusiveGroup: linkGroup + onClicked: { + checked = true + _udpSetting._currentHost = modelData + } + } + } + QGCTextField { + id: hostField + focus: true + visible: false + width: ScreenTools.defaultFontPixelWidth * 30 + onEditingFinished: { + if(subEditConfig) { + if(hostField.text !== "") { + subEditConfig.addHost(hostField.text) + hostField.text = "" + } + hostField.visible = false + } + } + Keys.onReleased: { + if (event.key === Qt.Key_Escape) { + hostField.text = "" + hostField.visible = false + } + } + } + Rectangle { + height: 1 + width: _secondColumn + color: qgcPal.button + } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: parent.width + } + Item { + width: _secondColumn + height: udpButtonRow.height + Row { + id: udpButtonRow + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 10 + text: "Add" + onClicked: { + if(hostField.visible && hostField.text !== "") { + subEditConfig.addHost(hostField.text) + hostField.text = "" + hostField.visible = false + } else + hostField.visible = true + } + } + QGCButton { + width: ScreenTools.defaultFontPixelWidth * 10 + enabled: _udpSetting._currentHost && _udpSetting._currentHost !== "" + text: "Remove" + onClicked: { + subEditConfig.removeHost(_udpSetting._currentHost) + } + } + } + } + } + } + } + } +}