From 49dcd8785b712957153cf489a2b68c9168725812 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 15 Jun 2026 17:01:27 +0800 Subject: [PATCH] feat: add readMemoryLocation method and update GatewayController for memory identity reads Signed-off-by: Tony --- README.md | 9 ++++ .../dali_domain/include/dali_domain.hpp | 2 + components/dali_domain/src/dali_domain.cpp | 19 ++++++++ .../gateway_bridge/src/gateway_bridge.cpp | 1 + .../src/gateway_controller.cpp | 46 +++++++++++++++++++ 5 files changed, 77 insertions(+) diff --git a/README.md b/README.md index 3f584ff..66ddf60 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,15 @@ Unsupported ids return an `unsupported` operation error so old clients can fall back to their app-side workflow. Legacy opcodes such as `0x12`, `0x13`, `0x14`, `0x30`, `0x32`, `0x60`-`0x65`, and `0x39` remain available for compatibility. +Operation id `84` performs a direct-short-address DALI identity memory batch +read. The start TLV must include target field `0x01` as a u8 short address +`0..63`; group and broadcast reads are rejected. Kind field `0x20` as u8 +selects compact (`0`) or extended (`nonzero`) identity coverage. Results are +repeated field `0x30` byte-list entries, each with payload +`[bank, location, value]`. Missing byte replies are omitted; a completed result +with no entries reports `no response` so clients can fall back to single-byte +`READ MEMORY LOCATION` reads. + Opcode `0x66` controls passive raw-report leases. Command `28 01 66 01 ` enables or disables a volatile per-gateway lease; `ttl=0` disables. The response is diff --git a/components/dali_domain/include/dali_domain.hpp b/components/dali_domain/include/dali_domain.hpp index d10baec..7648c89 100644 --- a/components/dali_domain/include/dali_domain.hpp +++ b/components/dali_domain/include/dali_domain.hpp @@ -149,6 +149,8 @@ class DaliDomainService { std::optional discoverDeviceTypes( uint8_t gateway_id, int short_address, const std::vector& fallback_types = {}, int max_next_types = 16) const; + std::optional readMemoryLocation(uint8_t gateway_id, int short_address, uint8_t bank, + uint8_t location) const; std::optional baseStatusSnapshot(uint8_t gateway_id, int short_address) const; std::optional dt1Snapshot(uint8_t gateway_id, int short_address) const; diff --git a/components/dali_domain/src/dali_domain.cpp b/components/dali_domain/src/dali_domain.cpp index 71fcc35..165e81f 100644 --- a/components/dali_domain/src/dali_domain.cpp +++ b/components/dali_domain/src/dali_domain.cpp @@ -942,6 +942,25 @@ std::optional DaliDomainService::discoverDeviceTypes( return snapshot; } +std::optional DaliDomainService::readMemoryLocation(uint8_t gateway_id, + int short_address, + uint8_t bank, + uint8_t location) const { + const auto* channel = findChannelByGateway(gateway_id); + if (channel == nullptr || channel->dali == nullptr) { + return std::nullopt; + } + if (short_address < 0 || short_address > 63) { + return std::nullopt; + } + markBusActivity(gateway_id); + const auto value = channel->dali->base.readMemoryLocation(short_address, bank, location); + if (!value.has_value()) { + return std::nullopt; + } + return static_cast(value.value() & 0xFF); +} + std::optional DaliDomainService::baseStatusSnapshot( uint8_t gateway_id, int short_address) const { const auto* channel = findChannelByGateway(gateway_id); diff --git a/components/gateway_bridge/src/gateway_bridge.cpp b/components/gateway_bridge/src/gateway_bridge.cpp index b74770f..cf51a1b 100644 --- a/components/gateway_bridge/src/gateway_bridge.cpp +++ b/components/gateway_bridge/src/gateway_bridge.cpp @@ -633,6 +633,7 @@ bool OperationRequiresDt1(BridgeOperation operation) { case BridgeOperation::dt1GetRatedDuration: case BridgeOperation::dt1GetExtendedVersion: case BridgeOperation::dt1GetEmergencyDeviceType: + case BridgeOperation::readMemoryIdentity: return true; default: return false; diff --git a/components/gateway_controller/src/gateway_controller.cpp b/components/gateway_controller/src/gateway_controller.cpp index e9b99c5..d42cd92 100644 --- a/components/gateway_controller/src/gateway_controller.cpp +++ b/components/gateway_controller/src/gateway_controller.cpp @@ -167,6 +167,7 @@ constexpr uint16_t kBridgeOperationSetColourRGB = 23; constexpr uint16_t kBridgeOperationSetColourRGBW = 80; constexpr uint16_t kBridgeOperationSetColourRGBCW = 81; constexpr uint16_t kBridgeOperationSetColourRGBWAF = 82; +constexpr uint16_t kBridgeOperationReadMemoryIdentity = 84; constexpr uint16_t kDaliCmdQueryStatus = 0x90; constexpr uint16_t kDaliCmdQueryBallast = 0x91; constexpr const char* kBridgeTransportInvalidFrameResponse = @@ -2993,6 +2994,51 @@ void GatewayController::runOperationTask(GatewayOperationTaskContext* context) { progress = 100; break; } + case kBridgeOperationReadMemoryIdentity: { + if (target < 0 || target > 63) { + fail(kOperationStatusInvalid); + break; + } + struct MemoryLocation { + uint8_t bank; + uint8_t location; + }; + std::vector locations; + locations.reserve(38); + for (uint8_t location = 0x00; location <= 0x05; ++location) { + locations.push_back(MemoryLocation{0, location}); + } + const bool extended = TlvIntOr(fields, kTlvFieldKind, 0) != 0; + const uint8_t bank1_end = extended ? 0x1F : 0x06; + for (uint8_t location = 0x00; location <= bank1_end; ++location) { + locations.push_back(MemoryLocation{1, location}); + } + const int total = std::max(1, locations.size()); + for (size_t index = 0; index < locations.size(); ++index) { + if (canceled()) { + fail(kOperationStatusAborted); + break; + } + const auto location = locations[index]; + const auto value = dali_domain_.readMemoryLocation(gateway_id, target, location.bank, + location.location); + if (value.has_value()) { + const uint8_t entry[] = {location.bank, location.location, value.value()}; + AppendTlvBytes(result_tlvs, kTlvFieldEntry, kTlvTypeBytes, entry, sizeof(entry)); + ++count; + } + update_progress(kOperationEventItemResult, + static_cast((index + 1) * 100 / total), + location.location, count, + value.has_value() ? kOperationStatusOk : kOperationStatusNoResponse); + } + if (status == kOperationStatusOk && count == 0) { + fail(kOperationStatusNoResponse); + } else { + progress = 100; + } + break; + } case kBridgeOperationAllocateAllShortAddresses: case kBridgeOperationResetAndAllocateShortAddresses: case kBridgeOperationStopAddressAllocation: {