Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comms: Update UdpIODevice #12038

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading