fix(abcip): ALMA review round 2 - IsRestatement contract docs, ALMD no-InAlarm warning, un-ack retrigger
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -196,6 +196,44 @@ public sealed class AbCipAlarmProjectionTests
|
||||
await drv.ShutdownAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>hw4 F2 — an ALMD that declared members but no InAlarm (a pre-hw4 InFaulted config) would read
|
||||
/// BadNodeIdUnknown forever; it logs ONE Warning per tag instead of staying silent.</summary>
|
||||
[Fact]
|
||||
public async Task An_almd_without_a_declared_InAlarm_warns_once()
|
||||
{
|
||||
var legacy = new AbCipTagDefinition("Legacy", Device, "Legacy", AbCipDataType.Structure, Members:
|
||||
[new AbCipStructureMember("InFaulted", AbCipDataType.DInt), new AbCipStructureMember("Acked", AbCipDataType.DInt),
|
||||
new AbCipStructureMember("Severity", AbCipDataType.DInt)]);
|
||||
var log = new CapturingLogger();
|
||||
var opts = new AbCipDriverOptions
|
||||
{
|
||||
Devices = [new AbCipDeviceOptions(Device)],
|
||||
RawTags = AbCipRawTags.From(legacy),
|
||||
EnableAlarmProjection = true,
|
||||
AlarmPollInterval = TimeSpan.FromMilliseconds(20),
|
||||
};
|
||||
var drv = new AbCipDriver(opts, "drv-1", new FakeAbCipTagFactory(), logger: log);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
var h1 = await drv.SubscribeAlarmsAsync(["Legacy"], CancellationToken.None);
|
||||
var h2 = await drv.SubscribeAlarmsAsync(["Legacy"], CancellationToken.None);
|
||||
await drv.UnsubscribeAlarmsAsync(h1, CancellationToken.None);
|
||||
await drv.UnsubscribeAlarmsAsync(h2, CancellationToken.None);
|
||||
await drv.ShutdownAsync(CancellationToken.None);
|
||||
log.Warnings.Count(m => m.Contains("Legacy") && m.Contains("InAlarm")).ShouldBe(1);
|
||||
}
|
||||
|
||||
private sealed class CapturingLogger : Microsoft.Extensions.Logging.ILogger<AbCipDriver>
|
||||
{
|
||||
public List<string> Warnings { get; } = [];
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||
public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => true;
|
||||
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId,
|
||||
TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
if (logLevel == Microsoft.Extensions.Logging.LogLevel.Warning) lock (Warnings) Warnings.Add(formatter(state, exception));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that alarm clear event fires on 1-to-0 transition.</summary>
|
||||
[Fact]
|
||||
public async Task Clear_Event_Fires_On_1_to_0_Transition()
|
||||
|
||||
@@ -254,6 +254,25 @@ public sealed class AbCipAlmaProjectionTests
|
||||
"only the genuine PLC ack — the unacked HH excursion must keep the condition unacknowledged");
|
||||
}
|
||||
|
||||
/// <summary>F3 — fully acked, then a declared *Acked goes false with the SAME limits active (a limit that left and re-entered
|
||||
/// between polls): the PLC says unacknowledged, so it is told as a Retrigger.</summary>
|
||||
[Fact]
|
||||
public async Task An_ack_falling_with_an_unchanged_active_set_retriggers()
|
||||
{
|
||||
var r = await StartAsync(x =>
|
||||
{
|
||||
x.Set("HInAlarm", true);
|
||||
foreach (var a in new[] { "HHAcked", "HAcked", "LAcked", "LLAcked", "ROCPosAcked" }) x.Set(a, true);
|
||||
});
|
||||
await Next(r, x => x.Kind == AlarmTransitionKind.ActiveAcknowledged);
|
||||
r.Set("HAcked", false);
|
||||
var e = await Next(r, x => x.Kind == AlarmTransitionKind.Retrigger);
|
||||
await Settled(r);
|
||||
await r.StopAsync();
|
||||
e.Message.ShouldBe($"ALMA {Tag} H alarm");
|
||||
Snapshot(r).Count(x => x.Kind == AlarmTransitionKind.Retrigger).ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>An alarm already active AND acked when first seen is told atomically, never Raise-then-Acknowledge.</summary>
|
||||
[Fact]
|
||||
public async Task An_already_acked_active_alarm_is_active_acknowledged()
|
||||
|
||||
Reference in New Issue
Block a user