diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/InstanceActor.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/InstanceActor.cs index 9d53897e..327cf6e9 100644 --- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/InstanceActor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/InstanceActor.cs @@ -1117,9 +1117,11 @@ public class InstanceActor : ReceiveActor _alarmTimestamps.GetValueOrDefault(name, DateTimeOffset.UtcNow))); } - // Native source bindings with no live condition: emit a placeholder so the - // Debug View tree shows the configured binding node even when quiet. A binding - // is "quiet" when no retained event carries its canonical name. + // A native binding is "already represented" if any retained event carries its + // canonical name — it renders in the Debug View via those condition rows. The + // placeholder below covers ONLY bindings with zero retained events (truly quiet + // since startup). Do NOT narrow this to State==Active: a binding holding a + // retained Normal/resolved condition would then be rendered twice. var liveBindings = _latestAlarmEvents.Values .Where(e => !string.IsNullOrEmpty(e.NativeSourceCanonicalName)) .Select(e => e.NativeSourceCanonicalName) @@ -1130,7 +1132,8 @@ public class InstanceActor : ReceiveActor if (liveBindings.Contains(binding)) continue; states.Add(new AlarmStateChanged( - _instanceUniqueName, binding, AlarmState.Normal, 0, DateTimeOffset.UtcNow) + _instanceUniqueName, binding, AlarmState.Normal, 0, + _alarmTimestamps.GetValueOrDefault(binding, DateTimeOffset.MinValue)) { Kind = _nativeAlarmKinds.GetValueOrDefault(binding, AlarmKind.NativeOpcUa), NativeSourceCanonicalName = binding, diff --git a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/InstanceActorNativeAlarmTests.cs b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/InstanceActorNativeAlarmTests.cs index c76daedc..33b7bd37 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/InstanceActorNativeAlarmTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests/Actors/InstanceActorNativeAlarmTests.cs @@ -100,6 +100,7 @@ public class InstanceActorNativeAlarmTests : TestKit, IDisposable { var dcl = CreateTestProbe(); var actor = CreateInstanceActorWithDcl("inst", ConfigWithNativeSource("inst"), dcl.Ref); + dcl.ExpectMsg(); // No live condition is emitted: the configured "Pressure" binding is quiet. actor.Tell(new SubscribeDebugViewRequest("inst", "c")); @@ -121,6 +122,7 @@ public class InstanceActorNativeAlarmTests : TestKit, IDisposable { var dcl = CreateTestProbe(); var actor = CreateInstanceActorWithDcl("inst", ConfigWithNativeSource("inst"), dcl.Ref); + dcl.ExpectMsg(); // The NativeAlarmActor emits a live condition stamped with the binding's // canonical name (DV-1: NativeSourceCanonicalName), so the binding is "active".