Enhance bridge operation handling and improve JSON conversion

- Added new bridge operations to the bridgeOperationToString and bridgeOperationFromString functions for better command recognition.
- Implemented a fromCjson function to convert cJSON objects to DaliValue, supporting various data types including arrays and objects.
- Introduced isKnownBridgeRequestKey function to filter out unknown keys in bridge requests.
- Refactored handleDownlink to utilize the new fromCjson function for value and metadata extraction, improving code clarity and maintainability.
- Updated response creation to use a more structured approach with DaliValue's toJson method.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-04 09:45:15 +08:00
parent fa4acef881
commit 1550ab15d6
6 changed files with 1062 additions and 276 deletions
+9
View File
@@ -1,9 +1,13 @@
#pragma once
#include "base.hpp"
#include "addr.hpp"
#include "bridge_model.hpp"
#include "dali_comm.hpp"
#include "dt1.hpp"
#include "dt4.hpp"
#include "dt5.hpp"
#include "dt6.hpp"
#include "dt8.hpp"
#include <map>
@@ -30,6 +34,7 @@ struct DaliBridgeResult {
std::optional<int> data;
std::string error;
DaliValue::Object metadata;
DaliValue::Array results;
DaliValue::Object toJson() const;
};
@@ -49,7 +54,11 @@ class DaliBridgeEngine {
DaliComm& comm_;
DaliBase base_;
DaliDT1 dt1_;
DaliDT4 dt4_;
DaliDT5 dt5_;
DaliDT6 dt6_;
DaliDT8 dt8_;
DaliAddr addr_;
std::map<std::string, BridgeModel> models_;
DaliBridgeResult executeResolved(const DaliBridgeRequest& request,
+30
View File
@@ -57,6 +57,36 @@ enum class BridgeOperation {
startEmergencyFunctionTest = 18,
stopEmergencyTest = 19,
startEmergencyDurationTest = 20,
setColorTemperatureRaw = 21,
setColourXY = 22,
setColourRGB = 23,
setTemporaryColourTemperature = 24,
setTemporaryColourXY = 25,
setTemporaryRGBDimLevels = 26,
setTemporaryColourMask = 27,
activateTemporaryColour = 28,
copyReportToTemporary = 29,
storeDt8SceneSnapshot = 30,
storeDt8PowerOnLevelSnapshot = 31,
storeDt8SystemFailureLevelSnapshot = 32,
getDt1Snapshot = 33,
getDt4Snapshot = 34,
getDt5Snapshot = 35,
getDt6Snapshot = 36,
getDt8StatusSnapshot = 37,
getGroupMask = 38,
setGroupMask = 39,
getSceneLevel = 40,
setSceneLevel = 41,
removeSceneLevel = 42,
getSceneMap = 43,
getAddressSettings = 44,
setAddressSettings = 45,
searchAddressRange = 46,
allocateAllShortAddresses = 47,
resetAndAllocateShortAddresses = 48,
stopAddressAllocation = 49,
batch = 50,
};
enum class BridgeValueEncoding {
+4 -4
View File
@@ -255,13 +255,13 @@ std::optional<std::pair<int, int>> DaliBase::getGradualChange(int a) {
std::optional<int> DaliBase::getGradualChangeRate(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->second;
return rs->first;
}
std::optional<int> DaliBase::getGradualChangeSpeed(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->first;
return rs->second;
}
bool DaliBase::setPowerOnLevel(int a, int value) { return setDTR(value) && storeDTRAsPoweredBright(a); }
@@ -291,7 +291,7 @@ bool DaliBase::setFadeTime(int a, int value) {
std::optional<int> DaliBase::getFadeTime(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->first;
return rs->second;
}
bool DaliBase::setFadeRate(int a, int value) { return setDTR(value) && storeDTRAsFadeRate(a); }
@@ -299,7 +299,7 @@ bool DaliBase::setFadeRate(int a, int value) { return setDTR(value) && storeDTRA
std::optional<int> DaliBase::getFadeRate(int a) {
const auto rs = getGradualChange(a);
if (!rs.has_value()) return std::nullopt;
return rs->second;
return rs->first;
}
std::optional<int> DaliBase::getNextDeviceType(int a) { return query(a, DALI_CMD_QUERY_NEXT_DEVICE_TYPE); }
+833 -240
View File
File diff suppressed because it is too large Load Diff
+108
View File
@@ -349,6 +349,66 @@ const char* bridgeOperationToString(BridgeOperation operation) {
return "stop_emergency_test";
case BridgeOperation::startEmergencyDurationTest:
return "start_emergency_duration_test";
case BridgeOperation::setColorTemperatureRaw:
return "set_color_temperature_raw";
case BridgeOperation::setColourXY:
return "set_colour_xy";
case BridgeOperation::setColourRGB:
return "set_colour_rgb";
case BridgeOperation::setTemporaryColourTemperature:
return "set_temporary_colour_temperature";
case BridgeOperation::setTemporaryColourXY:
return "set_temporary_colour_xy";
case BridgeOperation::setTemporaryRGBDimLevels:
return "set_temporary_rgb_dim_levels";
case BridgeOperation::setTemporaryColourMask:
return "set_temporary_colour_mask";
case BridgeOperation::activateTemporaryColour:
return "activate_temporary_colour";
case BridgeOperation::copyReportToTemporary:
return "copy_report_to_temporary";
case BridgeOperation::storeDt8SceneSnapshot:
return "store_dt8_scene_snapshot";
case BridgeOperation::storeDt8PowerOnLevelSnapshot:
return "store_dt8_power_on_level_snapshot";
case BridgeOperation::storeDt8SystemFailureLevelSnapshot:
return "store_dt8_system_failure_level_snapshot";
case BridgeOperation::getDt1Snapshot:
return "get_dt1_snapshot";
case BridgeOperation::getDt4Snapshot:
return "get_dt4_snapshot";
case BridgeOperation::getDt5Snapshot:
return "get_dt5_snapshot";
case BridgeOperation::getDt6Snapshot:
return "get_dt6_snapshot";
case BridgeOperation::getDt8StatusSnapshot:
return "get_dt8_status_snapshot";
case BridgeOperation::getGroupMask:
return "get_group_mask";
case BridgeOperation::setGroupMask:
return "set_group_mask";
case BridgeOperation::getSceneLevel:
return "get_scene_level";
case BridgeOperation::setSceneLevel:
return "set_scene_level";
case BridgeOperation::removeSceneLevel:
return "remove_scene_level";
case BridgeOperation::getSceneMap:
return "get_scene_map";
case BridgeOperation::getAddressSettings:
return "get_address_settings";
case BridgeOperation::setAddressSettings:
return "set_address_settings";
case BridgeOperation::searchAddressRange:
return "search_address_range";
case BridgeOperation::allocateAllShortAddresses:
return "allocate_all_short_addresses";
case BridgeOperation::resetAndAllocateShortAddresses:
return "reset_and_allocate_short_addresses";
case BridgeOperation::stopAddressAllocation:
return "stop_address_allocation";
case BridgeOperation::batch:
return "batch";
case BridgeOperation::unknown:
default:
return "unknown";
@@ -381,6 +441,54 @@ BridgeOperation bridgeOperationFromString(const std::string& value) {
if (normalized == "start_emergency_duration_test") {
return BridgeOperation::startEmergencyDurationTest;
}
if (normalized == "set_color_temperature_raw") return BridgeOperation::setColorTemperatureRaw;
if (normalized == "set_colour_xy" || normalized == "set_color_xy") return BridgeOperation::setColourXY;
if (normalized == "set_colour_rgb" || normalized == "set_color_rgb") return BridgeOperation::setColourRGB;
if (normalized == "set_temporary_colour_temperature" || normalized == "set_temporary_color_temperature") {
return BridgeOperation::setTemporaryColourTemperature;
}
if (normalized == "set_temporary_colour_xy" || normalized == "set_temporary_color_xy") {
return BridgeOperation::setTemporaryColourXY;
}
if (normalized == "set_temporary_rgb_dim_levels") return BridgeOperation::setTemporaryRGBDimLevels;
if (normalized == "set_temporary_colour_mask" || normalized == "set_temporary_color_mask") {
return BridgeOperation::setTemporaryColourMask;
}
if (normalized == "activate_temporary_colour" || normalized == "activate_temporary_color") {
return BridgeOperation::activateTemporaryColour;
}
if (normalized == "copy_report_to_temporary") return BridgeOperation::copyReportToTemporary;
if (normalized == "store_dt8_scene_snapshot") return BridgeOperation::storeDt8SceneSnapshot;
if (normalized == "store_dt8_power_on_level_snapshot" || normalized == "store_dt8_power_on_snapshot") {
return BridgeOperation::storeDt8PowerOnLevelSnapshot;
}
if (normalized == "store_dt8_system_failure_level_snapshot" || normalized == "store_dt8_system_failure_snapshot") {
return BridgeOperation::storeDt8SystemFailureLevelSnapshot;
}
if (normalized == "get_dt1_snapshot" || normalized == "dt1_snapshot") return BridgeOperation::getDt1Snapshot;
if (normalized == "get_dt4_snapshot" || normalized == "dt4_snapshot") return BridgeOperation::getDt4Snapshot;
if (normalized == "get_dt5_snapshot" || normalized == "dt5_snapshot") return BridgeOperation::getDt5Snapshot;
if (normalized == "get_dt6_snapshot" || normalized == "dt6_snapshot") return BridgeOperation::getDt6Snapshot;
if (normalized == "get_dt8_status_snapshot" || normalized == "dt8_status_snapshot") {
return BridgeOperation::getDt8StatusSnapshot;
}
if (normalized == "get_group_mask") return BridgeOperation::getGroupMask;
if (normalized == "set_group_mask") return BridgeOperation::setGroupMask;
if (normalized == "get_scene_level") return BridgeOperation::getSceneLevel;
if (normalized == "set_scene_level") return BridgeOperation::setSceneLevel;
if (normalized == "remove_scene_level") return BridgeOperation::removeSceneLevel;
if (normalized == "get_scene_map" || normalized == "get_scenes") return BridgeOperation::getSceneMap;
if (normalized == "get_address_settings") return BridgeOperation::getAddressSettings;
if (normalized == "set_address_settings") return BridgeOperation::setAddressSettings;
if (normalized == "search_address_range") return BridgeOperation::searchAddressRange;
if (normalized == "allocate_all_short_addresses" || normalized == "allocate_all_addr") {
return BridgeOperation::allocateAllShortAddresses;
}
if (normalized == "reset_and_allocate_short_addresses" || normalized == "reset_and_alloc_addr") {
return BridgeOperation::resetAndAllocateShortAddresses;
}
if (normalized == "stop_address_allocation") return BridgeOperation::stopAddressAllocation;
if (normalized == "batch") return BridgeOperation::batch;
return BridgeOperation::unknown;
}
+78 -32
View File
@@ -1,5 +1,6 @@
#include "gateway_cloud.hpp"
#include <cstring>
#include <sstream>
#ifdef ESP_PLATFORM
@@ -33,6 +34,42 @@ int toInt(const cJSON* item, int fallback) {
return item->valueint;
}
DaliValue fromCjson(const cJSON* item) {
if (item == nullptr || cJSON_IsNull(item)) {
return DaliValue();
}
if (cJSON_IsBool(item)) {
return DaliValue(cJSON_IsTrue(item));
}
if (cJSON_IsNumber(item)) {
const double value = item->valuedouble;
if (value == static_cast<double>(item->valueint)) {
return DaliValue(item->valueint);
}
return DaliValue(value);
}
if (cJSON_IsString(item) && item->valuestring != nullptr) {
return DaliValue(std::string(item->valuestring));
}
if (cJSON_IsArray(item)) {
DaliValue::Array out;
for (const cJSON* child = item->child; child != nullptr; child = child->next) {
out.push_back(fromCjson(child));
}
return DaliValue(std::move(out));
}
if (cJSON_IsObject(item)) {
DaliValue::Object out;
for (const cJSON* child = item->child; child != nullptr; child = child->next) {
if (child->string != nullptr) {
out[child->string] = fromCjson(child);
}
}
return DaliValue(std::move(out));
}
return DaliValue();
}
cJSON* toCjson(const DaliValue& value) {
if (value.isNull()) {
return cJSON_CreateNull();
@@ -66,6 +103,18 @@ cJSON* toCjson(const DaliValue& value) {
return cJSON_CreateNull();
}
bool isKnownBridgeRequestKey(const char* key) {
if (key == nullptr) return true;
static const char* known[] = {"type", "seq", "sequence", "model",
"modelID", "modelId", "op", "operation",
"addr", "rawAddress", "cmd", "rawCommand",
"shortAddress", "short_address", "value", "meta"};
for (const char* item : known) {
if (std::strcmp(key, item) == 0) return true;
}
return false;
}
} // namespace
#endif
@@ -169,45 +218,58 @@ bool DaliCloudBridge::handleDownlink(const std::string& payload) {
return false;
}
const cJSON* seqItem = cJSON_GetObjectItemCaseSensitive(root, "seq");
const cJSON* modelItem = cJSON_GetObjectItemCaseSensitive(root, "model");
const cJSON* opItem = cJSON_GetObjectItemCaseSensitive(root, "op");
const cJSON* addrItem = cJSON_GetObjectItemCaseSensitive(root, "addr");
const cJSON* cmdItem = cJSON_GetObjectItemCaseSensitive(root, "cmd");
const cJSON* shortAddrItem = cJSON_GetObjectItemCaseSensitive(root, "shortAddress");
const cJSON* valueItem = cJSON_GetObjectItemCaseSensitive(root, "value");
DaliBridgeRequest request;
const cJSON* seqItem = cJSON_GetObjectItemCaseSensitive(root, "seq");
if (seqItem == nullptr) seqItem = cJSON_GetObjectItemCaseSensitive(root, "sequence");
request.sequence = toString(seqItem);
const cJSON* modelItem = cJSON_GetObjectItemCaseSensitive(root, "model");
if (modelItem == nullptr) modelItem = cJSON_GetObjectItemCaseSensitive(root, "modelID");
if (modelItem == nullptr) modelItem = cJSON_GetObjectItemCaseSensitive(root, "modelId");
request.modelID = toString(modelItem);
const cJSON* opItem = cJSON_GetObjectItemCaseSensitive(root, "op");
if (opItem == nullptr) opItem = cJSON_GetObjectItemCaseSensitive(root, "operation");
const std::string op = toString(opItem);
if (!op.empty()) {
request.operation = bridgeOperationFromString(op);
}
const cJSON* addrItem = cJSON_GetObjectItemCaseSensitive(root, "addr");
if (addrItem == nullptr) addrItem = cJSON_GetObjectItemCaseSensitive(root, "rawAddress");
const int addr = toInt(addrItem, -1);
if (addr >= 0) {
request.rawAddress = addr;
}
const cJSON* cmdItem = cJSON_GetObjectItemCaseSensitive(root, "cmd");
if (cmdItem == nullptr) cmdItem = cJSON_GetObjectItemCaseSensitive(root, "rawCommand");
const int cmd = toInt(cmdItem, -1);
if (cmd >= 0) {
request.rawCommand = cmd;
}
const cJSON* shortAddrItem = cJSON_GetObjectItemCaseSensitive(root, "shortAddress");
if (shortAddrItem == nullptr) shortAddrItem = cJSON_GetObjectItemCaseSensitive(root, "short_address");
const int shortAddr = toInt(shortAddrItem, -1);
if (shortAddr >= 0) {
request.shortAddress = shortAddr;
}
if (valueItem != nullptr) {
if (cJSON_IsNumber(valueItem)) {
request.value = valueItem->valuedouble;
} else if (cJSON_IsString(valueItem) && valueItem->valuestring != nullptr) {
request.value = std::string(valueItem->valuestring);
} else if (cJSON_IsBool(valueItem)) {
request.value = cJSON_IsTrue(valueItem);
if (const cJSON* valueItem = cJSON_GetObjectItemCaseSensitive(root, "value")) {
request.value = fromCjson(valueItem);
}
if (const cJSON* metaItem = cJSON_GetObjectItemCaseSensitive(root, "meta")) {
const DaliValue metaValue = fromCjson(metaItem);
if (const auto* metaObject = metaValue.asObject()) {
request.metadata = *metaObject;
}
}
for (const cJSON* child = root->child; child != nullptr; child = child->next) {
if (!isKnownBridgeRequestKey(child->string)) {
request.metadata[child->string] = fromCjson(child);
}
}
@@ -217,23 +279,7 @@ bool DaliCloudBridge::handleDownlink(const std::string& payload) {
const DaliBridgeResult result = bridge_.execute(request);
cJSON* resp = cJSON_CreateObject();
cJSON_AddStringToObject(resp, "type", "dali_resp");
cJSON_AddStringToObject(resp, "seq", result.sequence.c_str());
if (!result.modelID.empty()) {
cJSON_AddStringToObject(resp, "model", result.modelID.c_str());
}
cJSON_AddStringToObject(resp, "op", bridgeOperationToString(result.operation));
cJSON_AddBoolToObject(resp, "ok", result.ok);
if (result.data.has_value()) {
cJSON_AddNumberToObject(resp, "data", result.data.value());
}
if (!result.error.empty()) {
cJSON_AddStringToObject(resp, "error", result.error.c_str());
}
if (!result.metadata.empty()) {
cJSON_AddItemToObject(resp, "meta", toCjson(DaliValue(result.metadata)));
}
cJSON* resp = toCjson(DaliValue(result.toJson()));
char* raw = cJSON_PrintUnformatted(resp);
std::string out = raw == nullptr ? "{}" : std::string(raw);