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
+244 -111
View File
@@ -257,6 +257,66 @@ std::optional<uint8_t> JsonGatewayId(const cJSON* root) {
return static_cast<uint8_t>(gateway);
}
int BridgeTransportStatusCode(esp_err_t err) {
if (err == ESP_OK) {
return 200;
}
if (err == ESP_ERR_INVALID_ARG) {
return 400;
}
if (err == ESP_ERR_NOT_FOUND) {
return 404;
}
return 500;
}
std::string BuildBridgeTransportEnvelope(const GatewayBridgeHttpResponse& response) {
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
return "{}";
}
cJSON_AddNumberToObject(root, "statusCode",
static_cast<double>(BridgeTransportStatusCode(response.err)));
if (response.err == ESP_OK) {
cJSON* data = response.body.empty()
? cJSON_CreateObject()
: cJSON_ParseWithLength(response.body.data(), response.body.size());
if (data == nullptr) {
cJSON_AddNumberToObject(root, "statusCode", 500);
cJSON_AddStringToObject(root, "error", "bridge response is not valid JSON");
cJSON_AddStringToObject(root, "message", "bridge response is not valid JSON");
} else {
cJSON_AddItemToObject(root, "data", data);
}
const std::string body = PrintJson(root);
cJSON_Delete(root);
return body;
}
const char* message = "bridge request failed";
cJSON* details = response.body.empty()
? nullptr
: cJSON_ParseWithLength(response.body.data(), response.body.size());
if (details != nullptr && cJSON_IsObject(details)) {
if (const char* error = JsonString(details, "error")) {
message = error;
}
cJSON_AddItemToObject(root, "details", details);
details = nullptr;
} else {
cJSON_Delete(details);
if (!response.body.empty()) {
message = response.body.c_str();
}
}
cJSON_AddStringToObject(root, "error", message);
cJSON_AddStringToObject(root, "message", message);
const std::string body = PrintJson(root);
cJSON_Delete(root);
return body;
}
std::string QueryValue(std::string_view query, std::string_view key) {
if (query.empty() || key.empty()) {
return {};
@@ -2131,6 +2191,125 @@ struct GatewayBridgeService::ChannelRuntime {
return JsonOk(BridgeResultToCjson(result));
}
cJSON* knxStatusCjson() const {
cJSON* knx_json = cJSON_CreateObject();
if (knx_json == nullptr) {
return nullptr;
}
auto* endpoint_runtime = service.knx_endpoint_runtime_;
if (endpoint_runtime == nullptr) {
endpoint_runtime = const_cast<GatewayBridgeService&>(service).selectKnxEndpointRuntime();
}
bool programming_mode = false;
bool programming_control_available = false;
int endpoint_owner_gateway_id = -1;
if (endpoint_runtime != nullptr) {
LockGuard owner_guard(endpoint_runtime->lock);
endpoint_owner_gateway_id = endpoint_runtime->channel.gateway_id;
programming_control_available = endpoint_runtime->knx_router != nullptr &&
endpoint_runtime->knx_router->started();
if (programming_control_available) {
programming_mode = endpoint_runtime->knx_router->programmingMode();
}
}
const auto effective_knx =
knx_config.has_value() ? knx_config : service_config.default_knx_config;
cJSON_AddBoolToObject(knx_json, "enabled", service_config.knx_enabled);
cJSON_AddBoolToObject(knx_json, "startupEnabled", service_config.knx_startup_enabled);
cJSON_AddBoolToObject(knx_json, "started", knx_started);
cJSON_AddBoolToObject(knx_json, "routerReady", knx_router != nullptr && knx_router->started());
cJSON_AddBoolToObject(knx_json, "programmingMode", programming_mode);
cJSON_AddBoolToObject(knx_json, "programmingControlAvailable",
programming_control_available);
cJSON_AddBoolToObject(knx_json, "endpointOwner",
endpoint_owner_gateway_id == channel.gateway_id);
if (endpoint_owner_gateway_id >= 0) {
cJSON_AddNumberToObject(knx_json, "endpointOwnerGatewayId",
endpoint_owner_gateway_id);
}
const std::string router_error = knx_router == nullptr ? "" : knx_router->lastError();
cJSON_AddStringToObject(knx_json, "lastError",
knx_last_error.empty() ? router_error.c_str()
: knx_last_error.c_str());
cJSON* security_json = cJSON_CreateObject();
if (security_json != nullptr) {
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "dataSecureCompiled", true);
#else
cJSON_AddBoolToObject(security_json, "dataSecureCompiled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "knxnetIpSecureCompiled", true);
#else
cJSON_AddBoolToObject(security_json, "knxnetIpSecureCompiled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "knxnetIpSecureServicesRecognized", true);
#else
cJSON_AddBoolToObject(security_json, "knxnetIpSecureServicesRecognized", false);
#endif
cJSON_AddBoolToObject(security_json, "knxnetIpSecureImplemented", false);
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
cJSON_AddBoolToObject(security_json, "developmentEndpointsEnabled", true);
#else
cJSON_AddBoolToObject(security_json, "developmentEndpointsEnabled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_SECURITY_PLAIN_NVS)
cJSON_AddBoolToObject(security_json, "plainNvsStorage", true);
cJSON_AddStringToObject(security_json, "storage", "plain_nvs_development");
#else
cJSON_AddBoolToObject(security_json, "plainNvsStorage", false);
cJSON_AddStringToObject(security_json, "storage", "none");
#endif
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
const auto fdsk_info = openknx::LoadFactoryFdskInfo();
cJSON* fdsk_json = FactoryFdskInfoToCjson(fdsk_info, true);
if (fdsk_json != nullptr) {
cJSON_AddItemToObject(security_json, "factorySetupKey", fdsk_json);
}
cJSON* certificate_json =
FactoryCertificateToCjson(openknx::BuildFactoryCertificatePayload(), false);
if (certificate_json != nullptr) {
cJSON_AddItemToObject(security_json, "factoryCertificate", certificate_json);
}
cJSON* failures_json = SecurityFailuresToCjson();
if (failures_json != nullptr) {
cJSON_AddItemToObject(security_json, "failures", failures_json);
}
#endif
cJSON_AddItemToObject(knx_json, "security", security_json);
}
if (effective_knx.has_value()) {
cJSON_AddBoolToObject(knx_json, "daliRouterEnabled", effective_knx->dali_router_enabled);
cJSON_AddBoolToObject(knx_json, "ipRouterEnabled", effective_knx->ip_router_enabled);
cJSON_AddBoolToObject(knx_json, "tunnelEnabled", effective_knx->tunnel_enabled);
cJSON_AddBoolToObject(knx_json, "multicastEnabled", effective_knx->multicast_enabled);
cJSON_AddBoolToObject(knx_json, "etsDatabaseEnabled", effective_knx->ets_database_enabled);
cJSON_AddNumberToObject(knx_json, "etsBindingCount",
knx == nullptr ? 0 : knx->etsBindingCount());
cJSON_AddStringToObject(knx_json, "mappingMode",
GatewayKnxMappingModeToString(effective_knx->mapping_mode));
cJSON_AddNumberToObject(knx_json, "mainGroup", effective_knx->main_group);
cJSON_AddNumberToObject(knx_json, "udpPort", effective_knx->udp_port);
cJSON_AddStringToObject(knx_json, "multicastAddress",
effective_knx->multicast_address.c_str());
cJSON_AddNumberToObject(knx_json, "ipInterfaceIndividualAddress",
effective_knx->ip_interface_individual_address);
cJSON_AddNumberToObject(knx_json, "individualAddress",
effective_knx->individual_address);
cJSON* serial_json = cJSON_CreateObject();
if (serial_json != nullptr) {
cJSON_AddNumberToObject(serial_json, "uartPort", effective_knx->tp_uart.uart_port);
cJSON_AddNumberToObject(serial_json, "txPin", effective_knx->tp_uart.tx_pin);
cJSON_AddNumberToObject(serial_json, "rxPin", effective_knx->tp_uart.rx_pin);
cJSON_AddNumberToObject(serial_json, "baudrate", effective_knx->tp_uart.baudrate);
cJSON_AddBoolToObject(serial_json, "nineBitMode", effective_knx->tp_uart.nine_bit_mode);
cJSON_AddItemToObject(knx_json, "tpUart", serial_json);
}
}
return knx_json;
}
cJSON* statusCjson() const {
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
@@ -2202,118 +2381,8 @@ struct GatewayBridgeService::ChannelRuntime {
cJSON_AddItemToObject(root, "bacnet", bacnet_json);
}
cJSON* knx_json = cJSON_CreateObject();
cJSON* knx_json = knxStatusCjson();
if (knx_json != nullptr) {
auto* endpoint_runtime = service.knx_endpoint_runtime_;
if (endpoint_runtime == nullptr) {
endpoint_runtime = const_cast<GatewayBridgeService&>(service).selectKnxEndpointRuntime();
}
bool programming_mode = false;
bool programming_control_available = false;
int endpoint_owner_gateway_id = -1;
if (endpoint_runtime != nullptr) {
LockGuard owner_guard(endpoint_runtime->lock);
endpoint_owner_gateway_id = endpoint_runtime->channel.gateway_id;
programming_control_available = endpoint_runtime->knx_router != nullptr &&
endpoint_runtime->knx_router->started();
if (programming_control_available) {
programming_mode = endpoint_runtime->knx_router->programmingMode();
}
}
const auto effective_knx = knx_config.has_value() ? knx_config : service_config.default_knx_config;
cJSON_AddBoolToObject(knx_json, "enabled", service_config.knx_enabled);
cJSON_AddBoolToObject(knx_json, "startupEnabled", service_config.knx_startup_enabled);
cJSON_AddBoolToObject(knx_json, "started", knx_started);
cJSON_AddBoolToObject(knx_json, "routerReady", knx_router != nullptr && knx_router->started());
cJSON_AddBoolToObject(knx_json, "programmingMode", programming_mode);
cJSON_AddBoolToObject(knx_json, "programmingControlAvailable",
programming_control_available);
cJSON_AddBoolToObject(knx_json, "endpointOwner",
endpoint_owner_gateway_id == channel.gateway_id);
if (endpoint_owner_gateway_id >= 0) {
cJSON_AddNumberToObject(knx_json, "endpointOwnerGatewayId",
endpoint_owner_gateway_id);
}
const std::string router_error = knx_router == nullptr ? "" : knx_router->lastError();
cJSON_AddStringToObject(knx_json, "lastError",
knx_last_error.empty() ? router_error.c_str()
: knx_last_error.c_str());
cJSON* security_json = cJSON_CreateObject();
if (security_json != nullptr) {
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "dataSecureCompiled", true);
#else
cJSON_AddBoolToObject(security_json, "dataSecureCompiled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "knxnetIpSecureCompiled", true);
#else
cJSON_AddBoolToObject(security_json, "knxnetIpSecureCompiled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED)
cJSON_AddBoolToObject(security_json, "knxnetIpSecureServicesRecognized", true);
#else
cJSON_AddBoolToObject(security_json, "knxnetIpSecureServicesRecognized", false);
#endif
cJSON_AddBoolToObject(security_json, "knxnetIpSecureImplemented", false);
#if defined(CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS)
cJSON_AddBoolToObject(security_json, "developmentEndpointsEnabled", true);
#else
cJSON_AddBoolToObject(security_json, "developmentEndpointsEnabled", false);
#endif
#if defined(CONFIG_GATEWAY_KNX_SECURITY_PLAIN_NVS)
cJSON_AddBoolToObject(security_json, "plainNvsStorage", true);
cJSON_AddStringToObject(security_json, "storage", "plain_nvs_development");
#else
cJSON_AddBoolToObject(security_json, "plainNvsStorage", false);
cJSON_AddStringToObject(security_json, "storage", "none");
#endif
#if defined(CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED)
const auto fdsk_info = openknx::LoadFactoryFdskInfo();
cJSON* fdsk_json = FactoryFdskInfoToCjson(fdsk_info, false);
if (fdsk_json != nullptr) {
cJSON_AddItemToObject(security_json, "factorySetupKey", fdsk_json);
}
cJSON* certificate_json = FactoryCertificateToCjson(
openknx::BuildFactoryCertificatePayload(), false);
if (certificate_json != nullptr) {
cJSON_AddItemToObject(security_json, "factoryCertificate", certificate_json);
}
cJSON* failures_json = SecurityFailuresToCjson();
if (failures_json != nullptr) {
cJSON_AddItemToObject(security_json, "failures", failures_json);
}
#endif
cJSON_AddItemToObject(knx_json, "security", security_json);
}
if (effective_knx.has_value()) {
cJSON_AddBoolToObject(knx_json, "daliRouterEnabled", effective_knx->dali_router_enabled);
cJSON_AddBoolToObject(knx_json, "ipRouterEnabled", effective_knx->ip_router_enabled);
cJSON_AddBoolToObject(knx_json, "tunnelEnabled", effective_knx->tunnel_enabled);
cJSON_AddBoolToObject(knx_json, "multicastEnabled", effective_knx->multicast_enabled);
cJSON_AddBoolToObject(knx_json, "etsDatabaseEnabled", effective_knx->ets_database_enabled);
cJSON_AddNumberToObject(knx_json, "etsBindingCount",
knx == nullptr ? 0 : knx->etsBindingCount());
cJSON_AddStringToObject(knx_json, "mappingMode",
GatewayKnxMappingModeToString(effective_knx->mapping_mode));
cJSON_AddNumberToObject(knx_json, "mainGroup", effective_knx->main_group);
cJSON_AddNumberToObject(knx_json, "udpPort", effective_knx->udp_port);
cJSON_AddStringToObject(knx_json, "multicastAddress",
effective_knx->multicast_address.c_str());
cJSON_AddNumberToObject(knx_json, "ipInterfaceIndividualAddress",
effective_knx->ip_interface_individual_address);
cJSON_AddNumberToObject(knx_json, "individualAddress",
effective_knx->individual_address);
cJSON* serial_json = cJSON_CreateObject();
if (serial_json != nullptr) {
cJSON_AddNumberToObject(serial_json, "uartPort", effective_knx->tp_uart.uart_port);
cJSON_AddNumberToObject(serial_json, "txPin", effective_knx->tp_uart.tx_pin);
cJSON_AddNumberToObject(serial_json, "rxPin", effective_knx->tp_uart.rx_pin);
cJSON_AddNumberToObject(serial_json, "baudrate", effective_knx->tp_uart.baudrate);
cJSON_AddBoolToObject(serial_json, "nineBitMode", effective_knx->tp_uart.nine_bit_mode);
cJSON_AddItemToObject(knx_json, "tpUart", serial_json);
}
}
cJSON_AddItemToObject(root, "knx", knx_json);
}
@@ -2333,6 +2402,21 @@ struct GatewayBridgeService::ChannelRuntime {
return root;
}
GatewayBridgeHttpResponse knxStatusJson() const {
cJSON* root = cJSON_CreateObject();
if (root == nullptr) {
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate KNX status JSON");
}
cJSON_AddNumberToObject(root, "gatewayId", channel.gateway_id);
cJSON* knx_json = knxStatusCjson();
if (knx_json == nullptr) {
cJSON_Delete(root);
return ErrorResponse(ESP_ERR_NO_MEM, "failed to allocate KNX status JSON");
}
cJSON_AddItemToObject(root, "knx", knx_json);
return JsonOk(root);
}
GatewayBridgeHttpResponse configJson() const {
return GatewayBridgeHttpResponse{ESP_OK,
GatewayBridgeStoredConfigToJson(bridge_config, modbus_config,
@@ -4199,6 +4283,9 @@ GatewayBridgeHttpResponse GatewayBridgeService::handleGet(
if (action == "status") {
return JsonOk(runtime->statusCjson());
}
if (action == "knx_status") {
return runtime->knxStatusJson();
}
if (action == "config") {
return runtime->configJson();
}
@@ -4718,4 +4805,50 @@ GatewayBridgeHttpResponse GatewayBridgeService::handlePost(
return ErrorResponse(ESP_ERR_INVALID_ARG, "unknown bridge POST action");
}
std::string GatewayBridgeService::handleTransportRequest(uint8_t gateway_id,
std::string_view request) {
cJSON* root = request.empty() ? nullptr : cJSON_ParseWithLength(request.data(), request.size());
if (root == nullptr || !cJSON_IsObject(root)) {
cJSON_Delete(root);
return BuildBridgeTransportEnvelope(
ErrorResponse(ESP_ERR_INVALID_ARG, "invalid bridge request JSON"));
}
const char* action_raw = JsonString(root, "action");
const char* method_raw = JsonString(root, "method");
if (action_raw == nullptr || method_raw == nullptr) {
cJSON_Delete(root);
return BuildBridgeTransportEnvelope(
ErrorResponse(ESP_ERR_INVALID_ARG, "bridge request requires action and method"));
}
const auto request_gateway_id = JsonGatewayId(root);
if (request_gateway_id.has_value() && request_gateway_id.value() != gateway_id) {
cJSON_Delete(root);
return BuildBridgeTransportEnvelope(
ErrorResponse(ESP_ERR_INVALID_ARG, "bridge request gateway id mismatch"));
}
std::string method(method_raw);
std::transform(method.begin(), method.end(), method.begin(),
[](unsigned char ch) { return static_cast<char>(std::toupper(ch)); });
GatewayBridgeHttpResponse response;
if (method == "GET") {
const char* query = JsonString(root, "query");
response = handleGet(action_raw, gateway_id,
query == nullptr ? std::string() : std::string(query));
} else if (method == "POST") {
const cJSON* body_node = cJSON_GetObjectItemCaseSensitive(root, "body");
response = handlePost(action_raw, gateway_id,
body_node == nullptr ? std::string("{}")
: PrintJson(const_cast<cJSON*>(body_node)));
} else {
response = ErrorResponse(ESP_ERR_INVALID_ARG, "unsupported bridge request method");
}
cJSON_Delete(root);
return BuildBridgeTransportEnvelope(response);
}
} // namespace gateway