feat(gateway): add DALI bus ID configuration and enhance group object write handling

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-16 03:31:12 +08:00
parent e79223c87e
commit 277379abd7
13 changed files with 288 additions and 37 deletions
@@ -4,6 +4,7 @@
#include "esp_log.h"
#include "knx/cemi_server.h"
#include "knx/group_object.h"
#include "knx/secure_application_layer.h"
#include "knx/property.h"
#include "tpuart_uart_interface.h"
@@ -19,6 +20,7 @@ namespace gateway::openknx {
namespace {
thread_local EtsDeviceRuntime* active_function_property_runtime = nullptr;
EtsDeviceRuntime* active_group_object_runtime = nullptr;
class ActiveFunctionPropertyRuntimeScope {
public:
@@ -121,6 +123,7 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
}
ESP_LOGI("gateway_knx", "OpenKNX loading memory namespace=%s", nvs_namespace_.c_str());
device_.readMemory();
installGroupObjectCallbacks();
if (!IsUsableIndividualAddress(device_.deviceObject().individualAddress()) &&
IsUsableIndividualAddress(fallback_individual_address)) {
device_.deviceObject().individualAddress(fallback_individual_address);
@@ -149,6 +152,19 @@ EtsDeviceRuntime::~EtsDeviceRuntime() {
#ifdef USE_DATASECURE
device_.secureGroupWriteCallback(nullptr, nullptr);
#endif
#ifdef SMALL_GROUPOBJECT
if (active_group_object_runtime == this) {
GroupObject::classCallback(GroupObjectUpdatedHandler{});
}
#else
auto& table = device_.groupObjectTable();
for (uint16_t asap = 1; asap <= table.entryCount(); ++asap) {
table.get(asap).callback(GroupObjectUpdatedHandler{});
}
#endif
if (active_group_object_runtime == this) {
active_group_object_runtime = nullptr;
}
device_.functionPropertyCallback(nullptr);
device_.functionPropertyStateCallback(nullptr);
if (auto* server = device_.getCemiServer()) {
@@ -223,6 +239,11 @@ void EtsDeviceRuntime::setGroupWriteHandler(GroupWriteHandler handler) {
group_write_handler_ = std::move(handler);
}
void EtsDeviceRuntime::setGroupObjectWriteHandler(GroupObjectWriteHandler handler) {
group_object_write_handler_ = std::move(handler);
installGroupObjectCallbacks();
}
void EtsDeviceRuntime::setBusFrameSender(CemiFrameSender sender) {
bus_frame_sender_ = std::move(sender);
}
@@ -272,11 +293,20 @@ bool EtsDeviceRuntime::handleTunnelFrame(const uint8_t* data, size_t len,
if (!consumed) {
return false;
}
const bool suppress_group_object_route =
frame.messageCode() == L_data_req && frame.addressType() == GroupAddress &&
frame.apdu().type() == GroupValueWrite;
const bool previous_suppression = suppress_group_object_write_callback_;
if (suppress_group_object_route) {
suppress_group_object_write_callback_ = true;
}
sender_ = std::move(sender);
ActiveFunctionPropertyRuntimeScope callback_scope(this);
server->frameReceived(frame);
loop();
sender_ = nullptr;
suppress_group_object_write_callback_ = previous_suppression;
installGroupObjectCallbacks();
return consumed;
}
@@ -293,6 +323,7 @@ bool EtsDeviceRuntime::handleBusFrame(const uint8_t* data, size_t len) {
}
data_link_layer->externalFrameReceived(frame);
loop();
installGroupObjectCallbacks();
return consumed;
}
@@ -314,10 +345,13 @@ bool EtsDeviceRuntime::emitGroupValue(uint16_t group_object_number, const uint8_
} else {
std::copy_n(data, len, group_object.valueRef());
}
const bool previous_suppression = suppress_group_object_write_callback_;
suppress_group_object_write_callback_ = true;
sender_ = std::move(sender);
group_object.objectWritten();
loop();
sender_ = nullptr;
suppress_group_object_write_callback_ = previous_suppression;
return true;
}
@@ -349,10 +383,36 @@ void EtsDeviceRuntime::EmitTunnelFrame(CemiFrame& frame, void* context) {
void EtsDeviceRuntime::HandleSecureGroupWrite(uint16_t group_address, const uint8_t* data,
uint8_t data_length, void* context) {
auto* self = static_cast<EtsDeviceRuntime*>(context);
if (self == nullptr || !self->group_write_handler_) {
if (self == nullptr) {
return;
}
self->group_write_handler_(group_address, data, data_length);
if (self->group_object_write_handler_) {
return;
}
if (self->group_write_handler_) {
self->group_write_handler_(group_address, data, data_length);
}
}
void EtsDeviceRuntime::HandleGroupObjectWrite(GroupObject& ko) {
auto* self = active_group_object_runtime;
if (self == nullptr || self->suppress_group_object_write_callback_ ||
!self->group_object_write_handler_) {
return;
}
const size_t value_size = ko.valueSize();
const uint8_t* value = ko.valueRef();
if (value == nullptr || value_size == 0) {
ESP_LOGW("gateway_knx", "OpenKNX group-object callback ignored namespace=%s ko=%u len=%u",
self->nvs_namespace_.c_str(), static_cast<unsigned>(ko.asap()),
static_cast<unsigned>(value_size));
return;
}
const std::string value_hex = HexBytesString(value, value_size);
ESP_LOGI("gateway_knx", "OpenKNX group-object callback namespace=%s ko=%u len=%u value=%s",
self->nvs_namespace_.c_str(), static_cast<unsigned>(ko.asap()),
static_cast<unsigned>(value_size), value_hex.c_str());
self->group_object_write_handler_(ko.asap(), value, value_size);
}
bool EtsDeviceRuntime::HandleFunctionPropertyCommand(uint8_t object_index, uint8_t property_id,
@@ -395,6 +455,24 @@ bool EtsDeviceRuntime::DispatchFunctionProperty(FunctionPropertyHandler* handler
return true;
}
void EtsDeviceRuntime::installGroupObjectCallbacks() {
active_group_object_runtime = this;
auto& table = device_.groupObjectTable();
const uint16_t count = table.entryCount();
#ifdef SMALL_GROUPOBJECT
GroupObject::classCallback(&EtsDeviceRuntime::HandleGroupObjectWrite);
#else
for (uint16_t asap = 1; asap <= count; ++asap) {
table.get(asap).callback(&EtsDeviceRuntime::HandleGroupObjectWrite);
}
#endif
if (count != group_object_callback_count_) {
ESP_LOGI("gateway_knx", "OpenKNX group-object callbacks namespace=%s count=%u",
nvs_namespace_.c_str(), static_cast<unsigned>(count));
group_object_callback_count_ = count;
}
}
uint16_t EtsDeviceRuntime::DefaultTunnelClientAddress(uint16_t individual_address) {
if (!IsUsableIndividualAddress(individual_address)) {
return 0x1101;