abstract from knxd

This commit is contained in:
John
2022-09-03 11:22:59 +02:00
parent 1418396971
commit c3dc90914c
14 changed files with 407 additions and 81 deletions
+18 -7
View File
@@ -103,14 +103,24 @@ if(HAVE_MQTT)
endif(mqtt STREQUAL ON)
endif(HAVE_MQTT)
find_library(HAVE_KNX eibclient)
option(knx "disable support for KNX handling." ON)
if(knx STREQUAL ON)
message(STATUS "KNX enabled")
set(HAVE_KNX 1)
else(knx STREQUAL ON)
unset(HAVE_KNX)
endif(knx STREQUAL ON)
if(HAVE_KNX)
option(knx "disable support for KNX handling." ON)
if(knx STREQUAL ON)
message(STATUS "KNX enabled")
else(knx STREQUAL ON)
unset(HAVE_KNX)
endif(knx STREQUAL ON)
find_library(HAVE_KNXD eibclient)
if(HAVE_KNXD)
option(knxd "enable support for KNX handling via knxd." OFF)
if(knxd STREQUAL ON)
message(STATUS "KNX via knxd enabled")
else(knxd STREQUAL ON)
set(HAVE_KNXD 0)
endif(knxd STREQUAL ON)
endif(HAVE_KNXD)
endif(HAVE_KNX)
find_library(HAVE_SSL ssl)
@@ -169,6 +179,7 @@ endif(BUILD_TESTING)
add_subdirectory(src/ebusd)
add_subdirectory(src/lib/utils)
add_subdirectory(src/lib/ebus)
add_subdirectory(src/lib/knx)
add_subdirectory(src/tools)
if(EXISTS "${ROOT}/etc/debian_version")
+7 -1
View File
@@ -2,7 +2,13 @@ ACLOCAL_AMFLAGS = -I m4
SUBDIRS = docs \
src/lib/utils \
src/lib/ebus \
src/lib/ebus
if KNX
SUBDIRS += src/lib/knx
endif
SUBDIRS += \
src/ebusd \
src/tools
+4 -1
View File
@@ -7,9 +7,12 @@
/* Defined if MQTT handling is enabled. */
#cmakedefine HAVE_MQTT
/* Defined if KNX is enabled. */
/* Defined if KNX handling is enabled. */
#cmakedefine HAVE_KNX
/* Defined if KNX handling via knxd is enabled. */
#cmakedefine HAVE_KNXD
/* Defined if SSL is enabled. */
#cmakedefine HAVE_SSL
+13 -5
View File
@@ -65,13 +65,18 @@ AM_CONDITIONAL([MQTT], [test "x$with_mqtt" != "xno"])
AC_ARG_WITH(knx, AS_HELP_STRING([--without-knx], [disable support for KNX handling]), [], [with_knx=yes])
if test "x$with_knx" != "xno"; then
AC_CHECK_LIB([eibclient], [EIBSocketURL],
[AC_DEFINE_UNQUOTED(HAVE_KNX, [1], [Defined if KNX handling is enabled.])
EXTRA_LIBS+=" -leibclient"],
[AC_MSG_RESULT([Could not find EIBSocketURL in libeibclient.])
with_knx="no"])
AC_DEFINE_UNQUOTED(HAVE_KNX, [1], [Defined if KNX handling is enabled.])
AC_ARG_WITH(knxd, AS_HELP_STRING([--with-knxd], [enable support for KNX handling via knxd]), [
AS_IF([test "x$with_knxd" == "xyes"],
[AC_CHECK_LIB([eibclient], [EIBSocketURL],
[AC_DEFINE_UNQUOTED(HAVE_KNXD, [1], [Defined if KNX handling via knxd is enabled.])],
[AC_MSG_RESULT([Could not find EIBSocketURL in libeibclient.])
with_knxd="no"])
])
], [])
fi
AM_CONDITIONAL([KNX], [test "x$with_knx" != "xno"])
AM_CONDITIONAL([KNXD], [test "x$with_knxd" == "xyes"])
AC_ARG_WITH(ssl, AS_HELP_STRING([--without-ssl], [disable support for SSL]), [], [with_ssl=yes])
if test "x$with_ssl" != "xno"; then
@@ -141,6 +146,9 @@ AM_COND_IF([CONTRIB], [AC_CONFIG_FILES([
src/lib/ebus/contrib/Makefile
src/lib/ebus/contrib/test/Makefile
])])
AM_COND_IF([KNX], [AC_CONFIG_FILES([
src/lib/knx/Makefile
])])
AC_DEFINE_UNQUOTED(PACKAGE_PIDFILE, LOCALSTATEDIR "/run/" PACKAGE ".pid", [The path and name of the PID file.])
AC_DEFINE_UNQUOTED(PACKAGE_LOGFILE, LOCALSTATEDIR "/log/" PACKAGE ".log", [The path and name of the log file.])
+2 -1
View File
@@ -15,7 +15,8 @@ endif(HAVE_MQTT)
if(HAVE_KNX)
set(ebusd_SOURCES ${ebusd_SOURCES} knxhandler.cpp knxhandler.h)
set(ebusd_LIBS ${ebusd_LIBS} eibclient)
set(ebusd_LIBS ${ebusd_LIBS} knx)
include_directories(../lib/knx)
endif(HAVE_KNX)
if(HAVE_SSL)
+5 -3
View File
@@ -16,15 +16,17 @@ ebusd_SOURCES = \
if MQTT
ebusd_SOURCES += mqtthandler.cpp mqtthandler.h
endif
if KNX
ebusd_SOURCES += knxhandler.cpp knxhandler.h
endif
ebusd_LDADD = ../lib/utils/libutils.a \
../lib/ebus/libebus.a \
-lpthread \
@EXTRA_LIBS@
if KNX
ebusd_SOURCES += knxhandler.cpp knxhandler.h
ebusd_LDADD += ../lib/knx/libknx.a
endif
if SSL
ebusd_LDADD += -lssl -lcrypto
endif
+48 -50
View File
@@ -184,7 +184,7 @@ KnxHandler::KnxHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* m
logOtherError("knx", "invalid assignment %s to %s", key.c_str(), val.c_str());
continue;
}
auto dest = static_cast<eibaddr_t>(v << 11);
auto dest = static_cast<knx_addr_t>(v << 11);
if (pos2 == string::npos) {
// 2 level
v = parseInt(val.substr(pos+1).c_str(), 10, 0, 0x7ff, &res);
@@ -192,7 +192,7 @@ KnxHandler::KnxHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* m
logOtherError("knx", "invalid 2-level assignment %s to %s", key.c_str(), val.c_str());
continue;
}
dest |= static_cast<eibaddr_t>(v);
dest |= static_cast<knx_addr_t>(v);
} else {
// 3 level
v = parseInt(val.substr(pos+1, pos2).c_str(), 10, 0, 0x07, &res);
@@ -200,13 +200,13 @@ KnxHandler::KnxHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* m
logOtherError("knx", "invalid 3-level assignment %s to %s", key.c_str(), val.c_str());
continue;
}
dest |= static_cast<eibaddr_t>(v << 8);
dest |= static_cast<knx_addr_t>(v << 8);
v = parseInt(val.substr(pos+1, pos2).c_str(), 10, 0, 0xff, &res);
if (res != RESULT_OK) {
logOtherError("knx", "invalid 3-level assignment %s to %s", key.c_str(), val.c_str());
continue;
}
dest |= static_cast<eibaddr_t>(v);
dest |= static_cast<knx_addr_t>(v);
}
if (key.substr(0, 7) != "global/") {
messageCnt++;
@@ -248,7 +248,7 @@ KnxHandler::KnxHandler(UserInfo* userInfo, BusHandler* busHandler, MessageMap* m
KnxHandler::~KnxHandler() {
join();
if (m_con) {
EIBClose(m_con);
delete m_con;
m_con = nullptr;
}
}
@@ -348,7 +348,10 @@ float int16ToFloat(uint16_t val) {
return static_cast<float>(sig * exp2(exp) * (negative ? -0.01 : 0.01));
}
result_t KnxHandler::sendGroupValue(eibaddr_t dest, apci_t apci, dtlf_t& lengthFlag, unsigned int value, const SingleDataField *field) const {
result_t KnxHandler::sendGroupValue(knx_addr_t dest, apci_t apci, dtlf_t& lengthFlag, unsigned int value, const SingleDataField *field) const {
if (!m_con || !m_con->isConnected() || !m_con->getAddress()) {
return RESULT_EMPTY;
}
uint8_t data[] = {0, 0, 0, 0, 0, 0};
data[0] = static_cast<uint8_t>(apci>>8);
data[1] = static_cast<uint8_t>(apci&0xff);
@@ -408,7 +411,8 @@ result_t KnxHandler::sendGroupValue(eibaddr_t dest, apci_t apci, dtlf_t& lengthF
return RESULT_ERR_INVALID_NUM;
}
len += lengthFlag.length;
if (EIBSendGroup(m_con, dest, len, data) < 0) {
const char* err = m_con->sendGroup(dest, len, data);
if (err) {
logOtherError("knx", "unable to send %s, dest %4.4x, len %d",
apci==APCI_GROUPVALUE_WRITE ? "write" : apci==APCI_GROUPVALUE_READ ? "read" : "response",
dest, len);
@@ -421,7 +425,7 @@ result_t KnxHandler::sendGroupValue(eibaddr_t dest, apci_t apci, dtlf_t& lengthF
}
void KnxHandler::sendGlobalValue(global_t index, unsigned int value, bool response) {
if (!m_con) {
if (!m_con || !m_con->isConnected() || !m_con->getAddress()) {
return;
}
const auto vit = m_subscribedGlobals.find(index);
@@ -432,18 +436,18 @@ void KnxHandler::sendGlobalValue(global_t index, unsigned int value, bool respon
if (git == m_subscribedGroups.end()) {
return;
}
sendGroupValue(static_cast<eibaddr_t>(vit->second&0xffff),
sendGroupValue(static_cast<knx_addr_t>(vit->second&0xffff),
response ? APCI_GROUPVALUE_RESPONSE : APCI_GROUPVALUE_WRITE,
git->second.lengthFlag, value);
}
result_t KnxHandler::receiveTelegram(int maxlen, uint8_t *buf, int *recvlen,
eibaddr_t *src, eibaddr_t *dest) {
result_t KnxHandler::receiveTelegram(int maxlen, knx_transfer_t* typ, uint8_t *buf, int *recvlen,
knx_addr_t *src, knx_addr_t *dest) {
struct timespec tdiff = {
.tv_sec = 2,
.tv_nsec = 0,
};
int fd = EIB_Poll_FD(m_con);
int fd = m_con->getPollFd();
#ifdef HAVE_PPOLL
nfds_t nfds = 1;
struct pollfd fds[nfds];
@@ -482,28 +486,17 @@ result_t KnxHandler::receiveTelegram(int maxlen, uint8_t *buf, int *recvlen,
newData = FD_ISSET(fd, &readfds);
#endif
#endif
int len = EIB_Poll_Complete(m_con);
if (len == -1) {
// read failed
return RESULT_ERR_GENERIC_IO;
}
if (!newData) {
// timeout
return RESULT_ERR_TIMEOUT;
}
len = EIBGetGroup_Src(m_con, maxlen, buf, src, dest);
if (len < 0) {
return RESULT_ERR_GENERIC_IO;
}
if (len < 2) {
return RESULT_ERR_GENERIC_IO;
}
*recvlen = len;
return RESULT_OK;
*typ = m_con->getPollData(maxlen, buf, recvlen, src, dest);
return *typ == KNX_TRANSFER_NONE ? RESULT_EMPTY : RESULT_OK;
}
/*
void printResponse(eibaddr_t src, eibaddr_t dest, int len, const uint8_t *data) {
void printResponse(knx_addr_t src, knx_addr_t dest, int len, const uint8_t *data) {
int tctrl = data[0]>>2;
int apci = ((data[0]&0x03)<<8) | data[1];
if ((apci & APCI_GROUPVALUE_READ_MASK) == 0) {
apci &= ~APCI_GROUPVALUE_READ_MASK;
@@ -518,17 +511,22 @@ void printResponse(eibaddr_t src, eibaddr_t dest, int len, const uint8_t *data)
if (len>5) {
value = (value<<8) | data[5]; // up to 32 bits
}
logOtherDebug("knx", "recv from %4.4x to %4.4x, %s (%d), len %d", src, dest,
logOtherDebug("knx", "recv from %4.4x to %4.4x, %s (0x%3.3x, tctrl 0x%2.2x), len %d", src, dest,
apci==APCI_GROUPVALUE_WRITE ? "write" : apci==APCI_GROUPVALUE_READ ? "read"
: apci==APCI_GROUPVALUE_RESPONSE ? "response" : "other",
apci, len);
apci, tctrl, len);
}
*/
*/
void KnxHandler::handleReceivedTelegram(eibaddr_t src, eibaddr_t dest, int len, const uint8_t *data) {
void KnxHandler::handleReceivedTelegram(knx_transfer_t typ, knx_addr_t src, knx_addr_t dest, int len, const uint8_t *data) {
if (typ != KNX_TRANSFER_GROUP) {
logOtherNotice("knx", "skipping non-group PDU %3.3x", typ);
return;
}
int apci = ((data[0]&0x03)<<8) | data[1];
if ((apci & APCI_GROUPVALUE_READ_MASK) == 0) {
apci &= ~APCI_GROUPVALUE_READ_MASK;
int groupReadWriteApci = apci & APCI_GROUPVALUE_READ_WRITE_MASK;
if (groupReadWriteApci == APCI_GROUPVALUE_WRITE || groupReadWriteApci == APCI_GROUPVALUE_READ) {
apci = groupReadWriteApci;
}
bool isWrite = apci==APCI_GROUPVALUE_WRITE;
if (apci!=APCI_GROUPVALUE_READ && !isWrite) {
@@ -638,7 +636,8 @@ void KnxHandler::handleReceivedTelegram(eibaddr_t src, eibaddr_t dest, int len,
fval = int16ToFloat(static_cast<uint16_t>(value));
} else if (lengthFlag.length == 4) {
// convert from IEEE 754
fval = uintToFloat(value);
bool negative = (value & (1u << 31)) != 0;
fval = uintToFloat(value, negative);
} else {
logOtherNotice("knx", "unable to decode write request from %4.4x to %4.4x for %s/%s/%s, value %d",
src, dest, circuit.c_str(), name.c_str(), fieldName.c_str(), value);
@@ -703,24 +702,22 @@ void KnxHandler::run() {
int len = 0;
time_t definitionsSince = 0;
while (isRunning()) {
bool wasConnected = m_con != nullptr;
bool wasConnected = m_con != nullptr && m_con->isConnected();
bool needsWait = true;
if (!m_con) {
m_con = EIBSocketURL(g_url);
const char* err = nullptr;
if (!m_con) {
err = "open error";
} else if (EIBOpen_GroupSocket(m_con, 0) < 0) {
err = "open group error";
EIBClose_sync(m_con);
m_con = nullptr;
} else {
m_con = KnxConnection::create();
const char* err = m_con->open(g_url);
if (!err) {
m_lastErrorLogTime = 0;
logOtherNotice("knx", "connected");
sendGlobalValue(GLOBAL_VERSION, VERSION_INT);
sendGlobalValue(GLOBAL_RUNNING, 1);
}
if (err) {
if (m_con) {
delete m_con;
m_con = nullptr;
}
time(&now);
if (now > m_lastErrorLogTime + 10) { // log at most every 10 seconds
m_lastErrorLogTime = now;
@@ -792,7 +789,7 @@ void KnxHandler::run() {
continue;
}
// store association
eibaddr_t dest = git->second;
knx_addr_t dest = git->second;
auto subKey = static_cast<uint32_t>(dest | (isWrite ? FLAG_WRITE : FLAG_READ));
auto sit = m_subscribedGroups.find(subKey);
if (sit != m_subscribedGroups.cend()) {
@@ -866,19 +863,20 @@ void KnxHandler::run() {
}
}
if (m_con) {
eibaddr_t src, dest;
knx_addr_t src, dest;
knx_transfer_t typ;
// APDU data starting with octet 6 according to spec, contains 2 bits of application layer
result_t res = RESULT_OK;
do {
res = receiveTelegram(8, data, &len, &src, &dest);
res = receiveTelegram(sizeof(data), &typ, data, &len, &src, &dest);
if (res != RESULT_OK) {
if (res == RESULT_ERR_GENERIC_IO) {
EIBClose_sync(m_con);
delete m_con;
m_con = nullptr;
}
} else {
needsWait = false;
handleReceivedTelegram(src, dest, len, data);
handleReceivedTelegram(typ, src, dest, len, data);
}
} while (res == RESULT_OK);
}
@@ -913,7 +911,7 @@ void KnxHandler::run() {
if (!field || field->isIgnored()) {
continue;
}
eibaddr_t dest = destFlags&0xffff;
knx_addr_t dest = destFlags&0xffff;
unsigned int value = 0;
result = message->decodeLastDataNumField(nullptr, index, &value);
sendGroupValue(dest, APCI_GROUPVALUE_WRITE, sit->second.lengthFlag, value, field);
+10 -9
View File
@@ -19,7 +19,6 @@
#ifndef EBUSD_KNXHANDLER_H_
#define EBUSD_KNXHANDLER_H_
#include <eibclient.h>
#include <map>
#include <string>
#include <list>
@@ -29,11 +28,12 @@
#include "ebusd/bushandler.h"
#include "lib/ebus/message.h"
#include "lib/ebus/stringhelper.h"
#include "lib/knx/knx.h"
namespace ebusd {
/** @file ebusd/knxhandler.h
* A data handler enabling KNX support via knx.
* A data handler enabling KNX support.
*/
using std::map;
@@ -65,7 +65,7 @@ enum apci_t {
APCI_GROUPVALUE_WRITE = 0x080, //!< A_GroupValue_Write-PDU
};
#define APCI_GROUPVALUE_READ_MASK 0x3c0
#define APCI_GROUPVALUE_READ_WRITE_MASK 0x3c0
#define FLAG_READ 0x400000
#define FLAG_WRITE 0x800000
@@ -144,7 +144,7 @@ class KnxHandler : public DataSink, public DataSource, public WaitThread {
* @param field the message field or nullptr for non field related.
* @return the result code.
*/
result_t sendGroupValue(eibaddr_t dest, apci_t apci, dtlf_t& lengthFlag, unsigned int value, const SingleDataField *field = nullptr) const;
result_t sendGroupValue(knx_addr_t dest, apci_t apci, dtlf_t& lengthFlag, unsigned int value, const SingleDataField *field = nullptr) const;
/**
* Send a global value to the registered group address.
@@ -164,16 +164,17 @@ class KnxHandler : public DataSink, public DataSource, public WaitThread {
* @return the result code, either RESULT_OK on success, RESULT_ERR_GENERIC_IO on I/O error (e.g. socket closed),
* or RESULT_ERR_TIMEOUT if no data is available.
*/
result_t receiveTelegram(int maxlen, uint8_t *buf, int *recvlen, eibaddr_t *src, eibaddr_t *dest);
result_t receiveTelegram(int maxlen, knx_transfer_t* typ, uint8_t *buf, int *recvlen, knx_addr_t *src, knx_addr_t *dest);
/**
* Handle a received KNX telegram.
* @param typ the poll data type.
* @param src the source address.
* @param dest the destination group address.
* @param len the telegram length (starting with ovctet 6).
* @param data the telegram data buffer.
*/
void handleReceivedTelegram(eibaddr_t src, eibaddr_t dest, int len, const uint8_t *data);
void handleReceivedTelegram(knx_transfer_t typ, knx_addr_t src, knx_addr_t dest, int len, const uint8_t *data);
protected:
// @copydoc
@@ -188,7 +189,7 @@ class KnxHandler : public DataSink, public DataSource, public WaitThread {
StringReplacers m_replacers;
/** the group address for relevant message fields before being subscribed to by "circuit/message/field" name. */
map<string, eibaddr_t> m_messageFieldGroupAddress;
map<string, knx_addr_t> m_messageFieldGroupAddress;
/**
* the group addresses that need to be responded to.
@@ -209,8 +210,8 @@ class KnxHandler : public DataSink, public DataSource, public WaitThread {
/** the time the run thread was entered. */
time_t m_start;
/** the knx structure if initialized, or nullptr. */
EIBConnection* m_con;
/** the knx connection if initialized, or nullptr. */
KnxConnection* m_con;
/** the last update check result. */
string m_lastUpdateCheckResult;
+3 -4
View File
@@ -794,7 +794,6 @@ result_t NumberDataType::readSymbols(size_t offset, size_t length, const SymbolS
}
result_t NumberDataType::getFloatFromRawValue(unsigned int value, float* output) const {
int signedValue;
if (!hasFlag(REQ) && value == m_replacement) {
return RESULT_EMPTY;
}
@@ -814,9 +813,10 @@ result_t NumberDataType::getFloatFromRawValue(unsigned int value, float* output)
} else {
negative = false;
}
int signedValue;
if (m_bitCount == 32) {
if (hasFlag(EXP)) { // IEEE 754 binary32
float val = uintToFloat(value);
float val = uintToFloat(value, negative);
if (val != val) { // !isnan(val)
return RESULT_EMPTY;
}
@@ -830,7 +830,6 @@ result_t NumberDataType::getFloatFromRawValue(unsigned int value, float* output)
*output = static_cast<float>(val);
return RESULT_OK;
}
// less than 32 bit
if (!negative) {
if (m_divisor < 0) {
*output = static_cast<float>(value) * static_cast<float>(-m_divisor);
@@ -860,7 +859,6 @@ result_t NumberDataType::getFloatFromRawValue(unsigned int value, float* output)
result_t NumberDataType::readFromRawValue(unsigned int value,
OutputFormat outputFormat, ostream* output) const {
size_t length = (m_bitCount < 8) ? 1 : (m_bitCount/8);
int signedValue;
// initialize output
*output << setw(0) << std::resetiosflags(output->flags()) << dec << std::skipws << setprecision(6);
@@ -888,6 +886,7 @@ result_t NumberDataType::readFromRawValue(unsigned int value,
} else {
negative = false;
}
int signedValue;
if (m_bitCount == 32) {
if (hasFlag(EXP)) { // IEEE 754 binary32
float val = uintToFloat(value, negative);
+17
View File
@@ -0,0 +1,17 @@
add_definitions(-Wconversion -Wno-unused-parameter)
set(libknx_a_SOURCES
knx.h knx.cpp
)
set(libknx_a_LIBS
)
if(HAVE_KNXD)
set(libknx_a_SOURCES ${libknx_a_SOURCES} knxd.h)
set(libknx_a_LIBS ${libknx_a_LIBS} eibclient)
endif(HAVE_KNXD)
add_library(knx ${libknx_a_SOURCES})
target_link_libraries(knx ${libknx_a_LIBS})
+18
View File
@@ -0,0 +1,18 @@
AM_CXXFLAGS = -I$(top_srcdir)/src \
-isystem$(top_srcdir) \
-Wno-conversion
noinst_LIBRARIES = libknx.a
libknx_a_SOURCES = \
knx.h knx.cpp
if KNXD
libknx_a_SOURCES += knxd.h
libknx_a_LIBADD = -leibclient
else
libknx_a_SOURCES += knxnet.h
endif
distclean-local:
-rm -f Makefile.in
+37
View File
@@ -0,0 +1,37 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2022 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "lib/knx/knx.h"
#ifdef HAVE_KNXD
#include "lib/knx/knxd.h"
#else
#include "lib/knx/knxnet.h"
#endif
KnxConnection* KnxConnection::create() {
#ifdef HAVE_KNXD
return new KnxdConnection();
#else
return new KnxNetConnection();
#endif
}
+123
View File
@@ -0,0 +1,123 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2022 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_KNX_KNX_H_
#define LIB_KNX_KNX_H_
#include <cstdint>
// base KNX address type (group or individual)
typedef uint16_t knx_addr_t;
// the transfer types (lower 8 bits of transport control field with sequence=0, plus bit 8 with address type)
enum knx_transfer_t {
KNX_TRANSFER_NONE = -1, // no transfer available
KNX_TRANSFER_GROUP = 0x100, // data group or broadcast PDU
KNX_TRANSFER_TAG_GROUP = 0x104, // data tag group PDU
KNX_TRANSFER_INDIVIDUAL = 0x000, // data individual PDU
KNX_TRANSFER_CONNECTED = 0x040, // data connected PDU
KNX_TRANSFER_CONNECT = 0x080, // connect PDU
KNX_TRANSFER_DISCONNECT = 0x081, // disconnect PDU
KNX_TRANSFER_ACK = 0x0c2, // ACK PDU
KNX_TRANSFER_NAK = 0x0c3, // NAK PDU
};
/**
* An abstract KNX connection.
*/
class KnxConnection {
public:
/**
* Construct a new instance.
*/
KnxConnection() {}
/**
* Destructor.
*/
virtual ~KnxConnection() {}
/**
* Create a new KnxConnection.
* @param url the URL to connect to.
* @return the new KnxConnection, or @a nullptr on error.
*/
static KnxConnection* create();
/**
* Open a connection to the specified URL.
* @param url the URL to connect to.
* @return nullptr on success, or an error message.
*/
virtual const char* open(const char* url) = 0;
/**
* @return true if connected, false otherwise.
*/
virtual bool isConnected() const = 0;
/**
* Close the connection.
*/
virtual void close() = 0;
/**
* @return the file descriptor for polling.
*/
virtual int getPollFd() const = 0;
/**
* Get the available data (after the file descriptor was checked for availability).
* @param size the size of the data buffer.
* @param data the data buffer to copy the data to.
* @param len pointer to store the actual data length to.
* @param src optional pointer to store the source address (if any, depending on poll data type).
* @param dst optional pointer to store the destination address (if any, depending on poll data type).
* @return the polled transfer data type.
*/
virtual knx_transfer_t getPollData(int size, uint8_t* data, int* len, knx_addr_t* src, knx_addr_t* dst) = 0;
/**
* Send a group APDU.
* @param dst the destination address.
* @param len the APDU length.
* @param data the APDU data buffer.
* @return nullptr on success, or an error message.
*/
virtual const char* sendGroup(knx_addr_t dst, int len, const uint8_t* data) = 0;
/**
* @return true if connection allows programming via ETS.
*/
virtual bool isProgrammable() { return false; };
/**
* @return the individual address, or 0 if not programmed yet, or any non-zero value if not programmable.
*/
virtual knx_addr_t getAddress() { return 0xffff; };
/**
* @param address the individual address to set.
*/
virtual void setAddress(knx_addr_t address) {
// default implementation does nothing
}
};
#endif // LIB_KNX_KNX_H_
+102
View File
@@ -0,0 +1,102 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2022 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_KNX_KNXD_H_
#define LIB_KNX_KNXD_H_
#include <eibclient.h>
#include "lib/knx/knx.h"
class KnxdConnection : public KnxConnection {
public:
/**
* Construct a new instance.
*/
KnxdConnection()
: KnxConnection(), m_con(nullptr) {}
/**
* Destructor.
*/
virtual ~KnxdConnection() {
close();
}
// @copydoc
const char* open(const char* url) override {
close();
m_con = EIBSocketURL(url);
if (!m_con) {
return "open error";
}
if (EIBOpen_GroupSocket(m_con, 0) < 0) {
EIBClose_sync(m_con);
m_con = nullptr;
return "open group error";
}
return nullptr;
}
// @copydoc
bool isConnected() const override {
return m_con != nullptr;
}
void close() override {
if (m_con) {
EIBClose_sync(m_con);
m_con = nullptr;
}
}
// @copydoc
int getPollFd() const override {
return EIB_Poll_FD(m_con);
}
// @copydoc
knx_transfer_t getPollData(int size, uint8_t* data, int* len, knx_addr_t* src, knx_addr_t* dst) override {
int ret = EIB_Poll_Complete(m_con);
if (ret == -1) {
// read failed
return KNX_TRANSFER_NONE;
}
ret = EIBGetGroup_Src(m_con, size, data, src, dst);
if (ret < 2) {
return KNX_TRANSFER_NONE;
}
if (len) {
*len = ret;
}
return KNX_TRANSFER_GROUP;
}
// @copydoc
const char* sendGroup(knx_addr_t dst, int len, const uint8_t* data) override {
if (EIBSendGroup(m_con, dst, len, data) < 0) {
return "send error";
}
return nullptr;
}
private:
/** the knx structure if connected, or nullptr. */
EIBConnection* m_con;
};
#endif // LIB_KNX_KNXD_H_