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:
Tony
2026-05-28 15:44:17 +08:00
parent 078c37a20f
commit 8211514fe3
17 changed files with 432 additions and 75 deletions
@@ -22,6 +22,7 @@ class TpuartUartInterface;
class EtsDeviceRuntime {
public:
using CemiFrameSender = std::function<void(const uint8_t* data, size_t len)>;
using CemiFrameReceiver = std::function<bool(const uint8_t* data, size_t len)>;
using GroupWriteHandler = std::function<void(uint16_t group_address, const uint8_t* data,
size_t len)>;
using GroupObjectWriteHandler = std::function<void(uint16_t group_object_number,
@@ -62,6 +63,7 @@ class EtsDeviceRuntime {
void setGroupWriteHandler(GroupWriteHandler handler);
void setGroupObjectWriteHandler(GroupObjectWriteHandler handler);
void setBusFrameSender(CemiFrameSender sender);
void setTpFrameReceiver(CemiFrameReceiver receiver);
void setNetworkInterface(esp_netif_t* netif);
bool hasTpUart() const;
bool enableTpUart(bool enabled = true);
@@ -121,6 +123,7 @@ class EtsDeviceRuntime {
Bau07B0 device_;
CemiFrameSender sender_;
CemiFrameSender bus_frame_sender_;
CemiFrameReceiver tp_frame_receiver_;
GroupWriteHandler group_write_handler_;
GroupObjectWriteHandler group_object_write_handler_;
FunctionPropertyHandler command_handler_;
@@ -340,8 +340,10 @@ class GatewayKnxTpIpRouter {
::sockaddr_in data_remote{};
std::vector<uint8_t> last_received_cemi;
std::vector<uint8_t> last_tunnel_confirmation_packet;
std::vector<uint8_t> last_sent_cemi;
uint16_t secure_session_id{0};
bool oam_router_persona{false};
TickType_t last_sent_cemi_tick{0};
};
struct SecureSession {
@@ -468,12 +470,16 @@ class GatewayKnxTpIpRouter {
size_t suppress_routing_echo_len = 0);
bool handleOpenKnxBusFrame(const uint8_t* data, size_t len);
bool transmitOpenKnxTpFrame(const uint8_t* data, size_t len);
bool transmitOpenKnxTpFrameLocked(const uint8_t* data, size_t len);
bool handleOpenKnxTpIngressFrame(const uint8_t* data, size_t len);
void publishCloudCemiFrame(const uint8_t* data, size_t len);
void selectOpenKnxNetworkInterface(const ::sockaddr_in& remote);
bool routeOpenKnxGroupWrite(const uint8_t* data, size_t len, const char* context);
bool emitOpenKnxGroupValue(uint16_t group_object_number, const uint8_t* data, size_t len);
bool shouldRouteDaliApplicationFrames() const;
uint8_t advertisedMedium() const;
const char* ipInterfaceName() const;
const char* ipInterfaceFriendlyName() const;
void syncOpenKnxConfigFromDevice();
uint16_t effectiveIpInterfaceIndividualAddress() const;
uint16_t effectiveKnxDeviceIndividualAddress() const;
@@ -0,0 +1,38 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
namespace gateway {
enum class KnxPortKind : uint8_t {
kIpTunnel,
kIpRouting,
kTpUart,
kCloud,
kRfReserved,
};
struct KnxIngressContext {
KnxPortKind port{KnxPortKind::kIpTunnel};
bool oam_persona_hint{false};
bool broadcast_management{false};
};
class KnxResponseDeduplicator {
public:
KnxResponseDeduplicator() = default;
KnxResponseDeduplicator(const uint8_t* original, size_t len);
bool remember(const uint8_t* data, size_t len);
bool remember(const std::vector<uint8_t>& data);
size_t suppressedCount() const;
private:
std::vector<uint8_t> original_;
std::vector<std::vector<uint8_t>> sent_;
size_t suppressed_count_{0};
};
} // namespace gateway
@@ -43,9 +43,11 @@ class OamRouterRuntime {
DeviceObject* deviceObject();
Platform* platform();
void setNetworkInterface(esp_netif_t* netif);
void setBusFrameSender(CemiFrameSender sender);
bool handleTunnelFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
bool handleLocalBroadcastManagementFrame(const uint8_t* data, size_t len,
CemiFrameSender sender);
bool handleBusFrame(const uint8_t* data, size_t len, CemiFrameSender sender);
void loop();
private:
@@ -53,9 +55,11 @@ class OamRouterRuntime {
static void EmitTunnelFrame(CemiFrame& frame, void* context);
static uint16_t DefaultTunnelClientAddress(uint16_t individual_address);
bool shouldConsumeTunnelFrame(CemiFrame& frame) const;
bool shouldConsumeBusFrame(CemiFrame& frame) const;
std::string nvs_namespace_;
CemiFrameSender sender_;
CemiFrameSender bus_frame_sender_;
#if defined(ENABLE_BAU091A_PERSONA)
EspIdfPlatform platform_;
Bau091A device_;