Implement DALI Bridge Engine and Model Management

- Added `bridge.cpp` to handle DALI bridge operations including model management, command execution, and response formatting.
- Introduced `bridge_model.cpp` for defining bridge models, value transformations, and JSON serialization/deserialization.
- Created `bridge_provisioning.cpp` for managing bridge configuration storage and retrieval using NVS on ESP platform.
- Enhanced `gateway_cloud.cpp` to integrate DALI bridge requests and responses with cloud communication.
- Introduced `modbus_bridge.cpp` to handle Modbus-specific operations and register management.
- Implemented utility functions for converting between DaliValue and cJSON formats.
- Added error handling and metadata management in bridge responses.
This commit is contained in:
Tony
2026-04-22 10:14:42 +08:00
parent ec5f36b581
commit 21afc6b942
22 changed files with 2045 additions and 35 deletions
+42
View File
@@ -0,0 +1,42 @@
#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;
};
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_;
};
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include "base.hpp"
#include "bridge_model.hpp"
#include "dali_comm.hpp"
#include "dt1.hpp"
#include "dt8.hpp"
#include <map>
#include <optional>
#include <string>
#include <vector>
struct DaliBridgeRequest {
std::string sequence;
std::string modelID;
std::optional<BridgeOperation> operation;
std::optional<int> rawAddress;
std::optional<int> rawCommand;
std::optional<int> shortAddress;
DaliValue value;
DaliValue::Object metadata;
};
struct DaliBridgeResult {
std::string sequence;
std::string modelID;
BridgeOperation operation = BridgeOperation::unknown;
bool ok = false;
std::optional<int> data;
std::string error;
DaliValue::Object metadata;
DaliValue::Object toJson() const;
};
class DaliBridgeEngine {
public:
explicit DaliBridgeEngine(DaliComm& comm);
bool upsertModel(const BridgeModel& model);
bool removeModel(const std::string& modelID);
const BridgeModel* findModel(const std::string& modelID) const;
std::vector<BridgeModel> listModels() const;
DaliBridgeResult execute(const DaliBridgeRequest& request);
private:
DaliComm& comm_;
DaliBase base_;
DaliDT1 dt1_;
DaliDT8 dt8_;
std::map<std::string, BridgeModel> models_;
DaliBridgeResult executeResolved(const DaliBridgeRequest& request,
const BridgeModel* model,
BridgeOperation operation);
std::optional<int> resolveShortAddress(const DaliBridgeRequest& request,
const BridgeModel* model) const;
std::optional<int> resolveRawAddress(const DaliBridgeRequest& request,
const BridgeModel* model) const;
std::optional<int> resolveRawCommand(const DaliBridgeRequest& request,
const BridgeModel* model) const;
std::optional<int> resolveIntValue(const DaliBridgeRequest& request, const BridgeModel* model) const;
std::optional<double> resolveDoubleValue(const DaliBridgeRequest& request) const;
};
+117
View File
@@ -0,0 +1,117 @@
#pragma once
#include "model_value.hpp"
#include <optional>
#include <string>
enum class BridgeProtocolKind {
unknown = 0,
mqtt = 1,
modbus = 2,
bacnet = 3,
};
enum class BridgeObjectType {
unknown = 0,
holdingRegister = 1,
inputRegister = 2,
coil = 3,
discreteInput = 4,
analogValue = 5,
analogOutput = 6,
binaryValue = 7,
binaryOutput = 8,
multiStateValue = 9,
};
enum class BridgeOperation {
unknown = 0,
send = 1,
sendExt = 2,
query = 3,
setBrightness = 4,
setBrightnessPercent = 5,
on = 6,
off = 7,
recallMaxLevel = 8,
recallMinLevel = 9,
setColorTemperature = 10,
getBrightness = 11,
getStatus = 12,
getColorTemperature = 13,
getColorStatus = 14,
getEmergencyLevel = 15,
getEmergencyStatus = 16,
getEmergencyFailureStatus = 17,
startEmergencyFunctionTest = 18,
stopEmergencyTest = 19,
};
enum class BridgeValueEncoding {
none = 0,
integer = 1,
percentage = 2,
kelvin = 3,
};
struct BridgeValueTransform {
double scale = 1.0;
double offset = 0.0;
std::optional<int> clampMin;
std::optional<int> clampMax;
static BridgeValueTransform fromJson(const DaliValue::Object* json);
DaliValue::Object toJson() const;
int apply(double raw) const;
};
struct BridgeExternalPoint {
std::string network;
std::string device;
BridgeObjectType objectType = BridgeObjectType::unknown;
std::optional<int> objectInstance;
std::optional<int> registerAddress;
std::optional<int> bitIndex;
std::string property;
static BridgeExternalPoint fromJson(const DaliValue::Object* json);
DaliValue::Object toJson() const;
};
struct BridgeDaliTarget {
std::optional<int> shortAddress;
std::optional<int> rawAddress;
std::optional<int> rawCommand;
static BridgeDaliTarget fromJson(const DaliValue::Object* json);
DaliValue::Object toJson() const;
};
struct BridgeModel {
std::string id;
std::string name;
BridgeProtocolKind protocol = BridgeProtocolKind::unknown;
BridgeExternalPoint external;
BridgeDaliTarget dali;
BridgeOperation operation = BridgeOperation::unknown;
BridgeValueEncoding valueEncoding = BridgeValueEncoding::integer;
BridgeValueTransform valueTransform;
DaliValue::Object metadata;
static BridgeModel fromJson(const DaliValue::Object& json);
DaliValue::Object toJson() const;
std::string displayName() const;
};
const char* bridgeProtocolKindToString(BridgeProtocolKind kind);
BridgeProtocolKind bridgeProtocolKindFromString(const std::string& value);
const char* bridgeObjectTypeToString(BridgeObjectType type);
BridgeObjectType bridgeObjectTypeFromString(const std::string& value);
const char* bridgeOperationToString(BridgeOperation operation);
BridgeOperation bridgeOperationFromString(const std::string& value);
const char* bridgeValueEncodingToString(BridgeValueEncoding encoding);
BridgeValueEncoding bridgeValueEncodingFromString(const std::string& value);
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "bacnet_bridge.hpp"
#include "bridge_model.hpp"
#include "modbus_bridge.hpp"
#include <optional>
#include <string>
#include <vector>
#ifdef ESP_PLATFORM
extern "C" {
#include "esp_err.h"
}
#else
using esp_err_t = int;
#endif
struct BridgeRuntimeConfig {
std::vector<BridgeModel> models;
std::optional<ModbusBridgeConfig> modbus;
std::optional<BacnetBridgeConfig> bacnet;
DaliValue::Object metadata;
static BridgeRuntimeConfig fromJson(const DaliValue::Object& json);
DaliValue::Object toJson() const;
};
class BridgeProvisioningStore {
public:
explicit BridgeProvisioningStore(std::string nvsNamespace = "dali_bridge")
: nvsNamespace_(std::move(nvsNamespace)) {}
esp_err_t save(const BridgeRuntimeConfig& config) const;
esp_err_t load(BridgeRuntimeConfig* config) const;
esp_err_t clear() const;
private:
std::string nvsNamespace_;
};
+5
View File
@@ -1,6 +1,10 @@
#pragma once
#include "base.hpp"
#include "bacnet_bridge.hpp"
#include "bridge.hpp"
#include "bridge_model.hpp"
#include "bridge_provisioning.hpp"
#include "bus_monitor.hpp"
#include "comm.hpp"
#include "dali_comm.hpp"
@@ -14,6 +18,7 @@
#include "color.hpp"
#include "errors.hpp"
#include "log.hpp"
#include "modbus_bridge.hpp"
#include "query_scheduler.hpp"
#include "sequence.hpp"
#include "sequence_store.hpp"
+1
View File
@@ -11,6 +11,7 @@ class ColorStatus {
public:
explicit ColorStatus(int status) : status_(status) {}
int raw() const { return status_; }
bool xyOutOfRange() const { return (status_ & 0x01) != 0; }
bool ctOutOfRange() const { return (status_ & 0x02) != 0; }
bool autoCalibrationActive() const { return (status_ & 0x04) != 0; }
+4
View File
@@ -1,5 +1,6 @@
#pragma once
#include "bridge.hpp"
#include "dali_comm.hpp"
#include <atomic>
@@ -27,6 +28,8 @@ class DaliCloudBridge {
bool start(const GatewayCloudConfig& config);
void stop();
bool isConnected() const;
DaliBridgeEngine& bridge() { return bridge_; }
const DaliBridgeEngine& bridge() const { return bridge_; }
bool publishStatus(const std::string& status);
bool publishRegister(const std::string& payloadJson);
@@ -49,6 +52,7 @@ class DaliCloudBridge {
std::string topicRegister() const;
DaliComm& comm_;
DaliBridgeEngine bridge_;
GatewayCloudConfig config_;
std::atomic<bool> connected_{false};
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "bridge.hpp"
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
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<ModbusRegisterBinding> findHoldingRegister(int registerAddress) const;
std::vector<ModbusRegisterBinding> describeHoldingRegisters() const;
private:
DaliBridgeEngine& engine_;
ModbusBridgeConfig config_;
};