From 4356a6dea8617ebf702184fc89400dbfc6aa8dd5 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Sat, 14 Mar 2026 20:40:26 -0400 Subject: [PATCH] fix: add event-id latch to linked monitor score detection Linked monitor alarm detection was purely point-in-time: it only returned a score when the linked monitor's state was exactly ALARM at the instant of the check. If the linked monitor's entire alarm cycle (ALARM -> ALERT -> IDLE) occurred between two analysis cycles of the checking monitor, the alarm was missed entirely. Add a latch using last_event_id: if the linked monitor's event id has changed since the last check, a new event started and we return a score even if the ALARM state has already passed. Also remove dead code in hasAlarmed() where last_event_id was updated after an early return. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/zm_monitor_monitorlink.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/zm_monitor_monitorlink.cpp b/src/zm_monitor_monitorlink.cpp index 159974ca6..48060c86a 100644 --- a/src/zm_monitor_monitorlink.cpp +++ b/src/zm_monitor_monitorlink.cpp @@ -218,6 +218,19 @@ int Monitor::MonitorLink::score() { Debug(1, "Checking zone %u, zone_index is %d, score is %d", zone_id, zone_index, zone_scores[zone_index]); return zone_scores[zone_index]; } + + // Latch: detect if a new event started since we last checked. + // This catches alarms even if the linked monitor transitioned through + // ALARM -> ALERT -> IDLE between our analysis cycles. + if (shared_data->last_event_id != last_event_id) { + Debug(1, "New event %" PRIu64 " on linked monitor (was %" PRIu64 "), last_frame_score %d", + shared_data->last_event_id, last_event_id, shared_data->last_frame_score); + last_event_id = shared_data->last_event_id; + // Return last_frame_score if still available, otherwise minimum of 1 + // to ensure the alarm is not silently missed + return shared_data->last_frame_score > 0 ? shared_data->last_frame_score : 1; + } + if (shared_data->state == ALARM) { Debug(1, "Checking all zones score is %d", shared_data->last_frame_score); return shared_data->last_frame_score; @@ -228,6 +241,4 @@ int Monitor::MonitorLink::score() { bool Monitor::MonitorLink::hasAlarmed() { return this->score() > 0; - last_event_id = shared_data->last_event_id; - return false; }