From da67d9ff322067769eab482d51ad5590b9b9b588 Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 1 Aug 2026 17:00:22 +0800 Subject: [PATCH] feat(gateway): enhance MAC DALI command handling and increase UDP buffer size Signed-off-by: Tony --- README.md | 4 + .../gateway_network/src/gateway_network.cpp | 82 ++++++++++++++++++- 2 files changed, 82 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5ebd600..b1c16d3 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,10 @@ ports: - `{"type":"discover"}` returns `discoverResp` with device identity, UDP/TCP control ports, Ethernet status/config, and channel gateway/group data. - `{"type":"dali","data":""}` enqueues a raw gateway command. +- `{"type":"macDali","mac":"","data":""}` + enqueues a raw command only on the matching gateway. The gateway broadcasts + command responses back to the sender's UDP port, allowing local-link control + when its IPv4 address is unknown or configured for the wrong subnet. - `{"type":"setconfig","data":{"ip":"...","mask":"...","gw":"...","dns":"..."}}` stores and applies static Ethernet IPv4 settings. `ip` empty or `0.0.0.0` clears static config and returns Ethernet to DHCP. diff --git a/components/gateway_network/src/gateway_network.cpp b/components/gateway_network/src/gateway_network.cpp index 72146f2..82fbd39 100644 --- a/components/gateway_network/src/gateway_network.cpp +++ b/components/gateway_network/src/gateway_network.cpp @@ -37,7 +37,7 @@ namespace { constexpr const char* kTag = "gateway_network"; constexpr const char* kSetupApSsid = "LAMMIN_Gateway"; -constexpr size_t kUdpBufferSize = 256; +constexpr size_t kUdpBufferSize = 1024; constexpr size_t kTcpControlBufferSize = 512; constexpr uint8_t kEspNowBroadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -202,6 +202,56 @@ bool IsLikelyJson(const uint8_t* data, size_t len) { return index < len && data[index] == '{'; } +std::string NormalizeNetworkIdentity(std::string_view value) { + std::string normalized; + normalized.reserve(value.size()); + for (const unsigned char character : value) { + if (std::isxdigit(character) != 0) { + normalized.push_back(static_cast(std::tolower(character))); + } + } + return normalized; +} + +bool MatchesNetworkIdentity(const GatewayDeviceInfo& info, std::string_view value) { + const std::string target = NormalizeNetworkIdentity(value); + if (target.size() != 6 && target.size() != 12) { + return false; + } + const auto matches = [&target](std::string_view candidate) { + const std::string normalized = NormalizeNetworkIdentity(candidate); + return normalized == target || + (target.size() == 6 && normalized.size() >= 6 && + normalized.compare(normalized.size() - 6, 6, target) == 0); + }; + return matches(info.serial_id) || + (info.eth.has_value() && matches(info.eth->mac)) || + (info.wlan.has_value() && matches(info.wlan->mac)); +} + +enum class MacControlDisposition { kNotMacControl, kMatches, kOtherGateway }; + +MacControlDisposition InspectMacControl(const uint8_t* data, size_t len, + const GatewayDeviceInfo& info) { + if (!IsLikelyJson(data, len)) { + return MacControlDisposition::kNotMacControl; + } + cJSON* root = cJSON_ParseWithLength(reinterpret_cast(data), len); + if (root == nullptr) { + return MacControlDisposition::kNotMacControl; + } + const char* type = JsonString(root, "type"); + const char* mac = JsonString(root, "mac"); + const bool is_mac_control = type != nullptr && std::strcmp(type, "macDali") == 0; + const bool matches = is_mac_control && mac != nullptr && MatchesNetworkIdentity(info, mac); + cJSON_Delete(root); + if (!is_mac_control) { + return MacControlDisposition::kNotMacControl; + } + return matches ? MacControlDisposition::kMatches + : MacControlDisposition::kOtherGateway; +} + std::string PrintJson(cJSON* node) { if (node == nullptr) { return {}; @@ -1480,6 +1530,9 @@ void GatewayNetworkService::udpTaskLoop() { return; } + int broadcast = 1; + setsockopt(udp_socket_, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)); + sockaddr_in local_addr = {}; local_addr.sin_family = AF_INET; local_addr.sin_port = htons(config_.udp_port); @@ -1505,6 +1558,17 @@ void GatewayNetworkService::udpTaskLoop() { continue; } + const auto mac_control = InspectMacControl( + buffer, static_cast(read_len), runtime_.deviceInfo()); + if (mac_control == MacControlDisposition::kOtherGateway) { + continue; + } + if (mac_control == MacControlDisposition::kMatches && + remote_addr.ss_family == AF_INET) { + auto* remote_v4 = reinterpret_cast(&remote_addr); + remote_v4->sin_addr.s_addr = htonl(INADDR_BROADCAST); + } + { LockGuard guard(udp_lock_); udp_remote_addr_ = remote_addr; @@ -1765,7 +1829,15 @@ std::optional GatewayNetworkService::handleJsonControlFrame(const u return response; } - if (std::strcmp(type, "dali") == 0) { + if (std::strcmp(type, "dali") == 0 || std::strcmp(type, "macDali") == 0) { + const bool mac_dali = std::strcmp(type, "macDali") == 0; + if (mac_dali) { + const char* mac = JsonString(root, "mac"); + if (mac == nullptr || !MatchesNetworkIdentity(runtime_.deviceInfo(), mac)) { + cJSON_Delete(root); + return JsonResponse("macDaliResp", "not_target"); + } + } cJSON* payload = cJSON_GetObjectItemCaseSensitive(root, "data"); std::vector frame; if (cJSON_IsString(payload) && payload->valuestring != nullptr) { @@ -1773,10 +1845,12 @@ std::optional GatewayNetworkService::handleJsonControlFrame(const u } cJSON_Delete(root); if (frame.empty()) { - return JsonResponse("daliResp", "invalid"); + return JsonResponse(mac_dali ? "macDaliResp" : "daliResp", + "invalid"); } const bool accepted = enqueueControlFrameForTargets(frame); - return JsonResponse("daliResp", accepted ? "ok" : "rejected"); + return JsonResponse(mac_dali ? "macDaliResp" : "daliResp", + accepted ? "ok" : "rejected"); } if (std::strcmp(type, "setconfig") == 0) {