feat: Enhance OAM router functionality and improve KNX device handling
- Added support for OAM router configuration in app_main, allowing the IP interface individual address to be set based on the OAM router's individual address. - Updated GatewayBridgeService to validate IP interface addresses only when OAM router is disabled, ensuring proper address management. - Introduced KnxResponseDeduplicator to prevent duplicate responses in KNX communication. - Enhanced ETS device runtime to handle bus frames and set up frame receivers for OAM router. - Improved GatewayKnxTpIpRouter to manage OAM router interactions, including handling tunnel frames and bus frames. - Updated CMakeLists to include new knx_device_broker source file. - Refined logging messages to provide clearer context regarding the IP interface being used. - Added methods to retrieve IP interface names and friendly names based on the OAM router configuration. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "gateway_knx_private.hpp"
|
||||
|
||||
#include "knx_device_broker.h"
|
||||
|
||||
namespace gateway {
|
||||
|
||||
void GatewayKnxTpIpRouter::selectOpenKnxNetworkInterface(const sockaddr_in& remote) {
|
||||
@@ -18,7 +20,13 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
uint16_t response_service,
|
||||
const uint8_t* suppress_routing_echo,
|
||||
size_t suppress_routing_echo_len) {
|
||||
bool route_to_oam = response_client != nullptr && response_client->oam_router_persona;
|
||||
const KnxIngressContext ingress_context{
|
||||
response_service == kServiceRoutingIndication ? KnxPortKind::kIpRouting
|
||||
: KnxPortKind::kIpTunnel,
|
||||
response_client != nullptr && response_client->oam_router_persona,
|
||||
false,
|
||||
};
|
||||
bool route_to_oam = ingress_context.oam_persona_hint;
|
||||
bool route_to_all_internal_instances = false;
|
||||
if (data != nullptr && len >= 2) {
|
||||
std::vector<uint8_t> frame_data(data, data + len);
|
||||
@@ -36,25 +44,35 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
if (ets_device_ == nullptr && oam_router_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (response_client != nullptr && oam_router_ != nullptr &&
|
||||
(oam_router_->programmingMode() || !oam_router_->configured())) {
|
||||
response_client->oam_router_persona = true;
|
||||
}
|
||||
std::vector<uint8_t> tunnel_confirmation;
|
||||
const bool needs_tunnel_confirmation =
|
||||
response_client != nullptr && response_client->connected &&
|
||||
response_service == kServiceTunnellingRequest &&
|
||||
BuildTunnelConfirmationFrame(data, len, &tunnel_confirmation);
|
||||
bool sent_tunnel_confirmation = false;
|
||||
KnxResponseDeduplicator response_broker;
|
||||
const auto send_response =
|
||||
[this, response_client, response_service, needs_tunnel_confirmation,
|
||||
&tunnel_confirmation, &sent_tunnel_confirmation, suppress_routing_echo,
|
||||
suppress_routing_echo_len](const uint8_t* response, size_t response_len) {
|
||||
&tunnel_confirmation, &sent_tunnel_confirmation, &response_broker,
|
||||
suppress_routing_echo, suppress_routing_echo_len](const uint8_t* response,
|
||||
size_t response_len) {
|
||||
if (response == nullptr || response_len == 0) {
|
||||
return;
|
||||
}
|
||||
publishCloudCemiFrame(response, response_len);
|
||||
std::vector<uint8_t> response_data(response, response + response_len);
|
||||
if (!response_broker.remember(response_data)) {
|
||||
return;
|
||||
}
|
||||
publishCloudCemiFrame(response_data.data(), response_data.size());
|
||||
const bool routing_context =
|
||||
response_client == nullptr && response_service == kServiceRoutingIndication;
|
||||
const auto message_code = CemiMessageCode(response, response_len);
|
||||
const auto message_code = CemiMessageCode(response_data.data(), response_data.size());
|
||||
if (routing_context && suppress_routing_echo != nullptr &&
|
||||
IsLocalRoutingEchoIndication(response, response_len, suppress_routing_echo,
|
||||
IsLocalRoutingEchoIndication(response_data.data(), response_data.size(), suppress_routing_echo,
|
||||
suppress_routing_echo_len)) {
|
||||
return;
|
||||
}
|
||||
@@ -65,12 +83,14 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
tunnel_confirmation.data(), tunnel_confirmation.size());
|
||||
}
|
||||
|
||||
const uint16_t service = KnxIpServiceForCemi(response, response_len, response_service);
|
||||
const uint16_t service = KnxIpServiceForCemi(response_data.data(), response_data.size(),
|
||||
response_service);
|
||||
if (service == kServiceDeviceConfigurationRequest) {
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size());
|
||||
} else if (routing_context) {
|
||||
sendRoutingIndication(response, response_len);
|
||||
sendRoutingIndication(response_data.data(), response_data.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -80,20 +100,22 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sent_tunnel_confirmation =
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len) ||
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size()) ||
|
||||
sent_tunnel_confirmation;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (routing_context) {
|
||||
sendRoutingIndication(response, response_len);
|
||||
sendRoutingIndication(response_data.data(), response_data.size());
|
||||
return;
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size());
|
||||
return;
|
||||
}
|
||||
sendTunnelIndication(response, response_len);
|
||||
sendTunnelIndication(response_data.data(), response_data.size());
|
||||
};
|
||||
bool consumed = false;
|
||||
if (ets_device_ != nullptr) {
|
||||
@@ -112,6 +134,9 @@ bool GatewayKnxTpIpRouter::handleOpenKnxTunnelFrame(const uint8_t* data, size_t
|
||||
return consumed;
|
||||
}
|
||||
if (route_to_oam) {
|
||||
if (response_client != nullptr) {
|
||||
response_client->oam_router_persona = true;
|
||||
}
|
||||
return handleOamRouterTunnelFrame(data, len, response_client, response_service,
|
||||
suppress_routing_echo, suppress_routing_echo_len);
|
||||
}
|
||||
@@ -204,21 +229,26 @@ bool GatewayKnxTpIpRouter::handleOamRouterTunnelFrame(const uint8_t* data, size_
|
||||
response_service == kServiceTunnellingRequest &&
|
||||
BuildTunnelConfirmationFrame(data, len, &tunnel_confirmation);
|
||||
bool sent_tunnel_confirmation = false;
|
||||
KnxResponseDeduplicator response_broker;
|
||||
const bool consumed = oam_router_->handleTunnelFrame(
|
||||
data, len,
|
||||
[this, response_client, response_service, needs_tunnel_confirmation,
|
||||
&tunnel_confirmation, &sent_tunnel_confirmation,
|
||||
&tunnel_confirmation, &sent_tunnel_confirmation, &response_broker,
|
||||
suppress_routing_echo, suppress_routing_echo_len](const uint8_t* response,
|
||||
size_t response_len) {
|
||||
if (response == nullptr || response_len == 0) {
|
||||
return;
|
||||
}
|
||||
publishCloudCemiFrame(response, response_len);
|
||||
std::vector<uint8_t> response_data(response, response + response_len);
|
||||
if (!response_broker.remember(response_data)) {
|
||||
return;
|
||||
}
|
||||
publishCloudCemiFrame(response_data.data(), response_data.size());
|
||||
const bool routing_context =
|
||||
response_client == nullptr && response_service == kServiceRoutingIndication;
|
||||
const auto message_code = CemiMessageCode(response, response_len);
|
||||
const auto message_code = CemiMessageCode(response_data.data(), response_data.size());
|
||||
if (routing_context && suppress_routing_echo != nullptr &&
|
||||
IsLocalRoutingEchoIndication(response, response_len, suppress_routing_echo,
|
||||
IsLocalRoutingEchoIndication(response_data.data(), response_data.size(), suppress_routing_echo,
|
||||
suppress_routing_echo_len)) {
|
||||
return;
|
||||
}
|
||||
@@ -229,12 +259,14 @@ bool GatewayKnxTpIpRouter::handleOamRouterTunnelFrame(const uint8_t* data, size_
|
||||
tunnel_confirmation.data(), tunnel_confirmation.size());
|
||||
}
|
||||
|
||||
const uint16_t service = KnxIpServiceForCemi(response, response_len, response_service);
|
||||
const uint16_t service = KnxIpServiceForCemi(response_data.data(), response_data.size(),
|
||||
response_service);
|
||||
if (service == kServiceDeviceConfigurationRequest) {
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size());
|
||||
} else if (routing_context) {
|
||||
sendRoutingIndication(response, response_len);
|
||||
sendRoutingIndication(response_data.data(), response_data.size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -244,20 +276,22 @@ bool GatewayKnxTpIpRouter::handleOamRouterTunnelFrame(const uint8_t* data, size_
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sent_tunnel_confirmation =
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len) ||
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size()) ||
|
||||
sent_tunnel_confirmation;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (routing_context) {
|
||||
sendRoutingIndication(response, response_len);
|
||||
sendRoutingIndication(response_data.data(), response_data.size());
|
||||
return;
|
||||
}
|
||||
if (response_client != nullptr && response_client->connected) {
|
||||
sendCemiFrameToClient(*response_client, service, response, response_len);
|
||||
sendCemiFrameToClient(*response_client, service, response_data.data(),
|
||||
response_data.size());
|
||||
return;
|
||||
}
|
||||
sendTunnelIndication(response, response_len);
|
||||
sendTunnelIndication(response_data.data(), response_data.size());
|
||||
});
|
||||
if (needs_tunnel_confirmation && consumed && !sent_tunnel_confirmation) {
|
||||
sendCemiFrameToClient(*response_client, kServiceTunnellingRequest,
|
||||
@@ -268,6 +302,10 @@ bool GatewayKnxTpIpRouter::handleOamRouterTunnelFrame(const uint8_t* data, size_
|
||||
|
||||
bool GatewayKnxTpIpRouter::transmitOpenKnxTpFrame(const uint8_t* data, size_t len) {
|
||||
SemaphoreGuard guard(openknx_lock_);
|
||||
return transmitOpenKnxTpFrameLocked(data, len);
|
||||
}
|
||||
|
||||
bool GatewayKnxTpIpRouter::transmitOpenKnxTpFrameLocked(const uint8_t* data, size_t len) {
|
||||
if (ets_device_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -276,6 +314,56 @@ bool GatewayKnxTpIpRouter::transmitOpenKnxTpFrame(const uint8_t* data, size_t le
|
||||
return sent;
|
||||
}
|
||||
|
||||
bool GatewayKnxTpIpRouter::handleOpenKnxTpIngressFrame(const uint8_t* data, size_t len) {
|
||||
if (data == nullptr || len < 2 || (ets_device_ == nullptr && oam_router_ == nullptr)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t> frame_data(data, data + len);
|
||||
CemiFrame frame(frame_data.data(), static_cast<uint16_t>(frame_data.size()));
|
||||
if (!frame.valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool broadcast_management = IsKnxBroadcastManagementRequest(frame);
|
||||
const bool addressed_to_oam =
|
||||
oam_router_ != nullptr && MatchesOamRouterLocalIndividualAddress(frame, *oam_router_);
|
||||
if (!broadcast_management && !addressed_to_oam) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const KnxIngressContext ingress_context{KnxPortKind::kTpUart, addressed_to_oam,
|
||||
broadcast_management};
|
||||
KnxResponseDeduplicator response_broker(frame_data.data(), frame_data.size());
|
||||
const auto send_response = [this, &response_broker](const uint8_t* response,
|
||||
size_t response_len) {
|
||||
if (response == nullptr || response_len == 0) {
|
||||
return;
|
||||
}
|
||||
std::vector<uint8_t> response_data(response, response + response_len);
|
||||
if (!response_broker.remember(response_data)) {
|
||||
return;
|
||||
}
|
||||
publishCloudCemiFrame(response_data.data(), response_data.size());
|
||||
transmitOpenKnxTpFrameLocked(response_data.data(), response_data.size());
|
||||
sendTunnelIndication(response_data.data(), response_data.size());
|
||||
sendRoutingIndication(response_data.data(), response_data.size());
|
||||
};
|
||||
|
||||
bool consumed = false;
|
||||
if (ingress_context.broadcast_management && ets_device_ != nullptr) {
|
||||
consumed = ets_device_->handleBusFrame(data, len) || consumed;
|
||||
}
|
||||
if ((ingress_context.broadcast_management || ingress_context.oam_persona_hint) &&
|
||||
oam_router_ != nullptr) {
|
||||
consumed = oam_router_->handleBusFrame(data, len, send_response) || consumed;
|
||||
}
|
||||
if (consumed) {
|
||||
publishCloudCemiFrame(data, len);
|
||||
syncOpenKnxConfigFromDevice();
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
bool GatewayKnxTpIpRouter::handleOpenKnxBusFrame(const uint8_t* data, size_t len) {
|
||||
bool consumed = false;
|
||||
{
|
||||
@@ -441,6 +529,14 @@ uint8_t GatewayKnxTpIpRouter::advertisedMedium() const {
|
||||
return (config_.tunnel_enabled || tp_uart_online_) ? kKnxMediumTp1 : kKnxMediumIp;
|
||||
}
|
||||
|
||||
const char* GatewayKnxTpIpRouter::ipInterfaceName() const {
|
||||
return config_.oam_router.enabled ? "oam_ip_router" : "knx_ip_interface";
|
||||
}
|
||||
|
||||
const char* GatewayKnxTpIpRouter::ipInterfaceFriendlyName() const {
|
||||
return config_.oam_router.enabled ? "OAM IP Router" : "DaliMaster KNX IP";
|
||||
}
|
||||
|
||||
void GatewayKnxTpIpRouter::syncOpenKnxConfigFromDevice() {
|
||||
if (ets_device_ == nullptr) {
|
||||
return;
|
||||
@@ -454,6 +550,15 @@ void GatewayKnxTpIpRouter::syncOpenKnxConfigFromDevice() {
|
||||
updated.individual_address = snapshot.individual_address;
|
||||
changed = true;
|
||||
}
|
||||
if (oam_router_ != nullptr) {
|
||||
const auto oam_snapshot = oam_router_->snapshot();
|
||||
if (oam_snapshot.individual_address != 0 && oam_snapshot.individual_address != 0xffff &&
|
||||
oam_snapshot.individual_address != updated.oam_router.individual_address) {
|
||||
updated.oam_router.individual_address = oam_snapshot.individual_address;
|
||||
updated.ip_interface_individual_address = oam_snapshot.individual_address;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (snapshot.configured || !snapshot.associations.empty()) {
|
||||
std::vector<GatewayKnxEtsAssociation> associations;
|
||||
associations.reserve(snapshot.associations.size());
|
||||
@@ -480,6 +585,18 @@ void GatewayKnxTpIpRouter::syncOpenKnxConfigFromDevice() {
|
||||
}
|
||||
|
||||
uint16_t GatewayKnxTpIpRouter::effectiveIpInterfaceIndividualAddress() const {
|
||||
if (config_.oam_router.enabled) {
|
||||
if (oam_router_ != nullptr) {
|
||||
const uint16_t address = oam_router_->individualAddress();
|
||||
if (address != 0 && address != 0xffff) {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
if (config_.oam_router.individual_address != 0 &&
|
||||
config_.oam_router.individual_address != 0xffff) {
|
||||
return config_.oam_router.individual_address;
|
||||
}
|
||||
}
|
||||
if (config_.ip_interface_individual_address != 0 &&
|
||||
config_.ip_interface_individual_address != 0xffff) {
|
||||
return config_.ip_interface_individual_address;
|
||||
@@ -498,6 +615,10 @@ uint16_t GatewayKnxTpIpRouter::effectiveKnxDeviceIndividualAddress() const {
|
||||
}
|
||||
|
||||
uint16_t GatewayKnxTpIpRouter::effectiveTunnelAddress() const {
|
||||
if (config_.oam_router.enabled && config_.oam_router.tunnel_address_base != 0 &&
|
||||
config_.oam_router.tunnel_address_base != 0xffff) {
|
||||
return config_.oam_router.tunnel_address_base;
|
||||
}
|
||||
const uint16_t interface_address = effectiveIpInterfaceIndividualAddress();
|
||||
uint16_t device = static_cast<uint16_t>((interface_address & 0x00ff) + 1);
|
||||
if (device == 0 || device > 0xff) {
|
||||
|
||||
Reference in New Issue
Block a user