diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index 78b2782dffa..546e11b21a2 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -503,6 +503,27 @@ void LinkManager::_updateAutoConnectLinks() #ifdef QGC_ZEROCONF_ENABLED _addZeroConfAutoConnectLink(); #endif + + // check to see if nmea gps is configured for UDP input, if so, set it up to connect + if (_autoConnectSettings->autoConnectNmeaPort()->cookedValueString() == "UDP Port") { + if ((_nmeaSocket->localPort() != _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()) || (_nmeaSocket->state() != UdpIODevice::BoundState)) { + qCDebug(LinkManagerLog) << "Changing port for UDP NMEA stream"; + _nmeaSocket->close(); + _nmeaSocket->bind(QHostAddress::AnyIPv4, _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()); + QGCPositionManager::instance()->setNmeaSourceDevice(_nmeaSocket); + } +#ifndef NO_SERIAL_LINK + if (_nmeaPort) { + _nmeaPort->close(); + delete _nmeaPort; + _nmeaPort = nullptr; + _nmeaDeviceName = ""; + } +#endif + } else { + _nmeaSocket->close(); + } + #ifndef NO_SERIAL_LINK _addSerialAutoConnectLink(); #endif @@ -763,25 +784,6 @@ void LinkManager::resetMavlinkSigning() void LinkManager::_addSerialAutoConnectLink() { - // check to see if nmea gps is configured for UDP input, if so, set it up to connect - if (_autoConnectSettings->autoConnectNmeaPort()->cookedValueString() == "UDP Port") { - if ((_nmeaSocket->localPort() != _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()) - || (_nmeaSocket->state() != UdpIODevice::BoundState)) { - qCDebug(LinkManagerLog) << "Changing port for UDP NMEA stream"; - _nmeaSocket->close(); - _nmeaSocket->bind(QHostAddress::AnyIPv4, _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()); - QGCPositionManager::instance()->setNmeaSourceDevice(_nmeaSocket); - } - if (_nmeaPort) { - _nmeaPort->close(); - delete _nmeaPort; - _nmeaPort = nullptr; - _nmeaDeviceName = ""; - } - } else { - _nmeaSocket->close(); - } - QList portList; #ifdef Q_OS_ANDROID // Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial diff --git a/src/Comms/UdpIODevice.cc b/src/Comms/UdpIODevice.cc index 73b986ca083..a96114744c6 100644 --- a/src/Comms/UdpIODevice.cc +++ b/src/Comms/UdpIODevice.cc @@ -8,38 +8,57 @@ ****************************************************************************/ #include "UdpIODevice.h" +#include "QGCLoggingCategory.h" +QGC_LOGGING_CATEGORY(UdpIODeviceLog, "qgc.comms.udpiodevice") -UdpIODevice::UdpIODevice(QObject *parent) : QUdpSocket(parent) +UdpIODevice::UdpIODevice(QObject *parent) + : QUdpSocket(parent) { - // this might cause data to be available only after a second readyRead() signal - connect(this, &QUdpSocket::readyRead, this, &UdpIODevice::_readAvailableData); + // qCDebug(UdpIODeviceLog) << Q_FUNC_INFO << this; + + (void) connect(this, &QUdpSocket::readyRead, this, &UdpIODevice::_readAvailableData); +} + +UdpIODevice::~UdpIODevice() +{ + // qCDebug(UdpIODeviceLog) << Q_FUNC_INFO << this; } bool UdpIODevice::canReadLine() const { - return _buffer.indexOf('\n') > -1; + return _buffer.contains('\n'); } qint64 UdpIODevice::readLineData(char *data, qint64 maxSize) { - int length = _buffer.indexOf('\n') + 1; // add 1 to include the '\n' - if (length == 0) { + const qint64 newlinePos = _buffer.indexOf('\n'); + if (newlinePos < 0) { return 0; } - length = std::min(length, static_cast(maxSize)); - // copy lines to output - std::copy(_buffer.data(), _buffer.data() + length, data); - // trim buffer to remove consumed line - _buffer = _buffer.right(_buffer.size() - length); - // return number of bytes read + + const qint64 length = std::min(newlinePos + 1, maxSize); + (void) std::copy_n(_buffer.constData(), length, data); + + (void) _buffer.remove(0, length); return length; } -void UdpIODevice::_readAvailableData() { +qint64 UdpIODevice::readData(char *data, qint64 maxSize) +{ + const qint64 length = std::min(_buffer.size(), maxSize); + (void) std::copy_n(_buffer.constData(), length, data); + + (void) _buffer.remove(0, length); + return length; +} + +void UdpIODevice::_readAvailableData() +{ while (hasPendingDatagrams()) { - int previousSize = _buffer.size(); - _buffer.resize(static_cast(_buffer.size() + pendingDatagramSize())); - readDatagram((_buffer.data() + previousSize), pendingDatagramSize()); + const qint64 size = pendingDatagramSize(); + const int oldSize = _buffer.size(); + _buffer.resize(oldSize + static_cast(size)); + (void) readDatagram(_buffer.data() + oldSize, size); } } diff --git a/src/Comms/UdpIODevice.h b/src/Comms/UdpIODevice.h index 9cf980c5581..44c95aadd68 100644 --- a/src/Comms/UdpIODevice.h +++ b/src/Comms/UdpIODevice.h @@ -6,27 +6,29 @@ * COPYING.md in the root of the source code directory. * ****************************************************************************/ + #pragma once #include +#include #include -/** - * @brief QUdpSocket implementation of canReadLine() readLineData() in server mode. - * The UdpIODevice class works almost exactly as a QUdpSocket, but - * also implements canReadLine() and readLineData() while in the bound state. - * Regular QUdpSocket only allows to use these QIODevice interfaces when using - * connectToHost(), which means it is working as a client instead of server. - * - **/ +Q_DECLARE_LOGGING_CATEGORY(UdpIODeviceLog) +/// UdpIODevice provides a QIODevice interface over a QUdpSocket in server mode. +/// It allows line-based reading using canReadLine() and readLineData() even when the socket is in bound mode. class UdpIODevice: public QUdpSocket { Q_OBJECT + public: - UdpIODevice(QObject *parent = nullptr); - bool canReadLine() const; - qint64 readLineData(char *data, qint64 maxSize); + explicit UdpIODevice(QObject *parent = nullptr); + ~UdpIODevice(); + + bool canReadLine() const override; + qint64 readLineData(char* data, qint64 maxSize) override; + qint64 readData(char* data, qint64 maxSize) override; + bool isSequential() const override { return true; } private slots: void _readAvailableData(); diff --git a/src/PositionManager/PositionManager.cpp b/src/PositionManager/PositionManager.cpp index 6822953036b..cc8cfb659f2 100644 --- a/src/PositionManager/PositionManager.cpp +++ b/src/PositionManager/PositionManager.cpp @@ -11,7 +11,7 @@ #include "QGCApplication.h" #include "QGCCorePlugin.h" #include "SimulatedPosition.h" -#include "DeviceInfo.h" +// #include "DeviceInfo.h" #include "QGCLoggingCategory.h" #include @@ -181,13 +181,13 @@ void QGCPositionManager::_setPositionSource(QGCPositionSource source) (void) disconnect(_currentSource); _geoPositionInfo = QGeoPositionInfo(); - _gcsPosition = QGeoCoordinate(); - _gcsHeading = qQNaN(); - _gcsPositionHorizontalAccuracy = std::numeric_limits::infinity(); - - emit gcsPositionChanged(_gcsPosition); - emit gcsHeadingChanged(_gcsHeading); emit positionInfoUpdated(_geoPositionInfo); + + _setGCSPosition(QGeoCoordinate()); + + _setGCSHeading(qQNaN()); + + _gcsPositionHorizontalAccuracy = std::numeric_limits::infinity(); emit gcsPositionHorizontalAccuracyChanged(_gcsPositionHorizontalAccuracy); } diff --git a/src/UI/preferences/LinkSettings.qml b/src/UI/preferences/LinkSettings.qml index 91f9ff64c4c..a0ab8a756b4 100644 --- a/src/UI/preferences/LinkSettings.qml +++ b/src/UI/preferences/LinkSettings.qml @@ -49,12 +49,73 @@ SettingsPage { } } + SettingsGroupLayout { + heading: qsTr("NMEA GPS") + visible: QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.visible && QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.visible + + LabelledComboBox { + id: nmeaPortCombo + label: qsTr("Device") + + model: ListModel {} + + onActivated: (index) => { + if (index !== -1) { + QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.value = comboBox.textAt(index); + } + } + + Component.onCompleted: { + var model = [] + + model.push(qsTr("Disabled")) + model.push(qsTr("UDP Port")) + + if (QGroundControl.linkManager.serialPorts.length === 0) { + model.push(qsTr("Serial ")) + } else { + for (var i in QGroundControl.linkManager.serialPorts) { + model.push(QGroundControl.linkManager.serialPorts[i]) + } + } + nmeaPortCombo.model = model + + const index = nmeaPortCombo.comboBox.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.valueString); + nmeaPortCombo.currentIndex = index; + } + } + + LabelledComboBox { + id: nmeaBaudCombo + visible: (nmeaPortCombo.currentText !== "UDP Port") && (nmeaPortCombo.currentText !== "Disabled") + label: qsTr("Baudrate") + model: QGroundControl.linkManager.serialBaudRates + + onActivated: (index) => { + if (index !== -1) { + QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.value = parseInt(comboBox.textAt(index)); + } + } + + Component.onCompleted: { + const index = nmeaBaudCombo.comboBox.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.valueString); + nmeaBaudCombo.currentIndex = index; + } + } + + LabelledFactTextField { + visible: nmeaPortCombo.currentText === "UDP Port" + label: qsTr("NMEA stream UDP port") + fact: QGroundControl.settingsManager.autoConnectSettings.nmeaUdpPort + } + } + SettingsGroupLayout { heading: qsTr("Links") Repeater { model: _linkManager.linkConfigurations - + RowLayout { Layout.fillWidth: true visible: !object.dynamic