diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor index bd57adcd..7238786c 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugView.razor @@ -209,33 +209,7 @@ { ๐Ÿ’ฌ } - @node.Alarm.State - @FormatKind(node.Alarm.Kind) - @if (node.Alarm.Kind != AlarmKind.Computed) - { - @if (node.Alarm.Condition.Active && !node.Alarm.Condition.Acknowledged) - { - Unacked - } - @if (node.Alarm.Condition.Shelve != AlarmShelveState.Unshelved) - { - Shelved - } - @if (node.Alarm.Condition.Suppressed) - { - Suppressed - } - } - sev @node.Alarm.Condition.Severity - @if (node.Alarm.Level != AlarmLevel.None) - { - @FormatLevel(node.Alarm.Level) - } + } else if (node.IsNativeBinding) { @@ -577,6 +551,10 @@ _ => "bg-secondary" }; + /// + /// State badge class shared by the alarm leaf (via AlarmStateBadges) + /// and the native/composition branch summary badges below (worst-state roll-up). + /// private static string GetAlarmStateBadge(AlarmState state) => state switch { AlarmState.Active => "bg-danger", @@ -584,32 +562,6 @@ _ => "bg-secondary" }; - /// - /// Severity-tinted badge class for HiLo alarm levels. The critical bands - /// (HighHigh / LowLow) get the danger class; warning bands get amber. - /// - private static string GetAlarmLevelBadge(AlarmLevel level) => level switch - { - AlarmLevel.HighHigh or AlarmLevel.LowLow => "bg-danger", - AlarmLevel.High or AlarmLevel.Low => "bg-warning text-dark", - _ => "bg-secondary" - }; - - /// Badge class distinguishing computed (neutral) from native (info) alarms. - private static string GetKindBadge(AlarmKind kind) => kind switch - { - AlarmKind.Computed => "bg-secondary", - _ => "bg-info text-dark" - }; - - /// Short display label for the alarm kind. - private static string FormatKind(AlarmKind kind) => kind switch - { - AlarmKind.NativeOpcUa => "OPC UA", - AlarmKind.NativeMxAccess => "MxAccess", - _ => "Computed" - }; - /// /// Builds the row tooltip from the alarm's operator message plus native /// metadata (type, category, operator, raise time, current/limit value). @@ -629,15 +581,6 @@ return parts.Count == 0 ? null : string.Join(" ยท ", parts); } - private static string FormatLevel(AlarmLevel level) => level switch - { - AlarmLevel.HighHigh => "HiHi", - AlarmLevel.High => "Hi", - AlarmLevel.Low => "Lo", - AlarmLevel.LowLow => "LoLo", - _ => "โ€”" - }; - /// /// Runs on the render thread, guarded against the /// component being disposed mid-flight (CentralUI-009): InvokeAsync diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor new file mode 100644 index 00000000..66e9738b --- /dev/null +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/AlarmStateBadges.razor @@ -0,0 +1,80 @@ +@using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming +@using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums + +@* โ”€โ”€ Alarm leaf badge cluster โ”€โ”€ *@ +@* State / kind / native sub-state / severity / HiLo level badges for one alarm. *@ +@* Lifted verbatim from DebugView so the Alarm Summary page renders identically. *@ +@Alarm.State +@FormatKind(Alarm.Kind) +@if (Alarm.Kind != AlarmKind.Computed) +{ + @if (Alarm.Condition.Active && !Alarm.Condition.Acknowledged) + { + Unacked + } + @if (Alarm.Condition.Shelve != AlarmShelveState.Unshelved) + { + Shelved + } + @if (Alarm.Condition.Suppressed) + { + Suppressed + } +} +sev @Alarm.Condition.Severity +@if (Alarm.Level != AlarmLevel.None) +{ + @FormatLevel(Alarm.Level) +} + +@code { + /// The alarm condition whose badges this component renders. + [Parameter] public AlarmStateChanged Alarm { get; set; } = default!; + + private static string GetAlarmStateBadge(AlarmState state) => state switch + { + AlarmState.Active => "bg-danger", + AlarmState.Normal => "bg-success", + _ => "bg-secondary" + }; + + /// + /// Severity-tinted badge class for HiLo alarm levels. The critical bands + /// (HighHigh / LowLow) get the danger class; warning bands get amber. + /// + private static string GetAlarmLevelBadge(AlarmLevel level) => level switch + { + AlarmLevel.HighHigh or AlarmLevel.LowLow => "bg-danger", + AlarmLevel.High or AlarmLevel.Low => "bg-warning text-dark", + _ => "bg-secondary" + }; + + /// Badge class distinguishing computed (neutral) from native (info) alarms. + private static string GetKindBadge(AlarmKind kind) => kind switch + { + AlarmKind.Computed => "bg-secondary", + _ => "bg-info text-dark" + }; + + /// Short display label for the alarm kind. + private static string FormatKind(AlarmKind kind) => kind switch + { + AlarmKind.NativeOpcUa => "OPC UA", + AlarmKind.NativeMxAccess => "MxAccess", + _ => "Computed" + }; + + private static string FormatLevel(AlarmLevel level) => level switch + { + AlarmLevel.HighHigh => "HiHi", + AlarmLevel.High => "Hi", + AlarmLevel.Low => "Lo", + AlarmLevel.LowLow => "LoLo", + _ => "โ€”" + }; +} diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs new file mode 100644 index 00000000..35957146 --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Components/AlarmStateBadgesTests.cs @@ -0,0 +1,58 @@ +using Bunit; +using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared; +using ZB.MOM.WW.ScadaBridge.Commons.Messages.Streaming; +using ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms; +using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; + +namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components; + +/// +/// Covers the AlarmStateBadges shared component lifted out of +/// DebugView (M7-T13 prep). The badge cluster must render identically +/// wherever it is hosted, so these tests pin the rendered markup: state badge +/// class, kind label, native sub-state badges, severity, and HiLo level. +/// +public class AlarmStateBadgesTests : BunitContext +{ + private static AlarmStateChanged ActiveNativeHighUnacked() => new( + InstanceUniqueName: "Tank01", + AlarmName: "Tank01.Level.HiHi", + State: AlarmState.Active, + Priority: 700, + Timestamp: DateTimeOffset.UtcNow) + { + Kind = AlarmKind.NativeOpcUa, + Level = AlarmLevel.High, + Condition = new AlarmConditionState( + Active: true, + Acknowledged: false, + Confirmed: null, + Shelve: AlarmShelveState.Unshelved, + Suppressed: false, + Severity: 700), + }; + + [Fact] + public void Renders_StateKindSubStateSeverityAndLevel_Badges() + { + var alarm = ActiveNativeHighUnacked(); + + var cut = Render(p => p.Add(c => c.Alarm, alarm)); + + // State badge: Active condition tinted danger. + Assert.Contains("bg-danger", cut.Markup); + Assert.Contains("Active", cut.Markup); + + // Kind badge label for a native OPC UA alarm. + Assert.Contains("OPC UA", cut.Markup); + + // Native sub-state badge: active + unacknowledged. + Assert.Contains("Unacked", cut.Markup); + + // Severity span on the unified 0โ€“1000 scale. + Assert.Contains("sev 700", cut.Markup); + + // HiLo level badge label. + Assert.Contains("Hi", cut.Markup); + } +}