/* ======================================================================== * 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 Opc.Ua.Client.Subscriptions.MonitoredItems; namespace Opc.Ua.Client.Subscriptions { /// /// Immutable snapshot of a V2 's /// configuration plus the server-side identifiers needed to take /// over the subscription on a different session. /// /// /// /// Produced by and consumed by /// . /// /// /// The snapshot is itself an via the /// source generator. The fields /// carried on the wire are simple primitives (e.g. /// milliseconds for durations, for enums); the /// non-encoded projection exposes a /// consumer-friendly built from /// those surrogate fields. The companion /// factory does the inverse mapping at /// time. This split keeps the wire schema independent of /// .NET-specific types like that the source /// generator does not encode natively. /// /// [DataType(Namespace = Namespaces.OpcUaXsd)] public sealed partial record class SubscriptionStateSnapshot { /// /// Server-assigned subscription id, or 0 if the /// subscription had not been created on the server yet. /// [DataTypeField(Order = 1)] public partial uint ServerId { get; init; } /// /// Server's reported retransmission-queue sequence numbers at /// snapshot time. Diagnostic-only for the transfer leg of /// restore; transfer uses the TransferSubscriptions /// response's authoritative list. /// [DataTypeField(Order = 2)] public partial ArrayOf AvailableSequenceNumbers { get; init; } /// /// surrogate. /// [DataTypeField(Order = 10)] public partial bool Disabled { get; init; } /// /// surrogate. /// [DataTypeField(Order = 11)] public partial uint KeepAliveCount { get; init; } /// /// surrogate. /// [DataTypeField(Order = 12)] public partial uint LifetimeCount { get; init; } /// /// surrogate. /// [DataTypeField(Order = 13)] public partial byte Priority { get; init; } /// /// as /// whole milliseconds (the projection is /// rebuilt in the getter). /// [DataTypeField(Order = 14)] public partial int PublishingIntervalMs { get; init; } /// /// surrogate. /// [DataTypeField(Order = 15)] public partial bool PublishingEnabled { get; init; } /// /// /// surrogate. /// [DataTypeField(Order = 16)] public partial uint MaxNotificationsPerPublish { get; init; } /// /// as /// whole milliseconds. /// [DataTypeField(Order = 17)] public partial int MinLifetimeIntervalMs { get; init; } /// /// /// surrogate. /// [DataTypeField(Order = 18)] public partial bool SendInitialValuesOnTransfer { get; init; } /// /// Per-item state at snapshot time. Encoded inline (not via /// ) because /// is sealed /// and carries its own ordered fields. /// [DataTypeField(Order = 20, StructureHandling = StructureHandling.Inline)] public partial ArrayOf MonitoredItems { get; init; } /// /// Project the encoded surrogate fields back into a live /// . Not serialized. /// public SubscriptionOptions ToOptions() { return new SubscriptionOptions { Disabled = Disabled, KeepAliveCount = KeepAliveCount, LifetimeCount = LifetimeCount, Priority = Priority, PublishingInterval = TimeSpan.FromMilliseconds(PublishingIntervalMs), PublishingEnabled = PublishingEnabled, MaxNotificationsPerPublish = MaxNotificationsPerPublish, MinLifetimeInterval = TimeSpan.FromMilliseconds(MinLifetimeIntervalMs), SendInitialValuesOnTransfer = SendInitialValuesOnTransfer }; } /// /// Construct a from a /// live + the captured /// server-side state. The factory populates every surrogate /// field so the snapshot round-trips through /// without losing /// any options-field value. /// /// Live options at snapshot time. /// Server-assigned subscription id, or /// 0 if not yet created on the server. /// Server's /// retransmission-queue sequence numbers. /// Per-item snapshots. public static SubscriptionStateSnapshot AsOptions( SubscriptionOptions options, uint serverId, ArrayOf availableSequenceNumbers, ArrayOf monitoredItems) { if (options == null) { throw new ArgumentNullException(nameof(options)); } return new SubscriptionStateSnapshot { ServerId = serverId, AvailableSequenceNumbers = availableSequenceNumbers, Disabled = options.Disabled, KeepAliveCount = options.KeepAliveCount, LifetimeCount = options.LifetimeCount, Priority = options.Priority, PublishingIntervalMs = (int)Math.Min( int.MaxValue, Math.Max(0, options.PublishingInterval.TotalMilliseconds)), PublishingEnabled = options.PublishingEnabled, MaxNotificationsPerPublish = options.MaxNotificationsPerPublish, MinLifetimeIntervalMs = (int)Math.Min( int.MaxValue, Math.Max(0, options.MinLifetimeInterval.TotalMilliseconds)), SendInitialValuesOnTransfer = options.SendInitialValuesOnTransfer, MonitoredItems = monitoredItems }; } } }