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:
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user