feat(gateway): enhance DALI host activity tracking and presence management

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-26 22:21:36 +08:00
parent f922993d2f
commit 865bf8425a
9 changed files with 441 additions and 39 deletions
+111 -3
View File
@@ -315,7 +315,8 @@ struct DaliDomainService::DaliChannel {
DaliDomainService::DaliDomainService()
: raw_frame_sink_lock_(xSemaphoreCreateMutex()),
bus_activity_lock_(xSemaphoreCreateMutex()) {
bus_activity_lock_(xSemaphoreCreateMutex()),
host_activity_lock_(xSemaphoreCreateMutex()) {
esp_log_level_set(TAG, (esp_log_level_t)CONFIG_DALI_LOG_LEVEL);
}
@@ -328,6 +329,10 @@ DaliDomainService::~DaliDomainService() {
vSemaphoreDelete(bus_activity_lock_);
bus_activity_lock_ = nullptr;
}
if (host_activity_lock_ != nullptr) {
vSemaphoreDelete(host_activity_lock_);
host_activity_lock_ = nullptr;
}
}
bool DaliDomainService::bindTransport(const DaliChannelConfig& config, DaliTransportHooks hooks) {
@@ -568,11 +573,85 @@ bool DaliDomainService::isBusIdle(uint8_t gateway_id, uint32_t quiet_ms) const {
return (xTaskGetTickCount() - last_activity) >= pdMS_TO_TICKS(quiet_ms);
}
void DaliDomainService::markHostActivity(uint8_t gateway_id) const {
if (host_activity_lock_ != nullptr) {
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
}
last_host_activity_ticks_[gateway_id] = xTaskGetTickCount();
if (host_activity_lock_ != nullptr) {
xSemaphoreGive(host_activity_lock_);
}
}
void DaliDomainService::markHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr,
uint8_t command) const {
const TickType_t now = xTaskGetTickCount();
if (host_activity_lock_ != nullptr) {
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
}
last_host_activity_ticks_[gateway_id] = now;
recent_host_command_frames_[gateway_id] = RecentHostCommandFrame{raw_addr, command, now, true};
if (host_activity_lock_ != nullptr) {
xSemaphoreGive(host_activity_lock_);
}
}
bool DaliDomainService::hasRecentHostActivity(uint8_t gateway_id, uint32_t window_ms) const {
if (window_ms == 0) {
return false;
}
TickType_t last_activity = 0;
if (host_activity_lock_ != nullptr) {
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
}
if (const auto it = last_host_activity_ticks_.find(gateway_id);
it != last_host_activity_ticks_.end()) {
last_activity = it->second;
}
if (host_activity_lock_ != nullptr) {
xSemaphoreGive(host_activity_lock_);
}
if (last_activity == 0) {
return false;
}
return (xTaskGetTickCount() - last_activity) < pdMS_TO_TICKS(window_ms);
}
bool DaliDomainService::matchesRecentHostCommandFrame(uint8_t gateway_id, uint8_t raw_addr,
uint8_t command,
uint32_t window_ms) const {
if (window_ms == 0) {
return false;
}
RecentHostCommandFrame frame;
if (host_activity_lock_ != nullptr) {
xSemaphoreTake(host_activity_lock_, portMAX_DELAY);
}
if (const auto it = recent_host_command_frames_.find(gateway_id);
it != recent_host_command_frames_.end()) {
frame = it->second;
}
if (host_activity_lock_ != nullptr) {
xSemaphoreGive(host_activity_lock_);
}
if (!frame.valid || frame.raw_addr != raw_addr || frame.command != command) {
return false;
}
return (xTaskGetTickCount() - frame.tick) < pdMS_TO_TICKS(window_ms);
}
bool DaliDomainService::writeBridgeFrame(uint8_t gateway_id, const uint8_t* data, size_t len) const {
const auto* channel = findChannelByGateway(gateway_id);
if (channel == nullptr || !channel->hooks.send) {
return false;
}
if (data != nullptr && len == 3 && (data[0] == 0x10 || data[0] == 0x11)) {
markHostCommandFrame(gateway_id, data[1], data[2]);
} else {
markHostActivity(gateway_id);
}
markBusActivity(gateway_id);
return channel->hooks.send(data, len);
}
@@ -584,6 +663,11 @@ std::vector<uint8_t> DaliDomainService::transactBridgeFrame(uint8_t gateway_id,
if (channel == nullptr || !channel->hooks.transact) {
return {};
}
if (data != nullptr && len == 3 && (data[0] == 0x10 || data[0] == 0x11 || data[0] == 0x12)) {
markHostCommandFrame(gateway_id, data[1], data[2]);
} else {
markHostActivity(gateway_id);
}
markBusActivity(gateway_id);
return channel->hooks.transact(data, len);
}
@@ -593,6 +677,7 @@ bool DaliDomainService::sendRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t co
if (channel == nullptr || channel->comm == nullptr) {
return false;
}
markHostCommandFrame(gateway_id, raw_addr, command);
markBusActivity(gateway_id);
return channel->comm->sendRawNew(raw_addr, command);
}
@@ -602,6 +687,7 @@ bool DaliDomainService::sendExtRaw(uint8_t gateway_id, uint8_t raw_addr, uint8_t
if (channel == nullptr || channel->comm == nullptr) {
return false;
}
markHostCommandFrame(gateway_id, raw_addr, command);
markBusActivity(gateway_id);
return channel->comm->sendExtRawNew(raw_addr, command);
}
@@ -612,6 +698,7 @@ std::optional<uint8_t> DaliDomainService::queryRaw(uint8_t gateway_id, uint8_t r
if (channel == nullptr || channel->comm == nullptr) {
return std::nullopt;
}
markHostCommandFrame(gateway_id, raw_addr, command);
markBusActivity(gateway_id);
return channel->comm->queryRawNew(raw_addr, command);
}
@@ -683,6 +770,23 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
PutOptionalInt(snapshot, "emergencyModeRaw", detailed->emergencyMode);
PutOptionalInt(snapshot, "featuresRaw", detailed->feature);
PutOptionalInt(snapshot, "deviceStatusRaw", detailed->deviceStatus);
PutOptionalInt(snapshot, "batteryChargeLevel", detailed->batteryChargeLevel);
PutOptionalInt(snapshot, "functionTestDelayTime", detailed->functionTestDelayTime);
PutOptionalInt(snapshot, "durationTestDelayTime", detailed->durationTestDelayTime);
PutOptionalInt(snapshot, "functionTestIntervalDays", detailed->functionTestIntervalDays);
PutOptionalInt(snapshot, "durationTestIntervalWeeks", detailed->durationTestIntervalWeeks);
PutOptionalInt(snapshot, "testExecutionTimeoutDays", detailed->testExecutionTimeoutDays);
PutOptionalInt(snapshot, "prolongTimeHalfMinutes", detailed->prolongTimeHalfMinutes);
PutOptionalInt(snapshot, "durationTestResultMinutes", detailed->durationTestResultMinutes);
PutOptionalInt(snapshot, "lampEmergencyTimeHours", detailed->lampEmergencyTimeHours);
PutOptionalInt(snapshot, "lampTotalOperationTimeHours", detailed->lampTotalOperationTimeHours);
PutOptionalInt(snapshot, "emergencyLevel", detailed->emergencyLevel);
PutOptionalInt(snapshot, "emergencyMinLevel", detailed->emergencyMinLevel);
PutOptionalInt(snapshot, "emergencyMaxLevel", detailed->emergencyMaxLevel);
PutOptionalInt(snapshot, "ratedDurationMinutes", detailed->ratedDurationMinutes);
PutOptionalInt(snapshot, "extendedVersion", detailed->extendedVersion);
PutOptionalInt(snapshot, "physicalMinLevel", detailed->physicalMinLevel);
PutOptionalInt(snapshot, "emergencyDeviceTypeCode", detailed->emergencyDeviceTypeCode);
snapshot.bools["testInProgress"] = detailed->testInProgress;
snapshot.bools["lampFailure"] = detailed->lampFailure;
@@ -736,6 +840,8 @@ std::optional<DaliDomainSnapshot> DaliDomainService::dt1Snapshot(uint8_t gateway
snapshot.bools["hardwiredInhibitSupported"] = features.hardwiredInhibitSupported();
snapshot.bools["physicalSelectionSupported"] = features.physicalSelectionSupported();
snapshot.bools["relightInRestModeSupported"] = features.relightInRestModeSupported();
snapshot.ints["derivedEmergencyDeviceTypeCode"] =
features.emergencyDeviceTypeCode(detailed->physicalMinLevel);
}
if (detailed->deviceStatus.has_value()) {
const DaliDT1DeviceStatus status(detailed->deviceStatus.value());
@@ -1208,9 +1314,11 @@ std::optional<DaliAddressSettingsSnapshot> DaliDomainService::queryAddressSettin
DaliAddressSettingsSnapshot settings{};
if (const auto value = channel->dali->base.getPowerOnLevel(short_address); value.has_value()) {
settings.power_on_level = static_cast<uint8_t>(*value);
const auto power_on_level = channel->dali->base.getPowerOnLevel(short_address);
if (!power_on_level.has_value()) {
return std::nullopt;
}
settings.power_on_level = static_cast<uint8_t>(*power_on_level);
if (const auto value = channel->dali->base.getSystemFailureLevel(short_address);
value.has_value()) {
settings.system_failure_level = static_cast<uint8_t>(*value);