feat(native-alarms): per-binding IsMesReportable on native alarm source bindings (ScadaBridge#59)
TemplateNativeAlarmSource.IsMesReportable + tri-state InstanceNativeAlarmSourceOverride .IsMesReportableOverride flatten onto ResolvedNativeAlarmSource; NativeAlarmActor stamps every mirrored condition and InstanceActor the quiet-binding placeholder, so ScriptAlarm, Alarms.MesReportableAsync() and the #60 wire fields carry it. Additive to the 900-999 severity band. Migration AddNativeAlarmSourceMesReportable. Management commands, CLI --mes-reportable, Central UI (template dialog checkbox, instance override select), Transport DTOs/importer/diff and the revision hash (true-only, so existing hashes are unchanged) carry it. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -952,6 +952,31 @@ public class FlatteningServiceTests
|
||||
Assert.Equal("Override", result.Value.NativeAlarmSources[0].Source);
|
||||
}
|
||||
|
||||
/// <summary>Gitea #59: the binding flag flattens from the template and a tri-state instance override wins (null inherits).</summary>
|
||||
[Theory]
|
||||
[InlineData(true, null, true)]
|
||||
[InlineData(false, null, false)]
|
||||
[InlineData(false, true, true)]
|
||||
[InlineData(true, false, false)]
|
||||
public void Flatten_NativeAlarmSource_IsMesReportable_TemplateFlagAndOverride(bool templateFlag, bool? ovr, bool expected)
|
||||
{
|
||||
var template = CreateTemplate(1, "Base");
|
||||
template.NativeAlarmSources.Add(new TemplateNativeAlarmSource("Pressure")
|
||||
{ ConnectionName = "Opc", SourceReference = "ns=2;s=P1", IsMesReportable = templateFlag });
|
||||
var instance = CreateInstance();
|
||||
if (ovr.HasValue)
|
||||
instance.NativeAlarmSourceOverrides.Add(new InstanceNativeAlarmSourceOverride("Pressure")
|
||||
{ IsMesReportableOverride = ovr });
|
||||
|
||||
var result = _sut.Flatten(instance, [template],
|
||||
new Dictionary<int, IReadOnlyList<TemplateComposition>>(),
|
||||
new Dictionary<int, IReadOnlyList<Template>>(),
|
||||
new Dictionary<int, DataConnection>());
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(expected, result.Value.NativeAlarmSources[0].IsMesReportable);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a locked native alarm source ignores an instance override.</summary>
|
||||
[Fact]
|
||||
public void Flatten_LockedNativeAlarmSource_IgnoresInstanceOverride()
|
||||
|
||||
+23
@@ -413,6 +413,29 @@ public class RevisionHashServiceTests
|
||||
Assert.NotEqual(_sut.ComputeHash(configBefore), _sut.ComputeHash(configAfter));
|
||||
}
|
||||
|
||||
/// <summary>Gitea #59: the binding's MES-reportable flag participates in the hash; false hashes as before the field existed.</summary>
|
||||
[Fact]
|
||||
public void ComputeHash_NativeAlarmSourceIsMesReportableChange_ChangesHash()
|
||||
{
|
||||
var baseSource = new ResolvedNativeAlarmSource
|
||||
{
|
||||
CanonicalName = "Boiler.Alarms",
|
||||
ConnectionName = "plc1",
|
||||
SourceReference = "ns=2;s=Boiler"
|
||||
};
|
||||
var configFalse = new FlattenedConfiguration
|
||||
{
|
||||
InstanceUniqueName = "Instance1", TemplateId = 1, SiteId = 1,
|
||||
NativeAlarmSources = [baseSource]
|
||||
};
|
||||
var configTrue = configFalse with { NativeAlarmSources = [baseSource 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 { NativeAlarmSources = [baseSource with { IsMesReportable = false }] }));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that changing a native alarm source's locked flag changes the hash.</summary>
|
||||
[Fact]
|
||||
public void ComputeHash_NativeAlarmSourceIsLockedChange_ChangesHash()
|
||||
|
||||
Reference in New Issue
Block a user