mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-17 00:36:35 +02:00
353 lines
13 KiB
C++
353 lines
13 KiB
C++
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* *
|
|
* Copyright (C) 2019 Simon Stürz <simon.stuerz@guh.io> *
|
|
* *
|
|
* This file is part of nymea. *
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Lesser General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2.1 of the License, or (at your option) any later version. *
|
|
* *
|
|
* This library is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public *
|
|
* License along with this library; If not, see *
|
|
* <http://www.gnu.org/licenses/>. *
|
|
* *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
#include "knxtunnel.h"
|
|
|
|
#include "extern-plugininfo.h"
|
|
|
|
#include <QDateTime>
|
|
#include <QNetworkInterface>
|
|
|
|
#include <QKnxTpdu>
|
|
#include <QKnx1Bit>
|
|
#include <QKnxDatapointType>
|
|
#include <QKnxLinkLayerFrame>
|
|
#include <QKnx8BitUnsignedValue>
|
|
#include <QKnxLinkLayerFrameBuilder>
|
|
|
|
KnxTunnel::KnxTunnel(const QHostAddress &remoteAddress, QObject *parent) :
|
|
QObject(parent),
|
|
m_remoteAddress(remoteAddress)
|
|
{
|
|
m_timer = new QTimer(this);
|
|
m_timer->setSingleShot(false);
|
|
m_timer->setInterval(5000);
|
|
connect(m_timer, &QTimer::timeout, this, &KnxTunnel::onTimeout);
|
|
|
|
m_queueTimer = new QTimer(this);
|
|
m_queueTimer->setSingleShot(false);
|
|
m_queueTimer->setInterval(500);
|
|
connect(m_queueTimer, &QTimer::timeout, this, &KnxTunnel::onQueueTimeout);
|
|
|
|
|
|
m_tunnel = new QKnxNetIpTunnel(this);
|
|
m_tunnel->setLocalPort(0);
|
|
m_tunnel->setHeartbeatTimeout(1000);
|
|
|
|
connect(m_tunnel, &QKnxNetIpTunnel::frameReceived, this, &KnxTunnel::onTunnelFrameReceived);
|
|
connect(m_tunnel, &QKnxNetIpTunnel::connected, this, &KnxTunnel::onTunnelConnected);
|
|
connect(m_tunnel, &QKnxNetIpTunnel::disconnected, this, &KnxTunnel::onTunnelDisconnected);
|
|
connect(m_tunnel, &QKnxNetIpTunnel::stateChanged, this, &KnxTunnel::onTunnelStateChanged);
|
|
}
|
|
|
|
QHostAddress KnxTunnel::remoteAddress() const
|
|
{
|
|
return m_remoteAddress;
|
|
}
|
|
|
|
void KnxTunnel::setRemoteAddress(const QHostAddress &remoteAddress)
|
|
{
|
|
m_remoteAddress = remoteAddress;
|
|
}
|
|
|
|
bool KnxTunnel::connected() const
|
|
{
|
|
return m_tunnel->state() == QKnxNetIpTunnel::State::Connected;
|
|
}
|
|
|
|
bool KnxTunnel::connectTunnel()
|
|
{
|
|
// Get the local address for this tunnel remote address
|
|
QHostAddress localAddress = getLocalAddress(m_remoteAddress);
|
|
if (localAddress.isNull()) {
|
|
qCWarning(dcKnx()) << "Could connect to" << m_remoteAddress.toString() << ". There is no local interface for this server address. Make sure this device is connected to the correct network.";
|
|
return false;
|
|
}
|
|
|
|
m_tunnel->setLocalAddress(localAddress);
|
|
qCDebug(dcKnx()) << "Connecting tunnel to" << m_remoteAddress.toString() << "using" << m_tunnel->localAddress();
|
|
m_tunnel->connectToHost(m_remoteAddress, m_port);
|
|
return true;
|
|
}
|
|
|
|
void KnxTunnel::disconnectTunnel()
|
|
{
|
|
qCDebug(dcKnx()) << "Disonnecting tunnel from" << m_remoteAddress.toString();
|
|
m_tunnel->disconnectFromHost();
|
|
}
|
|
|
|
void KnxTunnel::sendKnxDpdSwitchFrame(const QKnxAddress &knxAddress, bool power)
|
|
{
|
|
qCDebug(dcKnx()) << "Send DpdSwitch" << knxAddress.toString() << (power ? "ON" : "OFF");
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
|
|
tpdu.setData(QKnxSwitch(power ? QKnxSwitch::State::On : QKnxSwitch::State::Off).bytes());
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::sendKnxDpdUpDownFrame(const QKnxAddress &knxAddress, bool status)
|
|
{
|
|
qCDebug(dcKnx()) << "Send DpdUpDown" << knxAddress.toString() << (status ? "UP" : "DOWN");
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
|
|
tpdu.setData(QKnxUpDown(status ? QKnxUpDown::State::Up : QKnxUpDown::State::Down).bytes());
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::sendKnxDpdStepFrame(const QKnxAddress &knxAddress, bool status)
|
|
{
|
|
qCDebug(dcKnx()) << "Send DpdStep" << knxAddress.toString() << (status ? "INCREASE" : "DECREASE");
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
|
|
tpdu.setData(QKnxStep(status ? QKnxStep::State::Increase : QKnxStep::State::Decrease).bytes());
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::sendKnxDpdScalingFrame(const QKnxAddress &knxAddress, int scale)
|
|
{
|
|
qCDebug(dcKnx()) << "Send DpdScaling" << knxAddress.toString() << scale;
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueWrite);
|
|
tpdu.setData(QKnxScaling(scale).bytes());
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::readKnxGroupValue(const QKnxAddress &knxAddress)
|
|
{
|
|
qCDebug(dcKnx()) << "Read knx group value from group address" << knxAddress.toString();
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::readKnxDpdSwitchState(const QKnxAddress &knxAddress)
|
|
{
|
|
qCDebug(dcKnx()) << "Read switch group value" << knxAddress.toString();
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::readKnxDpdScalingState(const QKnxAddress &knxAddress)
|
|
{
|
|
qCDebug(dcKnx()) << "Read scaling group value" << knxAddress.toString();
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::readKnxDpdTemperatureSensor(const QKnxAddress &knxAddress)
|
|
{
|
|
qCDebug(dcKnx()) << "Read temperature sensor group value" << knxAddress.toString();
|
|
|
|
QKnxTpdu tpdu;
|
|
tpdu.setTransportControlField(QKnxTpdu::TransportControlField::DataGroup);
|
|
tpdu.setApplicationControlField(QKnxTpdu::ApplicationControlField::GroupValueRead);
|
|
|
|
QKnxLinkLayerFrame frame = QKnxLinkLayerFrame::builder()
|
|
.setMessageCode(QKnxLinkLayerFrame::MessageCode::DataRequest)
|
|
.setMedium(QKnx::MediumType::NetIP)
|
|
.setDestinationAddress(knxAddress)
|
|
.setTpdu(tpdu)
|
|
.createFrame();
|
|
|
|
requestSendFrame(frame);
|
|
}
|
|
|
|
void KnxTunnel::printFrame(const QKnxLinkLayerFrame &frame)
|
|
{
|
|
qCDebug(dcKnx()) << "Frame: (" << frame.sourceAddress().toString() << "-->" << frame.destinationAddress().toString() << ")" << frame;
|
|
qCDebug(dcKnx()) << " Message code:" << frame.messageCode();
|
|
qCDebug(dcKnx()) << " MediumType" << frame.mediumType();
|
|
qCDebug(dcKnx()) << " Control field:" << frame.controlField();
|
|
qCDebug(dcKnx()) << " Extended control field:" << frame.extendedControlField();
|
|
qCDebug(dcKnx()) << " Additional infos:" << frame.additionalInfos();
|
|
qCDebug(dcKnx()) << " Bytes:" << frame.bytes().toHex().toByteArray();
|
|
qCDebug(dcKnx()) << " TPDU:" << frame.tpdu() << "Size:" << frame.tpdu().size();
|
|
qCDebug(dcKnx()) << " " << frame.tpdu().transportControlField();
|
|
qCDebug(dcKnx()) << " " << frame.tpdu().applicationControlField();
|
|
qCDebug(dcKnx()) << " " << frame.tpdu().mediumType();
|
|
qCDebug(dcKnx()) << " Sequence number:" << frame.tpdu().sequenceNumber();
|
|
qCDebug(dcKnx()) << " Data:" << frame.tpdu().data().toHex().toByteArray();
|
|
}
|
|
|
|
void KnxTunnel::requestSendFrame(const QKnxLinkLayerFrame &frame)
|
|
{
|
|
// If the timer is running, enqueue
|
|
if (m_queueTimer->isActive()) {
|
|
m_sendingQueue.enqueue(frame);
|
|
return;
|
|
}
|
|
|
|
// No queue, timer not running, lets send the message directly
|
|
sendFrame(frame);
|
|
m_queueTimer->start();
|
|
}
|
|
|
|
void KnxTunnel::sendFrame(const QKnxLinkLayerFrame &frame)
|
|
{
|
|
if (!connected()) {
|
|
qCWarning(dcKnx()) << "Cannot send frame on tunnel" << m_remoteAddress.toString() << " because the tunnel is not connected.";
|
|
return;
|
|
}
|
|
|
|
qCDebug(dcKnx()) << QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss") << "--> Sending frame" << frame << frame.destinationAddress().toString() << frame.tpdu().data().toHex().toByteArray();
|
|
//printFrame(frame);
|
|
m_tunnel->sendFrame(frame);
|
|
}
|
|
|
|
QHostAddress KnxTunnel::getLocalAddress(const QHostAddress &remoteAddress)
|
|
{
|
|
QHostAddress localAddress;
|
|
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
|
|
qCDebug(dcKnx()) << "Network interface" << interface.name() << interface.type();
|
|
foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) {
|
|
if (addressEntry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
|
|
qCDebug(dcKnx()) << " - " << addressEntry.ip().toString() << addressEntry.netmask().toString();
|
|
if (remoteAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) {
|
|
qCDebug(dcKnx()) << "Found local interface address for" << remoteAddress.toString() << "-->" << addressEntry.ip().toString() << interface.name();
|
|
localAddress = addressEntry.ip();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return localAddress;
|
|
}
|
|
|
|
void KnxTunnel::onTimeout()
|
|
{
|
|
qCDebug(dcKnx()) << "Tunnel reconnection timeout.";
|
|
connectTunnel();
|
|
}
|
|
|
|
void KnxTunnel::onQueueTimeout()
|
|
{
|
|
// If queue is empty, we are don for now.
|
|
if (m_sendingQueue.isEmpty()) {
|
|
m_queueTimer->stop();
|
|
return;
|
|
}
|
|
|
|
// Send next message
|
|
QKnxLinkLayerFrame frame = m_sendingQueue.dequeue();
|
|
sendFrame(frame);
|
|
m_queueTimer->start();
|
|
}
|
|
|
|
void KnxTunnel::onTunnelConnected()
|
|
{
|
|
qCDebug(dcKnx()) << "Tunnel connected.";
|
|
emit connectedChanged();
|
|
|
|
// Stop the reconnect timer
|
|
m_timer->stop();
|
|
}
|
|
|
|
void KnxTunnel::onTunnelDisconnected()
|
|
{
|
|
qCDebug(dcKnx()) << "Tunnel disconnected.";
|
|
emit connectedChanged();
|
|
|
|
// Start the reconnect timer
|
|
m_timer->start();
|
|
}
|
|
|
|
void KnxTunnel::onTunnelStateChanged(QKnxNetIpEndpointConnection::State state)
|
|
{
|
|
qCDebug(dcKnx()) << "Tunnel state changed" << state;
|
|
}
|
|
|
|
void KnxTunnel::onTunnelFrameReceived(const QKnxLinkLayerFrame &frame)
|
|
{
|
|
qCDebug(dcKnx()) << "<-- Tunnel frame received" << frame;
|
|
//printFrame(frame);
|
|
emit frameReceived(frame);
|
|
}
|
|
|