/****************************************************************************
**
** Copyright (C) 2018 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/qcoreapplication.h>#include <QtCore/qcommandlineparser.h>#include <QtCore/qdebug.h>#include <QtCore/qdatetime.h>#include <QtKnx/qknxnetiprouter.h>#include <QtKnx/qknxlinklayerframebuilder.h>#include <QtKnx/qknxnetiprouter.h>#include <QtKnx/qknxnetiproutingbusy.h>#include <QtKnx/qknxnetiproutingindication.h>#include <QtKnx/qknxnetiproutinglostmessage.h>#include <QtNetwork/qhostaddress.h>#include <QtNetwork/qnetworkinterface.h>#include <QtNetwork/qnetworkdatagram.h>#include <QtNetwork/qudpsocket.h>#ifdef Q_OS_WIN# include <QtCore/QWinEventNotifier># include <windows.h>#else# include <QtCore/QSocketNotifier># include <unistd.h>#endifvoid startRouter(QKnxNetIpRouter&router,constQCommandLineParser&cliParser)
{
router.start();
if (router.error() !=QKnxNetIpRouter::Error::None)
return;
qInfo().noquote() <<"Network interface used: "<< router.interfaceAffinity().name() <<"("<< router.interfaceAffinity().addressEntries().first().ip()
<<")";
qInfo().noquote() <<"Multicast address: "<< router.multicastAddress();
if (cliParser.isSet("receiver")) {
qInfo().noquote() <<"Working as a receiver of KNX Routing messages.";
return;
}
qInfo().noquote() <<"Type 'quit' to stop the application.";
if (cliParser.isSet("indication")) {
qInfo().noquote() <<"Type the hexadecimal content of the indication ""and hit enter. Setting no content by default ""uses 1100b4e000000002010000.";
} else {
qInfo().noquote() <<"Hit enter for sending the message";
}
}
void setupRouterCLI(QKnxNetIpRouter&router,constQCommandLineParser&cliParser,QTextStream& input,#ifdef Q_OS_WINQWinEventNotifier¬ifier)
{
QObject::connect(¬ifier,&QWinEventNotifier::activated,[&](HANDLE) {
#elseQSocketNotifier¬ifier)
{
QObject::connect(¬ifier,&QSocketNotifier::activated,[&](int) {
#endifauto tmp = input.readLine().toLatin1();
if (tmp =="quit") {
router.stop();
return;
}
if (cliParser.isSet("indication")) {
staticconstauto 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);
} elseif (cliParser.isSet("busy")) {
auto routingBusyFrame =QKnxNetIpRoutingBusyProxy::builder()
.setDeviceState(QKnxNetIp::DeviceState::IpFault)
.setRoutingBusyWaitTime(cliParser.value("BusyWaitTime").toUInt())
.setRoutingBusyControl(cliParser.value("BusyControlField").toUInt())
.create();
router.sendRoutingBusy(routingBusyFrame);
} elseif (cliParser.isSet("lost")) {
auto routingLostFrame =QKnxNetIpRoutingLostMessageProxy::builder()
.setDeviceState(QKnxNetIp::DeviceState::IpFault)
.setLostMessageCount(0xffff)
.create();
router.sendRoutingLostMessage(routingLostFrame);
}
});
}
void setupRouterSignalHandlers(QKnxNetIpRouter&router,constQNetworkInterface&iface,constQHostAddress&multicastAddress)
{
router.setInterfaceAffinity(iface);
Q_ASSERT(router.interfaceAffinity().index() == iface.index());
router.setMulticastAddress(multicastAddress);
QObject::connect(&router,&QKnxNetIpRouter::stateChanged,[&](QKnxNetIpRouter::State state) {
if (state ==QKnxNetIpRouter::State::Routing)
qInfo().noquote() <<QTime::currentTime().toString()
<<": Router is in Routing state.";
elseif (state ==QKnxNetIpRouter::State::Failure)
qInfo().noquote() <<QTime::currentTime().toString()
<<": Error: "<< router.errorString();
elseif (state ==QKnxNetIpRouter::State::NeighborBusy)
qInfo().noquote() <<QTime::currentTime().toString()
<<": Neighbor router is busy. Busy timer started";
elseif (state ==QKnxNetIpRouter::State::Stop)
QCoreApplication::quit();
});
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);
});
}
int main(int argc,char*argv[])
{
QCoreApplication a(argc, argv);
QCommandLineParser cliParser;
cliParser.addHelpOption();
cliParser.addOptions({
{ { "n","interface" },"Set the network interface name that ""the KNXnet/Ip routing interface will use.","InterfaceName","eth0" },
{ { "m","multicastAddress" },"Sets the multicast address that ""the KNXnet/Ip routing interface will use for sending routing messages.","multicastAddress","224.0.23.12" },
{ { "i","indication" },"Sends routing indication messages" },
{ { "b","busy" },"Sends routing busy messages" },
{ "busyControlField","Sets the busy control field of busy messages.","busyControlField","0" },
{ "busyWaitTime","Sets the wait time for busy messages.","busyWaitTime","10000" },
{ { "l","lost" },"Sends routing lost messages" },
{ { "r","receiver" },"Only work as a KNXnet/IP routing message receiver" },
});
cliParser.process(a);
QNetworkInterface iface { QNetworkInterface::interfaceFromName(cliParser.value("interface")) };
if (!iface.isValid()) {
auto interfaces = iface.allInterfaces();
QList<QNetworkInterface>::iterator found = std::find_if(
interfaces.begin(), interfaces.end(),[](constQNetworkInterface&i) {
return i.type() ==QNetworkInterface::Ethernet;
}
);
if (found == iface.allInterfaces().end()) {
qInfo().noquote() <<"No valid network interface given and no Ethernet adapter found.";
cliParser.showHelp();
}
iface =static_cast<QNetworkInterface>(*found);
}
QHostAddress multicastAddress =QHostAddress(cliParser.value("multicastAddress"));
if (!multicastAddress.isMulticast()) {
qInfo().noquote() <<"Multicast address invalid.";
cliParser.showHelp();
}
if (!cliParser.isSet("indication")
&&!cliParser.isSet("busy")
&&!cliParser.isSet("lost")
&&!cliParser.isSet("receiver")) {
qInfo().noquote() <<"Missing parameter that specifies the types of ""messages to send.";
cliParser.showHelp();
}
QKnxNetIpRouter router;
setupRouterSignalHandlers(router, iface, multicastAddress);
QTextStream input(stdin,QIODevice::ReadOnly);
#ifdef Q_OS_WINQWinEventNotifier notifier(GetStdHandle(STD_INPUT_HANDLE));
#elseQSocketNotifier notifier(STDIN_FILENO,QSocketNotifier::Read);
#endifif (!cliParser.isSet("receiver")) {
setupRouterCLI(router, cliParser, input, notifier);
}
startRouter(router, cliParser);
return a.exec();
}