feat(gateway): integrate bridge service into gateway controller for transport handling
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user