From 597952a75a891d52e4c27d5b4d57fab245392fc9 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 26 May 2026 22:21:53 +0800 Subject: [PATCH] Enhance DALI DT1 support: add new operations and extend DT1 status structure with additional fields Signed-off-by: Tony --- include/bridge_model.hpp | 29 +++++ include/dt1.hpp | 31 +++++ src/bridge.cpp | 249 +++++++++++++++++++++++++++++++++++++-- src/bridge_model.cpp | 128 ++++++++++++++++++++ src/dt1.cpp | 52 ++++++++ 5 files changed, 481 insertions(+), 8 deletions(-) diff --git a/include/bridge_model.hpp b/include/bridge_model.hpp index 9253952..893a775 100644 --- a/include/bridge_model.hpp +++ b/include/bridge_model.hpp @@ -88,6 +88,35 @@ enum class BridgeOperation { resetAndAllocateShortAddresses = 48, stopAddressAllocation = 49, batch = 50, + dt1Rest = 51, + dt1Inhibit = 52, + dt1RelightResetInhibit = 53, + dt1StartIdentification = 54, + dt1ResetFunctionTestDoneFlag = 55, + dt1ResetDurationTestDoneFlag = 56, + dt1ResetLampTime = 57, + dt1StoreEmergencyLevel = 58, + dt1StoreTestDelayTime = 59, + dt1StoreFunctionTestInterval = 60, + dt1StoreDurationTestInterval = 61, + dt1StoreTestExecutionTimeout = 62, + dt1StoreProlongTime = 63, + dt1PerformDtrSelectedFunction = 64, + dt1GetBatteryCharge = 65, + dt1GetFunctionTestDelayTime = 66, + dt1GetDurationTestDelayTime = 67, + dt1GetFunctionTestInterval = 68, + dt1GetDurationTestInterval = 69, + dt1GetTestExecutionTimeout = 70, + dt1GetProlongTime = 71, + dt1GetDurationTestResult = 72, + dt1GetLampEmergencyTime = 73, + dt1GetLampTotalOperationTime = 74, + dt1GetEmergencyMinLevel = 75, + dt1GetEmergencyMaxLevel = 76, + dt1GetRatedDuration = 77, + dt1GetExtendedVersion = 78, + dt1GetEmergencyDeviceType = 79, }; enum class BridgeValueEncoding { diff --git a/include/dt1.hpp b/include/dt1.hpp index 9d2f728..6ccc966 100644 --- a/include/dt1.hpp +++ b/include/dt1.hpp @@ -11,6 +11,23 @@ struct DT1TestStatusDetailed { std::optional emergencyMode; std::optional feature; std::optional deviceStatus; + std::optional batteryChargeLevel; + std::optional functionTestDelayTime; + std::optional durationTestDelayTime; + std::optional functionTestIntervalDays; + std::optional durationTestIntervalWeeks; + std::optional testExecutionTimeoutDays; + std::optional prolongTimeHalfMinutes; + std::optional durationTestResultMinutes; + std::optional lampEmergencyTimeHours; + std::optional lampTotalOperationTimeHours; + std::optional emergencyLevel; + std::optional emergencyMinLevel; + std::optional emergencyMaxLevel; + std::optional ratedDurationMinutes; + std::optional extendedVersion; + std::optional physicalMinLevel; + std::optional emergencyDeviceTypeCode; bool testInProgress = false; bool lampFailure = false; bool batteryFailure = false; @@ -41,6 +58,14 @@ struct DT1TestStatusDetailed { bool supportsAdjustableEmergencyLevel = false; }; +enum class DaliDT1EmergencyDeviceType { + unknown = 0, + typeA = 1, + typeB = 2, + typeC = 3, + typeD = 4, +}; + class DaliDT1DeviceStatus { public: explicit DaliDT1DeviceStatus(int raw) : raw_(raw & 0xFF) {} @@ -132,6 +157,9 @@ class DaliDT1Features { bool hardwiredInhibitSupported() const { return bit(0x20); } bool physicalSelectionSupported() const { return bit(0x40); } bool relightInRestModeSupported() const { return bit(0x80); } + DaliDT1EmergencyDeviceType emergencyDeviceType( + const std::optional& physicalMinLevel = std::nullopt) const; + int emergencyDeviceTypeCode(const std::optional& physicalMinLevel = std::nullopt) const; private: int raw_ = 0; @@ -187,8 +215,10 @@ class DaliDT1 { std::optional getBatteryChargeLevel(int a); std::optional getTestTiming(int a, int selector); + std::optional getFunctionTestDelayTime(int a); std::optional getFunctionTestDelayHighByte(int a); std::optional getFunctionTestDelayLowByte(int a); + std::optional getDurationTestDelayTime(int a); std::optional getDurationTestDelayHighByte(int a); std::optional getDurationTestDelayLowByte(int a); std::optional getEmergencyLevel(int a); @@ -223,5 +253,6 @@ class DaliDT1 { bool sendWithDTR(int a, int code, int value); std::optional query(int a, int code); std::optional queryTiming(int a, int selector); + std::optional queryTimingWordFromHighSelector(int a, int selector); }; diff --git a/src/bridge.cpp b/src/bridge.cpp index 37ba89e..7a6b90c 100644 --- a/src/bridge.cpp +++ b/src/bridge.cpp @@ -149,6 +149,21 @@ bool isReadOperation(BridgeOperation operation) { case BridgeOperation::getSceneMap: case BridgeOperation::getAddressSettings: case BridgeOperation::searchAddressRange: + case BridgeOperation::dt1GetBatteryCharge: + case BridgeOperation::dt1GetFunctionTestDelayTime: + case BridgeOperation::dt1GetDurationTestDelayTime: + case BridgeOperation::dt1GetFunctionTestInterval: + case BridgeOperation::dt1GetDurationTestInterval: + case BridgeOperation::dt1GetTestExecutionTimeout: + case BridgeOperation::dt1GetProlongTime: + case BridgeOperation::dt1GetDurationTestResult: + case BridgeOperation::dt1GetLampEmergencyTime: + case BridgeOperation::dt1GetLampTotalOperationTime: + case BridgeOperation::dt1GetEmergencyMinLevel: + case BridgeOperation::dt1GetEmergencyMaxLevel: + case BridgeOperation::dt1GetRatedDuration: + case BridgeOperation::dt1GetExtendedVersion: + case BridgeOperation::dt1GetEmergencyDeviceType: return true; default: return false; @@ -197,6 +212,23 @@ void addDt1DetailedMetadata(DaliBridgeResult* result, const DT1TestStatusDetaile putOptionalRaw(&result->metadata, "emergencyModeRaw", status.emergencyMode); putOptionalRaw(&result->metadata, "featuresRaw", status.feature); putOptionalRaw(&result->metadata, "deviceStatusRaw", status.deviceStatus); + putOptionalInt(&result->metadata, "batteryChargeLevel", status.batteryChargeLevel); + putOptionalInt(&result->metadata, "functionTestDelayTime", status.functionTestDelayTime); + putOptionalInt(&result->metadata, "durationTestDelayTime", status.durationTestDelayTime); + putOptionalInt(&result->metadata, "functionTestIntervalDays", status.functionTestIntervalDays); + putOptionalInt(&result->metadata, "durationTestIntervalWeeks", status.durationTestIntervalWeeks); + putOptionalInt(&result->metadata, "testExecutionTimeoutDays", status.testExecutionTimeoutDays); + putOptionalInt(&result->metadata, "prolongTimeHalfMinutes", status.prolongTimeHalfMinutes); + putOptionalInt(&result->metadata, "durationTestResultMinutes", status.durationTestResultMinutes); + putOptionalInt(&result->metadata, "lampEmergencyTimeHours", status.lampEmergencyTimeHours); + putOptionalInt(&result->metadata, "lampTotalOperationTimeHours", status.lampTotalOperationTimeHours); + putOptionalInt(&result->metadata, "emergencyLevel", status.emergencyLevel); + putOptionalInt(&result->metadata, "emergencyMinLevel", status.emergencyMinLevel); + putOptionalInt(&result->metadata, "emergencyMaxLevel", status.emergencyMaxLevel); + putOptionalInt(&result->metadata, "ratedDurationMinutes", status.ratedDurationMinutes); + putOptionalInt(&result->metadata, "extendedVersion", status.extendedVersion); + putOptionalInt(&result->metadata, "physicalMinLevel", status.physicalMinLevel); + putOptionalInt(&result->metadata, "emergencyDeviceTypeCode", status.emergencyDeviceTypeCode); result->metadata["testInProgress"] = status.testInProgress; result->metadata["lampFailure"] = status.lampFailure; result->metadata["batteryFailure"] = status.batteryFailure; @@ -227,6 +259,75 @@ void addDt1DetailedMetadata(DaliBridgeResult* result, const DT1TestStatusDetaile result->metadata["supportsAdjustableEmergencyLevel"] = status.supportsAdjustableEmergencyLevel; } +bool isDt1ValueReadOperation(BridgeOperation operation) { + switch (operation) { + case BridgeOperation::dt1GetBatteryCharge: + case BridgeOperation::dt1GetFunctionTestDelayTime: + case BridgeOperation::dt1GetDurationTestDelayTime: + case BridgeOperation::dt1GetFunctionTestInterval: + case BridgeOperation::dt1GetDurationTestInterval: + case BridgeOperation::dt1GetTestExecutionTimeout: + case BridgeOperation::dt1GetProlongTime: + case BridgeOperation::dt1GetDurationTestResult: + case BridgeOperation::dt1GetLampEmergencyTime: + case BridgeOperation::dt1GetLampTotalOperationTime: + case BridgeOperation::dt1GetEmergencyMinLevel: + case BridgeOperation::dt1GetEmergencyMaxLevel: + case BridgeOperation::dt1GetRatedDuration: + case BridgeOperation::dt1GetExtendedVersion: + case BridgeOperation::dt1GetEmergencyDeviceType: + return true; + default: + return false; + } +} + +std::optional readDt1Value(DaliDT1& dt1, DaliBase& base, BridgeOperation operation, + int address, DaliValue::Object* metadata) { + switch (operation) { + case BridgeOperation::dt1GetBatteryCharge: + return dt1.getBatteryChargeLevel(address); + case BridgeOperation::dt1GetFunctionTestDelayTime: + return dt1.getFunctionTestDelayTime(address); + case BridgeOperation::dt1GetDurationTestDelayTime: + return dt1.getDurationTestDelayTime(address); + case BridgeOperation::dt1GetFunctionTestInterval: + return dt1.getFunctionTestIntervalDays(address); + case BridgeOperation::dt1GetDurationTestInterval: + return dt1.getDurationTestIntervalWeeks(address); + case BridgeOperation::dt1GetTestExecutionTimeout: + return dt1.getTestExecutionTimeoutDays(address); + case BridgeOperation::dt1GetProlongTime: + return dt1.getProlongTimeHalfMinutes(address); + case BridgeOperation::dt1GetDurationTestResult: + return dt1.getDurationTestResult(address); + case BridgeOperation::dt1GetLampEmergencyTime: + return dt1.getLampEmergencyTimeHours(address); + case BridgeOperation::dt1GetLampTotalOperationTime: + return dt1.getLampTotalOperationTimeHours(address); + case BridgeOperation::dt1GetEmergencyMinLevel: + return dt1.getEmergencyMinLevel(address); + case BridgeOperation::dt1GetEmergencyMaxLevel: + return dt1.getEmergencyMaxLevel(address); + case BridgeOperation::dt1GetRatedDuration: + return dt1.getRatedDurationMinutes(address); + case BridgeOperation::dt1GetExtendedVersion: + return dt1.getExtendedVersionDT1(address); + case BridgeOperation::dt1GetEmergencyDeviceType: { + const auto features = dt1.getFeatureDecoded(address); + if (!features.has_value()) return std::nullopt; + const auto physicalMinLevel = base.getPhysicalMinLevel(address); + putRaw(metadata, "featuresRaw", features->raw()); + putOptionalInt(metadata, "physicalMinLevel", physicalMinLevel); + const int code = features->emergencyDeviceTypeCode(physicalMinLevel); + (*metadata)["emergencyDeviceTypeCode"] = code; + return code; + } + default: + return std::nullopt; + } +} + void addDt4SnapshotMetadata(DaliBridgeResult* result, DaliDT4& dt4, int address) { putOptionalInt(&result->metadata, "extendedVersion", dt4.getExtendedVersion(address)); putOptionalInt(&result->metadata, "dimmingCurve", dt4.getDimmingCurve(address)); @@ -713,7 +814,22 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ case BridgeOperation::getGroupMask: case BridgeOperation::getSceneLevel: case BridgeOperation::getSceneMap: - case BridgeOperation::getAddressSettings: { + case BridgeOperation::getAddressSettings: + case BridgeOperation::dt1GetBatteryCharge: + case BridgeOperation::dt1GetFunctionTestDelayTime: + case BridgeOperation::dt1GetDurationTestDelayTime: + case BridgeOperation::dt1GetFunctionTestInterval: + case BridgeOperation::dt1GetDurationTestInterval: + case BridgeOperation::dt1GetTestExecutionTimeout: + case BridgeOperation::dt1GetProlongTime: + case BridgeOperation::dt1GetDurationTestResult: + case BridgeOperation::dt1GetLampEmergencyTime: + case BridgeOperation::dt1GetLampTotalOperationTime: + case BridgeOperation::dt1GetEmergencyMinLevel: + case BridgeOperation::dt1GetEmergencyMaxLevel: + case BridgeOperation::dt1GetRatedDuration: + case BridgeOperation::dt1GetExtendedVersion: + case BridgeOperation::dt1GetEmergencyDeviceType: { const auto address = resolveTargetAddress(request, model); if (!address.has_value()) { result.error = "missing target address"; @@ -800,6 +916,14 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ result.ok = true; if (status->failureStatus.has_value()) result.data = status->failureStatus.value(); addDt1DetailedMetadata(&result, status.value()); + } else if (isDt1ValueReadOperation(operation)) { + const auto value = readDt1Value(dt1_, base_, operation, address.value(), &result.metadata); + if (!value.has_value()) { + result.error = "no response"; + return result; + } + result.ok = true; + result.data = value.value(); } else if (operation == BridgeOperation::getDt4Snapshot) { addDt4SnapshotMetadata(&result, dt4_, address.value()); result.ok = !result.metadata.empty(); @@ -954,18 +1078,127 @@ DaliBridgeResult DaliBridgeEngine::executeResolved(const DaliBridgeRequest& requ } case BridgeOperation::startEmergencyFunctionTest: case BridgeOperation::startEmergencyDurationTest: - case BridgeOperation::stopEmergencyTest: { + case BridgeOperation::stopEmergencyTest: + case BridgeOperation::dt1Rest: + case BridgeOperation::dt1Inhibit: + case BridgeOperation::dt1RelightResetInhibit: + case BridgeOperation::dt1StartIdentification: + case BridgeOperation::dt1ResetFunctionTestDoneFlag: + case BridgeOperation::dt1ResetDurationTestDoneFlag: + case BridgeOperation::dt1ResetLampTime: + case BridgeOperation::dt1StoreEmergencyLevel: + case BridgeOperation::dt1StoreTestDelayTime: + case BridgeOperation::dt1StoreFunctionTestInterval: + case BridgeOperation::dt1StoreDurationTestInterval: + case BridgeOperation::dt1StoreTestExecutionTimeout: + case BridgeOperation::dt1StoreProlongTime: + case BridgeOperation::dt1PerformDtrSelectedFunction: { const auto address = resolveTargetAddress(request, model); if (!address.has_value()) { result.error = "missing target address"; return result; } - if (operation == BridgeOperation::startEmergencyFunctionTest) { - result.ok = dt1_.startFunctionTestCmd(address.value()); - } else if (operation == BridgeOperation::startEmergencyDurationTest) { - result.ok = dt1_.startDurationTestCmd(address.value()); - } else { - result.ok = dt1_.stopTest(address.value()); + switch (operation) { + case BridgeOperation::startEmergencyFunctionTest: + result.ok = dt1_.startFunctionTestCmd(address.value()); + break; + case BridgeOperation::startEmergencyDurationTest: + result.ok = dt1_.startDurationTestCmd(address.value()); + break; + case BridgeOperation::stopEmergencyTest: + result.ok = dt1_.stopTest(address.value()); + break; + case BridgeOperation::dt1Rest: + result.ok = dt1_.rest(address.value()); + break; + case BridgeOperation::dt1Inhibit: + result.ok = dt1_.inhibit(address.value()); + break; + case BridgeOperation::dt1RelightResetInhibit: + result.ok = dt1_.reLightOrResetInhibit(address.value()); + break; + case BridgeOperation::dt1StartIdentification: + result.ok = dt1_.startIdentification(address.value()); + break; + case BridgeOperation::dt1ResetFunctionTestDoneFlag: + result.ok = dt1_.resetFunctionTestDoneFlag(address.value()); + break; + case BridgeOperation::dt1ResetDurationTestDoneFlag: + result.ok = dt1_.resetDurationTestDoneFlag(address.value()); + break; + case BridgeOperation::dt1ResetLampTime: + result.ok = dt1_.resetLampTime(address.value()); + break; + case BridgeOperation::dt1StoreEmergencyLevel: { + const auto value = intParam(request, {"emergencyLevel", "level", "value"}); + if (!value.has_value()) { + result.error = "missing emergency level"; + return result; + } + result.ok = dt1_.storeEmergencyLevel(address.value(), value.value()); + result.data = value.value(); + break; + } + case BridgeOperation::dt1StoreTestDelayTime: { + const auto value = intParam(request, {"delayTime", "quartersOfHour", "delay", "value"}); + if (!value.has_value()) { + result.error = "missing test delay time"; + return result; + } + result.ok = dt1_.storeTestDelayTime16(address.value(), value.value()); + result.data = value.value(); + break; + } + case BridgeOperation::dt1StoreFunctionTestInterval: { + const auto value = intParam(request, {"functionTestInterval", "days", "value"}); + if (!value.has_value()) { + result.error = "missing function test interval"; + return result; + } + result.ok = dt1_.storeFunctionTestIntervalDays(address.value(), value.value()); + result.data = value.value(); + break; + } + case BridgeOperation::dt1StoreDurationTestInterval: { + const auto value = intParam(request, {"durationTestInterval", "weeks", "value"}); + if (!value.has_value()) { + result.error = "missing duration test interval"; + return result; + } + result.ok = dt1_.storeDurationTestIntervalWeeks(address.value(), value.value()); + result.data = value.value(); + break; + } + case BridgeOperation::dt1StoreTestExecutionTimeout: { + const auto value = intParam(request, {"testExecutionTimeout", "timeoutDays", "days", "value"}); + if (!value.has_value()) { + result.error = "missing test execution timeout"; + return result; + } + result.ok = dt1_.storeTestExecutionTimeoutDays(address.value(), value.value()); + result.data = value.value(); + break; + } + case BridgeOperation::dt1StoreProlongTime: { + if (const auto minutes = intParam(request, {"minutes"})) { + result.ok = dt1_.storeProlongTimeMinutes(address.value(), minutes.value()); + result.data = minutes.value(); + } else if (const auto halfMinutes = intParam(request, {"halfMinutes", "half_minutes", "value"})) { + result.ok = dt1_.storeProlongTimeHalfMinutes(address.value(), halfMinutes.value()); + result.data = halfMinutes.value(); + } else { + result.error = "missing prolong time"; + return result; + } + break; + } + case BridgeOperation::dt1PerformDtrSelectedFunction: + result.ok = dt1_.performDTRSelectedFunction( + address.value(), intParam(request, {"dtr0", "DTR0"}), intParam(request, {"dtr1", "DTR1"})); + break; + default: + result.error = "unsupported op"; + return result; } break; } diff --git a/src/bridge_model.cpp b/src/bridge_model.cpp index 32f3eb0..1597591 100644 --- a/src/bridge_model.cpp +++ b/src/bridge_model.cpp @@ -412,6 +412,64 @@ const char* bridgeOperationToString(BridgeOperation operation) { return "stop_address_allocation"; case BridgeOperation::batch: return "batch"; + case BridgeOperation::dt1Rest: + return "dt1_rest"; + case BridgeOperation::dt1Inhibit: + return "dt1_inhibit"; + case BridgeOperation::dt1RelightResetInhibit: + return "dt1_relight_reset_inhibit"; + case BridgeOperation::dt1StartIdentification: + return "dt1_start_identification"; + case BridgeOperation::dt1ResetFunctionTestDoneFlag: + return "dt1_reset_function_test_done_flag"; + case BridgeOperation::dt1ResetDurationTestDoneFlag: + return "dt1_reset_duration_test_done_flag"; + case BridgeOperation::dt1ResetLampTime: + return "dt1_reset_lamp_time"; + case BridgeOperation::dt1StoreEmergencyLevel: + return "dt1_store_emergency_level"; + case BridgeOperation::dt1StoreTestDelayTime: + return "dt1_store_test_delay_time"; + case BridgeOperation::dt1StoreFunctionTestInterval: + return "dt1_store_function_test_interval"; + case BridgeOperation::dt1StoreDurationTestInterval: + return "dt1_store_duration_test_interval"; + case BridgeOperation::dt1StoreTestExecutionTimeout: + return "dt1_store_test_execution_timeout"; + case BridgeOperation::dt1StoreProlongTime: + return "dt1_store_prolong_time"; + case BridgeOperation::dt1PerformDtrSelectedFunction: + return "dt1_perform_dtr_selected_function"; + case BridgeOperation::dt1GetBatteryCharge: + return "dt1_get_battery_charge"; + case BridgeOperation::dt1GetFunctionTestDelayTime: + return "dt1_get_function_test_delay_time"; + case BridgeOperation::dt1GetDurationTestDelayTime: + return "dt1_get_duration_test_delay_time"; + case BridgeOperation::dt1GetFunctionTestInterval: + return "dt1_get_function_test_interval"; + case BridgeOperation::dt1GetDurationTestInterval: + return "dt1_get_duration_test_interval"; + case BridgeOperation::dt1GetTestExecutionTimeout: + return "dt1_get_test_execution_timeout"; + case BridgeOperation::dt1GetProlongTime: + return "dt1_get_prolong_time"; + case BridgeOperation::dt1GetDurationTestResult: + return "dt1_get_duration_test_result"; + case BridgeOperation::dt1GetLampEmergencyTime: + return "dt1_get_lamp_emergency_time"; + case BridgeOperation::dt1GetLampTotalOperationTime: + return "dt1_get_lamp_total_operation_time"; + case BridgeOperation::dt1GetEmergencyMinLevel: + return "dt1_get_emergency_min_level"; + case BridgeOperation::dt1GetEmergencyMaxLevel: + return "dt1_get_emergency_max_level"; + case BridgeOperation::dt1GetRatedDuration: + return "dt1_get_rated_duration"; + case BridgeOperation::dt1GetExtendedVersion: + return "dt1_get_extended_version"; + case BridgeOperation::dt1GetEmergencyDeviceType: + return "dt1_get_emergency_device_type"; case BridgeOperation::unknown: default: return "unknown"; @@ -483,6 +541,76 @@ BridgeOperation bridgeOperationFromString(const std::string& value) { 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 == "dt1_rest" || normalized == "rest") return BridgeOperation::dt1Rest; + if (normalized == "dt1_inhibit" || normalized == "inhibit") return BridgeOperation::dt1Inhibit; + if (normalized == "dt1_relight_reset_inhibit" || normalized == "dt1_re_light_reset_inhibit" || + normalized == "dt1_reset_inhibit") { + return BridgeOperation::dt1RelightResetInhibit; + } + if (normalized == "dt1_start_identification" || normalized == "start_identification") { + return BridgeOperation::dt1StartIdentification; + } + if (normalized == "dt1_reset_function_test_done_flag") { + return BridgeOperation::dt1ResetFunctionTestDoneFlag; + } + if (normalized == "dt1_reset_duration_test_done_flag") { + return BridgeOperation::dt1ResetDurationTestDoneFlag; + } + if (normalized == "dt1_reset_lamp_time") return BridgeOperation::dt1ResetLampTime; + if (normalized == "dt1_store_emergency_level") return BridgeOperation::dt1StoreEmergencyLevel; + if (normalized == "dt1_store_test_delay_time" || normalized == "dt1_store_test_delay") { + return BridgeOperation::dt1StoreTestDelayTime; + } + if (normalized == "dt1_store_function_test_interval") { + return BridgeOperation::dt1StoreFunctionTestInterval; + } + if (normalized == "dt1_store_duration_test_interval") { + return BridgeOperation::dt1StoreDurationTestInterval; + } + if (normalized == "dt1_store_test_execution_timeout") { + return BridgeOperation::dt1StoreTestExecutionTimeout; + } + if (normalized == "dt1_store_prolong_time") return BridgeOperation::dt1StoreProlongTime; + if (normalized == "dt1_perform_dtr_selected_function") { + return BridgeOperation::dt1PerformDtrSelectedFunction; + } + if (normalized == "dt1_get_battery_charge") return BridgeOperation::dt1GetBatteryCharge; + if (normalized == "dt1_get_function_test_delay_time") { + return BridgeOperation::dt1GetFunctionTestDelayTime; + } + if (normalized == "dt1_get_duration_test_delay_time") { + return BridgeOperation::dt1GetDurationTestDelayTime; + } + if (normalized == "dt1_get_function_test_interval") { + return BridgeOperation::dt1GetFunctionTestInterval; + } + if (normalized == "dt1_get_duration_test_interval") { + return BridgeOperation::dt1GetDurationTestInterval; + } + if (normalized == "dt1_get_test_execution_timeout") { + return BridgeOperation::dt1GetTestExecutionTimeout; + } + if (normalized == "dt1_get_prolong_time") return BridgeOperation::dt1GetProlongTime; + if (normalized == "dt1_get_duration_test_result") { + return BridgeOperation::dt1GetDurationTestResult; + } + if (normalized == "dt1_get_lamp_emergency_time") { + return BridgeOperation::dt1GetLampEmergencyTime; + } + if (normalized == "dt1_get_lamp_total_operation_time") { + return BridgeOperation::dt1GetLampTotalOperationTime; + } + if (normalized == "dt1_get_emergency_min_level") { + return BridgeOperation::dt1GetEmergencyMinLevel; + } + if (normalized == "dt1_get_emergency_max_level") { + return BridgeOperation::dt1GetEmergencyMaxLevel; + } + if (normalized == "dt1_get_rated_duration") return BridgeOperation::dt1GetRatedDuration; + if (normalized == "dt1_get_extended_version") return BridgeOperation::dt1GetExtendedVersion; + if (normalized == "dt1_get_emergency_device_type" || normalized == "dt1_get_type") { + return BridgeOperation::dt1GetEmergencyDeviceType; + } if (normalized == "search_address_range") return BridgeOperation::searchAddressRange; if (normalized == "allocate_all_short_addresses" || normalized == "allocate_all_addr") { return BridgeOperation::allocateAllShortAddresses; diff --git a/src/dt1.cpp b/src/dt1.cpp index b3133d0..b077a13 100644 --- a/src/dt1.cpp +++ b/src/dt1.cpp @@ -6,6 +6,21 @@ #include #include +DaliDT1EmergencyDeviceType DaliDT1Features::emergencyDeviceType( + const std::optional& physicalMinLevel) const { + if (switchedMaintainedControlGear()) { + if (!physicalMinLevel.has_value()) return DaliDT1EmergencyDeviceType::unknown; + return physicalMinLevel.value() >= 254 ? DaliDT1EmergencyDeviceType::typeB + : DaliDT1EmergencyDeviceType::typeA; + } + if (maintainedControlGear()) return DaliDT1EmergencyDeviceType::typeC; + return DaliDT1EmergencyDeviceType::typeD; +} + +int DaliDT1Features::emergencyDeviceTypeCode(const std::optional& physicalMinLevel) const { + return static_cast(emergencyDeviceType(physicalMinLevel)); +} + DaliDT1::DaliDT1(DaliBase& base) : base_(base) {} bool DaliDT1::enable() { return base_.dtSelect(1); } @@ -31,6 +46,15 @@ std::optional DaliDT1::queryTiming(int a, int selector) { return query(a, DALI_CMD_DT1_QUERY_TEST_TIMING); } +std::optional DaliDT1::queryTimingWordFromHighSelector(int a, int selector) { + if (!base_.setDTR(selector & 0xFF)) return std::nullopt; + const auto high = query(a, DALI_CMD_DT1_QUERY_TEST_TIMING); + if (!high.has_value()) return std::nullopt; + const auto low = base_.getDTR1(a); + if (!low.has_value()) return std::nullopt; + return ((high.value() & 0xFF) << 8) | (low.value() & 0xFF); +} + bool DaliDT1::enableDT1() { return enable(); } bool DaliDT1::startDT1Test(int a, int t) { @@ -63,6 +87,23 @@ std::optional DaliDT1::getDT1TestStatusDetailed(int a) { const auto feature = getFeatureDecoded(a); const auto deviceStatus = getDeviceStatus(a); + result.batteryChargeLevel = getBatteryChargeLevel(a); + result.functionTestDelayTime = getFunctionTestDelayTime(a); + result.durationTestDelayTime = getDurationTestDelayTime(a); + result.functionTestIntervalDays = getFunctionTestIntervalDays(a); + result.durationTestIntervalWeeks = getDurationTestIntervalWeeks(a); + result.testExecutionTimeoutDays = getTestExecutionTimeoutDays(a); + result.prolongTimeHalfMinutes = getProlongTimeHalfMinutes(a); + result.durationTestResultMinutes = getDurationTestResult(a); + result.lampEmergencyTimeHours = getLampEmergencyTimeHours(a); + result.lampTotalOperationTimeHours = getLampTotalOperationTimeHours(a); + result.emergencyLevel = getEmergencyLevel(a); + result.emergencyMinLevel = getEmergencyMinLevel(a); + result.emergencyMaxLevel = getEmergencyMaxLevel(a); + result.ratedDurationMinutes = getRatedDurationMinutes(a); + result.extendedVersion = getExtendedVersionDT1(a); + result.physicalMinLevel = base_.getPhysicalMinLevel(a); + if (!failure.has_value()) return std::nullopt; result.failureStatus = failure->raw(); @@ -118,6 +159,9 @@ std::optional DaliDT1::getDT1TestStatusDetailed(int a) { result.supportsAutoTest = feature.has_value() && feature->autoTestCapability(); result.supportsAdjustableEmergencyLevel = feature.has_value() && feature->adjustableEmergencyLevel(); + if (feature.has_value()) { + result.emergencyDeviceTypeCode = feature->emergencyDeviceTypeCode(result.physicalMinLevel); + } return result; } @@ -237,10 +281,18 @@ std::optional DaliDT1::getTestTiming(int a, int selector) { return queryTiming(a, selector); } +std::optional DaliDT1::getFunctionTestDelayTime(int a) { + return queryTimingWordFromHighSelector(a, 0x00); +} + std::optional DaliDT1::getFunctionTestDelayHighByte(int a) { return queryTiming(a, 0x00); } std::optional DaliDT1::getFunctionTestDelayLowByte(int a) { return queryTiming(a, 0x01); } +std::optional DaliDT1::getDurationTestDelayTime(int a) { + return queryTimingWordFromHighSelector(a, 0x02); +} + std::optional DaliDT1::getDurationTestDelayHighByte(int a) { return queryTiming(a, 0x02); } std::optional DaliDT1::getDurationTestDelayLowByte(int a) { return queryTiming(a, 0x03); }