feat(gateway): add support for full IP forwarding of KNX TP telegrams and enhance tunnel frame handling
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -509,9 +509,6 @@ bool EtsDeviceRuntime::shouldConsumeTunnelFrame(CemiFrame& frame) const {
|
||||
case M_FuncPropStateRead_req:
|
||||
return true;
|
||||
case L_data_req: {
|
||||
if (tpUartOnline()) {
|
||||
return true;
|
||||
}
|
||||
// In commissioning / programming mode ETS may address the device via its
|
||||
// individual address, the cEMI-client tunnel address (device+1), or the
|
||||
// unconfigured broadcast address 0xFFFF. Consume only those; let all
|
||||
|
||||
@@ -3476,16 +3476,20 @@ void GatewayKnxTpIpRouter::handleTunnellingRequest(const uint8_t* packet_data, s
|
||||
const bool consumed_by_openknx = handleOpenKnxTunnelFrame(
|
||||
cemi, cemi_len, client, kServiceTunnellingRequest);
|
||||
const bool routed_to_dali = routeOpenKnxGroupWrite(cemi, cemi_len, "KNX tunnel frame");
|
||||
if (!consumed_by_openknx && routed_to_dali) {
|
||||
const bool sent_to_tp = !consumed_by_openknx && !routed_to_dali &&
|
||||
transmitOpenKnxTpFrame(cemi, cemi_len);
|
||||
if ((!consumed_by_openknx && routed_to_dali) || sent_to_tp) {
|
||||
std::vector<uint8_t> tunnel_confirmation;
|
||||
if (BuildTunnelConfirmationFrame(cemi, cemi_len, &tunnel_confirmation)) {
|
||||
sendCemiFrameToClient(*client, kServiceTunnellingRequest, tunnel_confirmation.data(),
|
||||
tunnel_confirmation.size());
|
||||
}
|
||||
}
|
||||
if (consumed_by_openknx || routed_to_dali) {
|
||||
if (consumed_by_openknx || routed_to_dali || sent_to_tp) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(kTag, "KNX tunnel frame ignored: no OpenKNX/DALI/TP handler matched");
|
||||
}
|
||||
|
||||
void GatewayKnxTpIpRouter::handleDeviceConfigurationRequest(const uint8_t* packet_data, size_t len,
|
||||
@@ -3933,11 +3937,50 @@ void GatewayKnxTpIpRouter::sendTunnelIndication(const uint8_t* data, size_t len)
|
||||
if (data == nullptr || len == 0) {
|
||||
return;
|
||||
}
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (client.connected) {
|
||||
sendTunnelIndicationToClient(client, data, len);
|
||||
std::vector<uint8_t> frame_data(data, data + len);
|
||||
CemiFrame frame(frame_data.data(), static_cast<uint16_t>(frame_data.size()));
|
||||
if (!frame.valid()) {
|
||||
ESP_LOGW(kTag, "not sending invalid OpenKNX tunnel indication len=%u",
|
||||
static_cast<unsigned>(len));
|
||||
return;
|
||||
}
|
||||
|
||||
auto is_tunnel_recipient = [](const TunnelClient& client) {
|
||||
return client.connected && client.connection_type == kKnxConnectionTypeTunnel;
|
||||
};
|
||||
|
||||
auto send_to_client = [this, data, len](TunnelClient& client) {
|
||||
sendTunnelIndicationToClient(client, data, len);
|
||||
};
|
||||
|
||||
const bool suppress_source_echo =
|
||||
frame.addressType() == GroupAddress || frame.addressType() == IndividualAddress;
|
||||
const uint16_t source_address = suppress_source_echo ? frame.sourceAddress() : 0;
|
||||
|
||||
if (frame.addressType() == IndividualAddress) {
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (!is_tunnel_recipient(client)) {
|
||||
continue;
|
||||
}
|
||||
if (client.individual_address == source_address) {
|
||||
continue;
|
||||
}
|
||||
if (client.individual_address == frame.destinationAddress()) {
|
||||
send_to_client(client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& client : tunnel_clients_) {
|
||||
if (!is_tunnel_recipient(client)) {
|
||||
continue;
|
||||
}
|
||||
if (suppress_source_echo && client.individual_address == source_address) {
|
||||
continue;
|
||||
}
|
||||
send_to_client(client);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayKnxTpIpRouter::sendTunnelIndicationToClient(TunnelClient& client, const uint8_t* data,
|
||||
@@ -4152,6 +4195,10 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
sendRoutingIndication(response, response_len);
|
||||
return;
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
return;
|
||||
}
|
||||
sendTunnelIndication(response, response_len);
|
||||
});
|
||||
if (needs_tunnel_confirmation && consumed && !sent_tunnel_confirmation) {
|
||||
|
||||
Reference in New Issue
Block a user