using System.ComponentModel.DataAnnotations; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient; /// /// OPC UA Client (gateway) driver configuration. Bound from DriverConfig JSON at /// driver-host registration time. Models the settings documented in /// docs/v2/driver-specs.md §8. /// /// /// 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. /// // A record (not a plain class) so G-2b's OpcUaClientSecretResolution can produce a // credential-resolved copy with a `with` expression — every property keeps its init setter. public sealed record OpcUaClientDriverOptions { /// /// Remote OPC UA endpoint URL, e.g. opc.tcp://plc.internal:4840. Convenience /// shortcut for a single-endpoint deployment — equivalent to setting /// to a list with this one URL. When both are provided, /// the list wins and is ignored. /// public string EndpointUrl { get; init; } = "opc.tcp://localhost:4840"; /// /// v3 authored raw tags for this driver instance — the deploy artifact (Wave C) populates this /// with the cluster's raw OPC UA Client tags. Each carries the tag's /// RawPath identity plus a driver TagConfig blob whose nodeId key holds the /// upstream OPC UA node id string the driver dials. The driver builds a RawPath → nodeId /// resolution table (via NamespaceMap, in the sibling .Session project) from this /// list at connect time; a /// read/write/subscribe/history reference is always a RawPath resolved through that table. /// public IReadOnlyList RawTags { get; init; } = []; /// /// Ordered list of candidate endpoint URLs for failover. The driver tries each in /// order at 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 /// as a single-URL shortcut. /// public IReadOnlyList EndpointUrls { get; init; } = []; /// /// 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 which governs steady-state operations. /// public TimeSpan PerEndpointConnectTimeout { get; init; } = TimeSpan.FromSeconds(3); /// /// Security policy to require when selecting an endpoint. Either a /// enum constant or a free-form string (for /// forward-compatibility with future OPC UA policies not yet in the enum). /// Matched against EndpointDescription.SecurityPolicyUri suffix — the driver /// connects to the first endpoint whose policy name matches AND whose mode matches /// . When set to /// the driver picks any unsecured endpoint regardless of policy string. /// public OpcUaSecurityPolicy SecurityPolicy { get; init; } = OpcUaSecurityPolicy.None; /// Security mode. public OpcUaSecurityMode SecurityMode { get; init; } = OpcUaSecurityMode.None; /// Authentication type. public OpcUaAuthType AuthType { get; init; } = OpcUaAuthType.Anonymous; /// User name (required only for ). public string? Username { get; init; } /// Password (required only for ). public string? Password { get; init; } /// /// Filesystem path to the user-identity certificate (PFX/PEM). Required when /// is . The driver /// loads the cert + private key, which the remote server validates against its /// TrustedUserCertificates 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). /// public string? UserCertificatePath { get; init; } /// /// Optional password that unlocks when the PFX is /// protected. PEM files generally have their password on the adjacent key file; this /// knob only applies to password-locked PFX. /// public string? UserCertificatePassword { get; init; } /// Server-negotiated session timeout. Default 120s per driver-specs.md §8. public TimeSpan SessionTimeout { get; init; } = TimeSpan.FromSeconds(120); /// Client-side keep-alive interval. public TimeSpan KeepAliveInterval { get; init; } = TimeSpan.FromSeconds(5); /// /// When true, the driver accepts any self-signed / untrusted server certificate. /// Dev-only — must be false in production so MITM attacks against the opc.tcp /// channel fail closed. /// public bool AutoAcceptCertificates { get; init; } = false; /// /// 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. /// public string ApplicationUri { get; init; } = "urn:localhost:OtOpcUa:GatewayClient"; /// /// 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. /// public string SessionName { get; init; } = "OtOpcUa-Gateway"; /// Connect + per-operation timeout. public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(10); /// /// Root NodeId to mirror. Default null = ObjectsFolder (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. /// public string? BrowseRoot { get; init; } /// /// Cap on total nodes discovered during DiscoverAsync. 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. /// [Range(1, int.MaxValue)] public int MaxDiscoveredNodes { get; init; } = 10_000; /// /// 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. /// [Range(1, int.MaxValue)] public int MaxBrowseDepth { get; init; } = 10; /// /// Timeout for the AdminUI Test Connect probe, in seconds. The AdminUI clamps to a /// 60s server-side maximum; this default is what the form pre-fills for new instances. /// [Range(1, 60)] public int ProbeTimeoutSeconds { get; init; } = 15; // ---- #273 per-subscription tuning (defaults are the values that used to be hardcoded) ---- /// /// Fixed publishing interval for every data-change subscription this driver creates. null /// (default) keeps the historical behaviour: the interval the runtime asks for per /// SubscribeAsync call, floored at 50 ms. When set it overrides the per-call value (the /// 50 ms floor still applies); monitored-item sampling intervals follow it. /// public TimeSpan? SubscriptionPublishingInterval { get; init; } /// /// Publish cycles with no data before the server sends a keep-alive (default 10). Must be >= 1. /// public uint SubscriptionKeepAliveCount { get; init; } = 10; /// /// Publish cycles with no Publish request before the server drops the subscription (default 1000). /// Must be at least 3x (OPC UA Part 4 §5.13.2). /// public uint SubscriptionLifetimeCount { get; init; } = 1000; /// Max notifications per Publish response; 0 (default) = server decides. public uint SubscriptionMaxNotificationsPerPublish { get; init; } /// Relative subscription priority 0-255 (default 0). public byte SubscriptionPriority { get; init; } // ---- #277 ---- /// Certificate-revocation / issuer-store settings. Defaults preserve the SDK behaviour. public OpcUaCertificateValidationOptions CertificateValidation { get; init; } = new(); // ---- #278 ---- /// /// Optional discovery endpoint (a Local Discovery Server or the server itself), e.g. /// opc.tcp://lds.internal:4840. When set, InitializeAsync calls FindServers and /// GetEndpoints on it and tries the discovered URLs that match + /// after the statically configured / /// (pin the server with ). A discovery failure is non-fatal: the static list is used. Discovery /// itself always runs over an unsecured channel, as the spec requires. null (default) = off. /// public string? DiscoveryUrl { get; init; } /// /// ApplicationUri of the one server discovery may resolve to. An LDS usually registers several servers; /// with this set only that server's endpoints are used. When unset and discovery finds more than one /// server, connect fails with an error listing their ApplicationUris. Discovered endpoints are always /// tried AFTER the configured . /// public string? DiscoveryServerUri { get; init; } } /// /// Server-certificate revocation handling for the OPC UA Client driver (#277). The SDK reads CRLs from /// the crl sub-folder of the trusted-peer / trusted-issuer stores; these settings only decide how /// a certificate whose revocation status cannot be determined is treated and where the issuer store lives. /// public sealed record OpcUaCertificateValidationOptions { /// /// When true, a server certificate whose revocation status is unknown (no CRL for its issuer, or a /// stale one) is rejected. Default false keeps brownfield deployments without CRL infrastructure /// connecting. A certificate that IS revoked is always rejected — even with /// set. /// public bool RejectUnknownRevocationStatus { get; init; } /// /// Directory used as the trusted-issuer certificate store. Its crl/ sub-folder is where the SDK /// looks for issuer CRLs (.crl files), so this is the knob for pointing the driver at an /// operator-managed CRL set. null/empty (default) = %LocalAppData%/OtOpcUa/pki/issuers. /// public string? IssuerStorePath { get; init; } } /// OPC UA message security mode. public enum OpcUaSecurityMode { None, Sign, SignAndEncrypt, } /// /// OPC UA security policies recognized by the driver. Maps to the standard /// http://opcfoundation.org/UA/SecurityPolicy# URI suffixes the SDK uses for /// endpoint matching. /// /// /// and are deprecated per OPC UA /// spec v1.04 — they remain in the enum only for brownfield interop with older servers. /// Prefer , , or /// for new deployments. /// public enum OpcUaSecurityPolicy { /// No security. Unsigned, unencrypted wire. None, /// Deprecated (OPC UA 1.04). Retained for legacy server interop. Basic128Rsa15, /// Deprecated (OPC UA 1.04). Retained for legacy server interop. Basic256, /// Recommended baseline for current deployments. Basic256Sha256, /// Current OPC UA policy; AES-128 + SHA-256 + RSA-OAEP. Aes128_Sha256_RsaOaep, /// Current OPC UA policy; AES-256 + SHA-256 + RSA-PSS. Aes256_Sha256_RsaPss, } /// User authentication type sent to the remote server. public enum OpcUaAuthType { Anonymous, Username, Certificate, }