using System; using System.Threading; using ArchestrA; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Backend.Historian { /// /// Creates and opens Historian SDK connections. Extracted so tests can inject fakes that /// control connection success, failure, and timeout behavior. /// internal interface IHistorianConnectionFactory { HistorianAccess CreateAndConnect(HistorianConfiguration config, HistorianConnectionType type); } /// Production implementation — opens real Historian SDK connections. internal sealed class SdkHistorianConnectionFactory : IHistorianConnectionFactory { public HistorianAccess CreateAndConnect(HistorianConfiguration config, HistorianConnectionType type) { var conn = new HistorianAccess(); var args = new HistorianConnectionArgs { ServerName = config.ServerName, TcpPort = (ushort)config.Port, IntegratedSecurity = config.IntegratedSecurity, UseArchestrAUser = config.IntegratedSecurity, ConnectionType = type, ReadOnly = true, PacketTimeout = (uint)(config.CommandTimeoutSeconds * 1000) }; if (!config.IntegratedSecurity) { args.UserName = config.UserName ?? string.Empty; args.Password = config.Password ?? string.Empty; } if (!conn.OpenConnection(args, out var error)) { conn.Dispose(); throw new InvalidOperationException( $"Failed to open Historian SDK connection to {config.ServerName}:{config.Port}: {error.ErrorCode}"); } var timeoutMs = config.CommandTimeoutSeconds * 1000; var elapsed = 0; while (elapsed < timeoutMs) { var status = new HistorianConnectionStatus(); conn.GetConnectionStatus(ref status); if (status.ConnectedToServer) return conn; if (status.ErrorOccurred) { conn.Dispose(); throw new InvalidOperationException( $"Historian SDK connection failed: {status.Error}"); } Thread.Sleep(250); elapsed += 250; } conn.Dispose(); throw new TimeoutException( $"Historian SDK connection to {config.ServerName}:{config.Port} timed out after {config.CommandTimeoutSeconds}s"); } } }