Skip to content

Commit

Permalink
Comms: Update UdpIODevice
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey committed Dec 14, 2024
1 parent 3a39583 commit bc4b68a
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 54 deletions.
40 changes: 21 additions & 19 deletions src/Comms/LinkManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,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
Expand Down Expand Up @@ -761,25 +782,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<QGCSerialPortInfo> portList;
#ifdef Q_OS_ANDROID
// Android builds only support a single serial connection. Repeatedly calling availablePorts after that one serial
Expand Down
51 changes: 35 additions & 16 deletions src/Comms/UdpIODevice.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(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<qint64>(_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<int>(_buffer.size() + pendingDatagramSize()));
readDatagram((_buffer.data() + previousSize), pendingDatagramSize());
const qint64 size = pendingDatagramSize();
const int oldSize = _buffer.size();
_buffer.resize(oldSize + static_cast<int>(size));
(void) readDatagram(_buffer.data() + oldSize, size);
}
}
24 changes: 13 additions & 11 deletions src/Comms/UdpIODevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,29 @@
* COPYING.md in the root of the source code directory.
*
****************************************************************************/

#pragma once

#include <QtCore/QByteArray>
#include <QtCore/QLoggingCategory>
#include <QtNetwork/QUdpSocket>

/**
* @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();
Expand Down
14 changes: 7 additions & 7 deletions src/PositionManager/PositionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "QGCApplication.h"
#include "QGCCorePlugin.h"
#include "SimulatedPosition.h"
#include "DeviceInfo.h"
// #include "DeviceInfo.h"
#include "QGCLoggingCategory.h"

#include <QtCore/qapplicationstatic.h>
Expand Down Expand Up @@ -181,13 +181,13 @@ void QGCPositionManager::_setPositionSource(QGCPositionSource source)
(void) disconnect(_currentSource);

_geoPositionInfo = QGeoPositionInfo();
_gcsPosition = QGeoCoordinate();
_gcsHeading = qQNaN();
_gcsPositionHorizontalAccuracy = std::numeric_limits<qreal>::infinity();

emit gcsPositionChanged(_gcsPosition);
emit gcsHeadingChanged(_gcsHeading);
emit positionInfoUpdated(_geoPositionInfo);

_setGCSPosition(QGeoCoordinate());

_setGCSHeading(qQNaN());

_gcsPositionHorizontalAccuracy = std::numeric_limits<qreal>::infinity();
emit gcsPositionHorizontalAccuracyChanged(_gcsPositionHorizontalAccuracy);
}

Expand Down
60 changes: 59 additions & 1 deletion src/UI/preferences/LinkSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,70 @@ 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: {
model.append({text: "Disabled"})
model.append({text: "UDP Port"})

for (var i in QGroundControl.linkManager.serialPorts) {
nmeaPortCombo.model.append({text:QGroundControl.linkManager.serialPorts[i]})
}

const index = nmeaPortCombo.comboBox.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.valueString);
nmeaPortCombo.currentIndex = index;

if (QGroundControl.linkManager.serialPorts.length === 0) {
nmeaPortCombo.model.append({text: "Serial <none available>"})
}
}
}

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
Expand Down

0 comments on commit bc4b68a

Please sign in to comment.