485 lines
26 KiB
C#
485 lines
26 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
|
||
|
||
/// <summary>
|
||
/// OPC UA Client (gateway) driver configuration. Bound from <c>DriverConfig</c> JSON at
|
||
/// driver-host registration time. Models the settings documented in
|
||
/// <c>docs/v2/driver-specs.md</c> §8.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This driver connects to a REMOTE OPC UA server and re-exposes its address space
|
||
/// through the local OtOpcUa server — the opposite direction from the usual "server
|
||
/// exposes PLC data" flow. Tier A (pure managed, OPC Foundation reference SDK); universal
|
||
/// protections cover it.
|
||
/// </remarks>
|
||
public sealed class OpcUaClientDriverOptions
|
||
{
|
||
/// <summary>
|
||
/// Remote OPC UA endpoint URL, e.g. <c>opc.tcp://plc.internal:4840</c>. Convenience
|
||
/// shortcut for a single-endpoint deployment — equivalent to setting
|
||
/// <see cref="EndpointUrls"/> to a list with this one URL. When both are provided,
|
||
/// the list wins and <see cref="EndpointUrl"/> is ignored.
|
||
/// </summary>
|
||
public string EndpointUrl { get; init; } = "opc.tcp://localhost:4840";
|
||
|
||
/// <summary>
|
||
/// Ordered list of candidate endpoint URLs for failover. The driver tries each in
|
||
/// order at <see cref="OpcUaClientDriver.InitializeAsync"/> and on session drop;
|
||
/// the first URL that successfully connects wins. Typical use-case: an OPC UA server
|
||
/// pair running in hot-standby (primary 4840 + backup 4841) where either can serve
|
||
/// the same address space. Leave unset (or empty) to use <see cref="EndpointUrl"/>
|
||
/// as a single-URL shortcut.
|
||
/// </summary>
|
||
public IReadOnlyList<string> EndpointUrls { get; init; } = [];
|
||
|
||
/// <summary>
|
||
/// Per-endpoint connect-attempt timeout during the failover sweep. Short enough that
|
||
/// cycling through several dead servers doesn't blow the overall init budget, long
|
||
/// enough to tolerate a slow TLS handshake on a healthy server. Applied independently
|
||
/// of <see cref="Timeout"/> which governs steady-state operations.
|
||
/// </summary>
|
||
public TimeSpan PerEndpointConnectTimeout { get; init; } = TimeSpan.FromSeconds(3);
|
||
|
||
/// <summary>
|
||
/// Optional discovery URL pointing at a Local Discovery Server (LDS) or a server's
|
||
/// own discovery endpoint. When set, the driver runs <c>FindServers</c> +
|
||
/// <c>GetEndpoints</c> against this URL during <see cref="OpcUaClientDriver.InitializeAsync"/>
|
||
/// and prepends the discovered endpoint URLs to the failover candidate list. When
|
||
/// <see cref="EndpointUrls"/> is empty (and only <see cref="EndpointUrl"/> is set as
|
||
/// a fallback), the discovered URLs replace the candidate list entirely so a
|
||
/// discovery-driven deployment can be configured without specifying any endpoints
|
||
/// up front. Discovery failures are non-fatal — the driver logs and falls back to the
|
||
/// statically configured candidates.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// <b>FindServers requires SecurityMode=None on the discovery channel</b> per the
|
||
/// OPC UA spec — discovery is unauthenticated even when the data channel uses
|
||
/// <c>Sign</c> or <c>SignAndEncrypt</c>. The driver opens the discovery channel
|
||
/// unsecured regardless of <see cref="SecurityMode"/>; only the resulting data
|
||
/// session is bound to the configured policy.
|
||
/// </para>
|
||
/// <para>
|
||
/// Endpoints returned by discovery are filtered to those matching
|
||
/// <see cref="SecurityPolicy"/> + <see cref="SecurityMode"/> before being added to
|
||
/// the candidate list, so a discovery sweep against a multi-policy server only
|
||
/// surfaces endpoints the driver could actually connect to.
|
||
/// </para>
|
||
/// </remarks>
|
||
public string? DiscoveryUrl { get; init; }
|
||
|
||
/// <summary>
|
||
/// Security policy to require when selecting an endpoint. Either a
|
||
/// <see cref="OpcUaSecurityPolicy"/> enum constant or a free-form string (for
|
||
/// forward-compatibility with future OPC UA policies not yet in the enum).
|
||
/// Matched against <c>EndpointDescription.SecurityPolicyUri</c> suffix — the driver
|
||
/// connects to the first endpoint whose policy name matches AND whose mode matches
|
||
/// <see cref="SecurityMode"/>. When set to <see cref="OpcUaSecurityPolicy.None"/>
|
||
/// the driver picks any unsecured endpoint regardless of policy string.
|
||
/// </summary>
|
||
public OpcUaSecurityPolicy SecurityPolicy { get; init; } = OpcUaSecurityPolicy.None;
|
||
|
||
/// <summary>Security mode.</summary>
|
||
public OpcUaSecurityMode SecurityMode { get; init; } = OpcUaSecurityMode.None;
|
||
|
||
/// <summary>Authentication type.</summary>
|
||
public OpcUaAuthType AuthType { get; init; } = OpcUaAuthType.Anonymous;
|
||
|
||
/// <summary>User name (required only for <see cref="OpcUaAuthType.Username"/>).</summary>
|
||
public string? Username { get; init; }
|
||
|
||
/// <summary>Password (required only for <see cref="OpcUaAuthType.Username"/>).</summary>
|
||
public string? Password { get; init; }
|
||
|
||
/// <summary>
|
||
/// Filesystem path to the user-identity certificate (PFX/PEM). Required when
|
||
/// <see cref="AuthType"/> is <see cref="OpcUaAuthType.Certificate"/>. The driver
|
||
/// loads the cert + private key, which the remote server validates against its
|
||
/// <c>TrustedUserCertificates</c> store to authenticate the session's user token.
|
||
/// Leave unset to use the driver's application-instance certificate as the user
|
||
/// token (not typical — most deployments have a separate user cert).
|
||
/// </summary>
|
||
public string? UserCertificatePath { get; init; }
|
||
|
||
/// <summary>
|
||
/// Optional password that unlocks <see cref="UserCertificatePath"/> when the PFX is
|
||
/// protected. PEM files generally have their password on the adjacent key file; this
|
||
/// knob only applies to password-locked PFX.
|
||
/// </summary>
|
||
public string? UserCertificatePassword { get; init; }
|
||
|
||
/// <summary>Server-negotiated session timeout. Default 120s per driver-specs.md §8.</summary>
|
||
public TimeSpan SessionTimeout { get; init; } = TimeSpan.FromSeconds(120);
|
||
|
||
/// <summary>Client-side keep-alive interval.</summary>
|
||
public TimeSpan KeepAliveInterval { get; init; } = TimeSpan.FromSeconds(5);
|
||
|
||
/// <summary>Initial reconnect delay after a session drop.</summary>
|
||
public TimeSpan ReconnectPeriod { get; init; } = TimeSpan.FromSeconds(5);
|
||
|
||
/// <summary>
|
||
/// When <c>true</c>, the driver accepts any self-signed / untrusted server certificate.
|
||
/// Dev-only — must be <c>false</c> in production so MITM attacks against the opc.tcp
|
||
/// channel fail closed.
|
||
/// </summary>
|
||
public bool AutoAcceptCertificates { get; init; } = false;
|
||
|
||
/// <summary>
|
||
/// Application URI the driver reports during session creation. Must match the
|
||
/// subject-alt-name on the client certificate if one is used, which is why it's a
|
||
/// config knob rather than hard-coded.
|
||
/// </summary>
|
||
public string ApplicationUri { get; init; } = "urn:localhost:OtOpcUa:GatewayClient";
|
||
|
||
/// <summary>
|
||
/// Friendly name sent to the remote server for diagnostics. Shows up in the remote
|
||
/// server's session-list so operators can identify which gateway instance is calling.
|
||
/// </summary>
|
||
public string SessionName { get; init; } = "OtOpcUa-Gateway";
|
||
|
||
/// <summary>Connect + per-operation timeout.</summary>
|
||
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(10);
|
||
|
||
/// <summary>
|
||
/// Root NodeId to mirror. Default <c>null</c> = <c>ObjectsFolder</c> (i=85). Set to
|
||
/// a scoped root to restrict the address space the driver exposes locally — useful
|
||
/// when the remote server has tens of thousands of nodes and only a subset is
|
||
/// needed downstream.
|
||
/// </summary>
|
||
public string? BrowseRoot { get; init; }
|
||
|
||
/// <summary>
|
||
/// Cap on total nodes discovered during <c>DiscoverAsync</c>. Default 10_000 —
|
||
/// bounds memory on runaway remote servers without being so low that normal
|
||
/// deployments hit it. When the cap is reached discovery stops and a warning is
|
||
/// written to the driver health surface; the partially-discovered tree is still
|
||
/// projected into the local address space.
|
||
/// </summary>
|
||
public int MaxDiscoveredNodes { get; init; } = 10_000;
|
||
|
||
/// <summary>
|
||
/// Max hierarchical depth of the browse. Default 10 — deep enough for realistic
|
||
/// OPC UA information models, shallow enough that cyclic graphs can't spin the
|
||
/// browse forever.
|
||
/// </summary>
|
||
public int MaxBrowseDepth { get; init; } = 10;
|
||
|
||
/// <summary>
|
||
/// Per-subscription tuning knobs applied when the driver creates data + alarm
|
||
/// subscriptions on the upstream session. Defaults preserve the previous hard-coded
|
||
/// values so existing deployments see no behaviour change.
|
||
/// </summary>
|
||
public OpcUaSubscriptionDefaults Subscriptions { get; init; } = new();
|
||
|
||
/// <summary>
|
||
/// Server-certificate validation knobs applied during the
|
||
/// <c>CertificateValidator.CertificateValidation</c> callback. Surfaces explicit
|
||
/// handling for revoked certs (always rejected, never auto-accepted), unknown
|
||
/// revocation status (rejected only when <see cref="OpcUaCertificateValidationOptions.RejectUnknownRevocationStatus"/>
|
||
/// is set), SHA-1 signature rejection, and minimum RSA key size. Defaults preserve
|
||
/// existing behaviour wherever possible — the one tightening is
|
||
/// <see cref="OpcUaCertificateValidationOptions.RejectSHA1SignedCertificates"/>=true
|
||
/// since SHA-1 is spec-deprecated for OPC UA.
|
||
/// </summary>
|
||
public OpcUaCertificateValidationOptions CertificateValidation { get; init; } = new();
|
||
|
||
/// <summary>
|
||
/// Curation rules applied to the upstream address space during
|
||
/// <c>DiscoverAsync</c>. Lets operators trim the mirrored tree to the subset their
|
||
/// downstream clients actually need, rename namespace URIs so the local-side metadata
|
||
/// stays consistent across upstream-server swaps, and override the default
|
||
/// <c>"Remote"</c> root folder name. Defaults are empty / null which preserves the
|
||
/// pre-curation behaviour exactly — empty include = include all.
|
||
/// </summary>
|
||
public OpcUaClientCurationOptions Curation { get; init; } = new();
|
||
|
||
/// <summary>
|
||
/// When <c>true</c>, <c>DiscoverAsync</c> runs an additional pass that walks the upstream
|
||
/// <c>TypesFolder</c> (<c>i=86</c>) — ObjectTypes (<c>i=88</c>), VariableTypes
|
||
/// (<c>i=89</c>), DataTypes (<c>i=90</c>), ReferenceTypes (<c>i=91</c>) — and projects the
|
||
/// discovered type-definition nodes into the local address space via
|
||
/// <c>IAddressSpaceBuilder.RegisterTypeNode</c>. Default <c>false</c> — opt-in so
|
||
/// existing deployments don't suddenly see a flood of type nodes after upgrade. Enable
|
||
/// when downstream clients need the upstream type system to render structured values or
|
||
/// decode custom event fields.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// The type-mirror pass uses <c>Session.FetchTypeTreeAsync</c> on each of the four
|
||
/// root type nodes so the SDK's local TypeTree cache is populated efficiently (one
|
||
/// batched browse per root rather than per-node round trips). This PR ships the
|
||
/// <i>structural</i> mirror only — every type node is registered with its identity,
|
||
/// super-type chain, and IsAbstract flag, but structured-type binary encodings are
|
||
/// NOT primed. (The OPCFoundation SDK removed
|
||
/// <c>ISession.LoadDataTypeSystem(NodeId, CancellationToken)</c> from the public
|
||
/// surface in 1.5.378+; loading binary encodings now requires per-node walks of
|
||
/// <c>HasEncoding</c> + dictionary nodes which is tracked as a follow-up.) Clients
|
||
/// that need structured-type decoding can still consume
|
||
/// <c>Variant<ExtensionObject></c> on the wire.
|
||
/// </para>
|
||
/// <para>
|
||
/// <see cref="OpcUaClientCurationOptions.IncludePaths"/> +
|
||
/// <see cref="OpcUaClientCurationOptions.ExcludePaths"/> still apply to the type
|
||
/// walk; paths are slash-joined under their root (e.g.
|
||
/// <c>"ObjectTypes/BaseObjectType/SomeType"</c>). Most operators want all types so
|
||
/// empty include = include all is the right default.
|
||
/// </para>
|
||
/// </remarks>
|
||
public bool MirrorTypeDefinitions { get; init; } = false;
|
||
|
||
/// <summary>
|
||
/// When <c>true</c> (default), the driver subscribes to
|
||
/// <c>BaseModelChangeEventType</c> + <c>GeneralModelChangeEventType</c> on the
|
||
/// upstream <c>Server</c> node (<c>i=2253</c>) at the end of <see cref="OpcUaClientDriver.InitializeAsync"/>.
|
||
/// When the upstream advertises a topology change, the driver coalesces events over
|
||
/// <see cref="ModelChangeDebounce"/> and triggers a re-import (equivalent to calling
|
||
/// <c>ReinitializeAsync</c>) so the locally-mirrored address space tracks the upstream.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// The re-import path acquires the same <c>_gate</c> that read / write / browse /
|
||
/// subscribe paths use, which means there's a brief browse-gap (≈ the upstream
|
||
/// <c>DiscoverAsync</c> duration) during which downstream calls block on the
|
||
/// driver's gate. Operators can disable the watch when the upstream topology is
|
||
/// known-static and the gap isn't acceptable.
|
||
/// </para>
|
||
/// </remarks>
|
||
public bool WatchModelChanges { get; init; } = true;
|
||
|
||
/// <summary>
|
||
/// Coalescing window for upstream <c>ModelChangeEvent</c> notifications. The first
|
||
/// event in a window starts the timer; further events extend it; when the timer
|
||
/// fires the driver runs one re-import regardless of how many events arrived. Default
|
||
/// 5 seconds — long enough to absorb a bulk topology edit on the upstream server,
|
||
/// short enough that single-node adds re-import promptly.
|
||
/// </summary>
|
||
public TimeSpan ModelChangeDebounce { get; init; } = TimeSpan.FromSeconds(5);
|
||
|
||
/// <summary>
|
||
/// Reverse-connect (server-initiated) configuration. When
|
||
/// <see cref="ReverseConnectOptions.Enabled"/> is <c>true</c> the driver flips the
|
||
/// transport direction: instead of dialling the upstream server, it opens a TCP
|
||
/// listener on <see cref="ReverseConnectOptions.ListenerUrl"/> and waits for the
|
||
/// upstream server to initiate the connection ("ReverseHello"). Required for
|
||
/// OT-DMZ deployments where the firewall only permits outbound traffic from the
|
||
/// plant network — the upstream server reaches out, the gateway listens.
|
||
/// </summary>
|
||
public ReverseConnectOptions ReverseConnect { get; init; } = new();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Driver knobs for OPC UA reverse-connect (server-initiated) sessions. Mirrors the
|
||
/// SDK's <c>Opc.Ua.Client.ReverseConnectManager</c> surface but expressed as plain
|
||
/// config so the driver can decide listener-mode vs dial-mode at startup.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// <b>Direction</b>: in conventional OPC UA the client opens the TCP connection
|
||
/// to the server. Reverse-connect inverts this — the server initiates a TCP
|
||
/// connection to a listener the client exposes, then sends a <c>ReverseHello</c>
|
||
/// message naming itself; the client picks the right session config and continues
|
||
/// the OPC UA handshake on the inbound socket. Critical for OT-DMZ networks where
|
||
/// the plant firewall only allows outbound traffic.
|
||
/// </para>
|
||
/// <para>
|
||
/// <b>Singleton listener</b>: a single <c>ReverseConnectManager</c> per process
|
||
/// keyed on <see cref="ListenerUrl"/> multiplexes inbound connections across
|
||
/// driver instances. Two drivers configured with the same listener URL share one
|
||
/// underlying TCP socket; the manager dispatches by the upstream's reported
|
||
/// <c>ServerUri</c>. See <c>ReverseConnectListener</c>.
|
||
/// </para>
|
||
/// </remarks>
|
||
/// <param name="Enabled">
|
||
/// When <c>true</c>, the driver opens a listener at <see cref="ListenerUrl"/> and
|
||
/// waits for the upstream server to initiate the session. When <c>false</c>
|
||
/// (default), the driver uses the conventional dial path against
|
||
/// <see cref="OpcUaClientDriverOptions.EndpointUrls"/>.
|
||
/// </param>
|
||
/// <param name="ListenerUrl">
|
||
/// Local listener URL the SDK binds when reverse-connect is enabled. Typically
|
||
/// <c>opc.tcp://0.0.0.0:4844</c> to accept on every interface, or pinned to a
|
||
/// specific NIC for multi-homed gateways. Required when
|
||
/// <see cref="Enabled"/> is <c>true</c>.
|
||
/// </param>
|
||
/// <param name="ExpectedServerUri">
|
||
/// The upstream server's <c>ApplicationUri</c> the driver expects to see in the
|
||
/// <c>ReverseHello</c>. The SDK passes this as the <c>serverUri</c> filter to
|
||
/// <c>WaitForConnectionAsync</c> so connections from a different upstream are ignored
|
||
/// — important when the listener is shared across multiple drivers and several
|
||
/// upstreams might dial in. Leave <c>null</c> to accept the first connection regardless
|
||
/// (only safe when exactly one upstream targets the listener).
|
||
/// </param>
|
||
public sealed record ReverseConnectOptions(
|
||
bool Enabled = false,
|
||
string? ListenerUrl = null,
|
||
string? ExpectedServerUri = null);
|
||
|
||
/// <summary>
|
||
/// Selective import + namespace remap rules for the OPC UA Client driver. Pure local
|
||
/// filtering inside <c>BrowseRecursiveAsync</c> + <c>EnrichAndRegisterVariablesAsync</c>;
|
||
/// no new SDK calls.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// <b>Glob semantics</b>: patterns are matched against the slash-joined BrowseName
|
||
/// segments accumulated during the browse pass (e.g. <c>"Server/Diagnostics/SessionsDiagnosticsArray"</c>).
|
||
/// Two wildcards are supported — <c>*</c> matches any sequence of characters
|
||
/// (including empty / slashes) and <c>?</c> matches exactly one character. No
|
||
/// character classes, no <c>**</c>, no escapes — keep the surface tight so the doc
|
||
/// + behaviour stay simple.
|
||
/// </para>
|
||
/// <para>
|
||
/// Empty <see cref="IncludePaths"/> = include all (existing behaviour).
|
||
/// <see cref="ExcludePaths"/> wins over <see cref="IncludePaths"/> when both match.
|
||
/// Folders pruned by the rules are skipped wholesale — their descendants don't get
|
||
/// browsed, which keeps the wire cost down on large servers.
|
||
/// </para>
|
||
/// </remarks>
|
||
/// <param name="IncludePaths">
|
||
/// Glob patterns matched against the BrowsePath segment list. Empty = include all
|
||
/// (default — preserves pre-curation behaviour).
|
||
/// </param>
|
||
/// <param name="ExcludePaths">
|
||
/// Glob patterns matched against the BrowsePath segment list. Wins over
|
||
/// <see cref="IncludePaths"/> — useful for "include everything under <c>Plant/*</c>
|
||
/// except <c>Plant/Diagnostics</c>" rules.
|
||
/// </param>
|
||
/// <param name="NamespaceRemap">
|
||
/// Upstream-namespace-URI → local-namespace-URI translation table applied to the
|
||
/// <c>FullName</c> field of <c>DriverAttributeInfo</c> when registering variables.
|
||
/// The driver's stored <c>FullName</c> swaps the prefix before persisting so downstream
|
||
/// clients see the remapped URI. Lookup is case-sensitive — match the upstream URI
|
||
/// exactly. Defaults to empty (no remap).
|
||
/// </param>
|
||
/// <param name="RootAlias">
|
||
/// Replaces the default <c>"Remote"</c> folder name at the top of the mirrored tree.
|
||
/// Useful when multiple OPC UA Client drivers are aggregated and operators need to
|
||
/// distinguish them in the local browse tree. Default <c>null</c> = use <c>"Remote"</c>.
|
||
/// </param>
|
||
public sealed record OpcUaClientCurationOptions(
|
||
IReadOnlyList<string>? IncludePaths = null,
|
||
IReadOnlyList<string>? ExcludePaths = null,
|
||
IReadOnlyDictionary<string, string>? NamespaceRemap = null,
|
||
string? RootAlias = null);
|
||
|
||
/// <summary>
|
||
/// Knobs governing the server-certificate validation callback. Plumbed onto
|
||
/// <see cref="OpcUaClientDriverOptions.CertificateValidation"/> rather than the top-level
|
||
/// options to keep cert-related config grouped together.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// <b>CRL discovery:</b> the OPC UA SDK reads CRL files automatically from the
|
||
/// <c>crl/</c> sub-directory of each cert store (own, trusted, issuers). Drop the
|
||
/// issuer's <c>.crl</c> in that folder and the SDK picks it up — no driver-side wiring
|
||
/// required. When the directory is absent or empty, the SDK reports
|
||
/// <c>BadCertificateRevocationUnknown</c>, which this driver gates with
|
||
/// <see cref="RejectUnknownRevocationStatus"/>.
|
||
/// </para>
|
||
/// </remarks>
|
||
/// <param name="RejectSHA1SignedCertificates">
|
||
/// Reject server certificates whose signature uses SHA-1. Default <c>true</c> — SHA-1 was
|
||
/// deprecated by the OPC UA spec and is treated as a hard fail in production. Flip to
|
||
/// <c>false</c> only for short-term interop with legacy controllers.
|
||
/// </param>
|
||
/// <param name="RejectUnknownRevocationStatus">
|
||
/// When the SDK can't determine revocation status (no CRL present, or stale CRL),
|
||
/// reject the cert if <c>true</c>; allow if <c>false</c>. Default <c>false</c> — many
|
||
/// plant deployments don't run CRL infrastructure, and a hard-fail default would break
|
||
/// them on first connection. Set <c>true</c> in environments with a managed PKI.
|
||
/// </param>
|
||
/// <param name="MinimumCertificateKeySize">
|
||
/// Minimum RSA key size (bits) accepted. Certs with shorter keys are rejected. Default
|
||
/// <c>2048</c> matches the current OPC UA spec floor; raise to 3072 or 4096 for stricter
|
||
/// deployments. Non-RSA keys (ECC) bypass this check.
|
||
/// </param>
|
||
public sealed record OpcUaCertificateValidationOptions(
|
||
bool RejectSHA1SignedCertificates = true,
|
||
bool RejectUnknownRevocationStatus = false,
|
||
int MinimumCertificateKeySize = 2048);
|
||
|
||
/// <summary>
|
||
/// Tuning surface for OPC UA subscriptions created by <see cref="OpcUaClientDriver"/>.
|
||
/// Lifted from the per-call hard-coded literals so operators can tune publish cadence,
|
||
/// keep-alive ratio, and alarm-vs-data prioritisation without recompiling the driver.
|
||
/// Defaults match the original hard-coded values (KeepAlive=10, Lifetime=1000,
|
||
/// MaxNotifications=0 unlimited, Priority=0, MinPublishingInterval=50ms).
|
||
/// </summary>
|
||
/// <param name="KeepAliveCount">
|
||
/// Number of consecutive empty publish cycles before the server sends a keep-alive
|
||
/// response. Default 10 — high enough to suppress idle traffic, low enough that the
|
||
/// client notices a stalled subscription within ~5x the publish interval.
|
||
/// </param>
|
||
/// <param name="LifetimeCount">
|
||
/// Number of consecutive missed publish responses before the server tears down the
|
||
/// subscription. Must be ≥3×<see cref="KeepAliveCount"/> per OPC UA spec; default 1000
|
||
/// gives ~100 keep-alives of slack which is conservative on flaky networks.
|
||
/// </param>
|
||
/// <param name="MaxNotificationsPerPublish">
|
||
/// Cap on notifications returned per publish response. <c>0</c> = unlimited (the OPC UA
|
||
/// spec sentinel). Lower this to bound publish-message size on bursty servers.
|
||
/// </param>
|
||
/// <param name="Priority">
|
||
/// Subscription priority for data subscriptions (0..255). Higher = scheduled ahead of
|
||
/// lower. Default 0 matches the SDK's default for ordinary tag subscriptions.
|
||
/// </param>
|
||
/// <param name="MinPublishingIntervalMs">
|
||
/// Floor (ms) applied to <c>publishingInterval</c> requests. Sub-floor values are
|
||
/// clamped up so wire-side negotiations don't waste round-trips on intervals the server
|
||
/// will only round up anyway. Default 50ms.
|
||
/// </param>
|
||
/// <param name="AlarmsPriority">
|
||
/// Subscription priority for the alarm subscription (0..255). Higher than
|
||
/// <see cref="Priority"/> by default (1 vs 0) so alarm publishes aren't starved during
|
||
/// data-tag bursts.
|
||
/// </param>
|
||
public sealed record OpcUaSubscriptionDefaults(
|
||
int KeepAliveCount = 10,
|
||
uint LifetimeCount = 1000,
|
||
uint MaxNotificationsPerPublish = 0,
|
||
byte Priority = 0,
|
||
int MinPublishingIntervalMs = 50,
|
||
byte AlarmsPriority = 1);
|
||
|
||
/// <summary>OPC UA message security mode.</summary>
|
||
public enum OpcUaSecurityMode
|
||
{
|
||
None,
|
||
Sign,
|
||
SignAndEncrypt,
|
||
}
|
||
|
||
/// <summary>
|
||
/// OPC UA security policies recognized by the driver. Maps to the standard
|
||
/// <c>http://opcfoundation.org/UA/SecurityPolicy#</c> URI suffixes the SDK uses for
|
||
/// endpoint matching.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <see cref="Basic128Rsa15"/> and <see cref="Basic256"/> are <b>deprecated</b> per OPC UA
|
||
/// spec v1.04 — they remain in the enum only for brownfield interop with older servers.
|
||
/// Prefer <see cref="Basic256Sha256"/>, <see cref="Aes128_Sha256_RsaOaep"/>, or
|
||
/// <see cref="Aes256_Sha256_RsaPss"/> for new deployments.
|
||
/// </remarks>
|
||
public enum OpcUaSecurityPolicy
|
||
{
|
||
/// <summary>No security. Unsigned, unencrypted wire.</summary>
|
||
None,
|
||
/// <summary>Deprecated (OPC UA 1.04). Retained for legacy server interop.</summary>
|
||
Basic128Rsa15,
|
||
/// <summary>Deprecated (OPC UA 1.04). Retained for legacy server interop.</summary>
|
||
Basic256,
|
||
/// <summary>Recommended baseline for current deployments.</summary>
|
||
Basic256Sha256,
|
||
/// <summary>Current OPC UA policy; AES-128 + SHA-256 + RSA-OAEP.</summary>
|
||
Aes128_Sha256_RsaOaep,
|
||
/// <summary>Current OPC UA policy; AES-256 + SHA-256 + RSA-PSS.</summary>
|
||
Aes256_Sha256_RsaPss,
|
||
}
|
||
|
||
/// <summary>User authentication type sent to the remote server.</summary>
|
||
public enum OpcUaAuthType
|
||
{
|
||
Anonymous,
|
||
Username,
|
||
Certificate,
|
||
}
|