From 402d6a451b67a612d90b73b105aa1b9409774f11 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 4 May 2026 01:19:24 +0800 Subject: [PATCH] Refactor DALI component: remove Modbus bridge support, update documentation, and enhance bridge model handling --- CMakeLists.txt | 1 - README.md | 40 ++-- examples/esp32s3_bridge/README.md | 10 +- examples/esp32s3_bridge/main/main.cpp | 279 ++++---------------------- include/bridge_model.hpp | 4 + include/bridge_provisioning.hpp | 2 - include/dali.hpp | 1 - include/modbus_bridge.hpp | 36 ---- src/bridge_model.cpp | 12 ++ src/bridge_provisioning.cpp | 24 --- src/modbus_bridge.cpp | 55 ----- 11 files changed, 76 insertions(+), 388 deletions(-) delete mode 100644 include/modbus_bridge.hpp delete mode 100644 src/modbus_bridge.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2933ed8..9512a23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,6 @@ idf_component_register( "src/color.cpp" "src/gateway_cloud.cpp" "src/gateway_provisioning.cpp" - "src/modbus_bridge.cpp" INCLUDE_DIRS "include" REQUIRES mqtt cjson nvs_flash ) diff --git a/README.md b/README.md index 4827ba8..c8395c9 100644 --- a/README.md +++ b/README.md @@ -50,29 +50,28 @@ The component now includes a protocol-agnostic bridge layer for mapping external - `bridge_model.hpp` defines the strongly typed mapping model: protocol kind, external point, DALI target, default operation, and value transform. - `bridge.hpp` provides `DaliBridgeEngine`, which resolves models and dispatches requests into `DaliComm`, `DaliBase`, and `DaliDT8`. -- `bridge_provisioning.hpp` provides `BridgeProvisioningStore` for persisting bridge models and protocol config in ESP-IDF NVS. -- `modbus_bridge.hpp` provides a Modbus skeleton adapter keyed by holding register bindings. +- `bridge_provisioning.hpp` provides `BridgeProvisioningStore` for persisting bridge models and shared protocol config in ESP-IDF NVS. +- Modbus runtime support is owned by the native gateway project in `gateway/components/gateway_modbus`. - `bacnet_bridge.hpp` provides a BACnet skeleton adapter keyed by object type, instance, and property bindings. ### Example Model Mapping ```cpp -BridgeModel modbusBrightness; -modbusBrightness.id = "modbus-light-1"; -modbusBrightness.name = "Line 1 brightness"; -modbusBrightness.protocol = BridgeProtocolKind::modbus; -modbusBrightness.external.objectType = BridgeObjectType::holdingRegister; -modbusBrightness.external.registerAddress = 40001; -modbusBrightness.dali.shortAddress = 1; -modbusBrightness.operation = BridgeOperation::setBrightness; -modbusBrightness.valueTransform.clampMin = 0; -modbusBrightness.valueTransform.clampMax = 254; +BridgeModel brightness; +brightness.id = "line-1-brightness"; +brightness.name = "Line 1 brightness"; +brightness.dali.shortAddress = 1; +brightness.operation = BridgeOperation::setBrightness; +brightness.valueTransform.clampMin = 0; +brightness.valueTransform.clampMax = 254; DaliBridgeEngine engine(comm); -engine.upsertModel(modbusBrightness); +engine.upsertModel(brightness); -DaliModbusBridge modbus(engine); -modbus.handleHoldingRegisterWrite(40001, 180); +DaliBridgeRequest request; +request.modelID = "line-1-brightness"; +request.value = 180; +engine.execute(request); ``` ### Supported Bridge Operations @@ -108,16 +107,11 @@ Query-style operations return `data` when available and may include decoded flag ## Bridge Provisioning via NVS -Use `BridgeProvisioningStore` to persist bridge models and protocol-specific config for Modbus and BACnet: +Use `BridgeProvisioningStore` to persist bridge models and shared protocol-specific config such as BACnet: ```cpp BridgeRuntimeConfig runtime; -runtime.models.push_back(modbusBrightness); - -ModbusBridgeConfig modbus; -modbus.transport = "tcp-server"; -modbus.port = 1502; -runtime.modbus = modbus; +runtime.models.push_back(brightness); BridgeProvisioningStore store; store.save(runtime); @@ -128,6 +122,8 @@ if (store.load(&loaded) == ESP_OK) { } ``` +The gateway project stores Modbus TCP settings in the same persisted JSON shape, but parses and applies that section in `gateway/components/gateway_modbus` rather than in this standalone DALI component. + ## Cloud Bridge (ESP32 Gateway) The component now includes `DaliCloudBridge` in `include/gateway_cloud.hpp` to connect ESP32 gateways to the backend MQTT broker. diff --git a/examples/esp32s3_bridge/README.md b/examples/esp32s3_bridge/README.md index c51ac9d..beb88a9 100644 --- a/examples/esp32s3_bridge/README.md +++ b/examples/esp32s3_bridge/README.md @@ -1,6 +1,6 @@ # ESP32-S3 Bridge Example -This ESP-IDF example wires `dali_cpp` into a standalone application and demonstrates how to register strongly typed Modbus and BACnet bridge models. +This ESP-IDF example wires `dali_cpp` into a standalone application and demonstrates how to register strongly typed bridge and BACnet models. Modbus runtime support now lives in the native gateway project. ## Environment @@ -21,13 +21,9 @@ idf.py build - `DaliBridgeEngine` resolving model bindings. - `BridgeProvisioningStore` loading and saving model/config state in NVS. -- `DaliModbusBridge` mapping Modbus TCP holding-register writes to DALI operations. - `DaliBacnetBridge` mapping BACnet property writes to DALI brightness percentage updates. -- A simple Modbus TCP listener supporting write-single-register (`0x06`) and write-multiple-registers (`0x10`). - Placeholder DALI gateway callbacks where you can connect your UART transport. -## Modbus Mapping Notes +## Modbus Notes -- The example listens on port `1502` by default. -- Holding register `0` maps to bridge register `40001`. -- The default config is stored to NVS on first boot and reused on later boots. \ No newline at end of file +The gateway implementation in `gateway/components/gateway_modbus` owns Modbus TCP, generated DALI point tables, and provisioned Modbus overrides. Use `gateway/apps/gateway` to exercise Modbus behavior. \ No newline at end of file diff --git a/examples/esp32s3_bridge/main/main.cpp b/examples/esp32s3_bridge/main/main.cpp index a572992..9cc1304 100644 --- a/examples/esp32s3_bridge/main/main.cpp +++ b/examples/esp32s3_bridge/main/main.cpp @@ -3,32 +3,17 @@ #include "bridge_model.hpp" #include "bridge_provisioning.hpp" #include "dali_comm.hpp" -#include "modbus_bridge.hpp" -#include #include extern "C" { #include "esp_log.h" #include "nvs_flash.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "lwip/inet.h" -#include "lwip/sockets.h" } namespace { constexpr const char* kTag = "dali_bridge_example"; -constexpr int kModbusTaskStack = 6144; -constexpr int kDefaultModbusListenPort = 1502; - -struct ExampleContext { - DaliModbusBridge* modbus = nullptr; - uint16_t listenPort = kDefaultModbusListenPort; -}; - -ExampleContext gExampleContext; bool writeGateway(const uint8_t* data, size_t len) { ESP_LOGI(kTag, "placeholder DALI gateway write len=%u first=0x%02X", @@ -37,9 +22,9 @@ bool writeGateway(const uint8_t* data, size_t len) { return false; } -std::vector readGateway(size_t len, uint32_t timeoutMs) { +std::vector readGateway(size_t len, uint32_t timeout_ms) { ESP_LOGI(kTag, "placeholder DALI gateway read len=%u timeout=%u", - static_cast(len), static_cast(timeoutMs)); + static_cast(len), static_cast(timeout_ms)); return {}; } @@ -50,65 +35,10 @@ std::vector transactGateway(const uint8_t* data, size_t len) { return {}; } -uint16_t readBe16(const uint8_t* data) { - return static_cast((static_cast(data[0]) << 8) | data[1]); -} - -void writeBe16(uint8_t* data, uint16_t value) { - data[0] = static_cast((value >> 8) & 0xFF); - data[1] = static_cast(value & 0xFF); -} - -bool recvAll(int sock, uint8_t* buffer, size_t len) { - size_t received = 0; - while (received < len) { - const int ret = recv(sock, buffer + received, len - received, 0); - if (ret <= 0) { - return false; - } - received += static_cast(ret); - } - return true; -} - -bool sendAll(int sock, const uint8_t* buffer, size_t len) { - size_t sent = 0; - while (sent < len) { - const int ret = send(sock, buffer + sent, len - sent, 0); - if (ret <= 0) { - return false; - } - sent += static_cast(ret); - } - return true; -} - -int normalizeHoldingRegister(uint16_t zeroBasedAddress) { - return 40001 + static_cast(zeroBasedAddress); -} - -bool sendModbusFrame(int sock, const uint8_t* mbap, const std::vector& pdu) { - std::vector frame(7 + pdu.size()); - std::memcpy(frame.data(), mbap, 7); - writeBe16(&frame[4], static_cast(pdu.size() + 1)); - std::memcpy(frame.data() + 7, pdu.data(), pdu.size()); - return sendAll(sock, frame.data(), frame.size()); -} - -bool sendModbusException(int sock, const uint8_t* mbap, uint8_t functionCode, uint8_t exceptionCode) { - const std::vector pdu{static_cast(functionCode | 0x80), exceptionCode}; - return sendModbusFrame(sock, mbap, pdu); -} - -BridgeModel makeModbusBrightnessModel() { +BridgeModel makeBrightnessModel() { BridgeModel model; - model.id = "modbus-light-1"; - model.name = "Modbus line 1 brightness"; - model.protocol = BridgeProtocolKind::modbus; - model.external.network = "line-a"; - model.external.device = "plc-1"; - model.external.objectType = BridgeObjectType::holdingRegister; - model.external.registerAddress = 40001; + model.id = "line-1-brightness"; + model.name = "Line 1 brightness"; model.dali.shortAddress = 1; model.operation = BridgeOperation::setBrightness; model.valueEncoding = BridgeValueEncoding::integer; @@ -117,6 +47,15 @@ BridgeModel makeModbusBrightnessModel() { return model; } +BridgeModel makeStatusQueryModel() { + BridgeModel model; + model.id = "line-1-status"; + model.name = "Line 1 status"; + model.dali.shortAddress = 1; + model.operation = BridgeOperation::getStatus; + return model; +} + BridgeModel makeBacnetBrightnessModel() { BridgeModel model; model.id = "bacnet-zone-2"; @@ -133,31 +72,11 @@ BridgeModel makeBacnetBrightnessModel() { return model; } -BridgeModel makeStatusQueryModel() { - BridgeModel model; - model.id = "modbus-light-1-status"; - model.name = "Modbus line 1 status"; - model.protocol = BridgeProtocolKind::modbus; - model.external.network = "line-a"; - model.external.device = "plc-1"; - model.external.objectType = BridgeObjectType::holdingRegister; - model.external.registerAddress = 40002; - model.dali.shortAddress = 1; - model.operation = BridgeOperation::getStatus; - return model; -} - BridgeRuntimeConfig makeDefaultRuntimeConfig() { BridgeRuntimeConfig config; - config.models.push_back(makeModbusBrightnessModel()); - config.models.push_back(makeBacnetBrightnessModel()); + config.models.push_back(makeBrightnessModel()); config.models.push_back(makeStatusQueryModel()); - - ModbusBridgeConfig modbus; - modbus.transport = "tcp-server"; - modbus.port = kDefaultModbusListenPort; - modbus.unitID = 7; - config.modbus = modbus; + config.models.push_back(makeBacnetBrightnessModel()); BacnetBridgeConfig bacnet; bacnet.deviceInstance = 1001; @@ -174,151 +93,31 @@ void logResult(const char* label, const DaliBridgeResult& result) { result.error.empty() ? "" : result.error.c_str()); } -void handleModbusClient(int clientSock, const ExampleContext& context) { - uint8_t header[7]; - while (recvAll(clientSock, header, sizeof(header))) { - const uint16_t protocolID = readBe16(&header[2]); - const uint16_t length = readBe16(&header[4]); - if (protocolID != 0 || length < 2) { - break; - } - - std::vector pdu(length - 1); - if (!recvAll(clientSock, pdu.data(), pdu.size()) || pdu.empty()) { - break; - } - - const uint8_t functionCode = pdu[0]; - if (functionCode == 0x06 && pdu.size() == 5) { - const uint16_t registerAddress = readBe16(&pdu[1]); - const uint16_t value = readBe16(&pdu[3]); - const int holdingRegister = normalizeHoldingRegister(registerAddress); - const DaliBridgeResult result = context.modbus->handleHoldingRegisterWrite(holdingRegister, value); - logResult("modbus tcp write single", result); - if (!result.ok) { - sendModbusException(clientSock, header, functionCode, 0x04); - continue; - } - sendModbusFrame(clientSock, header, pdu); - continue; - } - - if (functionCode == 0x10 && pdu.size() >= 6) { - const uint16_t startAddress = readBe16(&pdu[1]); - const uint16_t quantity = readBe16(&pdu[3]); - const uint8_t byteCount = pdu[5]; - if (pdu.size() != static_cast(6 + byteCount) || byteCount != quantity * 2) { - sendModbusException(clientSock, header, functionCode, 0x03); - continue; - } - - bool ok = true; - for (uint16_t index = 0; index < quantity; ++index) { - const size_t offset = 6 + (index * 2); - const uint16_t value = readBe16(&pdu[offset]); - const int holdingRegister = normalizeHoldingRegister(static_cast(startAddress + index)); - const DaliBridgeResult result = context.modbus->handleHoldingRegisterWrite(holdingRegister, value); - logResult("modbus tcp write multiple", result); - if (!result.ok) { - ok = false; - break; - } - } - - if (!ok) { - sendModbusException(clientSock, header, functionCode, 0x04); - continue; - } - - std::vector response(5); - response[0] = functionCode; - writeBe16(&response[1], startAddress); - writeBe16(&response[3], quantity); - sendModbusFrame(clientSock, header, response); - continue; - } - - sendModbusException(clientSock, header, functionCode, 0x01); - } -} - -void modbusServerTask(void* arg) { - auto* context = static_cast(arg); - const int listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - if (listenSock < 0) { - ESP_LOGE(kTag, "failed to create Modbus listen socket"); - vTaskDelete(nullptr); - return; - } - - int reuse = 1; - setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - - sockaddr_in address = {}; - address.sin_family = AF_INET; - address.sin_addr.s_addr = htonl(INADDR_ANY); - address.sin_port = htons(context->listenPort); - - if (bind(listenSock, reinterpret_cast(&address), sizeof(address)) != 0 || - listen(listenSock, 2) != 0) { - ESP_LOGE(kTag, "failed to bind/listen Modbus TCP on port %u", context->listenPort); - close(listenSock); - vTaskDelete(nullptr); - return; - } - - ESP_LOGI(kTag, "Modbus TCP listener ready on port %u", context->listenPort); - while (true) { - sockaddr_in clientAddress = {}; - socklen_t clientLen = sizeof(clientAddress); - const int clientSock = accept(listenSock, reinterpret_cast(&clientAddress), &clientLen); - if (clientSock < 0) { - continue; - } - ESP_LOGI(kTag, "Modbus client connected"); - handleModbusClient(clientSock, *context); - close(clientSock); - ESP_LOGI(kTag, "Modbus client disconnected"); - } -} - } // namespace extern "C" void app_main(void) { - esp_err_t nvsErr = nvs_flash_init(); - if (nvsErr != ESP_OK) { - ESP_LOGW(kTag, "nvs_flash_init failed: %s", esp_err_to_name(nvsErr)); + esp_err_t nvs_err = nvs_flash_init(); + if (nvs_err != ESP_OK) { + ESP_LOGW(kTag, "nvs_flash_init failed: %s", esp_err_to_name(nvs_err)); } - BridgeProvisioningStore provisioningStore; - BridgeRuntimeConfig runtimeConfig; - if (provisioningStore.load(&runtimeConfig) != ESP_OK || runtimeConfig.models.empty()) { - runtimeConfig = makeDefaultRuntimeConfig(); - provisioningStore.save(runtimeConfig); + BridgeProvisioningStore provisioning_store; + BridgeRuntimeConfig runtime_config; + if (provisioning_store.load(&runtime_config) != ESP_OK || runtime_config.models.empty()) { + runtime_config = makeDefaultRuntimeConfig(); + provisioning_store.save(runtime_config); } static DaliComm comm(writeGateway, readGateway, transactGateway); static DaliBridgeEngine engine(comm); - for (const auto& model : runtimeConfig.models) { + for (const auto& model : runtime_config.models) { engine.upsertModel(model); } - static DaliModbusBridge modbus(engine); - ModbusBridgeConfig modbusConfig = runtimeConfig.modbus.value_or(ModbusBridgeConfig{}); - modbus.setConfig(modbusConfig); - static DaliBacnetBridge bacnet(engine); - BacnetBridgeConfig bacnetConfig = runtimeConfig.bacnet.value_or(BacnetBridgeConfig{}); - bacnet.setConfig(bacnetConfig); - - gExampleContext.modbus = &modbus; - gExampleContext.listenPort = modbus.config().port == 0 ? kDefaultModbusListenPort : modbus.config().port; - - for (const auto& binding : modbus.describeHoldingRegisters()) { - ESP_LOGI(kTag, "modbus binding model=%s register=%d", binding.modelID.c_str(), - binding.registerAddress); - } + BacnetBridgeConfig bacnet_config = runtime_config.bacnet.value_or(BacnetBridgeConfig{}); + bacnet.setConfig(bacnet_config); for (const auto& binding : bacnet.describeObjects()) { ESP_LOGI(kTag, "bacnet binding model=%s object=%s:%d property=%s", @@ -326,18 +125,18 @@ extern "C" void app_main(void) { binding.objectInstance, binding.property.c_str()); } - const DaliBridgeResult modbusResult = modbus.handleHoldingRegisterWrite(40001, 180); - logResult("modbus write", modbusResult); + DaliBridgeRequest brightness_request; + brightness_request.sequence = "startup-brightness"; + brightness_request.modelID = "line-1-brightness"; + brightness_request.value = 180; + logResult("bridge brightness", engine.execute(brightness_request)); - const DaliBridgeResult bacnetResult = + const DaliBridgeResult bacnet_result = bacnet.handlePropertyWrite(BridgeObjectType::analogOutput, 2, "presentValue", 75.0); - logResult("bacnet write", bacnetResult); + logResult("bacnet write", bacnet_result); - DaliBridgeRequest statusRequest; - statusRequest.sequence = "startup-status"; - statusRequest.modelID = "modbus-light-1-status"; - const DaliBridgeResult statusResult = engine.execute(statusRequest); - logResult("status query", statusResult); - - xTaskCreate(modbusServerTask, "modbus_tcp", kModbusTaskStack, &gExampleContext, 5, nullptr); -} \ No newline at end of file + DaliBridgeRequest status_request; + status_request.sequence = "startup-status"; + status_request.modelID = "line-1-status"; + logResult("status query", engine.execute(status_request)); +} diff --git a/include/bridge_model.hpp b/include/bridge_model.hpp index 810d22e..4f5bfe6 100644 --- a/include/bridge_model.hpp +++ b/include/bridge_model.hpp @@ -18,11 +18,15 @@ enum class BridgeObjectType { inputRegister = 2, coil = 3, discreteInput = 4, + analogInput = 10, analogValue = 5, analogOutput = 6, + binaryInput = 11, binaryValue = 7, binaryOutput = 8, + multiStateInput = 12, multiStateValue = 9, + multiStateOutput = 13, }; enum class BridgeDaliTargetKind { diff --git a/include/bridge_provisioning.hpp b/include/bridge_provisioning.hpp index 686307b..e674f9e 100644 --- a/include/bridge_provisioning.hpp +++ b/include/bridge_provisioning.hpp @@ -2,7 +2,6 @@ #include "bacnet_bridge.hpp" #include "bridge_model.hpp" -#include "modbus_bridge.hpp" #include #include @@ -18,7 +17,6 @@ using esp_err_t = int; struct BridgeRuntimeConfig { std::vector models; - std::optional modbus; std::optional bacnet; DaliValue::Object metadata; diff --git a/include/dali.hpp b/include/dali.hpp index f7e5117..913b4a8 100644 --- a/include/dali.hpp +++ b/include/dali.hpp @@ -21,7 +21,6 @@ #include "color.hpp" #include "errors.hpp" #include "log.hpp" -#include "modbus_bridge.hpp" #include "query_scheduler.hpp" #include "sequence.hpp" #include "sequence_store.hpp" diff --git a/include/modbus_bridge.hpp b/include/modbus_bridge.hpp deleted file mode 100644 index 8bc0727..0000000 --- a/include/modbus_bridge.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "bridge.hpp" - -#include -#include -#include -#include - -struct ModbusBridgeConfig { - std::string transport = "tcp"; - std::string host; - uint16_t port = 502; - uint8_t unitID = 1; -}; - -struct ModbusRegisterBinding { - std::string modelID; - int registerAddress = -1; -}; - -class DaliModbusBridge { - public: - explicit DaliModbusBridge(DaliBridgeEngine& engine); - - void setConfig(const ModbusBridgeConfig& config); - const ModbusBridgeConfig& config() const; - - DaliBridgeResult handleHoldingRegisterWrite(int registerAddress, int value) const; - std::optional findHoldingRegister(int registerAddress) const; - std::vector describeHoldingRegisters() const; - - private: - DaliBridgeEngine& engine_; - ModbusBridgeConfig config_; -}; \ No newline at end of file diff --git a/src/bridge_model.cpp b/src/bridge_model.cpp index 37ced9a..454f941 100644 --- a/src/bridge_model.cpp +++ b/src/bridge_model.cpp @@ -245,16 +245,24 @@ const char* bridgeObjectTypeToString(BridgeObjectType type) { return "coil"; case BridgeObjectType::discreteInput: return "discrete_input"; + case BridgeObjectType::analogInput: + return "analog_input"; case BridgeObjectType::analogValue: return "analog_value"; case BridgeObjectType::analogOutput: return "analog_output"; + case BridgeObjectType::binaryInput: + return "binary_input"; case BridgeObjectType::binaryValue: return "binary_value"; case BridgeObjectType::binaryOutput: return "binary_output"; + case BridgeObjectType::multiStateInput: + return "multi_state_input"; case BridgeObjectType::multiStateValue: return "multi_state_value"; + case BridgeObjectType::multiStateOutput: + return "multi_state_output"; case BridgeObjectType::unknown: default: return "unknown"; @@ -267,11 +275,15 @@ BridgeObjectType bridgeObjectTypeFromString(const std::string& value) { if (normalized == "input_register") return BridgeObjectType::inputRegister; if (normalized == "coil") return BridgeObjectType::coil; if (normalized == "discrete_input") return BridgeObjectType::discreteInput; + if (normalized == "analog_input") return BridgeObjectType::analogInput; if (normalized == "analog_value") return BridgeObjectType::analogValue; if (normalized == "analog_output") return BridgeObjectType::analogOutput; + if (normalized == "binary_input") return BridgeObjectType::binaryInput; if (normalized == "binary_value") return BridgeObjectType::binaryValue; if (normalized == "binary_output") return BridgeObjectType::binaryOutput; + if (normalized == "multi_state_input") return BridgeObjectType::multiStateInput; if (normalized == "multi_state_value") return BridgeObjectType::multiStateValue; + if (normalized == "multi_state_output") return BridgeObjectType::multiStateOutput; return BridgeObjectType::unknown; } diff --git a/src/bridge_provisioning.cpp b/src/bridge_provisioning.cpp index 43140be..bb20d18 100644 --- a/src/bridge_provisioning.cpp +++ b/src/bridge_provisioning.cpp @@ -101,28 +101,6 @@ esp_err_t readString(nvs_handle_t handle, const char* key, std::string* value) { return ESP_OK; } -std::optional modbusFromJson(const DaliValue* value) { - if (value == nullptr || value->asObject() == nullptr) { - return std::nullopt; - } - const auto& json = *value->asObject(); - ModbusBridgeConfig config; - config.transport = getObjectString(json, "transport").value_or("tcp"); - config.host = getObjectString(json, "host").value_or(""); - config.port = static_cast(getObjectInt(json, "port").value_or(502)); - config.unitID = static_cast(getObjectInt(json, "unitID").value_or(1)); - return config; -} - -DaliValue modbusToJson(const ModbusBridgeConfig& config) { - DaliValue::Object out; - out["transport"] = config.transport; - out["host"] = config.host; - out["port"] = static_cast(config.port); - out["unitID"] = static_cast(config.unitID); - return DaliValue(std::move(out)); -} - std::optional bacnetFromJson(const DaliValue* value) { if (value == nullptr || value->asObject() == nullptr) { return std::nullopt; @@ -249,7 +227,6 @@ BridgeRuntimeConfig BridgeRuntimeConfig::fromJson(const DaliValue::Object& json) } } } - config.modbus = modbusFromJson(getObjectValue(json, "modbus")); config.bacnet = bacnetFromJson(getObjectValue(json, "bacnet")); if (const auto* metadata = getObjectValue(json, "meta")) { if (const auto* object = metadata->asObject()) { @@ -267,7 +244,6 @@ DaliValue::Object BridgeRuntimeConfig::toJson() const { modelsValue.emplace_back(model.toJson()); } out["models"] = std::move(modelsValue); - if (modbus.has_value()) out["modbus"] = modbusToJson(modbus.value()); if (bacnet.has_value()) out["bacnet"] = bacnetToJson(bacnet.value()); if (!metadata.empty()) out["meta"] = metadata; return out; diff --git a/src/modbus_bridge.cpp b/src/modbus_bridge.cpp deleted file mode 100644 index 23d620c..0000000 --- a/src/modbus_bridge.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "modbus_bridge.hpp" - -#include - -DaliModbusBridge::DaliModbusBridge(DaliBridgeEngine& engine) : engine_(engine) {} - -void DaliModbusBridge::setConfig(const ModbusBridgeConfig& config) { config_ = config; } - -const ModbusBridgeConfig& DaliModbusBridge::config() const { return config_; } - -DaliBridgeResult DaliModbusBridge::handleHoldingRegisterWrite(int registerAddress, int value) const { - const auto binding = findHoldingRegister(registerAddress); - DaliBridgeRequest request; - request.sequence = "modbus-" + std::to_string(registerAddress); - request.value = value; - - if (!binding.has_value()) { - DaliBridgeResult result; - result.sequence = request.sequence; - result.error = "unmapped holding register"; - return result; - } - - request.modelID = binding->modelID; - return engine_.execute(request); -} - -std::optional DaliModbusBridge::findHoldingRegister(int registerAddress) const { - for (const auto& model : engine_.listModels()) { - if (model.protocol != BridgeProtocolKind::modbus) { - continue; - } - if (model.external.objectType != BridgeObjectType::holdingRegister) { - continue; - } - if (model.external.registerAddress.value_or(-1) != registerAddress) { - continue; - } - return ModbusRegisterBinding{model.id, registerAddress}; - } - return std::nullopt; -} - -std::vector DaliModbusBridge::describeHoldingRegisters() const { - std::vector bindings; - for (const auto& model : engine_.listModels()) { - if (model.protocol != BridgeProtocolKind::modbus || - model.external.objectType != BridgeObjectType::holdingRegister || - !model.external.registerAddress.has_value()) { - continue; - } - bindings.push_back(ModbusRegisterBinding{model.id, model.external.registerAddress.value()}); - } - return bindings; -} \ No newline at end of file