feat(gateway): integrate bridge service into gateway controller for transport handling

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-19 05:52:36 +08:00
parent 3bc5355041
commit 226855362b
6 changed files with 391 additions and 112 deletions
@@ -3,6 +3,7 @@
#include "dali_domain.hpp"
#include "esp_log.h"
#include "esp_system.h"
#include "gateway_bridge.hpp"
#include "gateway_runtime.hpp"
#include <algorithm>
@@ -21,6 +22,16 @@ constexpr uint8_t kDaliSceneCount = 16;
constexpr uint8_t kDaliCmdOff = 0x00;
constexpr uint8_t kDaliCmdRecallMax = 0x05;
constexpr TickType_t kMaintenancePollTicks = pdMS_TO_TICKS(20);
constexpr uint8_t kBridgeTransportRequestOpcode = 0xB0;
constexpr uint8_t kBridgeTransportResponseOpcode = 0xB1;
constexpr uint8_t kBridgeTransportVersion = 1;
constexpr size_t kBridgeTransportMaxChunkBytes = 120;
constexpr const char* kBridgeTransportInvalidFrameResponse =
"{\"statusCode\":400,\"error\":\"invalid bridge transport frame\","
"\"message\":\"invalid bridge transport frame\"}";
constexpr const char* kBridgeTransportUnavailableResponse =
"{\"statusCode\":500,\"error\":\"bridge service is not enabled\","
"\"message\":\"bridge service is not enabled\"}";
class LockGuard {
public:
@@ -73,6 +84,10 @@ void AppendStringBytes(std::vector<uint8_t>& out, std::string_view value) {
}
}
uint16_t BridgeTransportRequestKey(uint8_t gateway_id, uint8_t sequence) {
return static_cast<uint16_t>((static_cast<uint16_t>(gateway_id) << 8) | sequence);
}
void AppendPaddedName(std::vector<uint8_t>& out, std::string_view name) {
const auto normalized = NormalizeName(name);
out.push_back(static_cast<uint8_t>(normalized.size()));
@@ -185,6 +200,10 @@ void GatewayController::addGatewayNameSink(GatewayNameSink sink) {
}
}
void GatewayController::setBridgeService(GatewayBridgeService* bridge_service) {
bridge_service_ = bridge_service;
}
bool GatewayController::setupMode() const {
return setup_mode_;
}
@@ -659,6 +678,9 @@ void GatewayController::dispatchCommand(const std::vector<uint8_t>& command) {
case 0xA2:
handleInternalGroupCommand(gateway_id, command);
break;
case kBridgeTransportRequestOpcode:
handleBridgeTransportCommand(gateway_id, command);
break;
default:
ESP_LOGW(kTag, "unhandled opcode=0x%02x gateway=%u", opcode, gateway_id);
break;
@@ -714,12 +736,120 @@ void GatewayController::publishPayload(uint8_t, const std::vector<uint8_t>& payl
publishFrame(GatewayRuntime::buildNotificationFrame(payload));
}
void GatewayController::publishBridgeTransportResponse(uint8_t gateway_id, uint8_t version,
uint8_t sequence,
std::string_view response) {
const size_t total_chunks =
std::max<size_t>(1, (response.size() + kBridgeTransportMaxChunkBytes - 1) /
kBridgeTransportMaxChunkBytes);
for (size_t index = 0; index < total_chunks; ++index) {
const size_t start = index * kBridgeTransportMaxChunkBytes;
const size_t chunk_length =
std::min(kBridgeTransportMaxChunkBytes, response.size() - start);
std::vector<uint8_t> payload{
kBridgeTransportResponseOpcode,
gateway_id,
version,
sequence,
static_cast<uint8_t>(total_chunks),
static_cast<uint8_t>(index),
static_cast<uint8_t>(response.size() & 0xFF),
static_cast<uint8_t>((response.size() >> 8) & 0xFF),
static_cast<uint8_t>(chunk_length & 0xFF),
static_cast<uint8_t>((chunk_length >> 8) & 0xFF),
};
payload.reserve(payload.size() + chunk_length);
for (size_t offset = 0; offset < chunk_length; ++offset) {
payload.push_back(static_cast<uint8_t>(response[start + offset]));
}
publishPayload(gateway_id, payload);
}
}
void GatewayController::publishFrame(const std::vector<uint8_t>& frame) {
for (const auto& sink : notification_sinks_) {
sink(frame);
}
}
void GatewayController::handleBridgeTransportCommand(uint8_t gateway_id,
const std::vector<uint8_t>& command) {
const uint8_t version = command.size() > 4 ? command[4] : kBridgeTransportVersion;
const uint8_t sequence = command.size() > 5 ? command[5] : 0;
const uint16_t request_key = BridgeTransportRequestKey(gateway_id, sequence);
if (command.size() < 11) {
bridge_transport_requests_.erase(request_key);
publishBridgeTransportResponse(gateway_id, version, sequence,
kBridgeTransportInvalidFrameResponse);
return;
}
const uint8_t total_chunks = command[6];
const uint8_t chunk_index = command[7];
const uint16_t payload_length =
static_cast<uint16_t>(command[8] | (static_cast<uint16_t>(command[9]) << 8));
if (version != kBridgeTransportVersion || total_chunks == 0 || chunk_index >= total_chunks) {
bridge_transport_requests_.erase(request_key);
publishBridgeTransportResponse(gateway_id, version, sequence,
kBridgeTransportInvalidFrameResponse);
return;
}
auto& state = bridge_transport_requests_[request_key];
if (chunk_index == 0 || state.version != version || state.payload_length != payload_length ||
state.total_chunks != total_chunks) {
state = BridgeTransportRequestState{};
state.version = version;
state.payload_length = payload_length;
state.total_chunks = total_chunks;
}
const size_t payload_start = 10;
const size_t payload_end = command.size() - 1;
if (payload_end < payload_start) {
bridge_transport_requests_.erase(request_key);
publishBridgeTransportResponse(gateway_id, version, sequence,
kBridgeTransportInvalidFrameResponse);
return;
}
if (state.chunks.find(chunk_index) == state.chunks.end()) {
state.chunks[chunk_index] =
std::vector<uint8_t>(command.begin() + payload_start, command.begin() + payload_end);
}
if (state.chunks.size() < total_chunks) {
return;
}
std::vector<uint8_t> request_bytes;
request_bytes.reserve(payload_length);
for (uint8_t index = 0; index < total_chunks; ++index) {
const auto it = state.chunks.find(index);
if (it == state.chunks.end()) {
bridge_transport_requests_.erase(request_key);
publishBridgeTransportResponse(gateway_id, version, sequence,
kBridgeTransportInvalidFrameResponse);
return;
}
request_bytes.insert(request_bytes.end(), it->second.begin(), it->second.end());
}
bridge_transport_requests_.erase(request_key);
if (request_bytes.size() != payload_length) {
publishBridgeTransportResponse(gateway_id, version, sequence,
kBridgeTransportInvalidFrameResponse);
return;
}
const std::string response =
bridge_service_ == nullptr
? std::string(kBridgeTransportUnavailableResponse)
: bridge_service_->handleTransportRequest(
gateway_id,
std::string_view(reinterpret_cast<const char*>(request_bytes.data()),
request_bytes.size()));
publishBridgeTransportResponse(gateway_id, version, sequence, response);
}
void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) {
if (frame.data.size() != 2 && frame.data.size() != 3) {
return;