diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateAlarmDialog.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateAlarmDialog.razor index b51ae4da..e14e30db 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateAlarmDialog.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateAlarmDialog.razor @@ -41,6 +41,13 @@ +
+
+ + +
Returned by the MES alarm-status API as IsFlaggedForMES. Instances can override this unless the alarm is locked.
+
+
@if (_error != null) {
@_error
@@ -67,6 +74,9 @@ [Parameter] public string? InitialTriggerConfig { get; set; } [Parameter] public bool InitialIsLocked { get; set; } + /// Seeds the "MES reportable" flag returned by the MES alarm-status API. + [Parameter] public bool InitialIsMesReportable { get; set; } + /// Attribute choices offered by the structured trigger editor's picker. [Parameter] public IReadOnlyList AvailableAttributes { get; set; } = Array.Empty(); @@ -82,6 +92,7 @@ private AlarmTriggerType _triggerType; private string? _triggerConfig; private bool _isLocked; + private bool _isMesReportable; private string? _error; private bool _busy; @@ -92,6 +103,7 @@ _triggerType = InitialTriggerType; _triggerConfig = InitialTriggerConfig; _isLocked = InitialIsLocked; + _isMesReportable = InitialIsMesReportable; } private async Task Submit() @@ -103,7 +115,7 @@ _busy = true; try { - var error = await OnSaveAsync(new AlarmDraft(_name, _triggerType, _priority, _triggerConfig, _isLocked)); + var error = await OnSaveAsync(new AlarmDraft(_name, _triggerType, _priority, _triggerConfig, _isLocked, _isMesReportable)); if (error == null) { Context.Close(true); @@ -125,10 +137,12 @@ /// Priority level. /// Structured trigger configuration payload. /// Whether instances may override the alarm. + /// Whether the MES alarm-status API reports this alarm. public sealed record AlarmDraft( string Name, AlarmTriggerType TriggerType, int Priority, string? TriggerConfig, - bool IsLocked); + bool IsLocked, + bool IsMesReportable); } diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateEdit.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateEdit.razor index ed0d21c6..21d9cccb 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateEdit.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TemplateEdit.razor @@ -907,6 +907,7 @@ Trigger Priority Config + MES Lock Actions @@ -922,6 +923,16 @@ config readable instead of merely hidden. *@ @(alarm.TriggerConfiguration ?? "—") + + @if (alarm.IsMesReportable) + { + MES + } + else + { + — + } + @if (alarm.IsLocked) { @@ -1436,7 +1447,7 @@ private Task OpenAddAlarmDialog() => ShowAlarmDialogAsync( editId: null, - new TemplateAlarmDialog.AlarmDraft(string.Empty, default, 500, null, false)); + new TemplateAlarmDialog.AlarmDraft(string.Empty, default, 500, null, false, false)); private Task OpenEditAlarmDialog(TemplateAlarm alarm) => ShowAlarmDialogAsync( @@ -1446,7 +1457,8 @@ alarm.TriggerType, alarm.PriorityLevel, alarm.TriggerConfiguration, - alarm.IsLocked)); + alarm.IsLocked, + alarm.IsMesReportable)); private async Task ShowAlarmDialogAsync(int? editId, TemplateAlarmDialog.AlarmDraft initial) { @@ -1461,6 +1473,7 @@ InitialPriority="initial.Priority" InitialTriggerConfig="@initial.TriggerConfig" InitialIsLocked="initial.IsLocked" + InitialIsMesReportable="initial.IsMesReportable" AvailableAttributes="choices" OnSaveAsync="d => SaveAlarmDraftAsync(editId, d)" />, size: "modal-dialog-scrollable"); @@ -1521,6 +1534,7 @@ PriorityLevel = draft.Priority, TriggerConfiguration = draft.TriggerConfig?.Trim(), IsLocked = draft.IsLocked, + IsMesReportable = draft.IsMesReportable, Description = existing.Description, OnTriggerScriptId = existing.OnTriggerScriptId, }; @@ -1536,7 +1550,8 @@ TriggerType = draft.TriggerType, PriorityLevel = draft.Priority, TriggerConfiguration = draft.TriggerConfig?.Trim(), - IsLocked = draft.IsLocked + IsLocked = draft.IsLocked, + IsMesReportable = draft.IsMesReportable }; var addResult = await TemplateService.AddAlarmAsync(_selectedTemplate.Id, alarm, user); diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/TemplateAlarmDialogMesReportableTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/TemplateAlarmDialogMesReportableTests.cs new file mode 100644 index 00000000..d4351251 --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Design/TemplateAlarmDialogMesReportableTests.cs @@ -0,0 +1,67 @@ +namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Design; + +/// +/// The template alarm authoring surface exposes TemplateAlarm.IsMesReportable: +/// an "MES reportable" checkbox in the dialog body and an MES column on the +/// template's Alarms tab. TemplateEdit is a heavyweight page (~10 injected +/// services with their own graphs), so — like the rest of the TemplateEdit coverage +/// in this suite (see TemplateNativeAlarmSourceEditorTests) — these are +/// structural assertions over the component source that pin the wiring so it cannot +/// silently regress. The persistence behaviour itself is covered by the +/// TemplateService / ManagementActor alarm tests. +/// +public class TemplateAlarmDialogMesReportableTests +{ + private static string DesignComponentsRoot + { + get + { + var dir = AppContext.BaseDirectory; + for (var i = 0; i < 6 && dir is not null; i++) + dir = Directory.GetParent(dir)?.FullName; + return Path.Combine(dir!, "src", "ZB.MOM.WW.ScadaBridge.CentralUI", + "Components", "Pages", "Design"); + } + } + + private static string AlarmDialogMarkup + => File.ReadAllText(Path.Combine(DesignComponentsRoot, "TemplateAlarmDialog.razor")); + + private static string TemplateEditMarkup + => File.ReadAllText(Path.Combine(DesignComponentsRoot, "TemplateEdit.razor")); + + [Fact] + public void AlarmDialog_HasMesReportableCheckbox() + { + var markup = AlarmDialogMarkup; + Assert.Contains("@bind=\"_isMesReportable\"", markup); + Assert.Contains("id=\"alarmMesReportable\"", markup); + Assert.Contains("MES reportable", markup); + } + + [Fact] + public void AlarmDialog_ThreadsMesReportableThroughParameterAndDraft() + { + var markup = AlarmDialogMarkup; + Assert.Contains("InitialIsMesReportable", markup); + Assert.Contains("_isMesReportable = InitialIsMesReportable", markup); + Assert.Contains("bool IsMesReportable", markup); + } + + [Fact] + public void AlarmsTab_HasMesColumn() + { + var markup = TemplateEditMarkup; + Assert.Contains("MES", markup); + Assert.Contains("data-test=\"alarm-mes-badge\"", markup); + Assert.Contains("alarm.IsMesReportable", markup); + } + + [Fact] + public void AlarmsTab_PassesMesReportableIntoAndOutOfTheDialog() + { + var markup = TemplateEditMarkup; + Assert.Contains("InitialIsMesReportable=\"initial.IsMesReportable\"", markup); + Assert.Contains("IsMesReportable = draft.IsMesReportable", markup); + } +}