Files
gateway/components/gateway_cache/include/gateway_cache.hpp
T

228 lines
8.7 KiB
C++

#pragma once
#include <array>
#include <cstdint>
#include <map>
#include <optional>
#include <string>
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "nvs.h"
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{5000};
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,
};
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<uint8_t> power_on_level;
std::optional<uint8_t> system_failure_level;
std::optional<uint8_t> min_level;
std::optional<uint8_t> max_level;
std::optional<uint8_t> fade_time;
std::optional<uint8_t> 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<uint8_t> actual_level;
std::optional<uint8_t> scene_id;
bool use_min_level{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<std::optional<uint8_t>, 16> scene_levels{};
GatewayCacheDaliSettingsSnapshot settings;
GatewayCacheDaliRuntimeStatus status;
};
class GatewayCache {
public:
struct SceneEntry {
bool enabled{false};
uint8_t brightness{254};
uint8_t color_mode{2};
uint8_t data1{0};
uint8_t data2{0};
uint8_t data3{0};
std::string name;
};
struct GroupEntry {
bool enabled{false};
uint8_t target_type{2};
uint8_t target_value{0};
std::string name;
};
using SceneStore = std::array<SceneEntry, 16>;
using GroupStore = std::array<GroupEntry, 16>;
explicit GatewayCache(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);
SceneEntry scene(uint8_t gateway_id, uint8_t scene_id);
GroupEntry group(uint8_t gateway_id, uint8_t group_id);
bool setSceneEnabled(uint8_t gateway_id, uint8_t scene_id, bool enabled);
bool setSceneDetail(uint8_t gateway_id, uint8_t scene_id, uint8_t brightness,
uint8_t color_mode, uint8_t data1, uint8_t data2, uint8_t data3);
bool setSceneName(uint8_t gateway_id, uint8_t scene_id, std::string_view name);
bool deleteScene(uint8_t gateway_id, uint8_t scene_id);
std::pair<uint8_t, uint8_t> sceneMask(uint8_t gateway_id);
bool setGroupEnabled(uint8_t gateway_id, uint8_t group_id, bool enabled);
bool setGroupDetail(uint8_t gateway_id, uint8_t group_id, uint8_t target_type,
uint8_t target_value);
bool setGroupName(uint8_t gateway_id, uint8_t group_id, std::string_view name);
bool deleteGroup(uint8_t gateway_id, uint8_t group_id);
std::pair<uint8_t, uint8_t> 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);
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<uint16_t> group_mask);
bool setDaliSceneLevel(uint8_t gateway_id, uint8_t short_address, uint8_t scene_id,
std::optional<uint8_t> level);
bool setDaliSettings(uint8_t gateway_id, uint8_t short_address,
std::optional<GatewayCacheDaliSettingsSnapshot> settings);
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 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<uint8_t> dtr0;
std::optional<uint8_t> dtr1;
std::optional<uint8_t> dtr2;
};
static void TaskEntry(void* arg);
void taskLoop();
bool flushDirty();
bool openStorageLocked();
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 commitStorageLocked();
bool shouldTrackUpdateFlagsLocked() const;
uint32_t nextDaliRuntimeRevisionLocked();
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<uint8_t> 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);
std::string readStringLocked(std::string_view key);
bool writeStringLocked(std::string_view key, std::string_view value);
bool eraseKeyLocked(std::string_view key);
GatewayCacheConfig config_;
GatewayCachePriorityMode priority_mode_;
TaskHandle_t task_handle_{nullptr};
SemaphoreHandle_t lock_{nullptr};
nvs_handle_t storage_{0};
std::map<uint8_t, SceneStore> scenes_;
std::map<uint8_t, GroupStore> groups_;
std::map<uint8_t, std::array<GatewayCacheDaliAddressState, 64>> dali_states_;
std::map<uint8_t, std::array<GatewayCacheDaliRuntimeStatus, 16>> dali_group_status_;
std::map<uint8_t, GatewayCacheDaliRuntimeStatus> dali_broadcast_status_;
std::map<uint8_t, DtrState> dtr_states_;
std::map<uint8_t, GatewayCacheChannelFlags> channel_flags_;
uint32_t dali_runtime_revision_{0};
bool dirty_{false};
};
} // namespace gateway