/* ========================================================================
* 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
};
}
}
}