/* ========================================================================
* 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.Threading;
namespace Opc.Ua.Client.Subscriptions
{
///
/// Subscription options
///
public record class SubscriptionOptions
{
///
/// The subscription is disabled
///
public bool Disabled { get; init; }
///
/// Set keep alive count
///
public uint KeepAliveCount { get; init; }
///
/// The life time of the subscription in counts of
/// publish interval.
/// LifetimeCount shall be at least 3*KeepAliveCount.
///
public uint LifetimeCount { get; init; }
///
/// Set desired priority of the subscription
///
public byte Priority { get; init; }
///
/// Set desired publishing interval
///
public TimeSpan PublishingInterval { get; init; }
///
/// Set desired publishing enabled
///
public bool PublishingEnabled { get; init; }
///
/// Set max notifications per publish
///
public uint MaxNotificationsPerPublish { get; init; }
///
/// Set min lifetime interval
///
public TimeSpan MinLifetimeInterval { get; init; }
///
/// When the V2 manager restores this subscription via
/// with
/// transferSubscriptions: true, request the server to
/// send the latest cached value of every monitored item as
/// part of the take-over (OPC UA Part 4 §5.13.7
/// TransferSubscriptions's sendInitialValues
/// argument). Defaults to false: the server only sends
/// values that arrived after the subscription was suspended,
/// matching the post-disconnect semantics that the V2 manager
/// expects for failover-on-recreate.
///
public bool SendInitialValuesOnTransfer { get; init; }
///
/// Controls how the client reacts to an unsolicited
/// Good_SubscriptionTransferred StatusChangeNotification
/// from the server. Defaults to
/// which
/// matches the spec-strict behaviour (OPC UA Part 4 §5.14.7).
/// Set to
///
/// to automatically recreate the subscription on the same
/// session when the notification arrives without the client
/// having initiated a TransferSubscriptions request — this
/// addresses server quirks that leave subscriptions stuck.
///
public SubscriptionRecoveryPolicy RecoveryPolicy { get; init; }
///
///
/// Opts the logical subscription out of the V2 unbounded-item
/// behaviour. When false (the default), the engine
/// transparently splits monitored items across multiple
/// server-side partition subscriptions as needed so callers
/// can add an effectively unlimited number of items. When
/// true, the subscription is bound to a single
/// server-side subscription and
///
/// calls that would exceed the server's
///
/// cap surface the server's
/// Bad_TooManyMonitoredItems per-item status (the
/// pre-V2 behaviour).
///
///
public bool DisableUnboundedItemMode { get; init; }
///
///
/// Per-partition upper bound on monitored items. When the
/// logical subscription decides to split (see
/// ) the wrapper uses
/// this value as the proactive per-partition cap; new items
/// land on a fresh partition once the current one reaches
/// the cap.
///
///
/// The reactive fallback further pins the effective cap of
/// any individual partition if the server returns
/// Bad_TooManyMonitoredItems for a
/// CreateMonitoredItems request — the partition is
/// marked no-grow and the next add mints a new partition.
/// This lets the wrapper handle servers that enforce a
/// stricter limit than they advertise (or that advertise
/// none at all).
///
///
/// null (the default) means "no proactive cap" — the
/// wrapper relies entirely on the reactive fallback to
/// discover the server's actual limit. Set this to a smaller
/// value to keep each partition smaller for snapshot /
/// transfer scaling, or for predictable partitioning at a
/// specific threshold; the wrapper does not consult
///
/// to seed this value automatically.
///
///
/// Has no effect when
/// is true.
///
///
public uint? MaxMonitoredItemsPerPartition { get; init; }
///
///
/// Hard upper bound on the number of partitions a single
/// logical subscription is allowed to grow to. The wrapper
/// refuses to mint a new partition once the count reaches
/// this value; the failed TryAdd returns false
/// and the caller observes the standard add-failed path.
///
///
/// This cap exists primarily as a denial-of-service
/// safeguard: a hostile or buggy server that rejects every
/// CreateMonitoredItems request with
/// Bad_TooManyMonitoredItems would otherwise cause
/// the wrapper's reactive fallback to mint a fresh server-
/// side subscription per add attempt, growing client memory
/// and server handle space without bound. The cap defaults
/// to 32, which comfortably covers legitimate
/// fan-out scenarios (a server allowing 1000 items per
/// subscription supports 32k items per logical subscription)
/// but stops runaway growth.
///
///
/// Has no effect when
/// is true.
///
///
public uint MaxPartitionCount { get; init; } = 32;
///
///
/// Idle timeout before a secondary (non-primary) partition
/// subscription is deleted on the server after its last
/// monitored item is removed. The primary partition is never
/// deleted while the logical subscription is alive so the
/// logical subscription's server-side identifier remains
/// stable for callers that log or correlate by id.
///
///
/// Defaults to 30s. Set to
/// to delete empty secondary partitions immediately; set to
/// to never delete
/// empty secondary partitions.
///
///
/// Has no effect when
/// is true.
///
///
public TimeSpan SecondaryPartitionIdleTimeout { get; init; }
= TimeSpan.FromSeconds(30);
}
}