mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-16 21:02:51 +08:00
Quickstarts alarms: report one event per Enable instead of two
ConditionTypeHolder.OnEnableDisableAlarm runs as the OnEnableDisable hook, before ConditionState.OnEnableCalled/OnDisableCalled change the EnabledState. It switched the state and called ReportEvent itself, and the core then reported the same change again (AutoReportStateChanges is set by every concrete holder), so each Enable produced two condition events with different EventIds. Disable was not affected because the hook's ReportEvent skips disabled conditions. Part 9 5.5.2: a change of EnabledState causes one ConditionType Event Notification. The hook now only sets the Message and leaves the state change and its event to the core; it still changes the state and reports for a condition without AutoReportStateChanges. The CTT's A and C Enable Test_002 took the second notification as the answer to its next call (worked around in the CTT scripts as C61). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -282,13 +282,21 @@ namespace Alarms
|
||||
|
||||
if (enabling != alarm.EnabledState!.Id!.Value)
|
||||
{
|
||||
alarm.SetEnableState(SystemContext, enabling);
|
||||
alarm.Message!.Value = enabling ?
|
||||
LocalizedText.From("Enabling") :
|
||||
LocalizedText.From("Disabling alarm " + MapName);
|
||||
|
||||
// if disabled, it will not fire
|
||||
ReportEvent();
|
||||
// This runs before ConditionState.OnEnableCalled/OnDisableCalled change the EnabledState.
|
||||
// A condition with AutoReportStateChanges reports that change itself; reporting here as well
|
||||
// sent two Event Notifications for one Enable (Part 9 5.5.2: a change of EnabledState causes
|
||||
// one ConditionType Event Notification).
|
||||
if (!alarm.AutoReportStateChanges)
|
||||
{
|
||||
alarm.SetEnableState(SystemContext, enabling);
|
||||
|
||||
// if disabled, it will not fire
|
||||
ReportEvent();
|
||||
}
|
||||
}
|
||||
else if (enabling)
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
* ======================================================================*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
|
||||
@@ -369,9 +370,39 @@ namespace Opc.Ua.History.Tests
|
||||
AssertEventRetain(enableEvent, expected: true,
|
||||
"Enable should restore Retain when the source changed to an active alarm while disabled.");
|
||||
|
||||
// One EnabledState change causes one Event Notification (Part 9 5.5.2). Both notifications of a
|
||||
// duplicate report are generated during the call, so count the events of the call window.
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
int enableEvents = CountEventsInWindow(collector, alarmId, enableStart, enableEnd);
|
||||
Assert.That(enableEvents, Is.EqualTo(1),
|
||||
"Enable should report the EnabledState change with exactly one condition event.");
|
||||
|
||||
await NormalizeAlarmAsync(alarmId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static int CountEventsInWindow(
|
||||
AlarmEventCollector collector,
|
||||
NodeId alarmId,
|
||||
DateTime start,
|
||||
DateTime end)
|
||||
{
|
||||
if (!collector.EventLog.TryGetValue(alarmId, out List<EventFieldList>? events))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
foreach (EventFieldList e in events)
|
||||
{
|
||||
if (AlarmEventCollector.TryGetDateTime(e, AlarmEventCollector.FieldIndex.Time, out DateTime time) &&
|
||||
time >= start.AddMilliseconds(-50) &&
|
||||
time <= end.AddMilliseconds(50))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void AssertTransitionTimeInCallWindow(
|
||||
EventFieldList eventFields,
|
||||
AlarmEventCollector.FieldIndex fieldIndex,
|
||||
|
||||
Reference in New Issue
Block a user