summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2019-08-16 09:39:26 +0200
committerKarsten Heimrich <karsten.heimrich@qt.io>2019-08-21 16:35:53 +0200
commit5e1e8e069c1af260e2fcf26777a5e746cf9ae9c6 (patch)
tree3349227ba2b437bb3bcefb04c86d888b4636efd2
parenta8b3aacf2630933b16dc093b9ab8505ead8e25c6 (diff)
Implement device discovery on all available interfaces
Change-Id: Ifbe8ea3611d08e63751ee714c6cf40044ff37e1b Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r--examples/knx/discoverer/main.cpp35
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent.cpp23
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent.h15
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp383
-rw-r--r--src/knx/netip/qknxnetipserverdiscoveryagent_p.h86
-rw-r--r--src/knx/netip/qknxnetipserverinfo.cpp4
-rw-r--r--src/knx/netip/qknxnetipserverinfo.h1
7 files changed, 479 insertions, 68 deletions
diff --git a/examples/knx/discoverer/main.cpp b/examples/knx/discoverer/main.cpp
index 0e539bc..a2c292b 100644
--- a/examples/knx/discoverer/main.cpp
+++ b/examples/knx/discoverer/main.cpp
@@ -56,8 +56,6 @@
#include <QtKnx/QKnxNetIpServerDiscoveryAgent>
#include <QtKnx/QKnxNetIpSrpBuilder>
-#include <QtNetwork/QNetworkInterface>
-
static QString familyToString(QKnxNetIp::ServiceFamily id)
{
switch (id) {
@@ -83,17 +81,6 @@ static QString familyToString(QKnxNetIp::ServiceFamily id)
return "Unknown";
}
-QString interfaceFromAddress(const QHostAddress &address)
-{
- auto interfaces = QNetworkInterface::allInterfaces();
- for (const auto &interface : qAsConst(interfaces)) {
- if (interface.allAddresses().contains(address))
- return interface.humanReadableName();
- }
- return QString(address == QHostAddress(QKnxNetIp::Constants::MulticastAddress)
- ? "Multicast" : "Unknown");
-}
-
int main(int argc, char *argv[])
{
QCoreApplication discoverer(argc, argv);
@@ -130,10 +117,8 @@ int main(int argc, char *argv[])
agent.setLocalPort(parser.value("localPort").toUInt());
agent.setTimeout(parser.value("timeout").toInt() * 1000);
- if (parser.isSet("localAddress")) {
+ if (parser.isSet("localAddress"))
agent.setLocalAddress(QHostAddress(parser.value("localAddress")));
- agent.setResponseType(QKnxNetIpServerDiscoveryAgent::ResponseType::Unicast);
- }
if (parser.isSet("unicast"))
agent.setResponseType(QKnxNetIpServerDiscoveryAgent::ResponseType::Unicast);
@@ -189,15 +174,17 @@ int main(int argc, char *argv[])
QObject::connect(&agent, &QKnxNetIpServerDiscoveryAgent::finished, &discoverer,
&QCoreApplication::quit);
- agent.start();
-
- if (agent.error() == QKnxNetIpServerDiscoveryAgent::Error::None)
- discoverer.exec();
+ if (!parser.isSet("localAddress")) {
+ agent.start(QKnxNetIpServerDiscoveryAgent::InterfaceType::Ethernet
+ | QKnxNetIpServerDiscoveryAgent::InterfaceType::Wifi);
+ } else {
+ agent.start(QVector<QHostAddress> { agent.localAddress() });
+ }
- qInfo().noquote() << endl << "Device used to send the search request:";
- qInfo().noquote() << QString::fromLatin1(" Network interface: %1, address: %2, port: %3")
- .arg(interfaceFromAddress(agent.localAddress()), agent.localAddress().toString())
- .arg(agent.localPort());
+ if (agent.error() == QKnxNetIpServerDiscoveryAgent::Error::None
+ && agent.state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
+ discoverer.exec();
+ }
const auto servers = agent.discoveredServers();
if (servers.size() > 0) {
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent.cpp b/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
index 4ed44da..d00fd74 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent.cpp
@@ -326,7 +326,6 @@ void QKnxNetIpServerDiscoveryAgent::setTimeout(int msec)
{
Q_D(QKnxNetIpServerDiscoveryAgent);
d->timeout = msec;
- d->setupAndStartReceiveTimer();
}
/*!
@@ -492,6 +491,28 @@ void QKnxNetIpServerDiscoveryAgent::start(int timeout)
}
/*!
+ Starts a server discovery agent with the network interfaces specified by
+ the list of local host \a addresses.
+
+ \note Does not emit the errorOccurred signal.
+*/
+void QKnxNetIpServerDiscoveryAgent::start(const QVector<QHostAddress> &addresses)
+{
+ d_func()->start(addresses);
+}
+
+/*!
+ Starts a server discovery agent with the network interfaces specified by
+ the list of interface types \a types.
+
+ \note Does not emit the errorOccurred signal.
+*/
+void QKnxNetIpServerDiscoveryAgent::start(QKnxNetIpServerDiscoveryAgent::InterfaceTypes types)
+{
+ d_func()->start(types);
+}
+
+/*!
Stops a server discovery agent.
*/
void QKnxNetIpServerDiscoveryAgent::stop()
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent.h b/src/knx/netip/qknxnetipserverdiscoveryagent.h
index 8367628..b7bdf83 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent.h
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent.h
@@ -80,6 +80,15 @@ public:
Q_ENUM(DiscoveryMode)
Q_DECLARE_FLAGS(DiscoveryModes, DiscoveryMode)
+ enum class InterfaceType : quint8
+ {
+ Loopback = 0x01,
+ Ethernet = 0x02,
+ Wifi = 0x04
+ };
+ Q_ENUM(InterfaceType)
+ Q_DECLARE_FLAGS(InterfaceTypes, InterfaceType)
+
QKnxNetIpServerDiscoveryAgent(QObject *parent = nullptr);
~QKnxNetIpServerDiscoveryAgent() override;
@@ -123,9 +132,12 @@ public:
public Q_SLOTS:
void start();
- void start(int timeout);
void stop();
+ void start(int timeout);
+ void start(const QVector<QHostAddress> &localAddresses);
+ void start(QKnxNetIpServerDiscoveryAgent::InterfaceTypes types);
+
Q_SIGNALS:
void started();
void finished();
@@ -138,6 +150,7 @@ private:
QKnxNetIpServerDiscoveryAgent(QKnxNetIpServerDiscoveryAgentPrivate &dd, QObject *parent);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QKnxNetIpServerDiscoveryAgent::DiscoveryModes)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QKnxNetIpServerDiscoveryAgent::InterfaceTypes)
QT_END_NAMESPACE
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp b/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp
index e1365a7..002878c 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent_p.cpp
@@ -30,16 +30,10 @@
#include "qknxnetipserverdiscoveryagent.h"
#include "qknxnetipserverdiscoveryagent_p.h"
-#include <QtNetwork/qnetworkinterface.h>
+#include <QtCore/qthread.h>
QT_BEGIN_NAMESPACE
-QKnxNetIpServerDiscoveryAgentPrivate::QKnxNetIpServerDiscoveryAgentPrivate(const QHostAddress &addr,
- quint16 prt)
- : port(prt)
- , address(addr)
-{}
-
namespace QKnxPrivate
{
static void clearSocket(QUdpSocket **socket)
@@ -50,8 +44,253 @@ namespace QKnxPrivate
(*socket) = nullptr;
}
}
+
+ static void clearTimer(QTimer **timer)
+ {
+ if (*timer) {
+ (*timer)->stop();
+ (*timer)->disconnect();
+ (*timer)->deleteLater();
+ (*timer) = nullptr;
+ }
+ }
+
+ static QVector<Adapter> adaptersForAddresses(const QVector<QHostAddress> addresses)
+ {
+ QVector<Adapter> adapters;
+ auto all = QNetworkInterface::allInterfaces();
+ for (const auto &iface : all) {
+ const auto flags = iface.flags();
+ if (!flags.testFlag(QNetworkInterface::IsUp)
+ && !flags.testFlag(QNetworkInterface::IsRunning)
+ && !flags.testFlag(QNetworkInterface::CanMulticast)) continue;
+
+ const auto type = iface.type();
+ if (type != QNetworkInterface::Wifi
+ && type != QNetworkInterface::Loopback
+ && type != QNetworkInterface::Ethernet) continue;
+
+ const auto entries = iface.addressEntries();
+ for (const auto &entry : entries) {
+ const auto address = entry.ip();
+ if (address.protocol() != QAbstractSocket::IPv4Protocol)
+ continue;
+ if (!addresses.contains(address))
+ continue;
+ adapters.append({ address, iface });
+ break;
+ }
+ }
+ return adapters;
+ }
+}
+
+Discoverer::Discoverer(const QHostAddress &address, const QNetworkInterface iface, const DiscovererConfig &c)
+ : QObject(nullptr)
+ , m_address(address)
+ , m_iface(iface)
+ , m_config(c)
+{}
+
+Discoverer::~Discoverer()
+{
+ finish();
+}
+
+void Discoverer::start()
+{
+ if (m_state != QKnxNetIpServerDiscoveryAgent::State::NotRunning)
+ return;
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Starting);
+
+ auto isIPv4 = true;
+ m_address.toIPv4Address(&isIPv4);
+ if (!isIPv4) {
+ emit errorOccurred(QKnxNetIpServerDiscoveryAgent::Error::NotIPv4,
+ QKnxNetIpServerDiscoveryAgent::tr("Only IPv4 local address supported."));
+ return finish();
+ }
+
+ m_socket = new QUdpSocket;
+ connect(m_socket, &QUdpSocket::readyRead, this, &Discoverer::onReadyRead);
+ using overload = void (QUdpSocket::*)(QUdpSocket::SocketError);
+ connect(m_socket, static_cast<overload>(&QUdpSocket::error), this, &Discoverer::onError);
+
+ bool bound = m_socket->bind(m_config.Multicast ? QHostAddress::AnyIPv4
+ : m_address,
+ m_config.Multicast ? 3671 : 0,
+ QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
+ if (!bound)
+ return finish();
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Running);
+
+ if (m_config.Multicast) {
+ m_socket->setMulticastInterface(m_iface);
+ if (!m_socket->joinMulticastGroup(m_multicast, m_iface)) {
+ emit errorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
+ QKnxNetIpServerDiscoveryAgent::tr("Could not join multicast group."));
+ return finish();
+ }
+ m_socket->setSocketOption(QUdpSocket::SocketOption::MulticastTtlOption, m_config.Ttl);
+ }
+
+ m_timer = new QTimer;
+ m_timer->setSingleShot(true);
+ connect(m_timer, &QTimer::timeout, this, &Discoverer::onTimeout);
+
+ if (m_config.DiscoveryCoreV1) {
+ const auto frame = QKnxNetIpSearchRequestProxy::builder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::Builder()
+ .setPort(m_config.Nat ? 0 : (m_config.Multicast ? 3671 : m_socket->localPort()))
+ .setHostAddress(m_config.Nat ? QHostAddress::AnyIPv4
+ : (m_config.Multicast ? m_multicast : m_address))
+ .create())
+ .create();
+
+ if (frame.isValid())
+ m_socket->writeDatagram(frame.bytes().toByteArray(), m_multicast, 3671);
+ }
+
+ if (m_config.DiscoveryCoreV2) {
+ const auto frame = QKnxNetIpSearchRequestProxy::extendedBuilder()
+ .setDiscoveryEndpoint(QKnxNetIpHpaiProxy::Builder()
+ .setPort(m_config.Nat ? 0 : (m_config.Multicast ? 3671 : m_socket->localPort()))
+ .setHostAddress(m_config.Nat ? QHostAddress::AnyIPv4
+ : (m_config.Multicast ? m_multicast : m_address))
+ .create())
+ .setExtendedParameters(m_config.ExtendedSearchParameters)
+ .create();
+ if (frame.isValid())
+ m_socket->writeDatagram(frame.bytes().toByteArray(), m_multicast, 3671);
+ }
+
+ m_timer->start(m_config.Timeout);
+}
+
+void Discoverer::finish()
+{
+ if (m_state == QKnxNetIpServerDiscoveryAgent::State::Stopping
+ || m_state == QKnxNetIpServerDiscoveryAgent::State::NotRunning) {
+ return;
+ }
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Stopping);
+
+ QKnxPrivate::clearTimer(&m_timer);
+
+ if (m_socket) {
+ if (m_config.Multicast && m_socket->state() == QUdpSocket::BoundState) {
+ m_socket->setMulticastInterface({});
+ m_socket->leaveMulticastGroup(m_multicast, m_iface);
+ }
+ QKnxPrivate::clearSocket(&m_socket);
+ }
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::NotRunning);
+}
+
+void Discoverer::onTimeout()
+{
+ if (m_devices.isEmpty())
+ emit timeout();
+ finish();
+}
+
+void Discoverer::onReadyRead()
+{
+ while (m_socket->hasPendingDatagrams()) {
+ if (m_state != QKnxNetIpServerDiscoveryAgent::State::Running)
+ break;
+
+ const auto datagram = m_socket->receiveDatagram();
+ const 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;
+ }
+
+ const auto frame = QKnxNetIpFrame::fromBytes(data);
+ const auto proxy = QKnxNetIpSearchResponseProxy(frame);
+ if (!proxy.isValid())
+ continue;
+
+ if (m_config.DiscoveryCoreV1 && !proxy.isExtended()) {
+ emit deviceDiscovered(
+ {
+ (m_config.Nat ? QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(datagram.senderAddress())
+ .setPort(datagram.senderPort()).create()
+ : proxy.controlEndpoint()),
+ proxy.deviceHardware(),
+ proxy.supportedFamilies(),
+ m_iface
+ }
+ );
+ }
+
+ if (m_config.DiscoveryCoreV2 && proxy.isExtended()) {
+ const auto optionalDibs = proxy.optionalDibs();
+ emit deviceDiscovered(
+ {
+ (m_config.Nat ? QKnxNetIpHpaiProxy::builder()
+ .setHostAddress(datagram.senderAddress())
+ .setPort(datagram.senderPort()).create()
+ : proxy.controlEndpoint()),
+ proxy.deviceHardware(),
+ proxy.supportedFamilies(),
+ m_iface,
+ [&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 {};
+ }()
+ }
+ );
+ }
+ }
+}
+
+void Discoverer::onError(QUdpSocket::SocketError /* error */)
+{
+ emit errorOccurred(QKnxNetIpServerDiscoveryAgent::Error::Network,
+ m_socket ? m_socket->errorString()
+ : QKnxNetIpServerDiscoveryAgent::tr("Unknown socket error happened.")
+ );
+ finish();
+}
+
+void Discoverer::setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State state)
+{
+ m_state = state;
+ emit stateChanged(m_state);
+
+ if (m_state == QKnxNetIpServerDiscoveryAgent::State::Starting)
+ emit started();
+ if (m_state == QKnxNetIpServerDiscoveryAgent::State::Stopping)
+ emit finished(this);
}
+QKnxNetIpServerDiscoveryAgentPrivate::QKnxNetIpServerDiscoveryAgentPrivate(const QHostAddress &addr,
+ quint16 prt)
+ : port(prt)
+ , address(addr)
+{}
+
void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
{
usedPort = port;
@@ -61,6 +300,7 @@ void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
Q_Q(QKnxNetIpServerDiscoveryAgent);
socket = new QUdpSocket(q);
+ adapter = QKnxPrivate::adaptersForAddresses(QVector<QHostAddress> { address }).value(0);
QObject::connect(socket, &QUdpSocket::stateChanged, q, [&](QUdpSocket::SocketState s) {
Q_Q(QKnxNetIpServerDiscoveryAgent);
switch (s) {
@@ -177,8 +417,7 @@ void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
.setHostAddress(datagram.senderAddress())
.setPort(datagram.senderPort()).create()
: response.controlEndpoint()
- ), response.deviceHardware(), response.supportedFamilies(),
- QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex())
+ ), response.deviceHardware(), response.supportedFamilies(), adapter.iface
});
}
@@ -190,8 +429,7 @@ void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
.setHostAddress(datagram.senderAddress())
.setPort(datagram.senderPort()).create()
: response.controlEndpoint()
- ), response.deviceHardware(), response.supportedFamilies(),
- QNetworkInterface::interfaceFromIndex(datagram.interfaceIndex()),
+ ), response.deviceHardware(), response.supportedFamilies(), adapter.iface,
[&optionalDibs]() -> QKnxNetIpDib {
for (const auto &dib : qAsConst(optionalDibs)) {
if (dib.code() == QKnxNetIp::DescriptionType::TunnelingInfo)
@@ -212,19 +450,6 @@ void QKnxNetIpServerDiscoveryAgentPrivate::setupSocket()
});
}
-namespace QKnxPrivate
-{
- static void clearTimer(QTimer **timer)
- {
- if (*timer) {
- (*timer)->stop();
- (*timer)->disconnect();
- (*timer)->deleteLater();
- (*timer) = nullptr;
- }
- }
-}
-
void QKnxNetIpServerDiscoveryAgentPrivate::setupAndStartReceiveTimer()
{
Q_Q(QKnxNetIpServerDiscoveryAgent);
@@ -312,21 +537,6 @@ void QKnxNetIpServerDiscoveryAgentPrivate::setAndEmitErrorOccurred(
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)
@@ -343,10 +553,8 @@ void QKnxNetIpServerDiscoveryAgentPrivate::start()
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,
@@ -354,6 +562,99 @@ void QKnxNetIpServerDiscoveryAgentPrivate::start()
}
}
+void QKnxNetIpServerDiscoveryAgentPrivate::start(const QVector<Adapter> &adapters)
+{
+ if (state != QKnxNetIpServerDiscoveryAgent::State::NotRunning)
+ return;
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Starting);
+
+ if (adapters.isEmpty())
+ return stop();
+
+ Q_Q(QKnxNetIpServerDiscoveryAgent);
+ const QFlags<QKnxNetIpServerDiscoveryAgent::ResponseType> rtype(type);
+ const QFlags<QKnxNetIpServerDiscoveryAgent::DiscoveryMode> dmode(mode);
+ const DiscovererConfig config {
+ ttl,
+ timeout,
+ nat,
+ rtype.testFlag(QKnxNetIpServerDiscoveryAgent::ResponseType::Multicast),
+ dmode.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1),
+ dmode.testFlag(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2),
+ srps
+ };
+
+ bool connectError = (adapters.count() == 1);
+ for (const auto &adapter : adapters) {
+ auto discoverer = new Discoverer(adapter.address, adapter.iface, config);
+ discoveries.append(discoverer);
+
+ auto thread = new QThread;
+ discoverer->moveToThread(thread);
+ QObject::connect(thread, &QThread::started, discoverer, &Discoverer::start);
+ QObject::connect(thread, &QThread::finished, discoverer, &Discoverer::deleteLater);
+
+ QObject::connect(thread, &QThread::started, discoverer, &Discoverer::start);
+ QObject::connect(thread, &QThread::finished, discoverer, &Discoverer::finish);
+
+ QObject::connect(discoverer, &Discoverer::finished, q, [&](Discoverer *ds) {
+ discoveries.removeAll(ds);
+ if (discoveries.isEmpty())
+ stop();
+ });
+ QObject::connect(discoverer, &Discoverer::finished, thread, &QThread::quit);
+ QObject::connect(discoverer, &Discoverer::deviceDiscovered, q, [&](const QKnxNetIpServerInfo &server) {
+ setAndEmitDeviceDiscovered(server);
+ });
+ if (connectError) {
+ QObject::connect(discoverer, &Discoverer::errorOccurred, q,
+ [&](QKnxNetIpServerDiscoveryAgent::Error error, const QString &errorString) {
+ setAndEmitErrorOccurred(error, errorString);
+ });
+ }
+ thread->start();
+ }
+
+ setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State::Running);
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::start(const QVector<QHostAddress> addresses)
+{
+ start(QKnxPrivate::adaptersForAddresses(addresses));
+}
+
+void QKnxNetIpServerDiscoveryAgentPrivate::start(QKnxNetIpServerDiscoveryAgent::InterfaceTypes types)
+{
+ QVector<Adapter> adapters;
+ auto all = QNetworkInterface::allInterfaces();
+ for (const auto &iface : all) {
+ const auto flags = iface.flags();
+ if (!flags.testFlag(QNetworkInterface::IsUp)
+ && !flags.testFlag(QNetworkInterface::IsRunning)
+ && !flags.testFlag(QNetworkInterface::CanMulticast)) continue;
+
+ const bool wifi = types.testFlag(QKnxNetIpServerDiscoveryAgent::InterfaceType::Wifi);
+ const bool loopback = types.testFlag(QKnxNetIpServerDiscoveryAgent::InterfaceType::Loopback);
+ const bool ethernet = types.testFlag(QKnxNetIpServerDiscoveryAgent::InterfaceType::Ethernet);
+
+ const auto type = iface.type();
+ if (!(wifi && type == QNetworkInterface::Wifi)
+ && !(loopback && type == QNetworkInterface::Loopback)
+ && !(ethernet && type == QNetworkInterface::Ethernet)) continue;
+
+ const auto entries = iface.addressEntries();
+ for (const auto &entry : entries) {
+ const auto address = entry.ip();
+ if (address.protocol() != QAbstractSocket::IPv4Protocol)
+ continue;
+ adapters.append({ address, iface });
+ break;
+ }
+ }
+ start(adapters);
+}
+
void QKnxNetIpServerDiscoveryAgentPrivate::stop()
{
if (state == QKnxNetIpServerDiscoveryAgent::State::Stopping
diff --git a/src/knx/netip/qknxnetipserverdiscoveryagent_p.h b/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
index 960c214..46be5fe 100644
--- a/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
+++ b/src/knx/netip/qknxnetipserverdiscoveryagent_p.h
@@ -47,14 +47,94 @@
#include <QtKnx/qknxnetipsearchrequest.h>
#include <QtKnx/qknxnetipsearchresponse.h>
#include <QtKnx/qknxnetipserverdiscoveryagent.h>
+
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qnetworkdatagram.h>
+#include <QtNetwork/qnetworkinterface.h>
#include <QtNetwork/qudpsocket.h>
#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
+struct Adapter
+{
+ QHostAddress address;
+ QNetworkInterface iface;
+};
+
+struct DiscovererConfig
+{
+ DiscovererConfig() = default;
+ ~DiscovererConfig() = default;
+
+ DiscovererConfig(int ttl, int timeout, bool nat, bool multicast, bool coreV1, bool coreV2,
+ const QVector<QKnxNetIpSrp> &srps)
+ : Ttl(ttl)
+ , Timeout(timeout)
+ , Nat(nat)
+ , Multicast(multicast)
+ , DiscoveryCoreV1(coreV1)
+ , DiscoveryCoreV2(coreV2)
+ , ExtendedSearchParameters(srps)
+ {}
+
+ int Ttl { 64 };
+ int Timeout { 3000 };
+
+ bool Nat { false };
+ bool Multicast { true };
+
+ bool DiscoveryCoreV1 { true };
+ bool DiscoveryCoreV2 { true };
+
+ QVector<QKnxNetIpSrp> ExtendedSearchParameters;
+};
+
+class Discoverer : public QObject
+{
+ Q_OBJECT
+
+public:
+ Discoverer(const QHostAddress &address, const QNetworkInterface iface, const DiscovererConfig &config);
+ ~Discoverer() override;
+
+public slots:
+ void start();
+ void finish();
+
+signals:
+ void started();
+ void timeout();
+ void finished(Discoverer *iam);
+
+ void stateChanged(QKnxNetIpServerDiscoveryAgent::State state);
+ void errorOccurred(QKnxNetIpServerDiscoveryAgent::Error error, const QString &errorString);
+
+ void deviceDiscovered(const QKnxNetIpServerInfo &discoveryInfo);
+
+private slots:
+ void onTimeout();
+ void onReadyRead();
+ void onError(QUdpSocket::SocketError error);
+
+private:
+ void setAndEmitStateChanged(QKnxNetIpServerDiscoveryAgent::State state);
+
+private:
+ QHostAddress m_address;
+ QNetworkInterface m_iface;
+
+ QTimer *m_timer { nullptr };
+ QUdpSocket *m_socket { nullptr };
+
+ DiscovererConfig m_config;
+ QVector<QByteArray> m_devices;
+
+ QHostAddress m_multicast { QStringLiteral("224.0.23.12") };
+ QKnxNetIpServerDiscoveryAgent::State m_state { QKnxNetIpServerDiscoveryAgent::State::NotRunning };
+};
+
class Q_KNX_EXPORT QKnxNetIpServerDiscoveryAgentPrivate final : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QKnxNetIpServerDiscoveryAgent)
@@ -73,6 +153,9 @@ public:
void setAndEmitErrorOccurred(QKnxNetIpServerDiscoveryAgent::Error newError, const QString &message);
void start();
+ void start(const QVector<Adapter> &adapters);
+ void start(const QVector<QHostAddress> addresses);
+ void start(QKnxNetIpServerDiscoveryAgent::InterfaceTypes types);
void stop();
private:
@@ -102,6 +185,9 @@ private:
QKnxNetIpServerDiscoveryAgent::DiscoveryModes mode
{ QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1 };
QVector<QKnxNetIpSrp> srps;
+
+ Adapter adapter;
+ QVector<Discoverer*> discoveries;
};
QT_END_NAMESPACE
diff --git a/src/knx/netip/qknxnetipserverinfo.cpp b/src/knx/netip/qknxnetipserverinfo.cpp
index d0203ad..6df5307 100644
--- a/src/knx/netip/qknxnetipserverinfo.cpp
+++ b/src/knx/netip/qknxnetipserverinfo.cpp
@@ -98,7 +98,9 @@ QT_BEGIN_NAMESPACE
*/
QKnxNetIpServerInfo::QKnxNetIpServerInfo()
: d_ptr(new QKnxNetIpServerInfoPrivate)
-{}
+{
+ qRegisterMetaType<QKnxNetIpServerInfo>();
+}
/*!
Deletes a KNXnet/IP server information object.
diff --git a/src/knx/netip/qknxnetipserverinfo.h b/src/knx/netip/qknxnetipserverinfo.h
index da0a402..3d7ad48 100644
--- a/src/knx/netip/qknxnetipserverinfo.h
+++ b/src/knx/netip/qknxnetipserverinfo.h
@@ -47,6 +47,7 @@ struct QKnxNetIpServerInfoPrivate;
class Q_KNX_EXPORT QKnxNetIpServerInfo final
{
+ friend class Discoverer;
friend class QKnxNetIpServerDiscoveryAgentPrivate;
friend class QKnxNetIpServerDescriptionAgentPrivate;