Router Example

A CLI client using a QKnxNetIpRouter.

This example shows a CLI client using a QKnxNetIpRouter to monitor KNX frames sent by neighboring KNXnet/IP routers. It is a CLI interactive client that allows the user to send a predifined KNX frame on the default 224.0.23.12 multicast address that is the most commonly used in KNX installations.

Usage

Here are the parameters that the client allows. For example, the network interface used by the router and the multicast address used can be changed to the user's needs.

Options:
  -h, --help                                 Displays this help.
  -n, --interface <InterfaceName>            Sets the network interface name
                                             that the KNXnet/Ip routing
                                             interface will use.
  -m, --multicastAddress <multicastAddress>  Sets the multicast address that
                                             the KNXnet/Ip routing interface
                                             will use for sending routing
                                             messages.
  -i, --indication                           Sends routing indication messages.
  -b, --busy                                 Sends routing busy messages.
  --busyControlField <busyControlField>      Sets the busy control field of
                                             busy messages.
  --busyWaitTime <busyWaitTime>              Sets the wait time for busy
                                             messages.
  -l, --lost                                 Sends routing lost messages.
  -r, --receiver                             Determines that this router will only
                                             work as a KNXnet/IP routing message
                                             receiver.

There are two main modes of using the example. One of the modes is using it as a receiver of KNX frames. In this mode no other messages can be sent. This mode is used to monitor KNXnet/IP frames and it can be achieved by running the example like this:

router -n eth0 --receiver

In the other mode, in addition to receiving KNX frames the router can send a unique type of KNX frames that has been predefined. The type of frame to use can be specified as a parameter in the command line.

For sending indication frames and receiving any other KNX frame the example can be run like this:

router -n eth0 --indication

In this last command the router will send an indication KNX frame every time the user hits the Enter. The router will also display information about KNX frames received on that network interface.

For sending busy frames and routing lost messages, the example has to be run in one of the following ways:

router -n eth0 --busy --busyWaitTime 30
router -n eth0 --lost

As explained above, it will send the predifined messages every time the user hits the Enter. However, the message sent can be customized by pasting the frame representation as a hexadecimal stream of bytes and hitting Enter.

Here is an example of how the example looks like when running it:

router -n enp0s31f6 -i

11:40:26 : Router is in Routing state.
Network interface used:  enp0s31f6 ( QHostAddress("10.9.78.59") )
Multicast address:  QHostAddress("224.0.23.12")
Type 'quit' to stop the application.
Type the hexadecimal content of the indication and hit 'enter'. Setting no content by default uses 1100b4e000000002010000.

Implementation

The class QKnxNetIpRouter used in this example allows sending and receiving routing KNXnet/IP packets to and from other KNXnet/IP routers. There are some helper functions that will be explained in the following lines.

The main function starts by instantiating the QKnxNetIpRouter class. After that, in order to have a functional router, certain properties of the QKnxNetIpRouter instance must be set up. This is done by the method setupRouterSignalHandlers(). The following code snippet shows where this happens:

int main(int argc, char *argv[])
{
    ...
    QKnxNetIpRouter router;
    setupRouterSignalHandlers(router, iface, multicastAddress);

The most important step to get a functional router is to assign it a network interface. That way, the router is ready for monitoring and sending KNX frames from/to other KNX routers and the KNX system connected to that same network.

void setupRouterSignalHandlers(QKnxNetIpRouter &router, const QNetworkInterface &iface,
                 const QHostAddress &multicastAddress)
{
    router.setInterfaceAffinity(iface);
    Q_ASSERT(router.interfaceAffinity().index() == iface.index());
    router.setMulticastAddress(multicastAddress);

The next step consists of setting the handlers that allow monitoring KNXnet/IP frames received from neighboring KNX routers as well as the frames sent by this router.

void setupRouterSignalHandlers(QKnxNetIpRouter &router, const QNetworkInterface &iface,
                 const QHostAddress &multicastAddress)
{
    ...
    QObject::connect(&router, &QKnxNetIpRouter::routingIndicationReceived,
                     [](QKnxNetIpFrame frame) {
        QKnxNetIpRoutingIndicationProxy indication(frame);
        qInfo().noquote() << "Received routing indication"
                          << indication.isValid();
    });

    QObject::connect(&router, &QKnxNetIpRouter::routingBusyReceived,
                     [](QKnxNetIpFrame frame) {
        QKnxNetIpRoutingBusyProxy busy(frame);
        qInfo().noquote() << "Received routing busy, wait time: "
                          << busy.routingBusyWaitTime() << " busy control field: "
                          << busy.routingBusyControl();
    });

    QObject::connect(&router, &QKnxNetIpRouter::routingLostCountReceived,
                     [](QKnxNetIpFrame frame) {
        QKnxNetIpRoutingLostMessageProxy lost(frame);
        qInfo().noquote() << "Received routing lost count"
                          << lost.deviceState();
    });

    QObject::connect(&router, &QKnxNetIpRouter::routingIndicationSent,
                     [](QKnxNetIpFrame frame) {
        qInfo().noquote() << "Sent routing indication";
        Q_UNUSED(frame);
    });

    QObject::connect(&router, &QKnxNetIpRouter::routingBusySent,
                     [](QKnxNetIpFrame frame) {
        qInfo().noquote() << "Sent routing busy";
        Q_UNUSED(frame);
    });

    QObject::connect(&router, &QKnxNetIpRouter::routingLostCountSent,
                     [](QKnxNetIpFrame frame) {
        qInfo().noquote() << "Sent routing lost count";
        Q_UNUSED(frame);
    });
}

The client can respond to the Enter press events and send predefined KNX messages. For this purpose a QTextStream instance is used and a handler installed for capturing the signal activated() emitted by the socket. The code below demonstrates this:

void setupRouterCLI(QKnxNetIpRouter &router,
                    const QCommandLineParser &cliParser,
                    QTextStream & input,
#ifdef Q_OS_WIN
                    QWinEventNotifier &notifier)
{
    QObject::connect(&notifier, &QWinEventNotifier::activated, [&](HANDLE) {
#else
                    QSocketNotifier &notifier)
{
    QObject::connect(&notifier, &QSocketNotifier::activated, [&](int) {
#endif
    ...
            static const auto bytes = QKnxByteArray::fromHex("1100b4e000000002010000");
            auto frame = QKnxLinkLayerFrame::builder()
                .setData(tmp.isEmpty() ? bytes : QKnxByteArray::fromHex(tmp))
                .setMedium(QKnx::MediumType::NetIP)
                .createFrame();
            auto indication = QKnxNetIpRoutingIndicationProxy::builder()
                              .setCemi(frame)
                              .create();
            router.sendRoutingIndication(indication);
    ...
}

The last thing before calling QCoreApplication::exec() is the QKnxNetIpRouter::start() call. The call notifies the router to initialize its internal data and enables it to begin receiving and sending KNX frames, as seen here:

void startRouter(QKnxNetIpRouter &router, const QCommandLineParser &cliParser)
{
    router.start();

    if (router.error() != QKnxNetIpRouter::Error::None)
        return;
    ...
}

The startRouter() is a helper function that gets called at the end of main.

Files:

© 2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.