test(alarms): operator-driven watch on TestAlarm002 (raise then clear via subtag route)
This commit is contained in:
@@ -288,6 +288,97 @@ public sealed class AlarmSubtagLiveSmokeTests
|
||||
Log("CONFIRMED: runtime subtag path resolves and the AckMsg write succeeded.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Operator-driven watch on <c>TestMachine_001.TestAlarm002</c> (currently
|
||||
/// inactive). Subscribes the subtag route, then waits for an operator to
|
||||
/// toggle the alarm true (Raise) and back to false (Clear), asserting both
|
||||
/// transitions are synthesized with the degraded flag + synthetic GUID.
|
||||
/// Runs only with <c>MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1</c>; exits as soon
|
||||
/// as both transitions are seen.
|
||||
/// </summary>
|
||||
[LiveMxAccessFact]
|
||||
public void LiveWatch_TestAlarm002_RaiseThenClear()
|
||||
{
|
||||
Exception? threadException = null;
|
||||
ManualResetEventSlim done = new ManualResetEventSlim(false);
|
||||
Thread thread = new Thread(() =>
|
||||
{
|
||||
try { RunWatch002(); }
|
||||
catch (Exception ex) { threadException = ex; }
|
||||
finally { done.Set(); }
|
||||
});
|
||||
thread.IsBackground = false;
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
done.Wait();
|
||||
thread.Join();
|
||||
|
||||
List<string> lines = new List<string>();
|
||||
string? line;
|
||||
while (log.TryDequeue(out line)) { output.WriteLine(line); lines.Add(line); }
|
||||
string logPath = Environment.GetEnvironmentVariable("PROBE_LOG")
|
||||
?? System.IO.Path.Combine(System.IO.Path.GetTempPath(), "alarm002-watch.log");
|
||||
try { System.IO.File.WriteAllLines(logPath, lines); } catch { /* best effort */ }
|
||||
if (threadException != null) { throw threadException; }
|
||||
}
|
||||
|
||||
private void RunWatch002()
|
||||
{
|
||||
const string alarm = "TestMachine_001.TestAlarm002";
|
||||
string fullRef = AlarmRecordTransitionMapper.ComposeFullReference(Provider, Group, alarm);
|
||||
Log("=== Watch TestAlarm002 for operator-driven Raise + Clear ===");
|
||||
Log("AlarmFullReference: " + fullRef);
|
||||
|
||||
AlarmSubtagTarget target = new AlarmSubtagTarget
|
||||
{
|
||||
AlarmFullReference = fullRef,
|
||||
SourceObjectReference = alarm,
|
||||
ActiveSubtag = alarm + ".InAlarm",
|
||||
AckedSubtag = alarm + ".Acked",
|
||||
AckCommentSubtag = alarm + ".AckMsg",
|
||||
PrioritySubtag = alarm + ".Priority",
|
||||
};
|
||||
List<AlarmSubtagTarget> watchList = new List<AlarmSubtagTarget> { target };
|
||||
|
||||
MxAccessComObjectFactory factory = new MxAccessComObjectFactory();
|
||||
LmxSubtagAlarmSource source = new LmxSubtagAlarmSource(factory, clientName: null);
|
||||
using SubtagAlarmConsumer consumer = new SubtagAlarmConsumer(source, watchList);
|
||||
|
||||
ConcurrentQueue<MxAlarmTransitionEvent> transitions =
|
||||
new ConcurrentQueue<MxAlarmTransitionEvent>();
|
||||
consumer.AlarmTransitionEmitted += (_, e) =>
|
||||
{
|
||||
Log("Transition: " + DescribeTransition(e));
|
||||
transitions.Enqueue(e);
|
||||
};
|
||||
source.ValueChanged += (_, c) =>
|
||||
Log(string.Format("raw: {0} = {1}", c.ItemAddress, c.Value ?? "<null>"));
|
||||
|
||||
consumer.Subscribe(string.Format(@"\\{0}\Galaxy!{1}", Environment.MachineName, Group));
|
||||
|
||||
// Warm up so the items go active; drain any initial transition.
|
||||
DateTime warm = DateTime.UtcNow + TimeSpan.FromSeconds(8);
|
||||
while (DateTime.UtcNow < warm) { PumpMessages(); Thread.Sleep(100); }
|
||||
MxAlarmTransitionEvent? ignored;
|
||||
while (transitions.TryDequeue(out ignored)) { }
|
||||
|
||||
Log(">>> WATCHING. Toggle TestMachine_001.TestAlarm002 TRUE, hold ~10s, then FALSE. <<<");
|
||||
|
||||
MxAlarmTransitionEvent? raise = WaitForTransitionKind(
|
||||
transitions, AlarmTransitionKind.Raise, TimeSpan.FromSeconds(150), "Raise");
|
||||
Assert.NotNull(raise);
|
||||
Assert.True(raise!.Record.Degraded, "Subtag-synthesized records must have Degraded=true.");
|
||||
Assert.NotEqual(Guid.Empty, raise.Record.AlarmGuid);
|
||||
Assert.Equal(MxAlarmStateKind.UnackAlm, raise.Record.State);
|
||||
Log(string.Format("RAISE captured: Guid={0} State={1}", raise.Record.AlarmGuid, raise.Record.State));
|
||||
|
||||
MxAlarmTransitionEvent? clear = WaitForTransitionKind(
|
||||
transitions, AlarmTransitionKind.Clear, TimeSpan.FromSeconds(150), "Clear");
|
||||
Assert.NotNull(clear);
|
||||
Log(string.Format("CLEAR captured: State={0}", clear!.Record.State));
|
||||
Log("Operator-driven Raise -> Clear confirmed end-to-end via the subtag route.");
|
||||
}
|
||||
|
||||
private void RunSmoke()
|
||||
{
|
||||
Log(string.Format("AlarmFullReference: {0}", AlarmFullReference));
|
||||
|
||||
Reference in New Issue
Block a user