Refactor DALI component: remove BACnet bridge support and update related documentation
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -6,7 +6,6 @@ idf_component_register(
|
|||||||
"src/bridge.cpp"
|
"src/bridge.cpp"
|
||||||
"src/bridge_model.cpp"
|
"src/bridge_model.cpp"
|
||||||
"src/bridge_provisioning.cpp"
|
"src/bridge_provisioning.cpp"
|
||||||
"src/bacnet_bridge.cpp"
|
|
||||||
"src/decode.cpp"
|
"src/decode.cpp"
|
||||||
"src/device.cpp"
|
"src/device.cpp"
|
||||||
"src/dt1.cpp"
|
"src/dt1.cpp"
|
||||||
|
|||||||
@@ -50,9 +50,8 @@ 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_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.hpp` provides `DaliBridgeEngine`, which resolves models and dispatches requests into `DaliComm`, `DaliBase`, and `DaliDT8`.
|
||||||
- `bridge_provisioning.hpp` provides `BridgeProvisioningStore` for persisting bridge models and shared protocol config in ESP-IDF NVS.
|
- `bridge_provisioning.hpp` provides `BridgeProvisioningStore` for persisting bridge models in ESP-IDF NVS.
|
||||||
- Modbus runtime support is owned by the native gateway project in `gateway/components/gateway_modbus`.
|
- Modbus and BACnet runtime support are owned by the native gateway project in `gateway/components/gateway_modbus` and `gateway/components/gateway_bacnet`.
|
||||||
- `bacnet_bridge.hpp` provides a BACnet skeleton adapter keyed by object type, instance, and property bindings.
|
|
||||||
|
|
||||||
### Example Model Mapping
|
### Example Model Mapping
|
||||||
|
|
||||||
@@ -107,7 +106,7 @@ Query-style operations return `data` when available and may include decoded flag
|
|||||||
|
|
||||||
## Bridge Provisioning via NVS
|
## Bridge Provisioning via NVS
|
||||||
|
|
||||||
Use `BridgeProvisioningStore` to persist bridge models and shared protocol-specific config such as BACnet:
|
Use `BridgeProvisioningStore` to persist bridge models:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
BridgeRuntimeConfig runtime;
|
BridgeRuntimeConfig runtime;
|
||||||
@@ -224,4 +223,4 @@ idf.py set-target esp32s3
|
|||||||
idf.py build
|
idf.py build
|
||||||
```
|
```
|
||||||
|
|
||||||
The example persists its bridge config in NVS, starts a real Modbus TCP listener, registers Modbus and BACnet bridge models, and routes Modbus writes through the shared bridge engine. The DALI gateway callbacks are still placeholders where you should connect your UART or transport driver.
|
The example persists its bridge config in NVS, registers generic bridge models, and routes requests through the shared bridge engine. The DALI gateway callbacks are still placeholders where you should connect your UART or transport driver. Use `gateway/apps/gateway` to exercise the gateway-owned Modbus and BACnet runtimes.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ESP32-S3 Bridge Example
|
# ESP32-S3 Bridge Example
|
||||||
|
|
||||||
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.
|
This ESP-IDF example wires `dali_cpp` into a standalone application and demonstrates how to register strongly typed bridge models. Modbus and BACnet runtime support now live in the native gateway project.
|
||||||
|
|
||||||
## Environment
|
## Environment
|
||||||
|
|
||||||
@@ -21,9 +21,8 @@ idf.py build
|
|||||||
|
|
||||||
- `DaliBridgeEngine` resolving model bindings.
|
- `DaliBridgeEngine` resolving model bindings.
|
||||||
- `BridgeProvisioningStore` loading and saving model/config state in NVS.
|
- `BridgeProvisioningStore` loading and saving model/config state in NVS.
|
||||||
- `DaliBacnetBridge` mapping BACnet property writes to DALI brightness percentage updates.
|
|
||||||
- Placeholder DALI gateway callbacks where you can connect your UART transport.
|
- Placeholder DALI gateway callbacks where you can connect your UART transport.
|
||||||
|
|
||||||
## Modbus Notes
|
## Modbus Notes
|
||||||
|
|
||||||
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.
|
The gateway implementation in `gateway/components/gateway_modbus` owns Modbus TCP, generated DALI point tables, and provisioned Modbus overrides. `gateway/components/gateway_bacnet` owns BACnet/IP object publishing and present-value routing. Use `gateway/apps/gateway` to exercise protocol behavior.
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
#include "bacnet_bridge.hpp"
|
|
||||||
#include "bridge.hpp"
|
#include "bridge.hpp"
|
||||||
#include "bridge_model.hpp"
|
#include "bridge_model.hpp"
|
||||||
#include "bridge_provisioning.hpp"
|
#include "bridge_provisioning.hpp"
|
||||||
@@ -56,32 +55,10 @@ BridgeModel makeStatusQueryModel() {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
BridgeModel makeBacnetBrightnessModel() {
|
|
||||||
BridgeModel model;
|
|
||||||
model.id = "bacnet-zone-2";
|
|
||||||
model.name = "BACnet zone 2 level";
|
|
||||||
model.protocol = BridgeProtocolKind::bacnet;
|
|
||||||
model.external.network = "floor-2";
|
|
||||||
model.external.device = "bacnet-controller";
|
|
||||||
model.external.objectType = BridgeObjectType::analogOutput;
|
|
||||||
model.external.objectInstance = 2;
|
|
||||||
model.external.property = "presentValue";
|
|
||||||
model.dali.shortAddress = 2;
|
|
||||||
model.operation = BridgeOperation::setBrightnessPercent;
|
|
||||||
model.valueEncoding = BridgeValueEncoding::percentage;
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
BridgeRuntimeConfig makeDefaultRuntimeConfig() {
|
BridgeRuntimeConfig makeDefaultRuntimeConfig() {
|
||||||
BridgeRuntimeConfig config;
|
BridgeRuntimeConfig config;
|
||||||
config.models.push_back(makeBrightnessModel());
|
config.models.push_back(makeBrightnessModel());
|
||||||
config.models.push_back(makeStatusQueryModel());
|
config.models.push_back(makeStatusQueryModel());
|
||||||
config.models.push_back(makeBacnetBrightnessModel());
|
|
||||||
|
|
||||||
BacnetBridgeConfig bacnet;
|
|
||||||
bacnet.deviceInstance = 1001;
|
|
||||||
bacnet.localAddress = "192.168.10.20";
|
|
||||||
config.bacnet = bacnet;
|
|
||||||
|
|
||||||
config.metadata["example"] = "esp32s3_bridge";
|
config.metadata["example"] = "esp32s3_bridge";
|
||||||
return config;
|
return config;
|
||||||
@@ -115,26 +92,12 @@ extern "C" void app_main(void) {
|
|||||||
engine.upsertModel(model);
|
engine.upsertModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DaliBacnetBridge bacnet(engine);
|
|
||||||
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",
|
|
||||||
binding.modelID.c_str(), bridgeObjectTypeToString(binding.objectType),
|
|
||||||
binding.objectInstance, binding.property.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
DaliBridgeRequest brightness_request;
|
DaliBridgeRequest brightness_request;
|
||||||
brightness_request.sequence = "startup-brightness";
|
brightness_request.sequence = "startup-brightness";
|
||||||
brightness_request.modelID = "line-1-brightness";
|
brightness_request.modelID = "line-1-brightness";
|
||||||
brightness_request.value = 180;
|
brightness_request.value = 180;
|
||||||
logResult("bridge brightness", engine.execute(brightness_request));
|
logResult("bridge brightness", engine.execute(brightness_request));
|
||||||
|
|
||||||
const DaliBridgeResult bacnet_result =
|
|
||||||
bacnet.handlePropertyWrite(BridgeObjectType::analogOutput, 2, "presentValue", 75.0);
|
|
||||||
logResult("bacnet write", bacnet_result);
|
|
||||||
|
|
||||||
DaliBridgeRequest status_request;
|
DaliBridgeRequest status_request;
|
||||||
status_request.sequence = "startup-status";
|
status_request.sequence = "startup-status";
|
||||||
status_request.modelID = "line-1-status";
|
status_request.modelID = "line-1-status";
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "bridge.hpp"
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct BacnetBridgeConfig {
|
|
||||||
uint32_t deviceInstance = 4194303;
|
|
||||||
std::string localAddress;
|
|
||||||
uint16_t udpPort = 47808;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BacnetObjectBinding {
|
|
||||||
std::string modelID;
|
|
||||||
BridgeObjectType objectType = BridgeObjectType::unknown;
|
|
||||||
int objectInstance = -1;
|
|
||||||
std::string property;
|
|
||||||
BridgeOperation operation = BridgeOperation::unknown;
|
|
||||||
BridgeDaliTarget target;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DaliBacnetBridge {
|
|
||||||
public:
|
|
||||||
explicit DaliBacnetBridge(DaliBridgeEngine& engine);
|
|
||||||
|
|
||||||
void setConfig(const BacnetBridgeConfig& config);
|
|
||||||
const BacnetBridgeConfig& config() const;
|
|
||||||
|
|
||||||
DaliBridgeResult handlePropertyWrite(BridgeObjectType objectType,
|
|
||||||
int objectInstance,
|
|
||||||
const std::string& property,
|
|
||||||
const DaliValue& value) const;
|
|
||||||
std::optional<BacnetObjectBinding> findObject(BridgeObjectType objectType,
|
|
||||||
int objectInstance,
|
|
||||||
const std::string& property) const;
|
|
||||||
std::vector<BacnetObjectBinding> describeObjects() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
DaliBridgeEngine& engine_;
|
|
||||||
BacnetBridgeConfig config_;
|
|
||||||
};
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "bacnet_bridge.hpp"
|
|
||||||
#include "bridge_model.hpp"
|
#include "bridge_model.hpp"
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -17,7 +15,6 @@ using esp_err_t = int;
|
|||||||
|
|
||||||
struct BridgeRuntimeConfig {
|
struct BridgeRuntimeConfig {
|
||||||
std::vector<BridgeModel> models;
|
std::vector<BridgeModel> models;
|
||||||
std::optional<BacnetBridgeConfig> bacnet;
|
|
||||||
DaliValue::Object metadata;
|
DaliValue::Object metadata;
|
||||||
|
|
||||||
static BridgeRuntimeConfig fromJson(const DaliValue::Object& json);
|
static BridgeRuntimeConfig fromJson(const DaliValue::Object& json);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base.hpp"
|
#include "base.hpp"
|
||||||
#include "bacnet_bridge.hpp"
|
|
||||||
#include "bridge.hpp"
|
#include "bridge.hpp"
|
||||||
#include "bridge_model.hpp"
|
#include "bridge_model.hpp"
|
||||||
#include "bridge_provisioning.hpp"
|
#include "bridge_provisioning.hpp"
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
#include "bacnet_bridge.hpp"
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
DaliBacnetBridge::DaliBacnetBridge(DaliBridgeEngine& engine) : engine_(engine) {}
|
|
||||||
|
|
||||||
void DaliBacnetBridge::setConfig(const BacnetBridgeConfig& config) { config_ = config; }
|
|
||||||
|
|
||||||
const BacnetBridgeConfig& DaliBacnetBridge::config() const { return config_; }
|
|
||||||
|
|
||||||
DaliBridgeResult DaliBacnetBridge::handlePropertyWrite(BridgeObjectType objectType,
|
|
||||||
int objectInstance,
|
|
||||||
const std::string& property,
|
|
||||||
const DaliValue& value) const {
|
|
||||||
const auto binding = findObject(objectType, objectInstance, property);
|
|
||||||
DaliBridgeRequest request;
|
|
||||||
request.sequence = "bacnet-" + std::to_string(objectInstance);
|
|
||||||
request.value = value;
|
|
||||||
|
|
||||||
if (!binding.has_value()) {
|
|
||||||
DaliBridgeResult result;
|
|
||||||
result.sequence = request.sequence;
|
|
||||||
result.error = "unmapped bacnet object";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.modelID = binding->modelID;
|
|
||||||
return engine_.execute(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<BacnetObjectBinding> DaliBacnetBridge::findObject(BridgeObjectType objectType,
|
|
||||||
int objectInstance,
|
|
||||||
const std::string& property) const {
|
|
||||||
for (const auto& model : engine_.listModels()) {
|
|
||||||
if (model.protocol != BridgeProtocolKind::bacnet) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (model.external.objectType != objectType) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (model.external.objectInstance.value_or(-1) != objectInstance) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!model.external.property.empty() && model.external.property != property) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return BacnetObjectBinding{model.id, objectType, objectInstance, property,
|
|
||||||
model.operation, model.dali};
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BacnetObjectBinding> DaliBacnetBridge::describeObjects() const {
|
|
||||||
std::vector<BacnetObjectBinding> bindings;
|
|
||||||
for (const auto& model : engine_.listModels()) {
|
|
||||||
if (model.protocol != BridgeProtocolKind::bacnet || !model.external.objectInstance.has_value()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bindings.push_back(BacnetObjectBinding{model.id, model.external.objectType,
|
|
||||||
model.external.objectInstance.value(),
|
|
||||||
model.external.property, model.operation,
|
|
||||||
model.dali});
|
|
||||||
}
|
|
||||||
return bindings;
|
|
||||||
}
|
|
||||||
@@ -101,26 +101,6 @@ esp_err_t readString(nvs_handle_t handle, const char* key, std::string* value) {
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<BacnetBridgeConfig> bacnetFromJson(const DaliValue* value) {
|
|
||||||
if (value == nullptr || value->asObject() == nullptr) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const auto& json = *value->asObject();
|
|
||||||
BacnetBridgeConfig config;
|
|
||||||
config.deviceInstance = static_cast<uint32_t>(getObjectInt(json, "deviceInstance").value_or(4194303));
|
|
||||||
config.localAddress = getObjectString(json, "localAddress").value_or("");
|
|
||||||
config.udpPort = static_cast<uint16_t>(getObjectInt(json, "udpPort").value_or(47808));
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
DaliValue bacnetToJson(const BacnetBridgeConfig& config) {
|
|
||||||
DaliValue::Object out;
|
|
||||||
out["deviceInstance"] = static_cast<int64_t>(config.deviceInstance);
|
|
||||||
out["localAddress"] = config.localAddress;
|
|
||||||
out["udpPort"] = static_cast<int>(config.udpPort);
|
|
||||||
return DaliValue(std::move(out));
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t saveJsonObject(const std::string& nvs_namespace, const char* key,
|
esp_err_t saveJsonObject(const std::string& nvs_namespace, const char* key,
|
||||||
const DaliValue::Object& object) {
|
const DaliValue::Object& object) {
|
||||||
if (key == nullptr || key[0] == '\0') {
|
if (key == nullptr || key[0] == '\0') {
|
||||||
@@ -227,7 +207,6 @@ BridgeRuntimeConfig BridgeRuntimeConfig::fromJson(const DaliValue::Object& json)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.bacnet = bacnetFromJson(getObjectValue(json, "bacnet"));
|
|
||||||
if (const auto* metadata = getObjectValue(json, "meta")) {
|
if (const auto* metadata = getObjectValue(json, "meta")) {
|
||||||
if (const auto* object = metadata->asObject()) {
|
if (const auto* object = metadata->asObject()) {
|
||||||
config.metadata = *object;
|
config.metadata = *object;
|
||||||
@@ -244,7 +223,6 @@ DaliValue::Object BridgeRuntimeConfig::toJson() const {
|
|||||||
modelsValue.emplace_back(model.toJson());
|
modelsValue.emplace_back(model.toJson());
|
||||||
}
|
}
|
||||||
out["models"] = std::move(modelsValue);
|
out["models"] = std::move(modelsValue);
|
||||||
if (bacnet.has_value()) out["bacnet"] = bacnetToJson(bacnet.value());
|
|
||||||
if (!metadata.empty()) out["meta"] = metadata;
|
if (!metadata.empty()) out["meta"] = metadata;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user