diff --git a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs index 8ab43bdf..45f0eb7e 100644 --- a/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs +++ b/src/Core/ZB.MOM.WW.OtOpcUa.Commons/OpcUa/IOpcUaAddressSpaceSink.cs @@ -20,7 +20,8 @@ public interface IOpcUaAddressSpaceSink /// this projects the whole /// (Enabled/Active/Acked/Confirmed/Shelving/Severity/Message) onto it and recomputes Retain; /// otherwise it falls back to the legacy two-element [Active, Acknowledged] placeholder - /// variable. No OPC UA event is fired — that is T16's responsibility. + /// variable. A materialised condition also fires a Part 9 condition event on each transition (T16) + /// so subscribed clients receive the alarm event, not just the changed attributes. /// The OPC UA node ID of the alarm (== ScriptedAlarmId for materialised conditions). /// The full condition state to project onto the node. /// The source timestamp in UTC. diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs index b8fed3f0..de2c51bb 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/OtOpcUaNodeManager.cs @@ -224,13 +224,21 @@ public sealed class OtOpcUaNodeManager : CustomNodeManager2 snapshot.Initialize(SystemContext, alarm); alarm.ReportEvent(SystemContext, snapshot); } - catch (Exception) + catch (Exception ex) { // A failed event report must NOT break the state projection or the calling actor: the node's // state has already been applied + ClearChangeMasks'd, so attribute subscribers still see the - // change; only the event delivery is lost. There is no logger on this CustomNodeManager2 - // (the SDK base class carries none), so swallow rather than propagate. T19's live Client.CLI + // change; only the event delivery is lost. This CustomNodeManager2 carries no ILogger, so log + // through the SDK's static trace (Utils.LogError) instead of swallowing silently — a recurring + // failure here is then visible in the server log rather than invisible. T19's live Client.CLI // run is the integration proof that the happy path delivers. + // Utils.LogError routes to the SDK's trace sink. It's [Obsolete] in 1.5.378 in favour of an + // ITelemetryContext/ILogger this CustomNodeManager2 doesn't have wired — suppress the + // deprecation here (wiring the telemetry logger through is a separate follow-up); the point is + // that a recurring failure is visible in the server trace rather than silently swallowed. +#pragma warning disable CS0618 // Type or member is obsolete + Utils.LogError(ex, "OtOpcUaNodeManager: failed to report Part 9 condition event for {0}", alarm.NodeId); +#pragma warning restore CS0618 } }