QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
UDPGPSTransport.cc
Go to the documentation of this file.
1#include "UDPGPSTransport.h"
2
3#include <algorithm>
4#include <cstring>
5#include <utility>
6
7#include <QtNetwork/QNetworkDatagram>
8#include <QtNetwork/QUdpSocket>
9
10#include "QGCLoggingCategory.h"
11
12QGC_LOGGING_CATEGORY(UDPGPSTransportLog, "GPS.Transport.UDPGPSTransport")
13
14UDPGPSTransport::UDPGPSTransport(quint16 port, GPSCancelToken cancelToken, std::chrono::milliseconds peerIdleTimeout)
15 : GPSDeviceTransport(std::move(cancelToken))
16 , _port(port)
17 , _peerIdleTimeout(peerIdleTimeout)
18{}
19
21
23{
24 if (isCancelled()) {
26 }
27 _socket = std::make_unique<QUdpSocket>();
28 if (!_socket->bind(QHostAddress::Any, _port)) {
29 qCWarning(UDPGPSTransportLog) << "Cannot listen for receiver data on UDP port" << _port
30 << _socket->errorString();
31 return {GPSOpenStatus::Error, _socket->errorString()};
32 }
33 return {GPSOpenStatus::Opened};
34}
35
37{
38 return !_socket || _socket->state() != QAbstractSocket::BoundState;
39}
40
41void UDPGPSTransport::_receivePending()
42{
43 while (_socket && _socket->hasPendingDatagrams()) {
44 const QNetworkDatagram datagram = _socket->receiveDatagram();
45 if (!datagram.isValid()) {
46 break;
47 }
48 // The dual-stack socket may report an IPv4 sender as an IPv4-mapped IPv6 address.
49 const bool selected = _peerPort != 0 && datagram.senderPort() == _peerPort &&
50 datagram.senderAddress().isEqual(_peerAddress, QHostAddress::TolerantConversion);
51 if (!selected) {
52 if (_peerPort != 0 && _peerIdle.durationElapsed() < _peerIdleTimeout) {
53 continue;
54 }
55 qCDebug(UDPGPSTransportLog) << "Receiving from UDP sender" << datagram.senderAddress()
56 << datagram.senderPort();
57 _peerAddress = datagram.senderAddress();
58 _peerPort = static_cast<quint16>(datagram.senderPort());
59 // A replaced sender's partial sentence must not join the new stream.
60 _pending.clear();
61 }
62 _peerIdle.start();
63 _pending.append(datagram.data());
64 if (const qsizetype excess = _pending.size() - READ_BUFFER_BYTES; excess > 0) {
65 _pending.remove(0, excess);
66 qCWarning(UDPGPSTransportLog)
67 << "Receiver input outran the decoder; dropped the oldest" << excess << "bytes";
68 }
69 }
70}
71
72QIODevice* UDPGPSTransport::device() const
73{
74 return _socket.get();
75}
76
77bool UDPGPSTransport::waitReadable(QDeadlineTimer deadline)
78{
79 const auto buffered = [this]() {
80 _receivePending();
81 return !_pending.isEmpty();
82 };
83 return buffered() || waitForDevice(buffered, deadline);
84}
85
86qint64 UDPGPSTransport::take(std::span<uint8_t> buffer)
87{
88 const qsizetype taken = (std::min) (static_cast<qsizetype>(buffer.size()), _pending.size());
89 std::memcpy(buffer.data(), _pending.constData(), static_cast<size_t>(taken));
90 _pending.remove(0, taken);
91 return qint64(taken);
92}
93
94GPSWriteResult UDPGPSTransport::writeData(QByteArrayView, QDeadlineTimer)
95{
97}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool waitForDevice(const std::function< bool()> &ready, QDeadlineTimer deadline)
static constexpr qint64 READ_BUFFER_BYTES
bool isCancelled() const
QIODevice * device() const override
The open device, or null before open().
bool waitReadable(QDeadlineTimer deadline) override
Waits until input is buffered. Input reported buffered is delivered even after the link fails.
GPSOpenResult open() override
~UDPGPSTransport() override
GPSWriteResult writeData(QByteArrayView bytes, QDeadlineTimer deadline) override
Receives non-empty bytes and an unexpired, capped deadline that implementations must honor.
bool fatalError() const override
qint64 take(std::span< uint8_t > buffer) override
Takes buffered input.