feat(gateway_cache): enhance DALI state management and caching

- Increased flush interval to 10 seconds and added a refresh interval of 120 seconds in GatewayCacheConfig.
- Introduced a new boolean `stale` in GatewayCacheDaliRuntimeStatus to track stale states.
- Added methods for setting actual DALI levels and persisting DALI address states.
- Implemented functions to build and apply DALI state payloads, including handling scene levels.
- Enhanced the GatewayCache class to manage DALI states more effectively, including loading and persisting states.
- Updated GatewayController to support cache refresh operations, including handling cache commands and reporting cache status.
- Added mechanisms for periodic cache refresh based on idle time and configured intervals.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-21 15:34:26 +08:00
parent 2b8ef31263
commit 0827befb06
10 changed files with 748 additions and 64 deletions
+152 -26
View File
@@ -314,11 +314,21 @@ struct DaliDomainService::DaliChannel {
};
DaliDomainService::DaliDomainService()
: raw_frame_sink_lock_(xSemaphoreCreateMutex()) {
: raw_frame_sink_lock_(xSemaphoreCreateMutex()),
bus_activity_lock_(xSemaphoreCreateMutex()) {
esp_log_level_set(TAG, (esp_log_level_t)CONFIG_DALI_LOG_LEVEL);
}
DaliDomainService::~DaliDomainService() = default;
DaliDomainService::~DaliDomainService() {
if (raw_frame_sink_lock_ != nullptr) {
vSemaphoreDelete(raw_frame_sink_lock_);
raw_frame_sink_lock_ = nullptr;
}
if (bus_activity_lock_ != nullptr) {
vSemaphoreDelete(bus_activity_lock_);
bus_activity_lock_ = nullptr;
}
}
bool DaliDomainService::bindTransport(const DaliChannelConfig& config, DaliTransportHooks hooks) {
if (!hooks.send) {
@@ -533,12 +543,38 @@ void DaliDomainService::addRawFrameSink(std::function<void(const DaliRawFrame& f
bool DaliDomainService::resetBus(uint8_t gateway_id) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->comm != nullptr && channel->comm->resetBus();
if (channel == nullptr || channel->comm == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->comm->resetBus();
}
bool DaliDomainService::isBusIdle(uint8_t gateway_id, uint32_t quiet_ms) const {
TickType_t last_activity = 0;
if (bus_activity_lock_ != nullptr) {
xSemaphoreTake(bus_activity_lock_, portMAX_DELAY);
}
if (const auto it = last_bus_activity_ticks_.find(gateway_id);
it != last_bus_activity_ticks_.end()) {
last_activity = it->second;
}
if (bus_activity_lock_ != nullptr) {
xSemaphoreGive(bus_activity_lock_);
}
if (last_activity == 0) {
return true;
}
return (xTaskGetTickCount() - last_activity) >= pdMS_TO_TICKS(quiet_ms);
}
bool DaliDomainService::writeBridgeFrame(uint8_t gateway_id, const uint8_t* data, size_t len) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->hooks.send && channel->hooks.send(data, len);
if (channel == nullptr || !channel->hooks.send) {
return false;
}
markBusActivity(gateway_id);
return channel->hooks.send(data, len);
}
std::vector<uint8_t> DaliDomainService::transactBridgeFrame(uint8_t gateway_id,
@@ -548,17 +584,26 @@ std::vector<uint8_t> DaliDomainService::transactBridgeFrame(uint8_t gateway_id,
if (channel == nullptr || !channel->hooks.transact) {
return {};
}
markBusActivity(gateway_id);
return channel->hooks.transact(data, len);
}
bool DaliDomainService::sendRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->comm != nullptr && channel->comm->sendRawNew(raw_addr, command);
if (channel == nullptr || channel->comm == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->comm->sendRawNew(raw_addr, command);
}
bool DaliDomainService::sendExtRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t command) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->comm != nullptr && channel->comm->sendExtRawNew(raw_addr, command);
if (channel == nullptr || channel->comm == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->comm->sendExtRawNew(raw_addr, command);
}
std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t raw_addr,
@@ -567,6 +612,7 @@ std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t r
if (channel == nullptr || channel->comm == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
return channel->comm->queryRawNew(raw_addr, command);
}
@@ -577,6 +623,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::discoverDeviceTypes(
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const std::vector<int> fallback = fallback_types.empty() ? std::vector<int>{1, 4, 5, 6, 8}
: fallback_types;
auto discovery = channel->dali->base.discoverDeviceTypes(short_address, fallback,
@@ -598,6 +645,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::baseStatusSnapshot(
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto raw_status = channel->dali->base.getStatus(short_address);
if (!raw_status.has_value()) {
return std::nullopt;
@@ -623,6 +671,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto detailed = channel->dali->dt1.getDT1TestStatusDetailed(short_address);
if (!detailed.has_value()) {
return std::nullopt;
@@ -705,6 +754,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt4Snapshot(uint8_t gateway
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt4");
auto& dt4 = channel->dali->dt4;
PutOptionalInt(snapshot, "extendedVersion", dt4.getExtendedVersion(short_address));
@@ -787,6 +837,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt5Snapshot(uint8_t gateway
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt5");
auto& dt5 = channel->dali->dt5;
PutOptionalInt(snapshot, "extendedVersion", dt5.getExtendedVersion(short_address));
@@ -828,6 +879,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt6Snapshot(uint8_t gateway
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt6");
auto& dt6 = channel->dali->dt6;
PutOptionalInt(snapshot, "extendedVersion", dt6.getExtendedVersion(short_address));
@@ -891,6 +943,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8StatusSnapshot(
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
auto snapshot = MakeSnapshot(gateway_id, short_address, "dt8_status");
bool has_data = false;
@@ -925,6 +978,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SceneColorReport(
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto report = channel->dali->dt8.getSceneColorReport(short_address, scene);
if (!report.has_value()) {
return std::nullopt;
@@ -948,6 +1002,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8PowerOnLevelColorReport(
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto report = channel->dali->dt8.getPowerOnLevelColorReport(short_address);
if (!report.has_value()) {
return std::nullopt;
@@ -970,6 +1025,7 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt8SystemFailureLevelColorR
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto report = channel->dali->dt8.getSystemFailureLevelColorReport(short_address);
if (!report.has_value()) {
return std::nullopt;
@@ -992,8 +1048,11 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
int color_temperature, int red, int green,
int blue) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.storeSceneSnapshot(short_address, scene, brightness,
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.storeSceneSnapshot(short_address, scene, brightness,
ToDaliCppColorMode(color_mode), color_temperature,
red, green, blue);
}
@@ -1001,56 +1060,85 @@ bool DaliDomainService::storeDt8SceneSnapshot(uint8_t gateway_id, int short_addr
bool DaliDomainService::storeDt8PowerOnLevelSnapshot(uint8_t gateway_id, int short_address,
int level) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.storePowerOnLevelSnapshot(short_address, level);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.storePowerOnLevelSnapshot(short_address, level);
}
bool DaliDomainService::storeDt8SystemFailureLevelSnapshot(uint8_t gateway_id,
int short_address, int level) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.storeSystemFailureLevelSnapshot(short_address, level);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.storeSystemFailureLevelSnapshot(short_address, level);
}
bool DaliDomainService::setBright(uint8_t gateway_id, int short_address, int brightness) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->base.setBright(short_address, brightness);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->base.setBright(short_address, brightness);
}
bool DaliDomainService::setColTempRaw(uint8_t gateway_id, int short_address, int mirek) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.setColTempRaw(short_address, mirek);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColTempRaw(short_address, mirek);
}
bool DaliDomainService::setColTemp(uint8_t gateway_id, int short_address, int kelvin) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.setColorTemperature(short_address, kelvin);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColorTemperature(short_address, kelvin);
}
bool DaliDomainService::setColourRaw(uint8_t gateway_id, int raw_addr, int x, int y) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.setColourRaw(raw_addr, x, y);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColourRaw(raw_addr, x, y);
}
bool DaliDomainService::setColourRGB(uint8_t gateway_id, int short_address, int r, int g,
int b) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->dt8.setColourRGB(short_address, r, g, b);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->dt8.setColourRGB(short_address, r, g, b);
}
bool DaliDomainService::on(uint8_t gateway_id, int short_address) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr && channel->dali->base.on(short_address);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->base.on(short_address);
}
bool DaliDomainService::off(uint8_t gateway_id, int short_address) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr && channel->dali->base.off(short_address);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->base.off(short_address);
}
bool DaliDomainService::off(int short_address) const {
@@ -1061,6 +1149,20 @@ bool DaliDomainService::off(int short_address) const {
return off(channels_.front()->config.gateway_id, short_address);
}
std::optional<uint8_t> DaliDomainService::queryActualLevel(uint8_t gateway_id,
int short_address) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || channel->dali == nullptr) {
return std::nullopt;
}
markBusActivity(gateway_id);
const auto level = channel->dali->base.getBright(short_address);
if (!level.has_value()) {
return std::nullopt;
}
return static_cast<uint8_t>(std::clamp(*level, 0, 254));
}
std::optional<uint16_t> DaliDomainService::queryGroupMask(uint8_t gateway_id,
int short_address) const {
const auto* channel = findChannelByGateway(gateway_id);
@@ -1068,6 +1170,8 @@ std::optional<uint16_t> DaliDomainService::queryGroupMask(uint8_t gateway_id,
return std::nullopt;
}
markBusActivity(gateway_id);
const auto group_mask = channel->dali->base.getGroup(short_address);
if (!group_mask.has_value()) {
return std::nullopt;
@@ -1083,6 +1187,8 @@ std::optional<uint8_t> DaliDomainService::querySceneLevel(uint8_t gateway_id, in
return std::nullopt;
}
markBusActivity(gateway_id);
const auto level = channel->dali->base.getScene(short_address, scene);
if (!level.has_value()) {
return std::nullopt;
@@ -1098,6 +1204,8 @@ std::optional<DaliAddressSettingsSnapshot> DaliDomainService::queryAddressSettin
return std::nullopt;
}
markBusActivity(gateway_id);
DaliAddressSettingsSnapshot settings{};
if (const auto value = channel->dali->base.getPowerOnLevel(short_address); value.has_value()) {
@@ -1130,8 +1238,11 @@ std::optional<DaliAddressSettingsSnapshot> DaliDomainService::queryAddressSettin
bool DaliDomainService::applyGroupMask(uint8_t gateway_id, int short_address,
uint16_t group_mask) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->dali != nullptr &&
channel->dali->base.setGroup(short_address, group_mask);
if (channel == nullptr || channel->dali == nullptr) {
return false;
}
markBusActivity(gateway_id);
return channel->dali->base.setGroup(short_address, group_mask);
}
bool DaliDomainService::applySceneLevel(uint8_t gateway_id, int short_address, int scene,
@@ -1141,6 +1252,8 @@ bool DaliDomainService::applySceneLevel(uint8_t gateway_id, int short_address, i
return false;
}
markBusActivity(gateway_id);
if (*level == 255U) {
return channel->dali->base.removeScene(short_address, scene);
}
@@ -1156,6 +1269,8 @@ bool DaliDomainService::applyAddressSettings(uint8_t gateway_id, int short_addre
return false;
}
markBusActivity(gateway_id);
bool ok = true;
if (settings.power_on_level.has_value()) {
ok = ok && channel->dali->base.setPowerOnLevel(short_address, *settings.power_on_level);
@@ -1375,6 +1490,7 @@ void DaliDomainService::rawFrameTaskLoop() {
}
void DaliDomainService::notifyRawFrameSinks(const DaliRawFrame& frame) {
markBusActivity(frame.gateway_id);
if (raw_frame_sink_lock_ != nullptr) {
xSemaphoreTake(raw_frame_sink_lock_, portMAX_DELAY);
}
@@ -1387,6 +1503,16 @@ void DaliDomainService::notifyRawFrameSinks(const DaliRawFrame& frame) {
}
}
void DaliDomainService::markBusActivity(uint8_t gateway_id) const {
if (bus_activity_lock_ != nullptr) {
xSemaphoreTake(bus_activity_lock_, portMAX_DELAY);
}
last_bus_activity_ticks_[gateway_id] = xTaskGetTickCount();
if (bus_activity_lock_ != nullptr) {
xSemaphoreGive(bus_activity_lock_);
}
}
bool DaliDomainService::hasSerialPort(int uart_port) const {
return std::any_of(channels_.begin(), channels_.end(), [uart_port](const auto& channel) {
return channel->serial_bus.has_value() && channel->serial_bus->uart_port == uart_port;