From 555c2ec5bd60a0591f239b4293f99ed7b8ec55de Mon Sep 17 00:00:00 2001 From: Tony Date: Sat, 1 Aug 2026 01:19:13 +0800 Subject: [PATCH] Refactor GatewayController to integrate DaliGateway types - Updated GatewayController to use DaliGateway types instead of GatewayCache types for channel flags, targets, and states. - Modified methods to reflect changes in data structures, including flag checks and state management. - Adjusted cache handling to utilize DaliGatewayCache for address and group management. - Ensured compatibility with existing functionality while enhancing clarity and maintainability of the code. Signed-off-by: Tony --- README.md | 32 +- apps/gateway/main/app_main.cpp | 23 +- .../dali_domain/include/dali_domain.hpp | 4 + components/dali_domain/src/dali_domain.cpp | 8 + .../gateway_bridge/include/gateway_bridge.hpp | 4 +- .../gateway_bridge/src/gateway_bridge.cpp | 177 +-- .../gateway_cache/include/gateway_cache.hpp | 159 +-- .../gateway_cache/src/gateway_cache.cpp | 1061 +---------------- .../include/gateway_controller.hpp | 8 +- .../src/gateway_controller.cpp | 242 ++-- 10 files changed, 279 insertions(+), 1439 deletions(-) diff --git a/README.md b/README.md index 9cf27df..5ebd600 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ update as the project evolves: - `gateway_core/`: boot profile and top-level role bootstrap. - `dali/`: vendored ESP-IDF DALI HAL/backend reused from LuatOS, including native raw receive fan-out. - `dali_domain/`: native DALI domain facade over `dali_cpp` and raw frame sinks. - - `gateway_cache/`: DALI scene/group/settings/runtime cache used by controller reconciliation and protocol bridges. + - `gateway_cache/`: ESP-IDF NVS adapter for the portable `dali_cpp` cache, + plus gateway-internal scene/group definitions. - `gateway_bridge/`: per-channel bridge provisioning, command execution, protocol startup, and HTTP bridge actions. - `openknx_idf/`: ESP-IDF port layer for the OpenKNX `gateway/knx` and `gateway/tpuart` submodules, including NVS-backed OpenKNX memory, development KNX security storage, ETS cEMI programming support, UDP multicast/unicast plumbing, and a native TP-UART interface without the Arduino framework. - `gateway_modbus/`: gateway-owned Modbus TCP/RTU/ASCII config, generated DALI point tables, and provisioned Modbus model override dispatch. @@ -29,18 +30,25 @@ update as the project evolves: ## Gateway DALI cache -The gateway owns the shared DALI cache for multi-user deployments. App, BLE, -IP, Modbus, BACnet, KNX, and local control paths should treat the gateway cache -as the shared read surface instead of maintaining separate app-side device-state -caches. Transparent/setup raw forwarding paths remain bypass-oriented, but raw -DALI bus observation still feeds the passive decoder when frames are visible to -the gateway. +`dali_cpp::DaliGatewayCache` owns the shared DALI cache and is the authoritative +read surface for App, BLE, IP, Modbus, BACnet, KNX, and local control paths. It +interprets direct, group, broadcast, scene, DTR, and settings commands; owns +presence and reconciliation state; versions persisted address snapshots; and +emits platform-neutral `DaliGatewayStatusUpdate` callbacks. -`gateway_cache` stores internal scene/group data and per-short-address DALI -state. Device settings, group masks, scene levels, known flags, and the last -runtime status are batched to NVS using `GATEWAY_CACHE_FLUSH_INTERVAL_MS`, which -defaults to 10000 ms. Runtime status loaded from disk is marked stale until the -gateway observes a bus command or the background refresher verifies it again. +The ESP-IDF `gateway_cache` component no longer contains DALI cache behavior. +It binds the portable cache persistence callbacks to NVS and continues to own +only gateway-internal scene/group definitions. Device settings, group masks, +scene levels, known flags, and the last runtime status are batched through the +portable cache using `GATEWAY_CACHE_FLUSH_INTERVAL_MS`, which defaults to +10000 ms. Runtime status loaded from disk is marked stale until a bus command +or the background refresher verifies it again. + +Protocol adapters register for semantic status updates instead of decoding raw +DALI commands independently. The current KNX adapter maps the callback target, +status, and affected short-address list into its group-object status updates. +Transparent/setup raw forwarding paths remain bypass-oriented, while visible +raw DALI bus traffic still feeds the portable cache. `GATEWAY_CACHE_REFRESH_INTERVAL_MS` defaults to 120000 ms. When nonzero, the controller maintenance loop refreshes direct short-address actual levels one diff --git a/apps/gateway/main/app_main.cpp b/apps/gateway/main/app_main.cpp index 6ea588c..eb62a7a 100644 --- a/apps/gateway/main/app_main.cpp +++ b/apps/gateway/main/app_main.cpp @@ -511,11 +511,11 @@ constexpr bool kCacheFullStateMirrorEnabled = false; #endif #ifdef CONFIG_GATEWAY_CACHE_LOCAL_GATEWAY_FIRST -constexpr gateway::GatewayCachePriorityMode kCachePriorityMode = - gateway::GatewayCachePriorityMode::kLocalGatewayFirst; +constexpr DaliGatewayCachePriorityMode kCachePriorityMode = + DaliGatewayCachePriorityMode::localGatewayFirst; #else -constexpr gateway::GatewayCachePriorityMode kCachePriorityMode = - gateway::GatewayCachePriorityMode::kOutsideBusFirst; +constexpr DaliGatewayCachePriorityMode kCachePriorityMode = + DaliGatewayCachePriorityMode::outsideBusFirst; #endif #ifdef CONFIG_GATEWAY_DALI_103_APPLICATION_CONTROLLER_ENABLED @@ -1097,14 +1097,15 @@ extern "C" void app_main(void) { gateway::GatewayCacheConfig cache_config; cache_config.cache_enabled = kCacheSupported && kCacheStartupEnabled && s_runtime->cacheEnabled(); - cache_config.reconciliation_enabled = cache_config.cache_enabled && kCacheReconciliationEnabled; - cache_config.full_state_mirror_enabled = cache_config.reconciliation_enabled && - kCacheFullStateMirrorEnabled; cache_config.flush_interval_ms = static_cast(CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS); - cache_config.refresh_interval_ms = - static_cast(CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS); - cache_config.default_priority_mode = kCachePriorityMode; - s_cache = std::make_unique(cache_config); + const bool reconciliation_enabled = + cache_config.cache_enabled && kCacheReconciliationEnabled; + s_dali_domain->gatewayCache().configure( + DaliGatewayCacheConfig{cache_config.cache_enabled, reconciliation_enabled, + reconciliation_enabled && kCacheFullStateMirrorEnabled, + kCachePriorityMode}); + s_cache = std::make_unique(s_dali_domain->gatewayCache(), + cache_config); ESP_ERROR_CHECK(s_cache->start()); gateway::GatewayControllerConfig controller_config; diff --git a/components/dali_domain/include/dali_domain.hpp b/components/dali_domain/include/dali_domain.hpp index 2ba9d23..2c46b96 100644 --- a/components/dali_domain/include/dali_domain.hpp +++ b/components/dali_domain/include/dali_domain.hpp @@ -12,6 +12,7 @@ #include #include "esp_err.h" +#include "dali_gateway.hpp" #include "freertos/FreeRTOS.h" #include "freertos/queue.h" #include "freertos/semphr.h" @@ -137,6 +138,8 @@ class DaliDomainService { void setApplicationControllerIdentitySink(ApplicationControllerIdentitySink sink); void setApplicationControllerShortAddressProvider(ApplicationControllerShortAddressProvider provider); void setApplicationControllerShortAddressSink(ApplicationControllerShortAddressSink sink); + DaliGatewayCache& gatewayCache(); + const DaliGatewayCache& gatewayCache() const; bool resetBus(uint8_t gateway_id) const; bool pulseBusLow(uint8_t gateway_id, uint32_t duration_ms) const; @@ -263,6 +266,7 @@ class DaliDomainService { ApplicationControllerIdentitySink application_controller_identity_sink_; ApplicationControllerShortAddressProvider application_controller_short_address_provider_; ApplicationControllerShortAddressSink application_controller_short_address_sink_; + DaliGatewayCache gateway_cache_; QueueHandle_t raw_frame_dispatch_queue_{nullptr}; TaskHandle_t raw_frame_dispatch_task_handle_{nullptr}; TaskHandle_t raw_frame_task_handle_{nullptr}; diff --git a/components/dali_domain/src/dali_domain.cpp b/components/dali_domain/src/dali_domain.cpp index 4e4e067..ccc13cb 100644 --- a/components/dali_domain/src/dali_domain.cpp +++ b/components/dali_domain/src/dali_domain.cpp @@ -481,6 +481,14 @@ DaliDomainService::~DaliDomainService() { } } +DaliGatewayCache& DaliDomainService::gatewayCache() { + return gateway_cache_; +} + +const DaliGatewayCache& DaliDomainService::gatewayCache() const { + return gateway_cache_; +} + bool DaliDomainService::bindTransport(const DaliChannelConfig& config, DaliTransportHooks hooks) { if (!hooks.send) { return false; diff --git a/components/gateway_bridge/include/gateway_bridge.hpp b/components/gateway_bridge/include/gateway_bridge.hpp index e334449..38005cd 100644 --- a/components/gateway_bridge/include/gateway_bridge.hpp +++ b/components/gateway_bridge/include/gateway_bridge.hpp @@ -10,6 +10,7 @@ #include #include +#include "dali_gateway.hpp" #include "esp_err.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -19,7 +20,6 @@ namespace gateway { class DaliDomainService; -struct DaliRawFrame; class GatewayCache; struct GatewayBridgeHttpResponse { @@ -86,7 +86,7 @@ class GatewayBridgeService { size_t len); DaliBridgeResult routeKnxGroupObjectWrite(uint16_t group_object_number, const uint8_t* data, size_t len); - void handleDaliRawFrame(const DaliRawFrame& frame); + void handleDaliStatusUpdate(const DaliGatewayStatusUpdate& update); void collectUsedRuntimeResources(uint8_t except_gateway_id, std::set* modbus_tcp_ports, std::set* knx_udp_ports, diff --git a/components/gateway_bridge/src/gateway_bridge.cpp b/components/gateway_bridge/src/gateway_bridge.cpp index fbece64..2bd3886 100644 --- a/components/gateway_bridge/src/gateway_bridge.cpp +++ b/components/gateway_bridge/src/gateway_bridge.cpp @@ -103,11 +103,6 @@ struct BridgeDiscoveryEntry { DaliDomainSnapshot discovery; }; -struct DaliKnxStatusUpdate { - GatewayKnxDaliTarget target; - uint8_t actual_level{0}; -}; - using BridgeDiscoveryInventory = std::map; extern "C" uint8_t knx_platform_copy_security_failures(uint8_t* counters, size_t countersLen, @@ -472,54 +467,6 @@ bool ValidDaliAddress(int address) { return address >= 0 && address <= 127; } -std::optional DecodeKnxDaliTarget(uint8_t raw_addr) { - if (raw_addr <= 0x7F) { - return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kShortAddress, - static_cast(raw_addr >> 1)}; - } - if (raw_addr >= kDaliGroupRawMin && raw_addr <= kDaliGroupRawMax) { - return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kGroup, - static_cast((raw_addr - kDaliGroupRawMin) >> 1)}; - } - if (raw_addr == 0xFE || raw_addr == 0xFF) { - return GatewayKnxDaliTarget{GatewayKnxDaliTargetKind::kBroadcast, 127}; - } - return std::nullopt; -} - -std::optional DecodeDaliKnxStatusUpdate(const DaliRawFrame& frame) { - if (frame.data.size() != 2 && frame.data.size() != 3) { - return std::nullopt; - } - uint8_t raw_addr = 0; - uint8_t command = 0; - if (frame.data.size() == 2) { - raw_addr = frame.data[0]; - command = frame.data[1]; - if (raw_addr == 0xBE) { - return std::nullopt; - } - } else { - raw_addr = frame.data[1]; - command = frame.data[2]; - } - auto target = DecodeKnxDaliTarget(raw_addr); - if (!target.has_value()) { - return std::nullopt; - } - if ((raw_addr & 0x01U) == 0) { - if (command > 254) { - return std::nullopt; - } - return DaliKnxStatusUpdate{*target, command}; - } - if (command == kDaliCmdOff || command == kDaliCmdRecallMax) { - return DaliKnxStatusUpdate{*target, - static_cast(command == kDaliCmdOff ? 0 : 254)}; - } - return std::nullopt; -} - bool ValidShortAddress(int address) { return address >= 0 && address <= kMaxDaliShortAddress; } @@ -1583,6 +1530,7 @@ struct GatewayBridgeService::ChannelRuntime { GatewayBridgeServiceConfig service_config) : service(service), domain(domain), + dali_cache(domain.gatewayCache()), cache(cache), channel(std::move(channel)), service_config(service_config), @@ -1603,6 +1551,7 @@ struct GatewayBridgeService::ChannelRuntime { GatewayBridgeService& service; DaliDomainService& domain; + DaliGatewayCache& dali_cache; GatewayCache& cache; DaliChannelInfo channel; GatewayBridgeServiceConfig service_config; @@ -3141,7 +3090,7 @@ struct GatewayBridgeService::ChannelRuntime { } const auto* discovery = findDiscoveryEntryLocked(point.short_address); - const auto state = cache.daliAddressState(channel.gateway_id, + const auto state = dali_cache.addressState(channel.gateway_id, static_cast(point.short_address)); switch (point.generated_kind) { case GatewayModbusGeneratedKind::kShortDiscovered: @@ -3159,11 +3108,11 @@ struct GatewayBridgeService::ChannelRuntime { case GatewayModbusGeneratedKind::kShortSupportsDt8: return discovery != nullptr && SnapshotHasDeviceType(discovery->discovery, 8); case GatewayModbusGeneratedKind::kShortGroupMaskKnown: - return state.group_mask_known; + return state.groupMaskKnown; case GatewayModbusGeneratedKind::kShortActualLevelKnown: - return state.status.actual_level.has_value(); + return state.status.actualLevel.has_value(); case GatewayModbusGeneratedKind::kShortSceneKnown: - return state.status.scene_id.has_value(); + return state.status.sceneID.has_value(); case GatewayModbusGeneratedKind::kShortSettingsKnown: return state.settings.anyKnown(); case GatewayModbusGeneratedKind::kShortControlGearPresent: @@ -3195,7 +3144,7 @@ struct GatewayBridgeService::ChannelRuntime { } const auto* discovery = findDiscoveryEntryLocked(point.short_address); - const auto state = cache.daliAddressState(channel.gateway_id, + const auto state = dali_cache.addressState(channel.gateway_id, static_cast(point.short_address)); switch (point.generated_kind) { case GatewayModbusGeneratedKind::kShortInventoryState: @@ -3216,12 +3165,12 @@ struct GatewayBridgeService::ChannelRuntime { return discovery == nullptr ? kModbusUnknownRegister : DeviceTypeMask(discovery->discovery); case GatewayModbusGeneratedKind::kShortBrightness: case GatewayModbusGeneratedKind::kShortActualLevel: - return state.status.actual_level.has_value() - ? static_cast(state.status.actual_level.value()) + return state.status.actualLevel.has_value() + ? static_cast(state.status.actualLevel.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortSceneId: - return state.status.scene_id.has_value() - ? static_cast(state.status.scene_id.value()) + return state.status.sceneID.has_value() + ? static_cast(state.status.sceneID.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortRawStatus: { const auto snapshot = diagnosticSnapshotLocked(point.short_address, "base_status"); @@ -3233,30 +3182,30 @@ struct GatewayBridgeService::ChannelRuntime { return kModbusUnknownRegister; } case GatewayModbusGeneratedKind::kShortGroupMask: - return state.group_mask_known ? state.group_mask : kModbusUnknownRegister; + return state.groupMaskKnown ? state.groupMask : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortPowerOnLevel: - return state.settings.power_on_level.has_value() - ? static_cast(state.settings.power_on_level.value()) + return state.settings.powerOnLevel.has_value() + ? static_cast(state.settings.powerOnLevel.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortSystemFailureLevel: - return state.settings.system_failure_level.has_value() - ? static_cast(state.settings.system_failure_level.value()) + return state.settings.systemFailureLevel.has_value() + ? static_cast(state.settings.systemFailureLevel.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortMinLevel: - return state.settings.min_level.has_value() - ? static_cast(state.settings.min_level.value()) + return state.settings.minLevel.has_value() + ? static_cast(state.settings.minLevel.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortMaxLevel: - return state.settings.max_level.has_value() - ? static_cast(state.settings.max_level.value()) + return state.settings.maxLevel.has_value() + ? static_cast(state.settings.maxLevel.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortFadeTime: - return state.settings.fade_time.has_value() - ? static_cast(state.settings.fade_time.value()) + return state.settings.fadeTime.has_value() + ? static_cast(state.settings.fadeTime.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortFadeRate: - return state.settings.fade_rate.has_value() - ? static_cast(state.settings.fade_rate.value()) + return state.settings.fadeRate.has_value() + ? static_cast(state.settings.fadeRate.value()) : kModbusUnknownRegister; case GatewayModbusGeneratedKind::kShortColorTemperature: return kModbusUnknownRegister; @@ -3297,7 +3246,7 @@ struct GatewayBridgeService::ChannelRuntime { return false; } if (sent) { - cache.mirrorDaliCommand(channel.gateway_id, raw_command_address, mirrored_command); + dali_cache.mirrorForwardFrame(channel.gateway_id, raw_command_address, mirrored_command); } return sent; } @@ -3315,7 +3264,7 @@ struct GatewayBridgeService::ChannelRuntime { domain.markHostCommandFrame(channel.gateway_id, RawArcAddressFromDec(point.short_address), static_cast(value)); if (domain.setBright(channel.gateway_id, point.short_address, value)) { - cache.mirrorDaliCommand(channel.gateway_id, RawArcAddressFromDec(point.short_address), + dali_cache.mirrorForwardFrame(channel.gateway_id, RawArcAddressFromDec(point.short_address), static_cast(value)); return true; } @@ -3326,7 +3275,7 @@ struct GatewayBridgeService::ChannelRuntime { case GatewayModbusGeneratedKind::kShortGroupMask: domain.markHostActivity(channel.gateway_id); if (domain.applyGroupMask(channel.gateway_id, point.short_address, value)) { - cache.setDaliGroupMask(channel.gateway_id, static_cast(point.short_address), + dali_cache.setGroupMask(channel.gateway_id, static_cast(point.short_address), value); return true; } @@ -3340,40 +3289,40 @@ struct GatewayBridgeService::ChannelRuntime { if (value > 255) { return false; } - auto current = cache.daliAddressState(channel.gateway_id, + auto current = dali_cache.addressState(channel.gateway_id, static_cast(point.short_address)).settings; switch (point.generated_kind) { case GatewayModbusGeneratedKind::kShortPowerOnLevel: - current.power_on_level = static_cast(value); + current.powerOnLevel = static_cast(value); break; case GatewayModbusGeneratedKind::kShortSystemFailureLevel: - current.system_failure_level = static_cast(value); + current.systemFailureLevel = static_cast(value); break; case GatewayModbusGeneratedKind::kShortMinLevel: - current.min_level = static_cast(value); + current.minLevel = static_cast(value); break; case GatewayModbusGeneratedKind::kShortMaxLevel: - current.max_level = static_cast(value); + current.maxLevel = static_cast(value); break; case GatewayModbusGeneratedKind::kShortFadeTime: - current.fade_time = static_cast(value); + current.fadeTime = static_cast(value); break; case GatewayModbusGeneratedKind::kShortFadeRate: - current.fade_rate = static_cast(value); + current.fadeRate = static_cast(value); break; default: break; } DaliAddressSettingsSnapshot domain_settings; - domain_settings.power_on_level = current.power_on_level; - domain_settings.system_failure_level = current.system_failure_level; - domain_settings.min_level = current.min_level; - domain_settings.max_level = current.max_level; - domain_settings.fade_time = current.fade_time; - domain_settings.fade_rate = current.fade_rate; + domain_settings.power_on_level = current.powerOnLevel; + domain_settings.system_failure_level = current.systemFailureLevel; + domain_settings.min_level = current.minLevel; + domain_settings.max_level = current.maxLevel; + domain_settings.fade_time = current.fadeTime; + domain_settings.fade_rate = current.fadeRate; domain.markHostActivity(channel.gateway_id); if (domain.applyAddressSettings(channel.gateway_id, point.short_address, domain_settings)) { - cache.setDaliSettings(channel.gateway_id, static_cast(point.short_address), + dali_cache.setSettings(channel.gateway_id, static_cast(point.short_address), current); return true; } @@ -4318,7 +4267,9 @@ GatewayBridgeService::GatewayBridgeService(DaliDomainService& dali_domain, GatewayBridgeServiceConfig config) : dali_domain_(dali_domain), cache_(cache), config_(config) {} -GatewayBridgeService::~GatewayBridgeService() = default; +GatewayBridgeService::~GatewayBridgeService() { + dali_domain_.gatewayCache().setStatusUpdateCallback({}); +} esp_err_t GatewayBridgeService::start() { ConfigureDaliCppLogging(); @@ -4343,8 +4294,8 @@ esp_err_t GatewayBridgeService::start() { runtimes_.push_back(std::move(runtime)); } - dali_domain_.addRawFrameSink( - [this](const DaliRawFrame& frame) { handleDaliRawFrame(frame); }); + dali_domain_.gatewayCache().setStatusUpdateCallback( + [this](const DaliGatewayStatusUpdate& update) { handleDaliStatusUpdate(update); }); std::set used_serial_uarts; if (config_.modbus_enabled && config_.modbus_startup_enabled) { @@ -4583,14 +4534,13 @@ DaliBridgeResult GatewayBridgeService::routeKnxGroupObjectWrite(uint16_t group_o return runtime->knx->handleGroupObjectWrite(group_object_number, data, len); } -void GatewayBridgeService::handleDaliRawFrame(const DaliRawFrame& frame) { - const auto update = DecodeDaliKnxStatusUpdate(frame); - if (!update.has_value()) { +void GatewayBridgeService::handleDaliStatusUpdate(const DaliGatewayStatusUpdate& update) { + if (!update.status.actualLevel.has_value()) { return; } auto* owner = knx_endpoint_runtime_ != nullptr ? knx_endpoint_runtime_ : selectKnxEndpointRuntime(); - if (owner == nullptr || owner->channel.gateway_id != frame.gateway_id) { + if (owner == nullptr || owner->channel.gateway_id != update.channel) { return; } LockGuard guard(owner->lock); @@ -4600,32 +4550,29 @@ void GatewayBridgeService::handleDaliRawFrame(const DaliRawFrame& frame) { auto publish_target = [&](GatewayKnxDaliTargetKind kind, int address) { owner->knx_router->publishDaliStatus(GatewayKnxDaliTarget{kind, address}, - update->actual_level); + *update.status.actualLevel); }; - publish_target(update->target.kind, update->target.address); + GatewayKnxDaliTargetKind target_kind = GatewayKnxDaliTargetKind::kShortAddress; + int target_address = update.target.value; + if (update.target.kind == DaliGatewayTargetKind::group) { + target_kind = GatewayKnxDaliTargetKind::kGroup; + } else if (update.target.kind == DaliGatewayTargetKind::broadcast) { + target_kind = GatewayKnxDaliTargetKind::kBroadcast; + target_address = 127; + } + publish_target(target_kind, target_address); - if (update->target.kind == GatewayKnxDaliTargetKind::kGroup && - update->target.address >= 0 && update->target.address < 16) { - const uint16_t group_bit = static_cast(1U << update->target.address); - for (int short_address = 0; short_address <= kMaxDaliShortAddress; ++short_address) { - const auto state = owner->cache.daliAddressState(frame.gateway_id, - static_cast(short_address)); - if (!state.group_mask_known || (state.group_mask & group_bit) == 0) { - continue; - } + if (update.target.kind != DaliGatewayTargetKind::shortAddress) { + for (const uint8_t short_address : update.affectedShortAddresses) { publish_target(GatewayKnxDaliTargetKind::kShortAddress, short_address); } - return; } - if (update->target.kind == GatewayKnxDaliTargetKind::kBroadcast) { + if (update.target.kind == DaliGatewayTargetKind::broadcast) { for (int group = 0; group < 16; ++group) { publish_target(GatewayKnxDaliTargetKind::kGroup, group); } - for (int short_address = 0; short_address <= kMaxDaliShortAddress; ++short_address) { - publish_target(GatewayKnxDaliTargetKind::kShortAddress, short_address); - } } } diff --git a/components/gateway_cache/include/gateway_cache.hpp b/components/gateway_cache/include/gateway_cache.hpp index bc1913e..63ce67d 100644 --- a/components/gateway_cache/include/gateway_cache.hpp +++ b/components/gateway_cache/include/gateway_cache.hpp @@ -12,88 +12,17 @@ #include "freertos/semphr.h" #include "freertos/task.h" #include "nvs.h" -#include "dali_gateway.hpp" + +class DaliGatewayCache; namespace gateway { -enum class GatewayCachePriorityMode : uint8_t { - kOutsideBusFirst = 0, - kLocalGatewayFirst = 1, -}; - struct GatewayCacheConfig { std::string storage_namespace{"gateway_rt"}; bool cache_enabled{true}; - bool reconciliation_enabled{true}; - bool full_state_mirror_enabled{false}; uint32_t flush_interval_ms{10000}; - uint32_t refresh_interval_ms{120000}; uint32_t task_stack_size{4096}; UBaseType_t task_priority{3}; - GatewayCachePriorityMode default_priority_mode{GatewayCachePriorityMode::kOutsideBusFirst}; -}; - -enum class GatewayCacheRawFrameOrigin : uint8_t { - kLocalGateway = 0, - kOutsideBus = 1, -}; - -enum class GatewayCacheDaliTargetKind : uint8_t { - kShortAddress = 0, - kGroup = 1, - kBroadcast = 2, -}; - -enum class GatewayCacheDaliPresence : uint8_t { - kUnknown = 0, - kOnline = 1, - kOffline = 2, -}; - -struct GatewayCacheDaliTarget { - GatewayCacheDaliTargetKind kind{GatewayCacheDaliTargetKind::kShortAddress}; - uint8_t value{0}; -}; - -struct GatewayCacheChannelFlags { - bool need_update_group{false}; - bool need_update_scene{false}; - bool need_update_settings{false}; -}; - -struct GatewayCacheDaliSettingsSnapshot { - std::optional power_on_level; - std::optional system_failure_level; - std::optional min_level; - std::optional max_level; - std::optional fade_time; - std::optional fade_rate; - - bool anyKnown() const { - return power_on_level.has_value() || system_failure_level.has_value() || - min_level.has_value() || max_level.has_value() || fade_time.has_value() || - fade_rate.has_value(); - } -}; - -struct GatewayCacheDaliRuntimeStatus { - std::optional actual_level; - std::optional scene_id; - bool use_min_level{false}; - bool stale{false}; - uint32_t revision{0}; - - bool anyKnown() const { - return actual_level.has_value() || scene_id.has_value() || use_min_level; - } -}; - -struct GatewayCacheDaliAddressState { - bool group_mask_known{false}; - uint16_t group_mask{0}; - std::array, 16> scene_levels{}; - GatewayCacheDaliSettingsSnapshot settings; - GatewayCacheDaliRuntimeStatus status; }; class GatewayCache { @@ -118,11 +47,10 @@ class GatewayCache { using SceneStore = std::array; using GroupStore = std::array; - explicit GatewayCache(GatewayCacheConfig config = {}); + GatewayCache(DaliGatewayCache& dali_cache, GatewayCacheConfig config = {}); ~GatewayCache(); esp_err_t start(); - void preloadChannel(uint8_t gateway_id); SceneStore scenes(uint8_t gateway_id); GroupStore groups(uint8_t gateway_id); @@ -143,48 +71,10 @@ class GatewayCache { bool deleteGroup(uint8_t gateway_id, uint8_t group_id); std::pair groupMask(uint8_t gateway_id); - GatewayCacheChannelFlags channelFlags(uint8_t gateway_id); - GatewayCacheChannelFlags pendingChannelFlags(uint8_t gateway_id); - GatewayCacheDaliAddressState daliAddressState(uint8_t gateway_id, uint8_t short_address); - GatewayCacheDaliPresence daliAddressPresence(uint8_t gateway_id, uint8_t short_address); - void markDaliAddressPresence(uint8_t gateway_id, uint8_t short_address, - GatewayCacheDaliPresence presence); - std::optional decodeDaliTarget(uint8_t raw_addr); - std::vector reconciliationAddresses( - uint8_t gateway_id, std::optional target); - GatewayCacheDaliRuntimeStatus daliGroupStatus(uint8_t gateway_id, uint8_t group_id); - GatewayCacheDaliRuntimeStatus daliBroadcastStatus(uint8_t gateway_id); - bool setDaliGroupMask(uint8_t gateway_id, uint8_t short_address, - std::optional group_mask); - bool setDaliSceneLevel(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id, - std::optional level); - bool setDaliSettings(uint8_t gateway_id, uint8_t short_address, - std::optional settings); - bool setDaliActualLevel(uint8_t gateway_id, uint8_t short_address, - std::optional level); - bool clearChannelFlagsIfMatched(uint8_t gateway_id, const GatewayCacheChannelFlags& flags); - void markGroupUpdateNeeded(uint8_t gateway_id, bool needed = true); - void markSceneUpdateNeeded(uint8_t gateway_id, bool needed = true); - void markSettingsUpdateNeeded(uint8_t gateway_id, bool needed = true); - bool cacheEnabled() const; bool setCacheEnabled(bool enabled); - bool reconciliationEnabled() const; - bool fullStateMirrorEnabled() const; - bool mirrorDaliCommand(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); - bool observeDaliCommand(uint8_t gateway_id, uint8_t raw_addr, uint8_t command, - GatewayCacheRawFrameOrigin origin); - - GatewayCachePriorityMode priorityMode(); - void setPriorityMode(GatewayCachePriorityMode mode); private: - struct DtrState { - std::optional dtr0; - std::optional dtr1; - std::optional dtr2; - }; - static void TaskEntry(void* arg); void taskLoop(); bool flushDirty(); @@ -193,63 +83,22 @@ class GatewayCache { void closeStorageLocked(); bool persistSceneLocked(uint8_t gateway_id, uint8_t scene_id, const SceneEntry& scene); bool persistGroupLocked(uint8_t gateway_id, uint8_t group_id, const GroupEntry& group); - bool persistDaliAddressStateLocked(uint8_t gateway_id, uint8_t short_address, - const GatewayCacheDaliAddressState& state); bool commitStorageLocked(); - bool shouldTrackUpdateFlagsLocked() const; - uint32_t nextDaliRuntimeRevisionLocked(); - void markDaliAddressPresenceLocked(uint8_t gateway_id, uint8_t short_address, - GatewayCacheDaliPresence presence); - bool mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr, uint8_t command); - void clearDaliTargetStateLocked(uint8_t gateway_id, const GatewayCacheDaliTarget& target, - uint32_t revision); - void applyDaliTargetRuntimeStatusLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, - const GatewayCacheDaliRuntimeStatus& status); - void applyDaliRuntimeStatusToAddressLocked(GatewayCacheDaliAddressState& state, - const GatewayCacheDaliRuntimeStatus& status); - void applyDaliTargetGroupMutationLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, uint8_t group_id, - bool add_to_group); - void applyDaliTargetSceneLevelLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, uint8_t scene_id, - std::optional level); - void applyDaliTargetSettingsLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, uint8_t command, - uint8_t value); - void refreshDaliAddressAggregateStatusLocked(uint8_t gateway_id, - GatewayCacheDaliAddressState& state); - GatewayCacheDaliAddressState& ensureDaliAddressStateLocked(uint8_t gateway_id, - uint8_t short_address); - GatewayCacheDaliRuntimeStatus& ensureDaliGroupStatusLocked(uint8_t gateway_id, - uint8_t group_id); - GatewayCacheDaliRuntimeStatus& ensureDaliBroadcastStatusLocked(uint8_t gateway_id); SceneStore& ensureSceneStoreLocked(uint8_t gateway_id); GroupStore& ensureGroupStoreLocked(uint8_t gateway_id); void loadSceneStoreLocked(uint8_t gateway_id, SceneStore& scenes); void loadGroupStoreLocked(uint8_t gateway_id, GroupStore& groups); - void loadDaliStateStoreLocked(uint8_t gateway_id, - std::array& states); std::string readStringLocked(std::string_view key); bool writeStringLocked(std::string_view key, std::string_view value); bool eraseKeyLocked(std::string_view key); - void syncDaliStatesFromCoreLocked(uint8_t gateway_id); + DaliGatewayCache& dali_cache_; GatewayCacheConfig config_; - GatewayCachePriorityMode priority_mode_; - DaliGatewayCache dali_gateway_cache_; TaskHandle_t task_handle_{nullptr}; SemaphoreHandle_t lock_{nullptr}; nvs_handle_t storage_{0}; std::map scenes_; std::map groups_; - std::map> dali_states_; - std::map> dali_presence_; - std::map> dali_group_status_; - std::map dali_broadcast_status_; - std::map dtr_states_; - std::map channel_flags_; - uint32_t dali_runtime_revision_{0}; bool dirty_{false}; }; diff --git a/components/gateway_cache/src/gateway_cache.cpp b/components/gateway_cache/src/gateway_cache.cpp index 9e574ce..4a1c2b3 100644 --- a/components/gateway_cache/src/gateway_cache.cpp +++ b/components/gateway_cache/src/gateway_cache.cpp @@ -6,6 +6,7 @@ #include #include +#include "dali_gateway.hpp" #include "esp_log.h" namespace gateway { @@ -14,47 +15,6 @@ namespace { constexpr const char* kTag = "gateway_cache"; constexpr size_t kMaxNameBytes = 32; -constexpr uint8_t kDaliRawBroadcastArc = 0xFE; -constexpr uint8_t kDaliRawBroadcastCommand = 0xFF; -constexpr uint8_t kDaliGroupRawMin = 0x80; -constexpr uint8_t kDaliGroupRawMax = 0x9F; -constexpr uint8_t kDaliCmdOff = 0x00; -constexpr uint8_t kDaliCmdRecallMax = 0x05; -constexpr uint8_t kDaliCmdRecallMin = 0x06; -constexpr uint8_t kDaliCmdGoToSceneMin = 0x10; -constexpr uint8_t kDaliCmdGoToSceneMax = 0x1F; -constexpr uint8_t kDaliCmdReset = 0x20; -constexpr uint8_t kDaliCmdStoreDtrAsMaxLevel = 0x2A; -constexpr uint8_t kDaliCmdStoreDtrAsMinLevel = 0x2B; -constexpr uint8_t kDaliCmdStoreDtrAsSystemFailureLevel = 0x2C; -constexpr uint8_t kDaliCmdStoreDtrAsPowerOnLevel = 0x2D; -constexpr uint8_t kDaliCmdStoreDtrAsFadeTime = 0x2E; -constexpr uint8_t kDaliCmdStoreDtrAsFadeRate = 0x2F; -constexpr uint8_t kDaliCmdSetSceneMin = 0x40; -constexpr uint8_t kDaliCmdRemoveSceneMax = 0x5F; -constexpr uint8_t kDaliCmdAddToGroupMin = 0x60; -constexpr uint8_t kDaliCmdRemoveFromGroupMax = 0x7F; -constexpr uint8_t kDaliCmdStoreDtrAsShortAddress = 0x80; -constexpr uint8_t kDaliCmdSetDtr0 = 0xA3; -constexpr uint8_t kDaliCmdSpecialProgramShortAddress = 0xB7; -constexpr uint8_t kDaliCmdSetDtr1 = 0xC3; -constexpr uint8_t kDaliCmdSetDtr2 = 0xC5; -constexpr uint8_t kDaliCmdDt8StoreDtrAsColorX = 0xE0; -constexpr uint8_t kDaliCmdDt8StoreDtrAsColorY = 0xE1; -constexpr uint8_t kDaliCmdDt8StorePrimaryMin = 0xF0; -constexpr uint8_t kDaliCmdDt8StartAutoCalibration = 0xF6; -constexpr int kDaliStatePayloadVersion = 1; -constexpr uint32_t kDaliStateGroupMaskKnown = 1U << 0; -constexpr uint32_t kDaliStateActualKnown = 1U << 1; -constexpr uint32_t kDaliStateSceneKnown = 1U << 2; -constexpr uint32_t kDaliStateUseMinLevel = 1U << 3; -constexpr uint32_t kDaliStateStatusStale = 1U << 4; -constexpr uint32_t kDaliStatePowerOnKnown = 1U << 5; -constexpr uint32_t kDaliStateSystemFailureKnown = 1U << 6; -constexpr uint32_t kDaliStateMinKnown = 1U << 7; -constexpr uint32_t kDaliStateMaxKnown = 1U << 8; -constexpr uint32_t kDaliStateFadeTimeKnown = 1U << 9; -constexpr uint32_t kDaliStateFadeRateKnown = 1U << 10; class LockGuard { public: @@ -115,153 +75,6 @@ bool IsDefaultGroup(const GatewayCache::GroupEntry& group) { return !group.enabled && group.target_type == 2 && group.target_value == 0; } -DaliGatewayCachePriorityMode ToDaliCppPriorityMode(GatewayCachePriorityMode mode) { - return mode == GatewayCachePriorityMode::kLocalGatewayFirst - ? DaliGatewayCachePriorityMode::localGatewayFirst - : DaliGatewayCachePriorityMode::outsideBusFirst; -} - -GatewayCacheDaliPresence FromDaliCppPresence(DaliGatewayPresence presence) { - switch (presence) { - case DaliGatewayPresence::online: - return GatewayCacheDaliPresence::kOnline; - case DaliGatewayPresence::offline: - return GatewayCacheDaliPresence::kOffline; - case DaliGatewayPresence::unknown: - default: - return GatewayCacheDaliPresence::kUnknown; - } -} - -DaliGatewayPresence ToDaliCppPresence(GatewayCacheDaliPresence presence) { - switch (presence) { - case GatewayCacheDaliPresence::kOnline: - return DaliGatewayPresence::online; - case GatewayCacheDaliPresence::kOffline: - return DaliGatewayPresence::offline; - case GatewayCacheDaliPresence::kUnknown: - default: - return DaliGatewayPresence::unknown; - } -} - -GatewayCacheDaliRuntimeStatus FromDaliCppStatus(const DaliGatewayRuntimeStatus& value) { - return {value.actualLevel, value.sceneID, value.useMinLevel, value.stale, value.revision}; -} - -DaliGatewayRuntimeStatus ToDaliCppStatus(const GatewayCacheDaliRuntimeStatus& value) { - return {value.actual_level, value.scene_id, value.use_min_level, value.stale, value.revision}; -} - -GatewayCacheDaliSettingsSnapshot FromDaliCppSettings(const DaliGatewaySettingsSnapshot& value) { - return {value.powerOnLevel, value.systemFailureLevel, value.minLevel, value.maxLevel, - value.fadeTime, value.fadeRate}; -} - -DaliGatewaySettingsSnapshot ToDaliCppSettings(const GatewayCacheDaliSettingsSnapshot& value) { - return {value.power_on_level, value.system_failure_level, value.min_level, value.max_level, - value.fade_time, value.fade_rate}; -} - -GatewayCacheDaliAddressState FromDaliCppAddressState(const DaliGatewayAddressState& value) { - GatewayCacheDaliAddressState result; - result.group_mask_known = value.groupMaskKnown; - result.group_mask = value.groupMask; - result.scene_levels = value.sceneLevels; - result.settings = FromDaliCppSettings(value.settings); - result.status = FromDaliCppStatus(value.status); - return result; -} - -DaliGatewayAddressState ToDaliCppAddressState(const GatewayCacheDaliAddressState& value) { - DaliGatewayAddressState result; - result.groupMaskKnown = value.group_mask_known; - result.groupMask = value.group_mask; - result.sceneLevels = value.scene_levels; - result.settings = ToDaliCppSettings(value.settings); - result.status = ToDaliCppStatus(value.status); - return result; -} - -GatewayCacheChannelFlags FromDaliCppFlags(const DaliGatewayChannelFlags& value) { - return {value.needUpdateGroup, value.needUpdateScene, value.needUpdateSettings}; -} - -DaliGatewayChannelFlags ToDaliCppFlags(const GatewayCacheChannelFlags& value) { - return {value.need_update_group, value.need_update_scene, value.need_update_settings}; -} - -std::optional ToDaliCppTarget(std::optional target) { - if (!target.has_value()) return std::nullopt; - DaliGatewayTargetKind kind = DaliGatewayTargetKind::shortAddress; - if (target->kind == GatewayCacheDaliTargetKind::kGroup) { - kind = DaliGatewayTargetKind::group; - } else if (target->kind == GatewayCacheDaliTargetKind::kBroadcast) { - kind = DaliGatewayTargetKind::broadcast; - } - return DaliGatewayTarget{kind, target->value}; -} - -std::optional FromDaliCppTarget(std::optional target) { - if (!target.has_value()) return std::nullopt; - GatewayCacheDaliTargetKind kind = GatewayCacheDaliTargetKind::kShortAddress; - if (target->kind == DaliGatewayTargetKind::group) { - kind = GatewayCacheDaliTargetKind::kGroup; - } else if (target->kind == DaliGatewayTargetKind::broadcast) { - kind = GatewayCacheDaliTargetKind::kBroadcast; - } - return GatewayCacheDaliTarget{kind, target->value}; -} - -std::optional DecodeDaliTarget(uint8_t raw_addr) { - if (raw_addr <= 0x7F) { - return GatewayCacheDaliTarget{GatewayCacheDaliTargetKind::kShortAddress, - static_cast(raw_addr >> 1)}; - } - if (raw_addr >= kDaliGroupRawMin && raw_addr <= kDaliGroupRawMax) { - return GatewayCacheDaliTarget{GatewayCacheDaliTargetKind::kGroup, - static_cast((raw_addr - kDaliGroupRawMin) >> 1)}; - } - if (raw_addr == kDaliRawBroadcastArc || raw_addr == kDaliRawBroadcastCommand) { - return GatewayCacheDaliTarget{GatewayCacheDaliTargetKind::kBroadcast, 0}; - } - return std::nullopt; -} - -void ClearDaliState(GatewayCacheDaliAddressState& state) { - state.group_mask_known = false; - state.group_mask = 0; - state.scene_levels.fill(std::nullopt); - state.settings = {}; - state.status = {}; -} - -void ApplyObservedSettingsValue(GatewayCacheDaliSettingsSnapshot& settings, uint8_t command, - uint8_t value) { - switch (command) { - case kDaliCmdStoreDtrAsMaxLevel: - settings.max_level = value; - break; - case kDaliCmdStoreDtrAsMinLevel: - settings.min_level = value; - break; - case kDaliCmdStoreDtrAsSystemFailureLevel: - settings.system_failure_level = value; - break; - case kDaliCmdStoreDtrAsPowerOnLevel: - settings.power_on_level = value; - break; - case kDaliCmdStoreDtrAsFadeTime: - settings.fade_time = value; - break; - case kDaliCmdStoreDtrAsFadeRate: - settings.fade_rate = value; - break; - default: - break; - } -} - std::string BuildScenePayload(const GatewayCache::SceneEntry& scene) { char payload[32] = {0}; std::snprintf(payload, sizeof(payload), "%u,%u,%u,%u,%u,%u", scene.enabled ? 1 : 0, @@ -276,149 +89,31 @@ std::string BuildGroupPayload(const GatewayCache::GroupEntry& group) { return std::string(payload); } -uint8_t ByteValue(int value) { - return static_cast(std::clamp(value, 0, 255)); -} - -uint16_t WordValue(int value) { - return static_cast(std::clamp(value, 0, 0xffff)); -} - -uint16_t SceneKnownMask(const GatewayCacheDaliAddressState& state) { - uint16_t mask = 0; - for (size_t index = 0; index < state.scene_levels.size(); ++index) { - if (state.scene_levels[index].has_value()) { - mask |= static_cast(1U << index); - } - } - return mask; -} - -bool IsDefaultDaliAddressState(const GatewayCacheDaliAddressState& state) { - return !state.group_mask_known && state.group_mask == 0 && SceneKnownMask(state) == 0 && - !state.settings.anyKnown() && !state.status.anyKnown(); -} - -uint32_t DaliStateFlags(const GatewayCacheDaliAddressState& state) { - uint32_t flags = 0; - if (state.group_mask_known) { - flags |= kDaliStateGroupMaskKnown; - } - if (state.status.actual_level.has_value()) { - flags |= kDaliStateActualKnown; - } - if (state.status.scene_id.has_value()) { - flags |= kDaliStateSceneKnown; - } - if (state.status.use_min_level) { - flags |= kDaliStateUseMinLevel; - } - if (state.status.stale) { - flags |= kDaliStateStatusStale; - } - if (state.settings.power_on_level.has_value()) { - flags |= kDaliStatePowerOnKnown; - } - if (state.settings.system_failure_level.has_value()) { - flags |= kDaliStateSystemFailureKnown; - } - if (state.settings.min_level.has_value()) { - flags |= kDaliStateMinKnown; - } - if (state.settings.max_level.has_value()) { - flags |= kDaliStateMaxKnown; - } - if (state.settings.fade_time.has_value()) { - flags |= kDaliStateFadeTimeKnown; - } - if (state.settings.fade_rate.has_value()) { - flags |= kDaliStateFadeRateKnown; - } - return flags; -} - -std::string BuildDaliStatePayload(const GatewayCacheDaliAddressState& state) { - const uint16_t scene_known_mask = SceneKnownMask(state); - std::string payload = std::to_string(kDaliStatePayloadVersion); - payload += "," + std::to_string(DaliStateFlags(state)); - payload += "," + std::to_string(state.status.revision); - payload += "," + std::to_string(state.group_mask); - payload += "," + std::to_string(state.status.actual_level.value_or(0)); - payload += "," + std::to_string(state.status.scene_id.value_or(0)); - payload += "," + std::to_string(state.settings.power_on_level.value_or(0)); - payload += "," + std::to_string(state.settings.system_failure_level.value_or(0)); - payload += "," + std::to_string(state.settings.min_level.value_or(0)); - payload += "," + std::to_string(state.settings.max_level.value_or(0)); - payload += "," + std::to_string(state.settings.fade_time.value_or(0)); - payload += "," + std::to_string(state.settings.fade_rate.value_or(0)); - payload += "," + std::to_string(scene_known_mask); - for (const auto& scene_level : state.scene_levels) { - payload += "," + std::to_string(scene_level.value_or(255)); - } - return payload; -} - -void ApplyDaliStatePayload(std::string_view raw, GatewayCacheDaliAddressState& state) { - const auto values = ParseCsv(raw); - if (values.size() < 13 || values[0] != kDaliStatePayloadVersion) { - return; - } - - const uint32_t flags = static_cast(std::max(values[1], 0)); - state.group_mask_known = (flags & kDaliStateGroupMaskKnown) != 0; - state.group_mask = state.group_mask_known ? WordValue(values[3]) : 0; - - state.status = {}; - state.status.revision = static_cast(std::max(values[2], 0)); - state.status.stale = (flags & kDaliStateStatusStale) != 0; - state.status.use_min_level = (flags & kDaliStateUseMinLevel) != 0; - if ((flags & kDaliStateActualKnown) != 0) { - state.status.actual_level = ByteValue(values[4]); - } - if ((flags & kDaliStateSceneKnown) != 0) { - state.status.scene_id = static_cast(std::min(ByteValue(values[5]), 15)); - } - - state.settings = {}; - if ((flags & kDaliStatePowerOnKnown) != 0) { - state.settings.power_on_level = ByteValue(values[6]); - } - if ((flags & kDaliStateSystemFailureKnown) != 0) { - state.settings.system_failure_level = ByteValue(values[7]); - } - if ((flags & kDaliStateMinKnown) != 0) { - state.settings.min_level = ByteValue(values[8]); - } - if ((flags & kDaliStateMaxKnown) != 0) { - state.settings.max_level = ByteValue(values[9]); - } - if ((flags & kDaliStateFadeTimeKnown) != 0) { - state.settings.fade_time = ByteValue(values[10]); - } - if ((flags & kDaliStateFadeRateKnown) != 0) { - state.settings.fade_rate = ByteValue(values[11]); - } - - state.scene_levels.fill(std::nullopt); - const uint16_t scene_known_mask = WordValue(values[12]); - for (uint8_t scene_id = 0; scene_id < state.scene_levels.size(); ++scene_id) { - const size_t value_index = 13 + scene_id; - if ((scene_known_mask & (1U << scene_id)) != 0 && value_index < values.size()) { - state.scene_levels[scene_id] = ByteValue(values[value_index]); - } - } -} - } // namespace -GatewayCache::GatewayCache(GatewayCacheConfig config) - : config_(std::move(config)), - priority_mode_(config_.default_priority_mode), - dali_gateway_cache_(DaliGatewayCacheConfig{config_.cache_enabled, - config_.reconciliation_enabled, - config_.full_state_mirror_enabled, - ToDaliCppPriorityMode(config_.default_priority_mode)}), - lock_(xSemaphoreCreateRecursiveMutex()) {} +GatewayCache::GatewayCache(DaliGatewayCache& dali_cache, GatewayCacheConfig config) + : dali_cache_(dali_cache), + config_(std::move(config)), + lock_(xSemaphoreCreateRecursiveMutex()) { + dali_cache_.setPersistenceCallbacks({ + [this](uint8_t channel, uint8_t short_address) -> std::optional { + LockGuard guard(lock_); + const auto value = readStringLocked(ShortKey("ds", channel, short_address)); + return value.empty() ? std::nullopt : std::optional(value); + }, + [this](uint8_t channel, uint8_t short_address, + const std::optional& payload) { + LockGuard guard(lock_); + if (!openStorageLocked()) return false; + const auto key = ShortKey("ds", channel, short_address); + return payload.has_value() ? writeStringLocked(key, *payload) : eraseKeyLocked(key); + }, + [this]() { + LockGuard guard(lock_); + return commitStorageLocked(); + }, + }); +} GatewayCache::~GatewayCache() { if (task_handle_ != nullptr) { @@ -426,11 +121,10 @@ GatewayCache::~GatewayCache() { task_handle_ = nullptr; } + if (config_.cache_enabled) flushDirty(); + dali_cache_.setPersistenceCallbacks({}); { LockGuard guard(lock_); - if (config_.cache_enabled) { - flushDirty(); - } closeStorageLocked(); } @@ -467,33 +161,12 @@ esp_err_t GatewayCache::start() { return ESP_ERR_NO_MEM; } - ESP_LOGI(kTag, - "cache started namespace=%s flush_interval_ms=%u refresh_interval_ms=%u " - "reconciliation=%d full_mirror=%d", - config_.storage_namespace.c_str(), static_cast(config_.flush_interval_ms), - static_cast(config_.refresh_interval_ms), - config_.reconciliation_enabled, config_.full_state_mirror_enabled); + ESP_LOGI(kTag, "cache adapter started namespace=%s flush_interval_ms=%u", + config_.storage_namespace.c_str(), + static_cast(config_.flush_interval_ms)); return ESP_OK; } -void GatewayCache::preloadChannel(uint8_t gateway_id) { - LockGuard guard(lock_); - if (!config_.cache_enabled) { - return; - } - ensureSceneStoreLocked(gateway_id); - ensureGroupStoreLocked(gateway_id); - auto [it, inserted] = dali_states_.try_emplace(gateway_id); - if (inserted) { - loadDaliStateStoreLocked(gateway_id, it->second); - } - std::array states; - for (size_t index = 0; index < states.size(); ++index) { - states[index] = ToDaliCppAddressState(it->second[index]); - } - dali_gateway_cache_.restoreAddressStates(gateway_id, states); -} - GatewayCache::SceneStore GatewayCache::scenes(uint8_t gateway_id) { LockGuard guard(lock_); if (!config_.cache_enabled) { @@ -783,608 +456,22 @@ std::pair GatewayCache::groupMask(uint8_t gateway_id) { return {static_cast(mask & 0xff), static_cast((mask >> 8) & 0xff)}; } -GatewayCacheChannelFlags GatewayCache::channelFlags(uint8_t gateway_id) { - LockGuard guard(lock_); - return FromDaliCppFlags(dali_gateway_cache_.channelFlags(gateway_id)); -} - -GatewayCacheChannelFlags GatewayCache::pendingChannelFlags(uint8_t gateway_id) { - LockGuard guard(lock_); - return FromDaliCppFlags(dali_gateway_cache_.pendingChannelFlags(gateway_id)); -} - -GatewayCacheDaliAddressState GatewayCache::daliAddressState(uint8_t gateway_id, - uint8_t short_address) { - LockGuard guard(lock_); - if (short_address >= 64) return {}; - return FromDaliCppAddressState(dali_gateway_cache_.addressState(gateway_id, short_address)); -} - -GatewayCacheDaliPresence GatewayCache::daliAddressPresence(uint8_t gateway_id, - uint8_t short_address) { - LockGuard guard(lock_); - return short_address >= 64 ? GatewayCacheDaliPresence::kUnknown - : FromDaliCppPresence(dali_gateway_cache_.addressPresence(gateway_id, - short_address)); -} - -void GatewayCache::markDaliAddressPresence(uint8_t gateway_id, uint8_t short_address, - GatewayCacheDaliPresence presence) { - LockGuard guard(lock_); - dali_gateway_cache_.markAddressPresence(gateway_id, short_address, ToDaliCppPresence(presence)); -} - -std::optional GatewayCache::decodeDaliTarget(uint8_t raw_addr) { - return FromDaliCppTarget(DaliGatewayCache::decodeTarget(raw_addr)); -} - -std::vector GatewayCache::reconciliationAddresses( - uint8_t gateway_id, std::optional target) { - LockGuard guard(lock_); - return dali_gateway_cache_.reconciliationAddresses(gateway_id, ToDaliCppTarget(target)); -} - -GatewayCacheDaliRuntimeStatus GatewayCache::daliGroupStatus(uint8_t gateway_id, - uint8_t group_id) { - LockGuard guard(lock_); - return group_id >= 16 ? GatewayCacheDaliRuntimeStatus{} - : FromDaliCppStatus(dali_gateway_cache_.groupStatus(gateway_id, group_id)); -} - -GatewayCacheDaliRuntimeStatus GatewayCache::daliBroadcastStatus(uint8_t gateway_id) { - LockGuard guard(lock_); - return FromDaliCppStatus(dali_gateway_cache_.broadcastStatus(gateway_id)); -} - -bool GatewayCache::setDaliGroupMask(uint8_t gateway_id, uint8_t short_address, - std::optional group_mask) { - LockGuard guard(lock_); - const bool changed = dali_gateway_cache_.setGroupMask(gateway_id, short_address, group_mask); - if (changed) { syncDaliStatesFromCoreLocked(gateway_id); dirty_ = true; } - return changed; -} - -bool GatewayCache::setDaliSceneLevel(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id, - std::optional level) { - LockGuard guard(lock_); - const bool changed = dali_gateway_cache_.setSceneLevel(gateway_id, short_address, scene_id, level); - if (changed) { syncDaliStatesFromCoreLocked(gateway_id); dirty_ = true; } - return changed; -} - -bool GatewayCache::setDaliSettings(uint8_t gateway_id, uint8_t short_address, - std::optional settings) { - LockGuard guard(lock_); - const auto core_settings = settings.has_value() - ? std::optional(ToDaliCppSettings(*settings)) - : std::nullopt; - const bool changed = dali_gateway_cache_.setSettings(gateway_id, short_address, core_settings); - if (changed) { syncDaliStatesFromCoreLocked(gateway_id); dirty_ = true; } - return changed; -} - -bool GatewayCache::setDaliActualLevel(uint8_t gateway_id, uint8_t short_address, - std::optional level) { - LockGuard guard(lock_); - const bool changed = dali_gateway_cache_.setActualLevel(gateway_id, short_address, level); - if (changed) { syncDaliStatesFromCoreLocked(gateway_id); dirty_ = true; } - return changed; -} - -bool GatewayCache::clearChannelFlagsIfMatched(uint8_t gateway_id, - const GatewayCacheChannelFlags& flags) { - LockGuard guard(lock_); - return dali_gateway_cache_.clearChannelFlagsIfMatched(gateway_id, ToDaliCppFlags(flags)); -} - -void GatewayCache::markGroupUpdateNeeded(uint8_t gateway_id, bool needed) { - LockGuard guard(lock_); - dali_gateway_cache_.markGroupUpdateNeeded(gateway_id, needed); -} - -void GatewayCache::markSceneUpdateNeeded(uint8_t gateway_id, bool needed) { - LockGuard guard(lock_); - dali_gateway_cache_.markSceneUpdateNeeded(gateway_id, needed); -} - -void GatewayCache::markSettingsUpdateNeeded(uint8_t gateway_id, bool needed) { - LockGuard guard(lock_); - dali_gateway_cache_.markSettingsUpdateNeeded(gateway_id, needed); -} - bool GatewayCache::cacheEnabled() const { - return dali_gateway_cache_.enabled(); + LockGuard guard(lock_); + return config_.cache_enabled; } bool GatewayCache::setCacheEnabled(bool enabled) { { LockGuard guard(lock_); - if (config_.cache_enabled == enabled) { - return true; - } + if (config_.cache_enabled == enabled) return true; } - - if (!enabled) { - // Preserve pending state before disabling the background cache worker. - if (!flushDirty()) { - return false; - } - LockGuard guard(lock_); - config_.cache_enabled = false; - dali_gateway_cache_.setEnabled(false); - return true; - } - + if (!enabled && !flushDirty()) return false; { LockGuard guard(lock_); - config_.cache_enabled = true; - dali_gateway_cache_.setEnabled(true); - } - return start() == ESP_OK; -} - -bool GatewayCache::reconciliationEnabled() const { - return dali_gateway_cache_.reconciliationEnabled(); -} - -bool GatewayCache::fullStateMirrorEnabled() const { - return dali_gateway_cache_.fullStateMirrorEnabled(); -} - -bool GatewayCache::mirrorDaliCommand(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) { - LockGuard guard(lock_); - const bool changed = dali_gateway_cache_.mirrorForwardFrame(gateway_id, raw_addr, command); - if (changed) { - syncDaliStatesFromCoreLocked(gateway_id); - dirty_ = true; - } - return changed; -} - -bool GatewayCache::observeDaliCommand(uint8_t gateway_id, uint8_t raw_addr, uint8_t command, - GatewayCacheRawFrameOrigin origin) { - LockGuard guard(lock_); - bool changed = false; - if (origin == GatewayCacheRawFrameOrigin::kOutsideBus) { - changed = dali_gateway_cache_.observeForwardFrame(gateway_id, raw_addr, command, - DaliGatewayFrameOrigin::outsideBus); - } else { - dali_gateway_cache_.mirrorForwardFrame(gateway_id, raw_addr, command); - } - if (dali_gateway_cache_.enabled()) { - syncDaliStatesFromCoreLocked(gateway_id); - dirty_ = true; - } - return changed; -} - -GatewayCachePriorityMode GatewayCache::priorityMode() { - LockGuard guard(lock_); - return dali_gateway_cache_.priorityMode() == DaliGatewayCachePriorityMode::localGatewayFirst - ? GatewayCachePriorityMode::kLocalGatewayFirst - : GatewayCachePriorityMode::kOutsideBusFirst; -} - -void GatewayCache::setPriorityMode(GatewayCachePriorityMode mode) { - LockGuard guard(lock_); - priority_mode_ = mode; - dali_gateway_cache_.setPriorityMode(ToDaliCppPriorityMode(mode)); -} - -void GatewayCache::syncDaliStatesFromCoreLocked(uint8_t gateway_id) { - const auto states = dali_gateway_cache_.addressStates(gateway_id); - auto& legacy = dali_states_[gateway_id]; - for (size_t index = 0; index < states.size(); ++index) { - legacy[index] = FromDaliCppAddressState(states[index]); - dali_runtime_revision_ = std::max(dali_runtime_revision_, legacy[index].status.revision); - } -} - -uint32_t GatewayCache::nextDaliRuntimeRevisionLocked() { - ++dali_runtime_revision_; - if (dali_runtime_revision_ == 0) { - ++dali_runtime_revision_; - } - return dali_runtime_revision_; -} - -bool GatewayCache::mirrorDaliCommandLocked(uint8_t gateway_id, uint8_t raw_addr, - uint8_t command) { - auto& dtr_state = dtr_states_[gateway_id]; - if (raw_addr == kDaliCmdSetDtr0) { - dtr_state.dtr0 = command; - return false; - } - if (raw_addr == kDaliCmdSetDtr1) { - dtr_state.dtr1 = command; - return false; - } - if (raw_addr == kDaliCmdSetDtr2) { - dtr_state.dtr2 = command; - return false; - } - - const auto target = DecodeDaliTarget(raw_addr); - if (!target.has_value()) { - return false; - } - - const bool arc_power_frame = (raw_addr & 0x01) == 0; - if (arc_power_frame) { - if (command > 254) { - return false; - } - GatewayCacheDaliRuntimeStatus status; - status.actual_level = command; - status.revision = nextDaliRuntimeRevisionLocked(); - status.stale = false; - applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status); - dirty_ = true; - return true; - } - - if (command == kDaliCmdReset) { - clearDaliTargetStateLocked(gateway_id, *target, nextDaliRuntimeRevisionLocked()); - dirty_ = true; - return true; - } - - if (command == kDaliCmdOff || command == kDaliCmdRecallMax) { - GatewayCacheDaliRuntimeStatus status; - status.actual_level = command == kDaliCmdOff ? 0 : 254; - status.revision = nextDaliRuntimeRevisionLocked(); - status.stale = false; - applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status); - dirty_ = true; - return true; - } - - if (command == kDaliCmdRecallMin) { - GatewayCacheDaliRuntimeStatus status; - status.use_min_level = true; - status.revision = nextDaliRuntimeRevisionLocked(); - status.stale = false; - applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status); - dirty_ = true; - return true; - } - - if (command >= kDaliCmdGoToSceneMin && command <= kDaliCmdGoToSceneMax) { - GatewayCacheDaliRuntimeStatus status; - status.scene_id = static_cast(command - kDaliCmdGoToSceneMin); - status.revision = nextDaliRuntimeRevisionLocked(); - status.stale = false; - applyDaliTargetRuntimeStatusLocked(gateway_id, *target, status); - dirty_ = true; - return true; - } - - if (command >= kDaliCmdAddToGroupMin && command <= kDaliCmdRemoveFromGroupMax) { - applyDaliTargetGroupMutationLocked(gateway_id, *target, - static_cast(command & 0x0F), - command < (kDaliCmdAddToGroupMin + 16)); - dirty_ = true; - return true; - } - - if (command >= kDaliCmdSetSceneMin && command < (kDaliCmdSetSceneMin + 16) && - dtr_state.dtr0.has_value()) { - applyDaliTargetSceneLevelLocked(gateway_id, *target, - static_cast(command - kDaliCmdSetSceneMin), - *dtr_state.dtr0); - dirty_ = true; - return true; - } - - if (command >= (kDaliCmdSetSceneMin + 16) && command <= kDaliCmdRemoveSceneMax) { - applyDaliTargetSceneLevelLocked( - gateway_id, *target, static_cast(command - (kDaliCmdSetSceneMin + 16)), - static_cast(255U)); - dirty_ = true; - return true; - } - - if (command >= kDaliCmdStoreDtrAsMaxLevel && command <= kDaliCmdStoreDtrAsFadeRate && - dtr_state.dtr0.has_value()) { - applyDaliTargetSettingsLocked(gateway_id, *target, command, *dtr_state.dtr0); - dirty_ = true; - return true; - } - - return false; -} - -void GatewayCache::clearDaliTargetStateLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, - uint32_t revision) { - auto clear_state = [revision](GatewayCacheDaliAddressState& state) { - ClearDaliState(state); - state.status.revision = revision; - }; - - switch (target.kind) { - case GatewayCacheDaliTargetKind::kShortAddress: - if (target.value < 64) { - clear_state(ensureDaliAddressStateLocked(gateway_id, target.value)); - } - break; - case GatewayCacheDaliTargetKind::kGroup: { - if (target.value >= 16) { - break; - } - auto& group_status = ensureDaliGroupStatusLocked(gateway_id, target.value); - group_status = {}; - group_status.revision = revision; - const uint16_t bit = static_cast(1U << target.value); - if (auto states = dali_states_.find(gateway_id); states != dali_states_.end()) { - for (auto& state : states->second) { - if (state.group_mask_known && (state.group_mask & bit) != 0) { - clear_state(state); - } - } - } - break; - } - case GatewayCacheDaliTargetKind::kBroadcast: { - auto& broadcast_status = ensureDaliBroadcastStatusLocked(gateway_id); - broadcast_status = {}; - broadcast_status.revision = revision; - auto& group_statuses = dali_group_status_[gateway_id]; - for (auto& group_status : group_statuses) { - group_status = {}; - group_status.revision = revision; - } - auto& states = dali_states_[gateway_id]; - for (auto& state : states) { - clear_state(state); - } - break; - } - default: - break; - } -} - -void GatewayCache::applyDaliTargetRuntimeStatusLocked( - uint8_t gateway_id, const GatewayCacheDaliTarget& target, - const GatewayCacheDaliRuntimeStatus& status) { - if (!status.anyKnown()) { - return; - } - - switch (target.kind) { - case GatewayCacheDaliTargetKind::kShortAddress: - if (target.value < 64) { - applyDaliRuntimeStatusToAddressLocked(ensureDaliAddressStateLocked(gateway_id, - target.value), - status); - } - break; - case GatewayCacheDaliTargetKind::kGroup: { - if (target.value >= 16) { - break; - } - ensureDaliGroupStatusLocked(gateway_id, target.value) = status; - const uint16_t bit = static_cast(1U << target.value); - if (auto states = dali_states_.find(gateway_id); states != dali_states_.end()) { - for (auto& state : states->second) { - if (state.group_mask_known && (state.group_mask & bit) != 0) { - applyDaliRuntimeStatusToAddressLocked(state, status); - } - } - } - break; - } - case GatewayCacheDaliTargetKind::kBroadcast: { - ensureDaliBroadcastStatusLocked(gateway_id) = status; - auto& states = dali_states_[gateway_id]; - for (auto& state : states) { - applyDaliRuntimeStatusToAddressLocked(state, status); - } - break; - } - default: - break; - } -} - -void GatewayCache::applyDaliRuntimeStatusToAddressLocked( - GatewayCacheDaliAddressState& state, const GatewayCacheDaliRuntimeStatus& status) { - if (!status.anyKnown() || status.revision <= state.status.revision) { - return; - } - - if (status.scene_id.has_value()) { - state.status.scene_id = status.scene_id; - state.status.use_min_level = false; - const uint8_t scene_id = *status.scene_id; - if (scene_id < state.scene_levels.size()) { - const auto scene_level = state.scene_levels[scene_id]; - if (scene_level.has_value() && *scene_level != 255U) { - state.status.actual_level = *scene_level; - } else if (status.actual_level.has_value()) { - state.status.actual_level = status.actual_level; - } - } - } else { - state.status.scene_id.reset(); - state.status.use_min_level = status.use_min_level; - if (status.use_min_level) { - state.status.actual_level = state.settings.min_level; - } else { - state.status.actual_level = status.actual_level; - } - } - state.status.revision = status.revision; - state.status.stale = status.stale; -} - -void GatewayCache::applyDaliTargetGroupMutationLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, - uint8_t group_id, bool add_to_group) { - if (group_id >= 16) { - return; - } - const uint16_t bit = static_cast(1U << group_id); - auto apply = [&](GatewayCacheDaliAddressState& state) { - if (!state.group_mask_known) { - return; - } - if (add_to_group) { - state.group_mask |= bit; - } else { - state.group_mask &= static_cast(~bit); - } - refreshDaliAddressAggregateStatusLocked(gateway_id, state); - }; - - switch (target.kind) { - case GatewayCacheDaliTargetKind::kShortAddress: - if (target.value < 64) { - apply(ensureDaliAddressStateLocked(gateway_id, target.value)); - } - break; - case GatewayCacheDaliTargetKind::kGroup: { - if (target.value >= 16) { - break; - } - const uint16_t target_bit = static_cast(1U << target.value); - if (auto states = dali_states_.find(gateway_id); states != dali_states_.end()) { - for (auto& state : states->second) { - if (state.group_mask_known && (state.group_mask & target_bit) != 0) { - apply(state); - } - } - } - break; - } - case GatewayCacheDaliTargetKind::kBroadcast: { - auto& states = dali_states_[gateway_id]; - for (auto& state : states) { - apply(state); - } - break; - } - default: - break; - } -} - -void GatewayCache::applyDaliTargetSceneLevelLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, - uint8_t scene_id, - std::optional level) { - if (scene_id >= 16) { - return; - } - auto apply = [scene_id, level](GatewayCacheDaliAddressState& state) { - state.scene_levels[scene_id] = level; - if (state.status.scene_id.has_value() && *state.status.scene_id == scene_id && - level.has_value() && *level != 255U) { - state.status.actual_level = *level; - } - }; - - switch (target.kind) { - case GatewayCacheDaliTargetKind::kShortAddress: - if (target.value < 64) { - apply(ensureDaliAddressStateLocked(gateway_id, target.value)); - } - break; - case GatewayCacheDaliTargetKind::kGroup: { - if (target.value >= 16) { - break; - } - const uint16_t bit = static_cast(1U << target.value); - if (auto states = dali_states_.find(gateway_id); states != dali_states_.end()) { - for (auto& state : states->second) { - if (state.group_mask_known && (state.group_mask & bit) != 0) { - apply(state); - } - } - } - break; - } - case GatewayCacheDaliTargetKind::kBroadcast: { - auto& states = dali_states_[gateway_id]; - for (auto& state : states) { - apply(state); - } - break; - } - default: - break; - } -} - -void GatewayCache::applyDaliTargetSettingsLocked(uint8_t gateway_id, - const GatewayCacheDaliTarget& target, - uint8_t command, uint8_t value) { - auto apply = [command, value](GatewayCacheDaliAddressState& state) { - ApplyObservedSettingsValue(state.settings, command, value); - if (command == kDaliCmdStoreDtrAsMinLevel && state.status.use_min_level) { - state.status.actual_level = value; - } - }; - - switch (target.kind) { - case GatewayCacheDaliTargetKind::kShortAddress: - if (target.value < 64) { - apply(ensureDaliAddressStateLocked(gateway_id, target.value)); - } - break; - case GatewayCacheDaliTargetKind::kGroup: { - if (target.value >= 16) { - break; - } - const uint16_t bit = static_cast(1U << target.value); - if (auto states = dali_states_.find(gateway_id); states != dali_states_.end()) { - for (auto& state : states->second) { - if (state.group_mask_known && (state.group_mask & bit) != 0) { - apply(state); - } - } - } - break; - } - case GatewayCacheDaliTargetKind::kBroadcast: { - auto& states = dali_states_[gateway_id]; - for (auto& state : states) { - apply(state); - } - break; - } - default: - break; - } -} - -void GatewayCache::refreshDaliAddressAggregateStatusLocked(uint8_t gateway_id, - GatewayCacheDaliAddressState& state) { - if (!state.group_mask_known) { - return; - } - - if (const auto broadcast = dali_broadcast_status_.find(gateway_id); - broadcast != dali_broadcast_status_.end()) { - if (!broadcast->second.stale) { - applyDaliRuntimeStatusToAddressLocked(state, broadcast->second); - } - } - - const auto groups = dali_group_status_.find(gateway_id); - if (groups == dali_group_status_.end()) { - return; - } - for (uint8_t group_id = 0; group_id < groups->second.size(); ++group_id) { - const uint16_t bit = static_cast(1U << group_id); - if ((state.group_mask & bit) != 0) { - if (groups->second[group_id].stale) { - continue; - } - applyDaliRuntimeStatusToAddressLocked(state, groups->second[group_id]); - } + config_.cache_enabled = enabled; } + return !enabled || start() == ESP_OK; } void GatewayCache::TaskEntry(void* arg) { @@ -1401,6 +488,9 @@ void GatewayCache::taskLoop() { } bool GatewayCache::flushDirty() { + if (!dali_cache_.flush()) { + return false; + } LockGuard guard(lock_); if (!config_.cache_enabled) { return true; @@ -1454,14 +544,6 @@ bool GatewayCache::flushDirty() { } } - for (const auto& [gateway_id, states] : dali_states_) { - for (uint8_t short_address = 0; short_address < states.size(); ++short_address) { - if (!persistDaliAddressStateLocked(gateway_id, short_address, states[short_address])) { - return false; - } - } - } - const esp_err_t commit_err = nvs_commit(storage_); if (commit_err != ESP_OK) { ESP_LOGE(kTag, "cache commit failed: %s", esp_err_to_name(commit_err)); @@ -1541,18 +623,6 @@ bool GatewayCache::persistGroupLocked(uint8_t gateway_id, uint8_t group_id, return commitStorageLocked(); } -bool GatewayCache::persistDaliAddressStateLocked( - uint8_t gateway_id, uint8_t short_address, const GatewayCacheDaliAddressState& state) { - if (short_address >= 64) { - return false; - } - if (!IsDefaultDaliAddressState(state)) { - return writeStringLocked(ShortKey("ds", gateway_id, short_address), - BuildDaliStatePayload(state)); - } - return eraseKeyLocked(ShortKey("ds", gateway_id, short_address)); -} - bool GatewayCache::commitStorageLocked() { if (storage_ == 0) { return false; @@ -1565,44 +635,6 @@ bool GatewayCache::commitStorageLocked() { return true; } -bool GatewayCache::shouldTrackUpdateFlagsLocked() const { - return config_.cache_enabled && config_.reconciliation_enabled; -} - -void GatewayCache::markDaliAddressPresenceLocked(uint8_t gateway_id, uint8_t short_address, - GatewayCacheDaliPresence presence) { - if (short_address >= 64) { - return; - } - auto& states = dali_presence_[gateway_id]; - const auto previous = states[short_address]; - states[short_address] = presence; - if (previous != presence) { - ESP_LOGD(kTag, "presence gateway=%u short=%u state=%u", gateway_id, short_address, - static_cast(presence)); - } -} - -GatewayCacheDaliAddressState& GatewayCache::ensureDaliAddressStateLocked(uint8_t gateway_id, - uint8_t short_address) { - auto [it, inserted] = dali_states_.try_emplace(gateway_id); - if (inserted) { - loadDaliStateStoreLocked(gateway_id, it->second); - } - return it->second[short_address]; -} - -GatewayCacheDaliRuntimeStatus& GatewayCache::ensureDaliGroupStatusLocked(uint8_t gateway_id, - uint8_t group_id) { - auto [it, inserted] = dali_group_status_.try_emplace(gateway_id); - (void)inserted; - return it->second[group_id]; -} - -GatewayCacheDaliRuntimeStatus& GatewayCache::ensureDaliBroadcastStatusLocked(uint8_t gateway_id) { - return dali_broadcast_status_[gateway_id]; -} - GatewayCache::SceneStore& GatewayCache::ensureSceneStoreLocked(uint8_t gateway_id) { auto [it, inserted] = scenes_.try_emplace(gateway_id); if (inserted) { @@ -1658,23 +690,6 @@ void GatewayCache::loadGroupStoreLocked(uint8_t gateway_id, GroupStore& groups) } } -void GatewayCache::loadDaliStateStoreLocked( - uint8_t gateway_id, std::array& states) { - for (uint8_t short_address = 0; short_address < states.size(); ++short_address) { - ClearDaliState(states[short_address]); - const auto raw = readStringLocked(ShortKey("ds", gateway_id, short_address)); - if (!raw.empty()) { - ApplyDaliStatePayload(raw, states[short_address]); - if (states[short_address].status.anyKnown()) { - states[short_address].status.stale = true; - } - if (states[short_address].status.revision > dali_runtime_revision_) { - dali_runtime_revision_ = states[short_address].status.revision; - } - } - } -} - std::string GatewayCache::readStringLocked(std::string_view key) { if (!openStorageLocked()) { return {}; diff --git a/components/gateway_controller/include/gateway_controller.hpp b/components/gateway_controller/include/gateway_controller.hpp index 15da8d8..d2c7a7a 100644 --- a/components/gateway_controller/include/gateway_controller.hpp +++ b/components/gateway_controller/include/gateway_controller.hpp @@ -10,6 +10,7 @@ #include #include +#include "dali_gateway.hpp" #include "gateway_cache.hpp" #include "esp_err.h" #include "freertos/FreeRTOS.h" @@ -148,9 +149,9 @@ class GatewayController { kSettings = 3, }; - GatewayCacheChannelFlags flags{}; + DaliGatewayChannelFlags flags{}; Phase phase{Phase::kReloadFlags}; - std::optional target; + std::optional target; std::vector addresses; size_t address_index{0}; uint8_t scene_id{0}; @@ -210,7 +211,7 @@ class GatewayController { void runOperationTask(GatewayOperationTaskContext* context); void dispatchCommand(const std::vector& command); void scheduleReconciliation(uint8_t gateway_id, - std::optional target = std::nullopt); + std::optional target = std::nullopt); bool hasPendingReconciliation() const; bool cacheRefreshEnabled() const; bool cacheMaintenanceSnoozed(uint8_t gateway_id) const; @@ -303,6 +304,7 @@ class GatewayController { GatewayRuntime& runtime_; DaliDomainService& dali_domain_; + DaliGatewayCache& dali_cache_; GatewayCache& cache_; GatewayBridgeService* bridge_service_{nullptr}; GatewayControllerConfig config_; diff --git a/components/gateway_controller/src/gateway_controller.cpp b/components/gateway_controller/src/gateway_controller.cpp index d70ed13..6c519ca 100644 --- a/components/gateway_controller/src/gateway_controller.cpp +++ b/components/gateway_controller/src/gateway_controller.cpp @@ -206,12 +206,12 @@ class LockGuard { SemaphoreHandle_t lock_; }; -bool AnyFlagSet(const GatewayCacheChannelFlags& flags) { - return flags.need_update_group || flags.need_update_scene || flags.need_update_settings; +bool AnyFlagSet(const DaliGatewayChannelFlags& flags) { + return flags.needUpdateGroup || flags.needUpdateScene || flags.needUpdateSettings; } -bool SameTarget(const std::optional& lhs, - const std::optional& rhs) { +bool SameTarget(const std::optional& lhs, + const std::optional& rhs) { if (lhs.has_value() != rhs.has_value()) { return false; } @@ -453,15 +453,15 @@ uint8_t CacheByte(std::optional value) { return value.value_or(0xFF); } -uint16_t CacheRuntimeFlags(const GatewayCacheDaliRuntimeStatus& status) { +uint16_t CacheRuntimeFlags(const DaliGatewayRuntimeStatus& status) { uint16_t flags = 0; - if (status.actual_level.has_value()) { + if (status.actualLevel.has_value()) { flags |= kCacheFlagActualKnown; } - if (status.scene_id.has_value()) { + if (status.sceneID.has_value()) { flags |= kCacheFlagSceneKnown; } - if (status.use_min_level) { + if (status.useMinLevel) { flags |= kCacheFlagUseMinLevel; } if (status.stale) { @@ -470,36 +470,36 @@ uint16_t CacheRuntimeFlags(const GatewayCacheDaliRuntimeStatus& status) { return flags; } -uint16_t CacheAddressFlags(const GatewayCacheDaliAddressState& state) { +uint16_t CacheAddressFlags(const DaliGatewayAddressState& state) { uint16_t flags = CacheRuntimeFlags(state.status); - if (state.group_mask_known) { + if (state.groupMaskKnown) { flags |= kCacheFlagGroupMaskKnown; } - if (state.settings.power_on_level.has_value()) { + if (state.settings.powerOnLevel.has_value()) { flags |= kCacheFlagPowerOnKnown; } - if (state.settings.system_failure_level.has_value()) { + if (state.settings.systemFailureLevel.has_value()) { flags |= kCacheFlagSystemFailureKnown; } - if (state.settings.min_level.has_value()) { + if (state.settings.minLevel.has_value()) { flags |= kCacheFlagMinKnown; } - if (state.settings.max_level.has_value()) { + if (state.settings.maxLevel.has_value()) { flags |= kCacheFlagMaxKnown; } - if (state.settings.fade_time.has_value()) { + if (state.settings.fadeTime.has_value()) { flags |= kCacheFlagFadeTimeKnown; } - if (state.settings.fade_rate.has_value()) { + if (state.settings.fadeRate.has_value()) { flags |= kCacheFlagFadeRateKnown; } return flags; } -uint16_t CacheSceneKnownMask(const GatewayCacheDaliAddressState& state) { +uint16_t CacheSceneKnownMask(const DaliGatewayAddressState& state) { uint16_t mask = 0; - for (size_t index = 0; index < state.scene_levels.size(); ++index) { - if (state.scene_levels[index].has_value()) { + for (size_t index = 0; index < state.sceneLevels.size(); ++index) { + if (state.sceneLevels[index].has_value()) { mask |= static_cast(1U << index); } } @@ -546,6 +546,7 @@ GatewayController::GatewayController(GatewayRuntime& runtime, DaliDomainService& GatewayCache& cache, GatewayControllerConfig config) : runtime_(runtime), dali_domain_(dali_domain), + dali_cache_(dali_domain.gatewayCache()), cache_(cache), config_(config), maintenance_lock_(xSemaphoreCreateMutex()), @@ -577,7 +578,7 @@ esp_err_t GatewayController::start() { dali_domain_.addRawFrameSink([this](const DaliRawFrame& frame) { handleDaliRawFrame(frame); }); for (const auto& channel : dali_domain_.channelInfo()) { - cache_.preloadChannel(channel.gateway_id); + dali_cache_.preloadChannel(channel.gateway_id); dali_domain_.resetBus(channel.gateway_id); publishPayload(channel.gateway_id, {0x02, channel.gateway_id, 0x88}); } @@ -746,19 +747,23 @@ bool GatewayController::setBleEnabled(bool enabled) { } bool GatewayController::cacheEnabled() const { - return cache_.cacheEnabled(); + return dali_cache_.enabled(); } bool GatewayController::setCacheEnabled(bool enabled) { if (!config_.cache_supported || !runtime_.setCacheEnabled(enabled)) { return false; } - if (cache_.setCacheEnabled(enabled)) { + if (!enabled && !cache_.setCacheEnabled(false)) { + runtime_.setCacheEnabled(dali_cache_.enabled()); + return false; + } + if (dali_cache_.setEnabled(enabled) && (enabled ? cache_.setCacheEnabled(true) : true)) { return true; } // Keep the persisted setting aligned with the live cache when the worker // cannot be started or stopped. - runtime_.setCacheEnabled(cache_.cacheEnabled()); + runtime_.setCacheEnabled(dali_cache_.enabled()); return false; } @@ -880,7 +885,7 @@ bool GatewayController::setChannelGatewayId(uint8_t channel_index, uint8_t gatew dali_domain_.updateChannelGatewayId(channel_index, current_gateway_id.value()); return false; } - cache_.preloadChannel(gateway_id); + dali_cache_.preloadChannel(gateway_id); { LockGuard guard(maintenance_lock_); reconciliation_jobs_.erase(current_gateway_id.value()); @@ -952,13 +957,13 @@ void GatewayController::taskLoop() { } void GatewayController::scheduleReconciliation(uint8_t gateway_id, - std::optional target) { - if (!cache_.reconciliationEnabled()) { + std::optional target) { + if (!dali_cache_.reconciliationEnabled()) { return; } - auto flags = cache_.pendingChannelFlags(gateway_id); - if (cache_.fullStateMirrorEnabled() && AnyFlagSet(flags)) { + auto flags = dali_cache_.pendingChannelFlags(gateway_id); + if (dali_cache_.fullStateMirrorEnabled() && AnyFlagSet(flags)) { flags = {true, true, true}; } if (!AnyFlagSet(flags)) { @@ -990,7 +995,7 @@ bool GatewayController::hasPendingReconciliation() const { } bool GatewayController::cacheRefreshEnabled() const { - return config_.cache_supported && cache_.cacheEnabled() && + return config_.cache_supported && dali_cache_.enabled() && config_.cache_refresh_interval_ms > 0; } @@ -1000,7 +1005,7 @@ bool GatewayController::cacheMaintenanceSnoozed(uint8_t gateway_id) const { } bool GatewayController::runMaintenanceStep() { - if (cache_.reconciliationEnabled()) { + if (dali_cache_.reconciliationEnabled()) { bool has_job = false; uint8_t gateway_id = 0; ReconciliationJob job; @@ -1045,24 +1050,24 @@ bool GatewayController::runMaintenanceStep() { bool GatewayController::runReconciliationStep(uint8_t gateway_id, ReconciliationJob& job) { if (job.phase == ReconciliationJob::Phase::kReloadFlags) { - job.flags = cache_.pendingChannelFlags(gateway_id); - if (cache_.fullStateMirrorEnabled() && AnyFlagSet(job.flags)) { + job.flags = dali_cache_.pendingChannelFlags(gateway_id); + if (dali_cache_.fullStateMirrorEnabled() && AnyFlagSet(job.flags)) { job.flags = {true, true, true}; } if (!AnyFlagSet(job.flags)) { return false; } - job.addresses = cache_.reconciliationAddresses(gateway_id, job.target); + job.addresses = dali_cache_.reconciliationAddresses(gateway_id, job.target); job.address_index = 0; job.scene_id = 0; if (job.addresses.empty()) { - cache_.clearChannelFlagsIfMatched(gateway_id, job.flags); + dali_cache_.clearChannelFlagsIfMatched(gateway_id, job.flags); return false; } - if (job.flags.need_update_group) { + if (job.flags.needUpdateGroup) { job.phase = ReconciliationJob::Phase::kGroups; - } else if (job.flags.need_update_scene) { + } else if (job.flags.needUpdateScene) { job.phase = ReconciliationJob::Phase::kScenes; } else { job.phase = ReconciliationJob::Phase::kSettings; @@ -1074,11 +1079,11 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation reconcileGroupStep(gateway_id, job.addresses[job.address_index++]); if (job.address_index >= job.addresses.size()) { job.address_index = 0; - if (job.flags.need_update_scene) { + if (job.flags.needUpdateScene) { job.phase = ReconciliationJob::Phase::kScenes; - } else if (job.flags.need_update_settings) { + } else if (job.flags.needUpdateSettings) { job.phase = ReconciliationJob::Phase::kSettings; - } else if (!cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { + } else if (!dali_cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { job.phase = ReconciliationJob::Phase::kReloadFlags; } else { return false; @@ -1087,8 +1092,8 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation return true; case ReconciliationJob::Phase::kScenes: { const uint8_t short_address = job.addresses[job.address_index]; - if (cache_.daliAddressPresence(gateway_id, short_address) == - GatewayCacheDaliPresence::kOffline) { + if (dali_cache_.addressPresence(gateway_id, short_address) == + DaliGatewayPresence::offline) { job.scene_id = 0; ++job.address_index; } else { @@ -1101,9 +1106,9 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation } if (job.address_index >= job.addresses.size()) { job.address_index = 0; - if (job.flags.need_update_settings) { + if (job.flags.needUpdateSettings) { job.phase = ReconciliationJob::Phase::kSettings; - } else if (!cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { + } else if (!dali_cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { job.phase = ReconciliationJob::Phase::kReloadFlags; } else { return false; @@ -1113,13 +1118,13 @@ bool GatewayController::runReconciliationStep(uint8_t gateway_id, Reconciliation } case ReconciliationJob::Phase::kSettings: { const uint8_t short_address = job.addresses[job.address_index++]; - if (cache_.daliAddressPresence(gateway_id, short_address) != - GatewayCacheDaliPresence::kOffline) { + if (dali_cache_.addressPresence(gateway_id, short_address) != + DaliGatewayPresence::offline) { reconcileSettingsStep(gateway_id, short_address); } if (job.address_index >= job.addresses.size()) { job.address_index = 0; - if (!cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { + if (!dali_cache_.clearChannelFlagsIfMatched(gateway_id, job.flags)) { job.phase = ReconciliationJob::Phase::kReloadFlags; } else { return false; @@ -1170,8 +1175,8 @@ bool GatewayController::runCacheRefreshStep() { } }; - if (cache_.daliAddressPresence(channel.gateway_id, job.short_address) == - GatewayCacheDaliPresence::kOffline) { + if (dali_cache_.addressPresence(channel.gateway_id, job.short_address) == + DaliGatewayPresence::offline) { advance_job(); return true; } @@ -1179,11 +1184,11 @@ bool GatewayController::runCacheRefreshStep() { maintenance_activity_gateway_.store(channel.gateway_id); const auto actual_level = dali_domain_.queryActualLevel(channel.gateway_id, job.short_address); maintenance_activity_gateway_.store(-1); - cache_.markDaliAddressPresence(channel.gateway_id, job.short_address, + dali_cache_.markAddressPresence(channel.gateway_id, job.short_address, actual_level.has_value() - ? GatewayCacheDaliPresence::kOnline - : GatewayCacheDaliPresence::kOffline); - cache_.setDaliActualLevel(channel.gateway_id, job.short_address, actual_level); + ? DaliGatewayPresence::online + : DaliGatewayPresence::offline); + dali_cache_.setActualLevel(channel.gateway_id, job.short_address, actual_level); advance_job(); return true; } @@ -1192,19 +1197,19 @@ bool GatewayController::runCacheRefreshStep() { } void GatewayController::reconcileGroupStep(uint8_t gateway_id, uint8_t short_address) { - const auto policy = cache_.priorityMode(); - const auto state = cache_.daliAddressState(gateway_id, short_address); + const auto policy = dali_cache_.priorityMode(); + const auto state = dali_cache_.addressState(gateway_id, short_address); - if (policy == GatewayCachePriorityMode::kLocalGatewayFirst && state.group_mask_known) { + if (policy == DaliGatewayCachePriorityMode::localGatewayFirst && state.groupMaskKnown) { maintenance_activity_gateway_.store(gateway_id); - const bool applied = dali_domain_.applyGroupMask(gateway_id, short_address, state.group_mask); + const bool applied = dali_domain_.applyGroupMask(gateway_id, short_address, state.groupMask); maintenance_activity_gateway_.store(-1); const auto verified_mask = dali_domain_.queryGroupMask(gateway_id, short_address); - cache_.markDaliAddressPresence(gateway_id, short_address, + dali_cache_.markAddressPresence(gateway_id, short_address, verified_mask.has_value() - ? GatewayCacheDaliPresence::kOnline - : GatewayCacheDaliPresence::kOffline); - cache_.setDaliGroupMask(gateway_id, short_address, verified_mask); + ? DaliGatewayPresence::online + : DaliGatewayPresence::offline); + dali_cache_.setGroupMask(gateway_id, short_address, verified_mask); if (!applied && verified_mask.has_value()) { ESP_LOGW(kTag, "group reconcile fallback gateway=%u short=%u", gateway_id, short_address); } @@ -1212,62 +1217,62 @@ void GatewayController::reconcileGroupStep(uint8_t gateway_id, uint8_t short_add } const auto group_mask = dali_domain_.queryGroupMask(gateway_id, short_address); - cache_.markDaliAddressPresence(gateway_id, short_address, - group_mask.has_value() ? GatewayCacheDaliPresence::kOnline - : GatewayCacheDaliPresence::kOffline); - cache_.setDaliGroupMask(gateway_id, short_address, group_mask); + dali_cache_.markAddressPresence(gateway_id, short_address, + group_mask.has_value() ? DaliGatewayPresence::online + : DaliGatewayPresence::offline); + dali_cache_.setGroupMask(gateway_id, short_address, group_mask); } void GatewayController::reconcileSceneStep(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id) { - const auto policy = cache_.priorityMode(); - const auto state = cache_.daliAddressState(gateway_id, short_address); + const auto policy = dali_cache_.priorityMode(); + const auto state = dali_cache_.addressState(gateway_id, short_address); - if (policy == GatewayCachePriorityMode::kLocalGatewayFirst && - state.scene_levels[scene_id].has_value()) { + if (policy == DaliGatewayCachePriorityMode::localGatewayFirst && + state.sceneLevels[scene_id].has_value()) { maintenance_activity_gateway_.store(gateway_id); - dali_domain_.applySceneLevel(gateway_id, short_address, scene_id, state.scene_levels[scene_id]); + dali_domain_.applySceneLevel(gateway_id, short_address, scene_id, state.sceneLevels[scene_id]); maintenance_activity_gateway_.store(-1); } const auto level = dali_domain_.querySceneLevel(gateway_id, short_address, scene_id); - cache_.markDaliAddressPresence(gateway_id, short_address, - level.has_value() ? GatewayCacheDaliPresence::kOnline - : GatewayCacheDaliPresence::kOffline); - cache_.setDaliSceneLevel(gateway_id, short_address, scene_id, level); + dali_cache_.markAddressPresence(gateway_id, short_address, + level.has_value() ? DaliGatewayPresence::online + : DaliGatewayPresence::offline); + dali_cache_.setSceneLevel(gateway_id, short_address, scene_id, level); } void GatewayController::reconcileSettingsStep(uint8_t gateway_id, uint8_t short_address) { - const auto policy = cache_.priorityMode(); - const auto state = cache_.daliAddressState(gateway_id, short_address); + const auto policy = dali_cache_.priorityMode(); + const auto state = dali_cache_.addressState(gateway_id, short_address); - if (policy == GatewayCachePriorityMode::kLocalGatewayFirst && state.settings.anyKnown()) { + if (policy == DaliGatewayCachePriorityMode::localGatewayFirst && state.settings.anyKnown()) { maintenance_activity_gateway_.store(gateway_id); dali_domain_.applyAddressSettings(gateway_id, short_address, { - state.settings.power_on_level, - state.settings.system_failure_level, - state.settings.min_level, - state.settings.max_level, - state.settings.fade_time, - state.settings.fade_rate, + state.settings.powerOnLevel, + state.settings.systemFailureLevel, + state.settings.minLevel, + state.settings.maxLevel, + state.settings.fadeTime, + state.settings.fadeRate, }); maintenance_activity_gateway_.store(-1); } const auto settings = dali_domain_.queryAddressSettings(gateway_id, short_address); - cache_.markDaliAddressPresence(gateway_id, short_address, - settings.has_value() ? GatewayCacheDaliPresence::kOnline - : GatewayCacheDaliPresence::kOffline); + dali_cache_.markAddressPresence(gateway_id, short_address, + settings.has_value() ? DaliGatewayPresence::online + : DaliGatewayPresence::offline); if (settings.has_value()) { - cache_.setDaliSettings(gateway_id, short_address, - GatewayCacheDaliSettingsSnapshot{settings->power_on_level, + dali_cache_.setSettings(gateway_id, short_address, + DaliGatewaySettingsSnapshot{settings->power_on_level, settings->system_failure_level, settings->min_level, settings->max_level, settings->fade_time, settings->fade_rate}); } else { - cache_.setDaliSettings(gateway_id, short_address, std::nullopt); + dali_cache_.setSettings(gateway_id, short_address, std::nullopt); } } @@ -1932,12 +1937,13 @@ void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) { dali_domain_.hasRecentHostActivity(frame.gateway_id, config_.cache_host_echo_ms); const bool local_activity = maintenance_activity || runtime_.hasActiveCommand(frame.gateway_id) || host_echo_activity || dali_domain_.isAllocAddr(frame.gateway_id); - const bool flagged = cache_.observeDaliCommand(frame.gateway_id, addr, data, - local_activity - ? GatewayCacheRawFrameOrigin::kLocalGateway - : GatewayCacheRawFrameOrigin::kOutsideBus); + const bool flagged = + local_activity + ? (dali_cache_.mirrorForwardFrame(frame.gateway_id, addr, data), false) + : dali_cache_.observeForwardFrame(frame.gateway_id, addr, data, + DaliGatewayFrameOrigin::outsideBus); if (flagged) { - scheduleReconciliation(frame.gateway_id, cache_.decodeDaliTarget(addr)); + scheduleReconciliation(frame.gateway_id, dali_cache_.decodeTarget(addr)); } if (setup_mode_ || dali_domain_.isAllocAddr(frame.gateway_id) || maintenance_activity || @@ -1952,7 +1958,7 @@ void GatewayController::handleDaliRawFrame(const DaliRawFrame& frame) { bool GatewayController::sendRawAndMirror(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) { const bool sent = dali_domain_.sendRaw(gateway_id, raw_addr, command); if (sent) { - cache_.mirrorDaliCommand(gateway_id, raw_addr, command); + dali_cache_.mirrorForwardFrame(gateway_id, raw_addr, command); } return sent; } @@ -1961,7 +1967,7 @@ bool GatewayController::sendExtRawAndMirror(uint8_t gateway_id, uint8_t raw_addr uint8_t command) { const bool sent = dali_domain_.sendExtRaw(gateway_id, raw_addr, command); if (sent) { - cache_.mirrorDaliCommand(gateway_id, raw_addr, command); + dali_cache_.mirrorForwardFrame(gateway_id, raw_addr, command); } return sent; } @@ -1971,7 +1977,7 @@ bool GatewayController::setBrightAndMirror(uint8_t gateway_id, int dec_address, dali_domain_.markHostCommandFrame(gateway_id, raw_addr, level); const bool sent = dali_domain_.setBright(gateway_id, dec_address, level); if (sent) { - cache_.mirrorDaliCommand(gateway_id, raw_addr, level); + dali_cache_.mirrorForwardFrame(gateway_id, raw_addr, level); } return sent; } @@ -1981,7 +1987,7 @@ bool GatewayController::offAndMirror(uint8_t gateway_id, int dec_address) { dali_domain_.markHostCommandFrame(gateway_id, raw_addr, kDaliCmdOff); const bool sent = dali_domain_.off(gateway_id, dec_address); if (sent) { - cache_.mirrorDaliCommand(gateway_id, raw_addr, kDaliCmdOff); + dali_cache_.mirrorForwardFrame(gateway_id, raw_addr, kDaliCmdOff); } return sent; } @@ -1991,7 +1997,7 @@ bool GatewayController::onAndMirror(uint8_t gateway_id, int dec_address) { dali_domain_.markHostCommandFrame(gateway_id, raw_addr, kDaliCmdRecallMax); const bool sent = dali_domain_.on(gateway_id, dec_address); if (sent) { - cache_.mirrorDaliCommand(gateway_id, raw_addr, kDaliCmdRecallMax); + dali_cache_.mirrorForwardFrame(gateway_id, raw_addr, kDaliCmdRecallMax); } return sent; } @@ -2311,7 +2317,7 @@ void GatewayController::handleGatewaySerialCommand(uint8_t channel_number, {channel_number, new_gateway_id}); return; } - cache_.preloadChannel(new_gateway_id); + dali_cache_.preloadChannel(new_gateway_id); reconciliation_jobs_.erase(old_gateway_id); cache_refresh_jobs_.erase(old_gateway_id); refreshRuntimeGatewayNames(); @@ -3393,11 +3399,11 @@ void GatewayController::handleGatewayCacheCommand(uint8_t gateway_id, const std::vector& command) { const uint8_t op = command.size() > 4 ? command[4] : kGatewayCacheOpSummary; const uint8_t arg = command.size() > 5 ? command[5] : 0; - const bool enabled = config_.cache_supported && cache_.cacheEnabled(); + const bool enabled = config_.cache_supported && dali_cache_.enabled(); if (op == kGatewayCacheOpSummary) { const uint8_t flags = static_cast((config_.cache_supported ? 0x01 : 0x00) | - (cache_.cacheEnabled() ? 0x02 : 0x00) | + (dali_cache_.enabled() ? 0x02 : 0x00) | (cacheRefreshEnabled() ? 0x04 : 0x00)); std::vector payload{kGatewayCacheOpcode, gateway_id, @@ -3423,21 +3429,21 @@ void GatewayController::handleGatewayCacheCommand(uint8_t gateway_id, kGatewayCacheStatusInvalidArgument, arg}); return; } - const auto state = cache_.daliAddressState(gateway_id, arg); + const auto state = dali_cache_.addressState(gateway_id, arg); std::vector payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, arg}; AppendLe16(payload, CacheAddressFlags(state)); - payload.push_back(CacheByte(state.status.actual_level)); - payload.push_back(CacheByte(state.status.scene_id)); - AppendLe16(payload, state.group_mask_known ? state.group_mask : 0); - payload.push_back(CacheByte(state.settings.power_on_level)); - payload.push_back(CacheByte(state.settings.system_failure_level)); - payload.push_back(CacheByte(state.settings.min_level)); - payload.push_back(CacheByte(state.settings.max_level)); - payload.push_back(CacheByte(state.settings.fade_time)); - payload.push_back(CacheByte(state.settings.fade_rate)); + payload.push_back(CacheByte(state.status.actualLevel)); + payload.push_back(CacheByte(state.status.sceneID)); + AppendLe16(payload, state.groupMaskKnown ? state.groupMask : 0); + payload.push_back(CacheByte(state.settings.powerOnLevel)); + payload.push_back(CacheByte(state.settings.systemFailureLevel)); + payload.push_back(CacheByte(state.settings.minLevel)); + payload.push_back(CacheByte(state.settings.maxLevel)); + payload.push_back(CacheByte(state.settings.fadeTime)); + payload.push_back(CacheByte(state.settings.fadeRate)); AppendLe32(payload, state.status.revision); AppendLe16(payload, CacheSceneKnownMask(state)); - for (const auto& level : state.scene_levels) { + for (const auto& level : state.sceneLevels) { payload.push_back(CacheByte(level)); } publishPayload(gateway_id, payload); @@ -3450,22 +3456,22 @@ void GatewayController::handleGatewayCacheCommand(uint8_t gateway_id, kGatewayCacheStatusInvalidArgument, arg}); return; } - const auto status = cache_.daliGroupStatus(gateway_id, arg); + const auto status = dali_cache_.groupStatus(gateway_id, arg); std::vector payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, arg}; AppendLe16(payload, CacheRuntimeFlags(status)); - payload.push_back(CacheByte(status.actual_level)); - payload.push_back(CacheByte(status.scene_id)); + payload.push_back(CacheByte(status.actualLevel)); + payload.push_back(CacheByte(status.sceneID)); AppendLe32(payload, status.revision); publishPayload(gateway_id, payload); return; } if (op == kGatewayCacheOpBroadcast) { - const auto status = cache_.daliBroadcastStatus(gateway_id); + const auto status = dali_cache_.broadcastStatus(gateway_id); std::vector payload{kGatewayCacheOpcode, gateway_id, op, kGatewayCacheStatusOk, 0}; AppendLe16(payload, CacheRuntimeFlags(status)); - payload.push_back(CacheByte(status.actual_level)); - payload.push_back(CacheByte(status.scene_id)); + payload.push_back(CacheByte(status.actualLevel)); + payload.push_back(CacheByte(status.sceneID)); AppendLe32(payload, status.revision); publishPayload(gateway_id, payload); return;