5#include <QtCore/QPointer>
6#include <QtNetwork/QNetworkDatagram>
7#include <QtNetwork/QUdpSocket>
18QHostAddress senderAddress(
const QNetworkDatagram& datagram)
20 const QHostAddress sender = datagram.senderAddress();
22 const quint32 address = sender.toIPv4Address(&ipv4);
23 return ipv4 ? QHostAddress(address) : sender;
31 qCDebug(RTCMUdpInputLog) <<
this;
36 qCDebug(RTCMUdpInputLog) <<
this;
43 auto*
const socket =
new QUdpSocket(
this);
44 if (!socket->bind(QHostAddress::Any, port)) {
45 qCDebug(RTCMUdpInputLog) <<
"Failed to bind UDP socket on port" << port <<
":" << socket->errorString();
52 connect(socket, &QUdpSocket::readyRead,
this, &RTCMUdpInput::_readDatagrams);
53 connect(socket, &QAbstractSocket::errorOccurred,
this, [socket]() {
54 qCWarning(RTCMUdpInputLog) <<
"UDP socket error on port" << socket->localPort() <<
":" << socket->errorString();
57 qCDebug(RTCMUdpInputLog) <<
"Listening for RTCM data on UDP port" << socket->localPort();
63 _drainScheduled =
false;
65 QUdpSocket*
const socket = std::exchange(_socket,
nullptr);
69 qCDebug(RTCMUdpInputLog) <<
"Stopped listening on UDP port" << socket->localPort();
70 socket->disconnect(
this);
72 socket->deleteLater();
75void RTCMUdpInput::_readDatagrams()
77 QUdpSocket*
const socket = _socket;
82 const auto current = [
this, socket]() {
return socket == _socket; };
84 while (current() && socket->hasPendingDatagrams() && budget.
available()) {
85 const QNetworkDatagram datagram = socket->receiveDatagram();
86 if (!datagram.isValid()) {
89 const QByteArray data = datagram.data();
94 const qint64 receivedAtMs = _scheduler->
nowMs();
95 const QHostAddress sender = senderAddress(datagram);
98 const QString instance = sender.toString();
99 const auto peer = _parserForPeer(sender, datagram.senderPort());
101 int framesDropped = 0;
103 if (!decoded.
valid) {
115 qCDebug(RTCMUdpInputLog) <<
"Datagram" << data.size() <<
"bytes -" <<
"framesFound:" << framesFound
116 <<
"framesDropped:" << framesDropped;
121void RTCMUdpInput::_scheduleRead()
123 if (!_socket || !_socket->hasPendingDatagrams() || _drainScheduled) {
126 _drainScheduled =
true;
128 QMetaObject::invokeMethod(
130 [
this, socket = QPointer<QUdpSocket>(_socket)]() {
131 if (socket && socket == _socket) {
132 _drainScheduled =
false;
136 Qt::QueuedConnection);
139std::shared_ptr<RTCMUdpInput::PeerParser> RTCMUdpInput::_parserForPeer(
const QHostAddress& address, quint16 port)
141 const qint64 now = _scheduler->
nowMs();
142 for (
auto it = _peerParsers.begin(); it != _peerParsers.end();) {
143 if (
const auto idle =
MonotonicClock::age(std::chrono::milliseconds(it.value()->lastReceivedMs),
144 std::chrono::milliseconds(now));
145 idle && *idle >= PEER_IDLE_TIMEOUT) {
146 it = _peerParsers.erase(it);
151 const QString key =
udpPeerKey(address, port);
152 auto peer = _peerParsers.value(key);
154 if (_peerParsers.size() >= MAX_PEERS) {
155 auto oldest = _peerParsers.begin();
156 for (
auto it = _peerParsers.begin(); it != _peerParsers.end(); ++it) {
157 if (it.value()->lastReceivedMs < oldest.value()->lastReceivedMs) {
161 _peerParsers.erase(oldest);
163 peer = std::make_shared<PeerParser>();
164 _peerParsers.insert(key, peer);
166 peer->lastReceivedMs = now;
std::atomic< quint64 > delivered
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QString udpPeerKey(const QHostAddress &address, quint16 port)
constexpr std::optional< std::chrono::duration< Rep, Period > > age(std::chrono::duration< Rep, Period > timestamp, std::chrono::duration< Rep, Period > now)
Age of timestamp at now, both in the same unit; empty for a missing or future timestamp.
void consume(qsizetype size)