/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Opc.Ua.Client.Subscriptions.MonitoredItems; namespace Opc.Ua.Client.Subscriptions { /// /// /// Data value change observed on a monitored item during the V2 /// publish dispatch. /// /// /// Lifetime note (pooled notifications): when the V2 subscription /// manager has /// enabled, the underlying MonitoredItemNotification that /// supplied this struct's fields is recycled back to its /// activator pool after the handler returns. This struct's /// references (, ) /// project the inner DataValue / DiagnosticInfo /// instances directly — those are not themselves pooled (out of /// scope), so a handler may safely copy this struct by value and /// continue using after the call. Pool-aware /// projection audit: confirmed safe — no reference to a pooled /// instance is surfaced. /// /// /// /// /// public record struct DataValueChange(IMonitoredItem? MonitoredItem, DataValue Value, DiagnosticInfo? DiagnosticInfo); /// /// /// Event notification observed during the V2 publish dispatch. /// /// /// Lifetime note (pooled notifications): when the V2 subscription /// manager has /// enabled, the underlying EventFieldList is recycled back /// to its activator pool after the handler returns. The /// property is captured by value as an /// wrapping the original event-fields /// array. Arrays are not pooled in this design (out of scope), so /// the captured backing array survives the recycle. Pool-aware /// projection audit: confirmed safe — no reference to a pooled /// instance is surfaced. /// /// /// /// public record struct EventNotification(IMonitoredItem? MonitoredItem, ArrayOf Fields); /// /// Notification handler /// public interface ISubscriptionNotificationHandler { /// /// Process data change notification /// /// /// /// /// /// /// /// ValueTask OnDataChangeNotificationAsync(ISubscription subscription, uint sequenceNumber, DateTime publishTime, ReadOnlyMemory notification, PublishState publishStateMask, IReadOnlyList stringTable); /// /// Process event notification /// /// /// /// /// /// /// /// ValueTask OnEventDataNotificationAsync(ISubscription subscription, uint sequenceNumber, DateTime publishTime, ReadOnlyMemory notification, PublishState publishStateMask, IReadOnlyList stringTable); /// /// Process keep alive notification /// /// /// /// /// /// ValueTask OnKeepAliveNotificationAsync(ISubscription subscription, uint sequenceNumber, DateTime publishTime, PublishState publishStateMask); /// /// Surface a transition in the subscription's lifecycle /// () or publish-side status /// (). Fires from the V2 /// engine on: /// /// Lifecycle transitions — /// , /// , /// , /// . /// Publish-side transitions — /// (a gap was detected and /// a republish was issued), /// (a missing message arrived or publishing resumed after a /// stop), (the /// subscription was taken over on a different session via /// TransferSubscriptions). /// /// /// Handlers that need derived state (publishing stopped, last /// keep-alive, republish-pending, etc.) maintain it themselves /// by responding to this callback — V2 is handler-centric and /// the engine deliberately does not expose those as polled /// properties on . /// /// /// Per-subscription delivery ordering is guaranteed by the V2 /// prioritized publish-ack channel: data / event / keep-alive /// callbacks for a given subscription always fire in /// publish-sequence order. State-change callbacks interleave /// with the notification stream at the moment the transition /// is observed. /// /// /// /// /// /// ValueTask OnSubscriptionStateChangedAsync(ISubscription subscription, SubscriptionState state, PublishState publishStateMask, CancellationToken ct = default); } }