using ZB.MOM.WW.ScadaBridge.Commons.Messages.DebugView; using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming; using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services; /// /// Read-only operator service that aggregates the current alarm picture across /// all Enabled instances of a single site (Operator Alarm Summary). /// /// /// /// There is no central alarm store. The summary is assembled at query time by /// fanning out one request per Enabled instance /// (via the injected , which delegates to /// the existing single-shot /// /// Ask) and flattening every snapshot's /// into s. The fan-out is best-effort: an instance /// whose snapshot fetch throws, times out, or reports /// is recorded in /// and never aborts the /// whole call. /// /// /// The page is read-only — there are no ack / shelve / write operations. All /// filtering and roll-up math happens client-side from the returned rows. /// /// public interface IAlarmSummaryService { /// /// Fetches and aggregates the current alarms across every Enabled instance of /// the given site. /// /// The site primary key. /// Cancellation token. /// /// An with one /// per active/mirrored alarm condition plus the unique names of any instances /// whose snapshot could not be obtained. /// Task GetSiteAlarmsAsync(int siteId, CancellationToken cancellationToken = default); /// /// Builds an directly from an in-memory live /// snapshot (plan #10, Task 5) — the current alarms served by /// /// — instead of fanning out per-instance snapshot Asks. Each /// is flattened to one /// (instance taken from /// ) and ordered with the SAME /// deterministic instance-then-alarm-name sort as /// , so the two paths are interchangeable. /// /// /// The live cache carries only alarm state, so it cannot know which Enabled /// instances are silent-but-reporting versus not reporting at all; /// is therefore always /// empty on this path. The page keeps its periodic /// poll as the authority for "not reporting" (and as a live-stream safety net). /// /// The current live alarm snapshot for a site. /// An whose is empty. AlarmSummaryResult BuildFromLiveAlarms(IReadOnlyList alarms); /// /// Pure roll-up over a set of s. Exposed so the /// page (and tests) can recompute the headline tiles without re-querying. /// /// The alarm rows to summarize. /// The aggregated roll-up. AlarmRollup ComputeRollup(IReadOnlyList rows); } /// The result of a site alarm-summary query. /// One row per alarm condition reported across the site's Enabled instances. /// /// Unique names of Enabled instances whose snapshot could not be obtained /// (fetch threw, timed out, or returned ). /// public sealed record AlarmSummaryResult( IReadOnlyList Alarms, IReadOnlyList NotReportingInstances); /// /// One alarm condition paired with the instance it belongs to. The /// carries everything the /// AlarmStateBadges component needs to render. /// /// Unique name of the owning instance. /// The alarm condition (state / kind / severity / level / native sub-state). public sealed record AlarmSummaryRow( string InstanceUniqueName, AlarmStateChanged Alarm); /// /// Pure point-in-time roll-up over a set of s. /// /// Count of rows whose is . /// Highest among active rows; 0 when none active. /// Active, unacknowledged native conditions (Kind != Computed). /// Per- row counts (only kinds with at least one row appear). public sealed record AlarmRollup( int TotalActive, int WorstSeverity, int UnackedCount, IReadOnlyDictionary CountsByKind);