From 3b0c2b3bfaf868bb737e3fb2a80d0356d6a39b0d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 9 Nov 2026 15:19:15 -0500 Subject: [PATCH] fix(template-engine): computed-alarm IsMesReportable in revision hash and diff, true-only (ScadaBridge#102 item 2) Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV --- .../Flattening/DiffService.cs | 1 + .../Flattening/RevisionHashService.cs | 7 ++++++ .../Flattening/DiffServiceTests.cs | 22 +++++++++++++++++++ .../Flattening/RevisionHashServiceTests.cs | 18 +++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/DiffService.cs b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/DiffService.cs index e32d1bbc..532e63ef 100644 --- a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/DiffService.cs +++ b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/DiffService.cs @@ -142,6 +142,7 @@ public class DiffService a.Description == b.Description && a.PriorityLevel == b.PriorityLevel && a.IsLocked == b.IsLocked && + a.IsMesReportable == b.IsMesReportable && a.TriggerType == b.TriggerType && a.TriggerConfiguration == b.TriggerConfiguration && a.OnTriggerScriptCanonicalName == b.OnTriggerScriptCanonicalName; diff --git a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/RevisionHashService.cs b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/RevisionHashService.cs index 9c8ad5f1..f240e3be 100644 --- a/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/RevisionHashService.cs +++ b/src/ZB.MOM.WW.ScadaBridge.TemplateEngine/Flattening/RevisionHashService.cs @@ -78,6 +78,8 @@ public class RevisionHashService Description = a.Description, PriorityLevel = a.PriorityLevel, IsLocked = a.IsLocked, + // true-only (null when false) so existing revision hashes are unchanged. + IsMesReportable = a.IsMesReportable ? true : null, TriggerType = a.TriggerType, TriggerConfiguration = a.TriggerConfiguration, OnTriggerScriptCanonicalName = a.OnTriggerScriptCanonicalName @@ -242,6 +244,11 @@ public class RevisionHashService /// public bool IsLocked { get; init; } /// + /// MES-reportable flag; null when false (the serializer drops nulls), so + /// pre-existing configurations hash identically. + /// + public bool? IsMesReportable { get; init; } + /// /// The canonical name of the script triggered by this alarm. /// public string? OnTriggerScriptCanonicalName { get; init; } diff --git a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/DiffServiceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/DiffServiceTests.cs index 9c9b516f..83cdf7b4 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/DiffServiceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/DiffServiceTests.cs @@ -230,6 +230,28 @@ public class DiffServiceTests Assert.Equal(DiffChangeType.Changed, diff.AlarmChanges[0].ChangeType); } + /// Gitea #102: flipping a computed alarm's MES-reportable flag is a diff. + [Fact] + public void ComputeDiff_AlarmIsMesReportableChange_DetectedAsChanged() + { + var oldConfig = new FlattenedConfiguration + { + InstanceUniqueName = "Instance1", + Alarms = [new ResolvedAlarm { CanonicalName = "HighTemp", TriggerType = "RangeViolation" }] + }; + var newConfig = new FlattenedConfiguration + { + InstanceUniqueName = "Instance1", + Alarms = [new ResolvedAlarm { CanonicalName = "HighTemp", TriggerType = "RangeViolation", IsMesReportable = true }] + }; + + var diff = _sut.ComputeDiff(oldConfig, newConfig); + + Assert.True(diff.HasChanges); + Assert.Single(diff.AlarmChanges); + Assert.Equal(DiffChangeType.Changed, diff.AlarmChanges[0].ChangeType); + } + /// Verifies that a native alarm source reference change is detected as changed. [Fact] public void ComputeDiff_NativeAlarmSourceReferenceChange_DetectedAsChanged() diff --git a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/RevisionHashServiceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/RevisionHashServiceTests.cs index 5d535be5..e2d13948 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/RevisionHashServiceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.TemplateEngine.Tests/Flattening/RevisionHashServiceTests.cs @@ -191,6 +191,24 @@ public class RevisionHashServiceTests Assert.NotEqual(_sut.ComputeHash(configBefore), _sut.ComputeHash(configAfter)); } + /// Gitea #102: the computed alarm's MES-reportable flag participates in the hash; false hashes as before the field existed. + [Fact] + public void ComputeHash_AlarmIsMesReportableChange_ChangesHash() + { + var baseAlarm = new ResolvedAlarm { CanonicalName = "HighTemp", TriggerType = "RangeViolation" }; + var configFalse = new FlattenedConfiguration + { + InstanceUniqueName = "Instance1", TemplateId = 1, SiteId = 1, + Alarms = [baseAlarm] + }; + var configTrue = configFalse with { Alarms = [baseAlarm with { IsMesReportable = true }] }; + + Assert.NotEqual(_sut.ComputeHash(configFalse), _sut.ComputeHash(configTrue)); + // false is omitted from the canonical JSON, so adding the field cannot invalidate stored hashes. + Assert.Equal(_sut.ComputeHash(configFalse), + _sut.ComputeHash(configFalse with { Alarms = [baseAlarm with { IsMesReportable = false }] })); + } + /// Verifies that editing a connection endpoint changes the hash. [Fact] public void ComputeHash_ConnectionEndpointEdit_ChangesHash()