using System; using System.Security.Principal; using System.Threading; using Serilog; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Backend; using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host.Ipc; namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Host; /// /// Entry point for the OtOpcUaFocasHost Windows service / console host. The /// supervisor (Proxy-side) spawns this process per FOCAS driver instance and passes the /// pipe name, allowed-SID, and per-process shared secret as environment variables. In /// PR B the backend is — PR C swaps in the real /// Fwlib32-backed handler once the session state + STA thread move out of the .NET 10 /// driver. /// public static class Program { public static int Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .WriteTo.File( @"%ProgramData%\OtOpcUa\focas-host-.log".Replace("%ProgramData%", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)), rollingInterval: RollingInterval.Day) .CreateLogger(); try { var pipeName = Environment.GetEnvironmentVariable("OTOPCUA_FOCAS_PIPE") ?? "OtOpcUaFocas"; var allowedSidValue = Environment.GetEnvironmentVariable("OTOPCUA_ALLOWED_SID") ?? throw new InvalidOperationException( "OTOPCUA_ALLOWED_SID not set — the FOCAS Proxy supervisor must pass the server principal SID"); var sharedSecret = Environment.GetEnvironmentVariable("OTOPCUA_FOCAS_SECRET") ?? throw new InvalidOperationException( "OTOPCUA_FOCAS_SECRET not set — the FOCAS Proxy supervisor must pass the per-process secret at spawn time"); var allowedSid = new SecurityIdentifier(allowedSidValue); using var server = new PipeServer(pipeName, allowedSid, sharedSecret, Log.Logger); using var cts = new CancellationTokenSource(); Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); }; Log.Information("OtOpcUaFocasHost starting — pipe={Pipe} allowedSid={Sid}", pipeName, allowedSidValue); var backendKind = (Environment.GetEnvironmentVariable("OTOPCUA_FOCAS_BACKEND") ?? "unconfigured") .ToLowerInvariant(); IFocasBackend backend = backendKind switch { "fake" => new FakeFocasBackend(), "unconfigured" => new UnconfiguredFocasBackend(), "fwlib32" => new UnconfiguredFocasBackend(), // real Fwlib32 backend lands with hardware integration follow-up _ => new UnconfiguredFocasBackend(), }; Log.Information("OtOpcUaFocasHost backend={Backend}", backendKind); var handler = new FwlibFrameHandler(backend, Log.Logger); server.RunAsync(handler, cts.Token).GetAwaiter().GetResult(); Log.Information("OtOpcUaFocasHost stopped cleanly"); return 0; } catch (Exception ex) { Log.Fatal(ex, "OtOpcUaFocasHost fatal"); return 2; } finally { Log.CloseAndFlush(); } } }