Files
dali_cpp/src/device.cpp
2026-03-26 12:04:08 +08:00

353 lines
13 KiB
C++

#include "device.hpp"
#include <utility>
namespace {
std::optional<std::vector<int>> asIntList(const DaliValue* value) {
if (!value) return std::nullopt;
const auto* arr = value->asArray();
if (!arr) return std::nullopt;
std::vector<int> out;
out.reserve(arr->size());
for (const auto& v : *arr) {
out.push_back(v.asInt().value_or(0));
}
return out;
}
DaliValue toIntArray(const std::optional<std::vector<int>>& values) {
if (!values.has_value()) return DaliValue();
DaliValue::Array out;
out.reserve(values->size());
for (const int v : *values) {
out.emplace_back(v);
}
return DaliValue(std::move(out));
}
} // namespace
DaliLongAddress DaliLongAddress::fromJson(const DaliValue::Object* json) {
if (!json) return DaliLongAddress{};
DaliLongAddress addr;
addr.h = getObjectInt(*json, "H").value_or(0);
addr.m = getObjectInt(*json, "M").value_or(0);
addr.l = getObjectInt(*json, "L").value_or(0);
return addr;
}
DaliValue::Object DaliLongAddress::toJson() const {
DaliValue::Object out;
out["H"] = h;
out["M"] = m;
out["L"] = l;
return out;
}
DaliDeviceCapabilities DaliDeviceCapabilities::fromJson(const DaliValue::Object* json) {
DaliDeviceCapabilities c;
if (!json) return c;
c.supportsDt1 = getObjectBool(*json, "dt1");
c.supportsDt8 = getObjectBool(*json, "dt8");
return c;
}
DaliValue::Object DaliDeviceCapabilities::toJson() const {
DaliValue::Object out;
if (supportsDt1.has_value()) out["dt1"] = supportsDt1.value();
if (supportsDt8.has_value()) out["dt8"] = supportsDt8.value();
return out;
}
void DaliDeviceCapabilities::merge(const DaliDeviceCapabilities& other) {
if (!supportsDt1.has_value()) supportsDt1 = other.supportsDt1;
if (!supportsDt8.has_value()) supportsDt8 = other.supportsDt8;
}
DaliStatusFlags DaliStatusFlags::fromJson(const DaliValue::Object* json) {
DaliStatusFlags s;
if (!json) return s;
s.controlGearPresent = getObjectBool(*json, "controlGearPresent");
s.lampFailure = getObjectBool(*json, "lampFailure");
s.lampPowerOn = getObjectBool(*json, "lampPowerOn");
s.limitError = getObjectBool(*json, "limitError");
s.fadingCompleted = getObjectBool(*json, "fadingCompleted");
s.resetState = getObjectBool(*json, "resetState");
s.missingShortAddress = getObjectBool(*json, "missingShortAddress");
s.psFault = getObjectBool(*json, "psFault");
return s;
}
DaliValue::Object DaliStatusFlags::toJson() const {
DaliValue::Object out;
if (controlGearPresent.has_value()) out["controlGearPresent"] = controlGearPresent.value();
if (lampFailure.has_value()) out["lampFailure"] = lampFailure.value();
if (lampPowerOn.has_value()) out["lampPowerOn"] = lampPowerOn.value();
if (limitError.has_value()) out["limitError"] = limitError.value();
if (fadingCompleted.has_value()) out["fadingCompleted"] = fadingCompleted.value();
if (resetState.has_value()) out["resetState"] = resetState.value();
if (missingShortAddress.has_value()) out["missingShortAddress"] = missingShortAddress.value();
if (psFault.has_value()) out["psFault"] = psFault.value();
return out;
}
bool DaliStatusFlags::hasData() const {
return controlGearPresent.has_value() || lampFailure.has_value() || lampPowerOn.has_value() ||
limitError.has_value() || fadingCompleted.has_value() || resetState.has_value() ||
missingShortAddress.has_value() || psFault.has_value();
}
void DaliStatusFlags::merge(const DaliStatusFlags& other) {
if (!controlGearPresent.has_value()) controlGearPresent = other.controlGearPresent;
if (!lampFailure.has_value()) lampFailure = other.lampFailure;
if (!lampPowerOn.has_value()) lampPowerOn = other.lampPowerOn;
if (!limitError.has_value()) limitError = other.limitError;
if (!fadingCompleted.has_value()) fadingCompleted = other.fadingCompleted;
if (!resetState.has_value()) resetState = other.resetState;
if (!missingShortAddress.has_value()) missingShortAddress = other.missingShortAddress;
if (!psFault.has_value()) psFault = other.psFault;
}
DaliDt8State DaliDt8State::fromJson(const DaliValue::Object* json) {
DaliDt8State s;
if (!json) return s;
s.colorType = getObjectInt(*json, "colorType");
s.activeMode = getObjectString(*json, "activeMode");
if (const auto* xyVal = getObjectValue(*json, "xy")) {
if (const auto* xy = xyVal->asObject()) {
s.xyX = getObjectInt(*xy, "x");
s.xyY = getObjectInt(*xy, "y");
}
}
if (const auto* gamutVal = getObjectValue(*json, "gamut")) {
if (const auto* gamut = gamutVal->asObject()) {
s.xyMinX = getObjectInt(*gamut, "xMin");
s.xyMaxX = getObjectInt(*gamut, "xMax");
s.xyMinY = getObjectInt(*gamut, "yMin");
s.xyMaxY = getObjectInt(*gamut, "yMax");
}
}
s.mirek = getObjectInt(*json, "mirek");
s.mirekMin = getObjectInt(*json, "mirekMin");
s.mirekMax = getObjectInt(*json, "mirekMax");
s.rgbwaf = asIntList(getObjectValue(*json, "rgbwaf"));
s.primaryN = asIntList(getObjectValue(*json, "primaryN"));
return s;
}
DaliValue::Object DaliDt8State::toJson() const {
DaliValue::Object out;
if (colorType.has_value()) out["colorType"] = colorType.value();
if (activeMode.has_value()) out["activeMode"] = activeMode.value();
if (xyX.has_value() || xyY.has_value()) {
DaliValue::Object xy;
if (xyX.has_value()) xy["x"] = xyX.value();
if (xyY.has_value()) xy["y"] = xyY.value();
out["xy"] = std::move(xy);
}
if (xyMinX.has_value() || xyMaxX.has_value() || xyMinY.has_value() || xyMaxY.has_value()) {
DaliValue::Object gamut;
if (xyMinX.has_value()) gamut["xMin"] = xyMinX.value();
if (xyMaxX.has_value()) gamut["xMax"] = xyMaxX.value();
if (xyMinY.has_value()) gamut["yMin"] = xyMinY.value();
if (xyMaxY.has_value()) gamut["yMax"] = xyMaxY.value();
out["gamut"] = std::move(gamut);
}
if (mirek.has_value()) out["mirek"] = mirek.value();
if (mirekMin.has_value()) out["mirekMin"] = mirekMin.value();
if (mirekMax.has_value()) out["mirekMax"] = mirekMax.value();
if (rgbwaf.has_value()) out["rgbwaf"] = toIntArray(rgbwaf);
if (primaryN.has_value()) out["primaryN"] = toIntArray(primaryN);
return out;
}
DaliDt1State DaliDt1State::fromJson(const DaliValue::Object* json) {
DaliDt1State s;
if (!json) return s;
s.emergencyLevel = getObjectInt(*json, "emergencyLevel");
s.emergencyMinLevel = getObjectInt(*json, "emergencyMinLevel");
s.emergencyMaxLevel = getObjectInt(*json, "emergencyMaxLevel");
s.prolongTimeMinutes = getObjectInt(*json, "prolongTimeMinutes");
s.ratedDurationMinutes = getObjectInt(*json, "ratedDurationMinutes");
s.testDelayTime = getObjectInt(*json, "testDelayTime");
s.failureStatus = getObjectInt(*json, "failureStatus");
s.emergencyStatus = getObjectInt(*json, "emergencyStatus");
s.emergencyMode = getObjectInt(*json, "emergencyMode");
s.feature = getObjectInt(*json, "feature");
s.version = getObjectInt(*json, "version");
return s;
}
DaliValue::Object DaliDt1State::toJson() const {
DaliValue::Object out;
if (emergencyLevel.has_value()) out["emergencyLevel"] = emergencyLevel.value();
if (emergencyMinLevel.has_value()) out["emergencyMinLevel"] = emergencyMinLevel.value();
if (emergencyMaxLevel.has_value()) out["emergencyMaxLevel"] = emergencyMaxLevel.value();
if (prolongTimeMinutes.has_value()) out["prolongTimeMinutes"] = prolongTimeMinutes.value();
if (ratedDurationMinutes.has_value()) out["ratedDurationMinutes"] = ratedDurationMinutes.value();
if (testDelayTime.has_value()) out["testDelayTime"] = testDelayTime.value();
if (failureStatus.has_value()) out["failureStatus"] = failureStatus.value();
if (emergencyStatus.has_value()) out["emergencyStatus"] = emergencyStatus.value();
if (emergencyMode.has_value()) out["emergencyMode"] = emergencyMode.value();
if (feature.has_value()) out["feature"] = feature.value();
if (version.has_value()) out["version"] = version.value();
return out;
}
DaliDevice DaliDevice::fromJson(const DaliValue::Object& json) {
DaliDevice d;
d.name = getObjectString(json, "name").value_or("");
d.id = getObjectString(json, "id").value_or("");
if (d.id.empty()) {
d.id = d.name.empty() ? "device-unknown" : d.name;
}
if (d.name.empty()) {
d.name = d.id;
}
d.shortAddress = getObjectInt(json, "shortAddress");
if (const auto* lv = getObjectValue(json, "longAddress")) {
if (const auto* lo = lv->asObject()) {
d.longAddress = DaliLongAddress::fromJson(lo);
}
}
d.isolated = getObjectBool(json, "isolated").value_or(false);
d.brightness = getObjectInt(json, "brightness");
d.groupBits = getObjectInt(json, "groupBits");
d.scenes = asIntList(getObjectValue(json, "scenes"));
d.fadeTime = getObjectInt(json, "fadeTime");
d.fadeRate = getObjectInt(json, "fadeRate");
d.powerOnLevel = getObjectInt(json, "powerOnLevel");
d.systemFailureLevel = getObjectInt(json, "systemFailureLevel");
d.minLevel = getObjectInt(json, "minLevel");
d.maxLevel = getObjectInt(json, "maxLevel");
d.operatingMode = getObjectInt(json, "operatingMode");
d.physicalMinLevel = getObjectInt(json, "physicalMinLevel");
d.deviceType = getObjectInt(json, "deviceType");
d.extType = asIntList(getObjectValue(json, "extType")).value_or(std::vector<int>{});
d.version = getObjectInt(json, "version");
if (const auto* cv = getObjectValue(json, "capabilities")) {
d.capabilities = DaliDeviceCapabilities::fromJson(cv->asObject());
}
if (const auto* dt8v = getObjectValue(json, "dt8")) {
if (const auto* obj = dt8v->asObject()) {
d.dt8 = DaliDt8State::fromJson(obj);
}
}
if (const auto* dt1v = getObjectValue(json, "dt1")) {
if (const auto* obj = dt1v->asObject()) {
d.dt1 = DaliDt1State::fromJson(obj);
}
}
if (const auto* sfv = getObjectValue(json, "statusFlags")) {
d.statusFlags = DaliStatusFlags::fromJson(sfv->asObject());
}
d.lastSyncedUtc = getObjectString(json, "lastSyncedUtc");
if (const auto* mv = getObjectValue(json, "meta")) {
if (const auto* obj = mv->asObject()) {
d.metadata = *obj;
}
}
return d;
}
DaliValue::Object DaliDevice::toJson() const {
DaliValue::Object out;
out["id"] = id;
out["name"] = name;
if (shortAddress.has_value()) out["shortAddress"] = shortAddress.value();
if (longAddress.has_value()) out["longAddress"] = longAddress->toJson();
out["isolated"] = isolated;
if (brightness.has_value()) out["brightness"] = brightness.value();
if (groupBits.has_value()) out["groupBits"] = groupBits.value();
if (scenes.has_value()) out["scenes"] = toIntArray(scenes);
if (fadeTime.has_value()) out["fadeTime"] = fadeTime.value();
if (fadeRate.has_value()) out["fadeRate"] = fadeRate.value();
if (powerOnLevel.has_value()) out["powerOnLevel"] = powerOnLevel.value();
if (systemFailureLevel.has_value()) out["systemFailureLevel"] = systemFailureLevel.value();
if (minLevel.has_value()) out["minLevel"] = minLevel.value();
if (maxLevel.has_value()) out["maxLevel"] = maxLevel.value();
if (operatingMode.has_value()) out["operatingMode"] = operatingMode.value();
if (physicalMinLevel.has_value()) out["physicalMinLevel"] = physicalMinLevel.value();
if (deviceType.has_value()) out["deviceType"] = deviceType.value();
if (!extType.empty()) {
DaliValue::Array arr;
arr.reserve(extType.size());
for (const int t : extType) arr.emplace_back(t);
out["extType"] = std::move(arr);
}
if (version.has_value()) out["version"] = version.value();
const auto caps = capabilities.toJson();
if (!caps.empty()) out["capabilities"] = caps;
if (dt8.has_value()) out["dt8"] = dt8->toJson();
if (dt1.has_value()) out["dt1"] = dt1->toJson();
if (statusFlags.hasData()) out["statusFlags"] = statusFlags.toJson();
if (lastSyncedUtc.has_value()) out["lastSyncedUtc"] = lastSyncedUtc.value();
if (!metadata.empty()) out["meta"] = metadata;
return out;
}
std::string DaliDevice::displayName() const { return name.empty() ? id : name; }
void DaliDevice::merge(const DaliDevice& other) {
if (other.shortAddress.has_value()) shortAddress = other.shortAddress;
if (!longAddress.has_value()) longAddress = other.longAddress;
isolated = other.isolated;
if (!brightness.has_value()) brightness = other.brightness;
if (!groupBits.has_value()) groupBits = other.groupBits;
if (!scenes.has_value() && other.scenes.has_value()) scenes = other.scenes;
if (!fadeTime.has_value()) fadeTime = other.fadeTime;
if (!fadeRate.has_value()) fadeRate = other.fadeRate;
if (!powerOnLevel.has_value()) powerOnLevel = other.powerOnLevel;
if (!systemFailureLevel.has_value()) systemFailureLevel = other.systemFailureLevel;
if (!minLevel.has_value()) minLevel = other.minLevel;
if (!maxLevel.has_value()) maxLevel = other.maxLevel;
if (!operatingMode.has_value()) operatingMode = other.operatingMode;
if (!physicalMinLevel.has_value()) physicalMinLevel = other.physicalMinLevel;
if (!deviceType.has_value()) deviceType = other.deviceType;
if (extType.empty() && !other.extType.empty()) extType = other.extType;
if (!version.has_value()) version = other.version;
capabilities.merge(other.capabilities);
if (!dt8.has_value() && other.dt8.has_value()) dt8 = other.dt8;
if (!dt1.has_value() && other.dt1.has_value()) dt1 = other.dt1;
statusFlags.merge(other.statusFlags);
if (other.lastSyncedUtc.has_value()) lastSyncedUtc = other.lastSyncedUtc;
for (const auto& kv : other.metadata) {
metadata[kv.first] = kv.second;
}
}