Discoverer Example

 /****************************************************************************
 **
 ** Copyright (C) 2017 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the examples of the Qt Knx module.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** 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.
 **
 ** BSD License Usage
 ** Alternatively, you may use this file under the terms of the BSD license
 ** as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of The Qt Company Ltd nor the names of its
 **     contributors may be used to endorse or promote products derived
 **     from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include <QtCore/QDebug>
 #include <QtCore/QCommandLineParser>
 #include <QtCore/QCoreApplication>

 #include <QtKnx/QKnxNetIpExtendedDeviceDibProxy>
 #include <QtKnx/QKnxNetIpServerDiscoveryAgent>
 #include <QtKnx/QKnxNetIpSrpBuilder>

 static QString familyToString(QKnxNetIp::ServiceFamily id)
 {
     switch (id) {
         case QKnxNetIp::ServiceFamily::Core:
             return "Core";
         case QKnxNetIp::ServiceFamily::DeviceManagement:
             return "Device Management";
         case QKnxNetIp::ServiceFamily::IpTunneling:
             return "Tunnel";
         case QKnxNetIp::ServiceFamily::IpRouting:
             return "Routing";
         case QKnxNetIp::ServiceFamily::RemoteLogging:
             return "Remote Logging";
         case QKnxNetIp::ServiceFamily::RemoteConfigAndDiagnosis:
             return "Remote Configuration";
         case QKnxNetIp::ServiceFamily::ObjectServer:
             return "Object Server";
         case QKnxNetIp::ServiceFamily::Security:
             return "Security";
         default:
             break;
     }
     return "Unknown";
 }

 int main(int argc, char *argv[])
 {
     QCoreApplication discoverer(argc, argv);
     QCoreApplication::setApplicationVersion("1.0");

     QCommandLineParser parser;
     parser.addHelpOption();

     parser.addOptions({
         { { "t", "timeout" }, "Discovery response timeout in seconds.", "timeout", "3" },
         { { "n", "nat" }, "Use Network Address Translation to traverse across network routers." },
         { { "u", "unicast" }, "Force unicast response. (defaults to multicast response)" },
         { { "a", "localAddress" }, "The local IP address a response shall be sent to. "
                 "Implies <unicast>", "localAddress", "127.0.0.1" },
         { { "p", "localPort" }, "The local UDP port a response shall be sent to (defaults to "
                 "system assigned). Implies <unicast>.", "localPort", "0" },
         { { "m", "searchMode" }, "Specifies the mode used to send search request. Possible "
                 "values: (default, extended, both).", "searchMode", "default" },
         { "filterProg", "Limit search responses to devices in programming mode. Implies "
                 "search mode extended or both." },
         { "filterMAC", "Limit search responses to the given MAC address. Implies search "
                 "mode extended or both.", "MAC" },
         { "filterService", "Limit search responses to devices supporting the given "
                 "KNXnet/IP service family in at least the given version (e.g. 0202). Implies "
                 "search mode extended or both.", "Service" },
         { "descriptionType", "Force returning DIBs inside the search responses to "
                 "to at least of the given set of IDs. (e.g. 010208). Implies search mode "
                 "extended or both.", "Type" }
     });
     parser.process(discoverer);

     QKnxNetIpServerDiscoveryAgent agent;
     agent.setNatAware(parser.isSet("nat"));
     agent.setLocalPort(parser.value("localPort").toUInt());
     agent.setTimeout(parser.value("timeout").toInt() * 1000);

     if (parser.isSet("localAddress"))
         agent.setLocalAddress(QHostAddress(parser.value("localAddress")));

     if (parser.isSet("unicast"))
         agent.setResponseType(QKnxNetIpServerDiscoveryAgent::ResponseType::Unicast);

     auto value = parser.value("searchMode");
     if (parser.isSet("searchMode") && value != "default") {
         if (value == "extended") {
             agent.setDiscoveryMode(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);
         } else if (value == "both") {
             agent.setDiscoveryMode(QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV1
                 | QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);
         } else {
             qInfo().noquote() << "Wrong argument for option <searchMode>:" << value;
             return EXIT_FAILURE;
         }
     }

     if (parser.isSet("filterProg")) {
         agent.setDiscoveryMode(agent.discoveryMode()
             | QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);
         agent.setExtendedSearchParameters({ QKnxNetIpSrpProxy::programmingModeBuilder().create() });
     }

     if (parser.isSet("filterMAC")) {
         agent.setDiscoveryMode(agent.discoveryMode()
             | QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);
         agent.setExtendedSearchParameters(QVector<QKnxNetIpSrp>({
             QKnxNetIpSrpProxy::macAddressBuilder()
                 .setMac(QKnxByteArray::fromHex(parser.value("filterMAC").toLatin1()))
                 .create()
         }) + agent.extendedSearchParameters());
     }

     if (parser.isSet("filterService")) {
         auto value = parser.value("filterService");
         if (value.size() == 4) {
             bool okLeft = false, okRight = false;
             quint8 left = value.left(2).toUShort(&okLeft, 16);
             quint8 right = value.mid(2).toUShort(&okRight, 16);
             if (!okLeft || !okRight) return EXIT_FAILURE;

             agent.setDiscoveryMode(agent.discoveryMode()
                 | QKnxNetIpServerDiscoveryAgent::DiscoveryMode::CoreV2);

             agent.setExtendedSearchParameters(QVector<QKnxNetIpSrp>({
                 QKnxNetIpSrpProxy::supportedFamilyBuilder()
                     .setServiceInfo({ QKnxNetIp::ServiceFamily(left), right })
                     .create()
             }) + agent.extendedSearchParameters());
         }
     }

     QObject::connect(&agent, &QKnxNetIpServerDiscoveryAgent::finished, &discoverer,
         &QCoreApplication::quit);

     if (!parser.isSet("localAddress")) {
         agent.start(QKnxNetIpServerDiscoveryAgent::InterfaceType::Ethernet
             | QKnxNetIpServerDiscoveryAgent::InterfaceType::Wifi);
     } else {
         agent.start(QVector<QHostAddress> { agent.localAddress() });
     }

     if (agent.error() == QKnxNetIpServerDiscoveryAgent::Error::None
         && agent.state() == QKnxNetIpServerDiscoveryAgent::State::Running) {
             discoverer.exec();
     }

     const auto servers = agent.discoveredServers();
     if (servers.size() > 0) {
         qInfo().noquote() << Qt::endl << QString::fromLatin1("%1 server(s) found on the network.")
             .arg(servers.size());
         for (auto server : servers) {
             qInfo().noquote() << QString::fromLatin1("  Network interface: %1, address: %2")
                 .arg(server.networkInterface().humanReadableName(), server.hostAddress().toString());
             qInfo().noquote() << QString::fromLatin1("  Server: %1").arg(server.deviceName());
             qInfo().noquote() << QString::fromLatin1("      Individual address: %1").arg(server
                 .individualAddress().toString());
             qInfo().noquote() << QString::fromLatin1("      Server control endpoint: %1:%2")
                 .arg(server.controlEndpointAddress().toString()).arg(server.controlEndpointPort());

             const auto services = server.supportedServices();
             qInfo().noquote() << QString::fromLatin1("    Supported services:");
             for (const auto service : services) {
                 qInfo().noquote() << QString::fromLatin1("      KNXnet/IP %1, Version: %2")
                     .arg(familyToString(service.ServiceFamily)).arg(service.ServiceFamilyVersion);
             }

             const auto dib = server.extendedHardware();
             QKnxNetIpExtendedDeviceDibProxy hardware(dib);
             if (hardware.isValid()) {
                 qInfo() << "    Extended hardware information:";
                 qInfo().noquote() << QString::fromLatin1("      Mask version: %1").arg(hardware
                     .deviceDescriptorType0(), 4, 16, QLatin1Char('0'));
                 qInfo().noquote() << QString::fromLatin1("      Max. local APDU length: %1")
                     .arg(hardware.maximumLocalApduLength());

                 auto status = server.mediumStatus();
                 if (status == QKnx::MediumStatus::CommunicationPossible)
                     qInfo() << "      Medium status: Communication possible";
                 else if (status == QKnx::MediumStatus::CommunicationImpossible)
                     qInfo() << "      Medium status: Communication impossible";
                 else
                     qInfo() << "      Medium status: Unknown";
             }
             qInfo() << "";
         }
     } else {
         qInfo().noquote() << "No server(s) found on the network.";
     }

     return 0;
 }