summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2019-08-16 09:43:01 +0200
committerKarsten Heimrich <karsten.heimrich@qt.io>2019-08-20 15:53:52 +0200
commit0e76b2d978d594c384a71f3fd3965200fffd42e3 (patch)
treef52160ca071bfcd0a76c9d78b0d39c7f8b41733b
parent5e95b06d82cc3e6fe8443e56325b5a6741f30943 (diff)
Refactor private implementation into their own source files
Change-Id: I688dbe79d48991da25da7d9b4c3c7e3f4135c9d6 Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r--src/knx/netip/netip.pri1
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent.cpp373
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent.h3
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp381
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent_p.h11
5 files changed, 415 insertions, 354 deletions
diff --git a/src/knx/netip/netip.pri b/src/knx/netip/netip.pri
index 88e2d58..4a154ab 100644
--- a/src/knx/netip/netip.pri
+++ b/src/knx/netip/netip.pri
@@ -96,6 +96,7 @@ SOURCES += $$PWD/qknxnetip.cpp \
$$PWD/qknxnetipsearchresponse.cpp \
$$PWD/qknxnetipserverdescriptionagent.cpp \
$$PWD/qknxnetipserverdiscoveryagent.cpp \
+ $$PWD/qknxnetipserverdiscoveryagent_p.cpp \
$$PWD/qknxnetipserverinfo.cpp \
$$PWD/qknxnetipservicefamiliesdib.cpp \
$$PWD/qknxnetipstruct.cpp \
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent.cpp b/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
index e73ddcf..4ed44da 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
@@ -119,6 +119,8 @@ QT_BEGIN_NAMESPACE
A network error occurred.
\value NotIPv4
The network protocol used is not IPv4.
+ \value Timeout
+ A timeout occurred while waiting for the description response.
\value Unknown
An unknown error occurred.
*/
@@ -181,293 +183,12 @@ QT_BEGIN_NAMESPACE
\a state.
*/
-QKnxNetIpServerDiscoveryAgentPrivate::QKnxNetIpServerDiscoveryAgentPrivate(const QHostAddress &addr,
- quint16 prt)
- : port(prt)
- , address(addr)
-{}
-
-namespace QKnxPrivate
-{
- static void clearSocket(QUdpSocket **socket)
- {
- if (*socket) {
- (*socket)->disconnect();
- (*socket)->deleteLater();
- (*socket) = nullptr;
- }
- }
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
-{
- usedPort = port;
- usedAddress = address;
- QKnxPrivate::clearSocket(&socket);
-
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- socket = new QUdpSocket(q);
-
- QObject::connect(socket, &QUdpSocket::stateChanged, q, [&](QUdpSocket::SocketState s) {
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- switch (s) {
- case QUdpSocket::BoundState:
- setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Running);
- socket->setSocketOption(QUdpSocket::SocketOption::MulticastTtlOption, ttl);
-
- if (type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast) {
- QNetworkInterface mni;
- const auto interfaces = QNetworkInterface::allInterfaces();
- for (const auto &iface : interfaces) {
- if (!iface.flags().testFlag(QNetworkInterface::CanMulticast))
- continue;
-
- const auto entries = iface.addressEntries();
- for (const auto &entry : entries) {
- auto ip = entry.ip();
- if (ip.protocol() != QAbstractSocket::NetworkLayerProtocol::IPv4Protocol)
- continue;
- if (ip != address)
- continue;
- mni = iface;
- break;
- }
- }
-
- if (mni.isValid())
- socket->setMulticastInterface(mni);
-
- if (socket->joinMulticastGroup(multicastAddress, mni)) {
- usedPort = multicastPort;
- usedAddress = multicastAddress;
- } else {
- setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
- QKnxNetIpServerDiscoveryAgent::tr("Could not join multicast group."));
- q->stop();
- }
- } else {
- usedPort = socket->localPort();
- usedAddress = socket->localAddress();
- }
-
- if (q->state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
- servers.clear();
-
- const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(discoveryMode);
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)) {
- auto frame = QKnxNetIpSearchRequestProxy::builder()
- .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
- .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
- .setPort(nat ? quint16(0u) : usedPort).create()
- ).create();
- socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
- multicastPort);
- }
-
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)) {
- auto frame = QKnxNetIpSearchRequestProxy::extendedBuilder()
- .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
- .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
- .setPort(nat ? quint16(0u) : usedPort).create()
- )
- .setExtendedParameters(srps).create();
- socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
- multicastPort);
- }
-
- setupAndStartReceiveTimer();
- setupAndStartFrequencyTimer();
- }
- break;
- default:
- break;
- }
- });
-
- using overload = void (QUdpSocket::*)(QUdpSocket::SocketError);
- QObject::connect(socket,
- static_cast<overload>(&QUdpSocket::error), q, [&](QUdpSocket::SocketError) {
- setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
- socket->errorString());
-
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- q->stop();
- });
-
- QObject::connect(socket, &QUdpSocket::readyRead, q, [&]() {
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- while (socket->hasPendingDatagrams()) {
- if (q->state() != QKnxNetIpServerDiscoveryAgent::State::Running)
- break;
-
- auto datagram = socket->receiveDatagram();
- auto data = QKnxByteArray::fromByteArray(datagram.data());
- const auto header = QKnxNetIpFrameHeader::fromBytes(data, 0);
- if (!header.isValid())
- continue;
-
- if (header.serviceType() != QKnxNetIp::ServiceType::SearchResponse &&
- header.serviceType() != QKnxNetIp::ServiceType::ExtendedSearchResponse) {
- continue;
- }
-
- auto frame = QKnxNetIpFrame::fromBytes(data);
- auto response = QKnxNetIpSearchResponseProxy(frame);
- if (!response.isValid())
- continue;
-
- const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(discoveryMode);
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)
- && !response.isExtended()) {
- setAndEmitDeviceDiscovered({
- (nat ? QKnxNetIpHpaiProxy::builder()
- .setHostAddress(datagram.senderAddress())
- .setPort(datagram.senderPort()).create()
- : response.controlEndpoint()
- ), response.deviceHardware(), response.supportedFamilies(),
- QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex())
- });
- }
-
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)
- && response.isExtended()) {
- const auto optionalDibs = response.optionalDibs();
- setAndEmitDeviceDiscovered({
- (nat ? QKnxNetIpHpaiProxy::builder()
- .setHostAddress(datagram.senderAddress())
- .setPort(datagram.senderPort()).create()
- : response.controlEndpoint()
- ), response.deviceHardware(), response.supportedFamilies(),
- QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex()),
- [&optionalDibs]() -> QKnxNetIpDib {
- for (const auto &dib : qAsConst(optionalDibs)) {
- if (dib.code() == QKnxNetIp::DescriptionType::TunnelingInfo)
- return dib;
- }
- return {};
- }(),
- [&optionalDibs]() -> QKnxNetIpDib {
- for (const auto &dib : qAsConst(optionalDibs)) {
- if (dib.code() == QKnxNetIp::DescriptionType::ExtendedDeviceInfo)
- return dib;
- }
- return {};
- }()
- });
- }
- }
- });
-}
-
-namespace QKnxPrivate
-{
- static void clearTimer(QTimer **timer)
- {
- if (*timer) {
- (*timer)->stop();
- (*timer)->disconnect();
- (*timer)->deleteLater();
- (*timer) = nullptr;
- }
- }
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setupAndStartReceiveTimer()
-{
- Q_Q(QKnxNetIpServerDiscoveryAgent);
-
- QKnxPrivate::clearTimer(&receiveTimer);
- if (timeout >= 0) {
- receiveTimer = new QTimer(q);
- receiveTimer->setSingleShot(true);
- receiveTimer->start(timeout);
- QObject::connect(receiveTimer, &QTimer::timeout, q, &QKnxNetIpServerDiscoveryAgent::stop);
- }
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setupAndStartFrequencyTimer()
-{
- Q_Q(QKnxNetIpServerDiscoveryAgent);
-
- QKnxPrivate::clearTimer(&frequencyTimer);
- if (frequency > 0) {
- frequencyTimer = new QTimer(q);
- frequencyTimer->setSingleShot(false);
- frequencyTimer->start(60000 / frequency);
-
- QObject::connect(frequencyTimer, &QTimer::timeout, q, [&]() {
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- if (q->state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
- servers.clear();
-
- const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(discoveryMode);
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)) {
- auto frame = QKnxNetIpSearchRequestProxy::builder()
- .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
- .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
- .setPort(nat ? quint16(0u) : usedPort).create()
- ).create();
- socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
- multicastPort);
- }
-
- if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)) {
- auto frame = QKnxNetIpSearchRequestProxy::extendedBuilder()
- .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
- .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
- .setPort(nat ? quint16(0u) : usedPort).create()
- )
- .setExtendedParameters(srps).create();
- socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
- multicastPort);
- }
- }
- });
- }
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitStateChanged(
- QKnxNetIpServerDiscoveryAgent::State newState)
-{
- state = newState;
-
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- emit q->stateChanged(newState);
-
- if (state == QKnxNetIpServerDiscoveryAgent::State::Running)
- emit q->started();
- else if (state == QKnxNetIpServerDiscoveryAgent::State::NotRunning)
- emit q->finished();
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitDeviceDiscovered(
- const QKnxNetIpServerInfo &discoveryInfo)
-{
- servers.append(discoveryInfo);
-
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- emit q->deviceDiscovered(discoveryInfo);
-}
-
-void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitErrorOccurred(
- QKnxNetIpServerDiscoveryAgent::Error newError, const QString &message)
-{
- error = newError;
- errorString = message;
-
- Q_Q(QKnxNetIpServerDiscoveryAgent);
- emit q->errorOccurred(error, errorString);
-}
-
-
-// -- QKnxNetIpServerDiscoveryAgent
-
/*!
Creates a KNXnet/IP server discovery agent with the parent \a parent.
*/
QKnxNetIpServerDiscoveryAgent::QKnxNetIpServerDiscoveryAgent(QObject *parent)
- : QKnxNetIpServerDiscoveryAgent(QHostAddress(QHostAddress::AnyIPv4), 0u, parent)
+ : QKnxNetIpServerDiscoveryAgent(QHostAddress(QHostAddress::Null), 0U, parent)
{}
/*!
@@ -475,7 +196,7 @@ QKnxNetIpServerDiscoveryAgent::QKnxNetIpServerDiscoveryAgent(QObject *parent)
*/
QKnxNetIpServerDiscoveryAgent::~QKnxNetIpServerDiscoveryAgent()
{
- stop();
+ d_func()->stop();
}
/*!
@@ -484,7 +205,7 @@ QKnxNetIpServerDiscoveryAgent::~QKnxNetIpServerDiscoveryAgent()
*/
QKnxNetIpServerDiscoveryAgent::QKnxNetIpServerDiscoveryAgent(const QHostAddress &localAddress,
QObject *parent)
- : QKnxNetIpServerDiscoveryAgent(localAddress, 0u, parent)
+ : QKnxNetIpServerDiscoveryAgent(localAddress, 0U, parent)
{}
/*!
@@ -505,8 +226,7 @@ QKnxNetIpServerDiscoveryAgent::QKnxNetIpServerDiscoveryAgent(const QHostAddress
*/
QKnxNetIpServerDiscoveryAgent::State QKnxNetIpServerDiscoveryAgent::state() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->state;
+ return d_func()->state;
}
/*!
@@ -514,8 +234,7 @@ QKnxNetIpServerDiscoveryAgent::State QKnxNetIpServerDiscoveryAgent::state() cons
*/
QKnxNetIpServerDiscoveryAgent::Error QKnxNetIpServerDiscoveryAgent::error() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->error;
+ return d_func()->error;
}
/*!
@@ -523,8 +242,7 @@ QKnxNetIpServerDiscoveryAgent::Error QKnxNetIpServerDiscoveryAgent::error() cons
*/
QString QKnxNetIpServerDiscoveryAgent::errorString() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->errorString;
+ return d_func()->errorString;
}
/*!
@@ -532,8 +250,7 @@ QString QKnxNetIpServerDiscoveryAgent::errorString() const
*/
QVector<QKnxNetIpServerInfo> QKnxNetIpServerDiscoveryAgent::discoveredServers() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->servers;
+ return d_func()->servers;
}
/*!
@@ -572,7 +289,8 @@ QHostAddress QKnxNetIpServerDiscoveryAgent::localAddress() const
}
/*!
- Sets the host address of a discovery agent to \a address.
+ Sets the host address of a discovery agent to \a address. To unset the local
+ address use \l QHostAddress::Null.
\note If the address changes during discovery, the new address will not be
used until the next run.
@@ -592,8 +310,7 @@ void QKnxNetIpServerDiscoveryAgent::setLocalAddress(const QHostAddress &address)
*/
int QKnxNetIpServerDiscoveryAgent::timeout() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->timeout;
+ return d_func()->timeout;
}
/*!
@@ -620,8 +337,7 @@ void QKnxNetIpServerDiscoveryAgent::setTimeout(int msec)
*/
int QKnxNetIpServerDiscoveryAgent::searchFrequency() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->frequency;
+ return d_func()->frequency;
}
/*!
@@ -646,8 +362,7 @@ void QKnxNetIpServerDiscoveryAgent::setSearchFrequency(int timesPerMinute)
*/
bool QKnxNetIpServerDiscoveryAgent::natAware() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->nat;
+ return d_func()->nat;
}
/*!
@@ -671,8 +386,7 @@ void QKnxNetIpServerDiscoveryAgent::setNatAware(bool useNat)
*/
quint8 QKnxNetIpServerDiscoveryAgent::multicastTtl() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->ttl;
+ return d_func()->ttl;
}
/*!
@@ -692,8 +406,7 @@ void QKnxNetIpServerDiscoveryAgent::setMulticastTtl(quint8 ttl)
*/
QKnxNetIpServerDiscoveryAgent::ResponseType QKnxNetIpServerDiscoveryAgent::responseType() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->type;
+ return d_func()->type;
}
/*!
@@ -714,8 +427,7 @@ void QKnxNetIpServerDiscoveryAgent::setResponseType(QKnxNetIpServerDiscoveryAgen
*/
QKnxNetIpServerDiscoveryAgent::DiscoveryModes QKnxNetIpServerDiscoveryAgent::discoveryMode() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->discoveryMode;
+ return d_func()->mode;
}
/*!
@@ -729,18 +441,18 @@ void QKnxNetIpServerDiscoveryAgent::setDiscoveryMode(QKnxNetIpServerDiscoveryAge
{
Q_D(QKnxNetIpServerDiscoveryAgent);
if (d->state == QKnxNetIpServerDiscoveryAgent::State::NotRunning)
- d->discoveryMode = mode;
+ d->mode = mode;
}
/*!
\since 5.12
+
Returns the search request parameter (SRP) objects used in an
\l {QKnxNetIpServerDiscoveryAgent::CoreV2} {extended search request}.
*/
QVector<QKnxNetIpSrp> QKnxNetIpServerDiscoveryAgent::extendedSearchParameters() const
{
- Q_D(const QKnxNetIpServerDiscoveryAgent);
- return d->srps;
+ return d_func()->srps;
}
/*!
@@ -757,8 +469,7 @@ QVector<QKnxNetIpSrp> QKnxNetIpServerDiscoveryAgent::extendedSearchParameters()
*/
void QKnxNetIpServerDiscoveryAgent::setExtendedSearchParameters(const QVector<QKnxNetIpSrp> &srps)
{
- Q_D(QKnxNetIpServerDiscoveryAgent);
- d->srps = srps;
+ d_func()->srps = srps;
}
/*!
@@ -766,26 +477,7 @@ void QKnxNetIpServerDiscoveryAgent::setExtendedSearchParameters(const QVector<QK
*/
void QKnxNetIpServerDiscoveryAgent::start()
{
- Q_D(QKnxNetIpServerDiscoveryAgent);
-
- if (d->state != QKnxNetIpServerDiscoveryAgent::State::NotRunning)
- return;
-
- auto isIPv4 = true;
- d->address.toIPv4Address(&isIPv4);
- if (isIPv4) {
- d->setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Starting);
-
- d->setupSocket();
- if (d->type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast) {
- d->socket->bind(QHostAddress::AnyIPv4, d->multicastPort, QUdpSocket::ShareAddress
- | QAbstractSocket::ReuseAddressHint);
- } else {
- d->socket->bind(d->address, d->port);
- }
- } else {
- d->setAndEmitErrorOccurred(Error::NotIPv4, tr("Only IPv4 local address supported."));
- }
+ d_func()->start();
}
/*!
@@ -796,7 +488,7 @@ void QKnxNetIpServerDiscoveryAgent::start()
void QKnxNetIpServerDiscoveryAgent::start(int timeout)
{
d_func()->timeout = timeout;
- start();
+ d_func()->start();
}
/*!
@@ -804,24 +496,7 @@ void QKnxNetIpServerDiscoveryAgent::start(int timeout)
*/
void QKnxNetIpServerDiscoveryAgent::stop()
{
- Q_D(QKnxNetIpServerDiscoveryAgent);
-
- if (d->state == State::Stopping || d->state == State::NotRunning)
- return;
-
- d->setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Stopping);
-
- if (d->type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast
- && d->socket->state() == QUdpSocket::BoundState) {
- d->socket->leaveMulticastGroup(d->multicastAddress);
- }
- d->socket->close();
-
- QKnxPrivate::clearSocket(&(d->socket));
- QKnxPrivate::clearTimer(&(d->receiveTimer));
- QKnxPrivate::clearTimer(&(d->frequencyTimer));
-
- d->setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::NotRunning);
+ d_func()->stop();
}
/*!
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent.h b/src/knx/netip/qknxnetipserverdiscoveryagent.h
index 0fd0455..8367628 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent.h
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent.h
@@ -60,6 +60,7 @@ public:
None,
Network,
NotIPv4,
+ Timeout,
Unknown = 0x80
};
Q_ENUM(Error)
@@ -80,7 +81,7 @@ public:
Q_DECLARE_FLAGS(DiscoveryModes, DiscoveryMode)
QKnxNetIpServerDiscoveryAgent(QObject *parent = nullptr);
- ~QKnxNetIpServerDiscoveryAgent();
+ ~QKnxNetIpServerDiscoveryAgent() override;
explicit QKnxNetIpServerDiscoveryAgent(const QHostAddress &localAddress,
QObject *parent = nullptr);
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp b/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp
new file mode 100644
index 0000000..e1365a7
--- /dev/null
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp
@@ -0,0 +1,381 @@
+/******************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtKnx module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+******************************************************************************/
+
+#include "qknxnetipserverdiscoveryagent.h"
+#include "qknxnetipserverdiscoveryagent_p.h"
+
+#include <QtNetwork/qnetworkinterface.h>
+
+QT_BEGIN_NAMESPACE
+
+QKnxNetIpServerDiscoveryAgentPrivate::QKnxNetIpServerDiscoveryAgentPrivate(const QHostAddress &addr,
+ quint16 prt)
+ : port(prt)
+ , address(addr)
+{}
+
+namespace QKnxPrivate
+{
+ static void clearSocket(QUdpSocket **socket)
+ {
+ if (*socket) {
+ (*socket)->disconnect();
+ (*socket)->deleteLater();
+ (*socket) = nullptr;
+ }
+ }
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
+{
+ usedPort = port;
+ usedAddress = address;
+ QKnxPrivate::clearSocket(&socket);
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ socket = new QUdpSocket(q);
+
+ QObject::connect(socket, &QUdpSocket::stateChanged, q, [&](QUdpSocket::SocketState s) {
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ switch (s) {
+ case QUdpSocket::BoundState:
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Running);
+ socket->setSocketOption(QUdpSocket::SocketOption::MulticastTtlOption, ttl);
+
+ if (type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast) {
+ QNetworkInterface mni;
+ const auto interfaces = QNetworkInterface::allInterfaces();
+ for (const auto &iface : interfaces) {
+ if (!iface.flags().testFlag(QNetworkInterface::CanMulticast))
+ continue;
+
+ const auto entries = iface.addressEntries();
+ for (const auto &entry : entries) {
+ auto ip = entry.ip();
+ if (ip.protocol() != QAbstractSocket::NetworkLayerProtocol::IPv4Protocol)
+ continue;
+ if (ip != address)
+ continue;
+ mni = iface;
+ break;
+ }
+ }
+
+ if (mni.isValid())
+ socket->setMulticastInterface(mni);
+
+ if (socket->joinMulticastGroup(multicastAddress, mni)) {
+ usedPort = multicastPort;
+ usedAddress = multicastAddress;
+ } else {
+ setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
+ QKnxNetIpServerDiscoveryAgent::tr("Could not join multicast group."));
+ q->stop();
+ }
+ } else {
+ usedPort = socket->localPort();
+ usedAddress = socket->localAddress();
+ }
+
+ if (q->state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
+ servers.clear();
+
+ const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(mode);
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)) {
+ auto frame = QKnxNetIpSearchRequestProxy::builder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
+ .setPort(nat ? quint16(0u) : usedPort).create()
+ ).create();
+ socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
+ multicastPort);
+ }
+
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)) {
+ auto frame = QKnxNetIpSearchRequestProxy::extendedBuilder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
+ .setPort(nat ? quint16(0u) : usedPort).create()
+ )
+ .setExtendedParameters(srps).create();
+ socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
+ multicastPort);
+ }
+
+ setupAndStartReceiveTimer();
+ setupAndStartFrequencyTimer();
+ }
+ break;
+ default:
+ break;
+ }
+ });
+
+ using overload = void (QUdpSocket::*)(QUdpSocket::SocketError);
+ QObject::connect(socket,
+ static_cast<overload>(&QUdpSocket::error), q, [&](QUdpSocket::SocketError) {
+ setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
+ socket->errorString());
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ q->stop();
+ });
+
+ QObject::connect(socket, &QUdpSocket::readyRead, q, [&]() {
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ while (socket->hasPendingDatagrams()) {
+ if (q->state() != QKnxNetIpServerDiscoveryAgent::State::Running)
+ break;
+
+ auto datagram = socket->receiveDatagram();
+ auto data = QKnxByteArray::fromByteArray(datagram.data());
+ const auto header = QKnxNetIpFrameHeader::fromBytes(data, 0);
+ if (!header.isValid())
+ continue;
+
+ if (header.serviceType() != QKnxNetIp::ServiceType::SearchResponse &&
+ header.serviceType() != QKnxNetIp::ServiceType::ExtendedSearchResponse) {
+ continue;
+ }
+
+ auto frame = QKnxNetIpFrame::fromBytes(data);
+ auto response = QKnxNetIpSearchResponseProxy(frame);
+ if (!response.isValid())
+ continue;
+
+ const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(mode);
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)
+ && !response.isExtended()) {
+ setAndEmitDeviceDiscovered({
+ (nat ? QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(datagram.senderAddress())
+ .setPort(datagram.senderPort()).create()
+ : response.controlEndpoint()
+ ), response.deviceHardware(), response.supportedFamilies(),
+ QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex())
+ });
+ }
+
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)
+ && response.isExtended()) {
+ const auto optionalDibs = response.optionalDibs();
+ setAndEmitDeviceDiscovered({
+ (nat ? QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(datagram.senderAddress())
+ .setPort(datagram.senderPort()).create()
+ : response.controlEndpoint()
+ ), response.deviceHardware(), response.supportedFamilies(),
+ QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex()),
+ [&optionalDibs]() -> QKnxNetIpDib {
+ for (const auto &dib : qAsConst(optionalDibs)) {
+ if (dib.code() == QKnxNetIp::DescriptionType::TunnelingInfo)
+ return dib;
+ }
+ return {};
+ }(),
+ [&optionalDibs]() -> QKnxNetIpDib {
+ for (const auto &dib : qAsConst(optionalDibs)) {
+ if (dib.code() == QKnxNetIp::DescriptionType::ExtendedDeviceInfo)
+ return dib;
+ }
+ return {};
+ }()
+ });
+ }
+ }
+ });
+}
+
+namespace QKnxPrivate
+{
+ static void clearTimer(QTimer **timer)
+ {
+ if (*timer) {
+ (*timer)->stop();
+ (*timer)->disconnect();
+ (*timer)->deleteLater();
+ (*timer) = nullptr;
+ }
+ }
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setupAndStartReceiveTimer()
+{
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+
+ QKnxPrivate::clearTimer(&receiveTimer);
+ if (timeout >= 0) {
+ receiveTimer = new QTimer(q);
+ receiveTimer->setSingleShot(true);
+ receiveTimer->start(timeout);
+ QObject::connect(receiveTimer, &QTimer::timeout, q, &QKnxNetIpServerDiscoveryAgent::stop);
+ }
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setupAndStartFrequencyTimer()
+{
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+
+ QKnxPrivate::clearTimer(&frequencyTimer);
+ if (frequency > 0) {
+ frequencyTimer = new QTimer(q);
+ frequencyTimer->setSingleShot(false);
+ frequencyTimer->start(60000 / frequency);
+
+ QObject::connect(frequencyTimer, &QTimer::timeout, q, [&]() {
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ if (q->state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
+ servers.clear();
+
+ const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> flags(mode);
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1)) {
+ auto frame = QKnxNetIpSearchRequestProxy::builder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
+ .setPort(nat ? quint16(0u) : usedPort).create()
+ ).create();
+ socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
+ multicastPort);
+ }
+
+ if (flags.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2)) {
+ auto frame = QKnxNetIpSearchRequestProxy::extendedBuilder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(nat ? QHostAddress::AnyIPv4 : usedAddress)
+ .setPort(nat ? quint16(0u) : usedPort).create()
+ )
+ .setExtendedParameters(srps).create();
+ socket->writeDatagram(frame.bytes().toByteArray(), multicastAddress,
+ multicastPort);
+ }
+ }
+ });
+ }
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitStateChanged(
+ QKnxNetIpServerDiscoveryAgent::State newState)
+{
+ state = newState;
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ emit q->stateChanged(newState);
+
+ if (state == QKnxNetIpServerDiscoveryAgent::State::Running)
+ emit q->started();
+ else if (state == QKnxNetIpServerDiscoveryAgent::State::NotRunning)
+ emit q->finished();
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitDeviceDiscovered(
+ const QKnxNetIpServerInfo &discoveryInfo)
+{
+ servers.append(discoveryInfo);
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ emit q->deviceDiscovered(discoveryInfo);
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitErrorOccurred(
+ QKnxNetIpServerDiscoveryAgent::Error newError, const QString &message)
+{
+ error = newError;
+ errorString = message;
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ emit q->errorOccurred(error, errorString);
+}
+
+namespace QKnxPrivate
+{
+ QString interfaceFromAddress(const QHostAddress &address)
+ {
+ auto interfaces = QNetworkInterface::allInterfaces();
+ for (const auto &interface : qAsConst(interfaces)) {
+ auto entries = interface.addressEntries();
+ for (const auto &entry : entries) {
+ if (entry.ip() == address)
+ return interface.humanReadableName();
+ }
+ }
+ return QLatin1String("Unknown");
+ }
+}
+void QKnxNetIpServerDiscoveryAgentPrivate::start()
+{
+ if (state != QKnxNetIpServerDiscoveryAgent::State::NotRunning)
+ return;
+
+ if (address.isNull())
+ address = QHostAddress(QHostAddress::AnyIPv4);
+
+ auto isIPv4 = true;
+ address.toIPv4Address(&isIPv4);
+ if (isIPv4) {
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Starting);
+ setupSocket();
+ if (type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast) {
+ socket->bind(QHostAddress::AnyIPv4, multicastPort, QUdpSocket::ShareAddress
+ | QAbstractSocket::ReuseAddressHint);
+ qDebug() << "Multicast response using iface:" << QKnxPrivate::interfaceFromAddress(address);
+ } else {
+ socket->bind(address, port);
+ qDebug() << "Unicast response using interface:" << QKnxPrivate::interfaceFromAddress(address);
+ }
+ } else {
+ setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error::NotIPv4,
+ QKnxNetIpServerDiscoveryAgent::tr("Only IPv4 local address supported."));
+ }
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::stop()
+{
+ if (state == QKnxNetIpServerDiscoveryAgent::State::Stopping
+ || state == QKnxNetIpServerDiscoveryAgent::State::NotRunning) {
+ return;
+ }
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Stopping);
+
+ if (socket) {
+ if (type == QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast
+ && socket->state() == QUdpSocket::BoundState) {
+ socket->leaveMulticastGroup(multicastAddress);
+ }
+ socket->close();
+ }
+
+ QKnxPrivate::clearSocket(&(socket));
+ QKnxPrivate::clearTimer(&(receiveTimer));
+ QKnxPrivate::clearTimer(&(frequencyTimer));
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::NotRunning);
+}
+
+QT_END_NAMESPACE
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent_p.h b/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
index 21bdcdf..960c214 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
@@ -70,7 +70,10 @@ public:
void setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State newState);
void setAndEmitDeviceDiscovered(const QKnxNetIpServerInfo &discoveryInfo);
- void setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error e, const QString &message);
+ void setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error newError, const QString &message);
+
+ void start();
+ void stop();
private:
QUdpSocket *socket { nullptr };
@@ -78,12 +81,12 @@ private:
QTimer *frequencyTimer { nullptr };
quint16 port { 0 }, usedPort { 0 };
- QHostAddress address { QHostAddress::AnyIPv4 }, usedAddress;
+ QHostAddress address { QHostAddress::Null }, usedAddress;
const quint16 multicastPort { QKnxNetIp::Constants::DefaultPort };
const QHostAddress multicastAddress { QLatin1String(QKnxNetIp::Constants::MulticastAddress) };
- quint8 ttl { 60 };
+ quint8 ttl { 64 };
bool nat { false };
int frequency { 0 };
int timeout { QKnxNetIp::Timeout::SearchTimeout };
@@ -96,7 +99,7 @@ private:
QKnxNetIpServerDiscoveryAgent::ResponseType
type { QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast };
- QKnxNetIpServerDiscoveryAgent::DiscoveryModes discoveryMode
+ QKnxNetIpServerDiscoveryAgent::DiscoveryModes mode
{ QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1 };
QVector<QKnxNetIpSrp> srps;
};