using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport.Probes; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport; /// /// Entry point for live-AVEVA test fixtures. Runs every relevant probe and returns a /// whose SkipReason feeds Assert.Skip when /// the environment isn't set up. Non-Windows hosts get a single aggregated Skip row per /// category instead of a flood of individual skips. /// /// /// Call shape: /// /// var report = await AvevaPrerequisites.CheckAllAsync(); /// if (report.SkipReason is not null) Assert.Skip(report.SkipReason); /// /// Categories in rough order of 'would I want to know first?': /// /// Environment — process bitness, OS platform, RPCSS up. /// AvevaInstall — Framework registry, install paths, no pending reboot. /// AvevaCoreService — aaBootstrap / aaGR / NmxSvc running. /// MxAccessCom — LMXProxy.LMXProxyServer ProgID → CLSID → file-on-disk. /// GalaxyRepository — SQL reachable, ZB exists, deployed-object count. /// OtOpcUaService — our two Windows services + GLAuth. /// AvevaSoftService — aaLogger etc., warn only. /// AvevaHistorian — aahClientAccessPoint etc., optional. /// /// What's NOT checked here: end-to-end subscribe / read / write against a real /// Galaxy tag. That's the job of the live-smoke tests this helper gates — the helper just /// tells them whether running is worthwhile. /// public static class AvevaPrerequisites { // -------- Individual service lists (kept as data so tests can inspect / override) -------- /// Services whose absence means live-Galaxy tests can't run at all. internal static readonly (string Name, string Purpose)[] CoreServices = [ ("aaBootstrap", "master service that starts the Platform process + brokers aa* communication"), ("aaGR", "Galaxy Repository host — mediates IDE / runtime access to ZB"), ("NmxSvc", "Network Message Exchange — MXAccess + Bootstrap transport"), ("MSSQLSERVER", "SQL Server instance that hosts the ZB database"), ]; /// Warn-but-don't-fail AVEVA services. internal static readonly (string Name, string Purpose)[] SoftServices = [ ("aaLogger", "ArchestrA Logger — diagnostic log receiver; stack runs without it but error visibility suffers"), ("aaUserValidator", "OS user/group auth for ArchestrA security; only required when Galaxy security mode isn't 'Open'"), ("aaGlobalDataCacheMonitorSvr", "cross-platform global data cache; single-node dev boxes run fine without it"), ]; /// Optional AVEVA Historian services — only required for HistoryRead IPC paths. internal static readonly (string Name, string Purpose)[] HistorianServices = [ ("aahClientAccessPoint", "AVEVA Historian Client Access Point — HistoryRead IPC endpoint"), ("aahGateway", "AVEVA Historian Gateway"), ]; /// OtOpcUa-stack Windows services + third-party deps we manage. internal static readonly (string Name, string Purpose, bool HardRequired)[] OtOpcUaServices = [ ("OtOpcUaGalaxyHost", "Galaxy.Host out-of-process service (net48 x86, STA + MXAccess)", true), ("OtOpcUa", "Main OPC UA server service (hosts Proxy + DriverHost + Admin-facing DB publisher)", false), ("GLAuth", "LDAP server (dev only) — glauth.exe on localhost:3893", false), ]; // -------- Orchestrator -------- public static async Task CheckAllAsync( Options? options = null, CancellationToken ct = default) { options ??= new Options(); var checks = new List(); // Environment checks.Add(MxAccessComProbe.CheckProcessBitness()); // AvevaInstall — registry + files checks.Add(RegistryProbe.CheckFrameworkInstalled()); checks.Add(RegistryProbe.CheckPlatformDeployed()); checks.Add(RegistryProbe.CheckRebootPending()); // AvevaCoreService foreach (var (name, purpose) in CoreServices) checks.Add(ServiceProbe.Check(name, PrerequisiteCategory.AvevaCoreService, hardRequired: true, whatItDoes: purpose)); // MxAccessCom checks.Add(MxAccessComProbe.Check()); // GalaxyRepository checks.Add(await SqlProbe.CheckZbDatabaseAsync(options.SqlConnectionString, ct)); // Deployed-object count only makes sense if the DB check passed. if (checks[checks.Count - 1].Status == PrerequisiteStatus.Pass) checks.Add(await SqlProbe.CheckDeployedObjectCountAsync(options.SqlConnectionString, ct)); // OtOpcUaService foreach (var (name, purpose, hard) in OtOpcUaServices) checks.Add(ServiceProbe.Check(name, PrerequisiteCategory.OtOpcUaService, hardRequired: hard, whatItDoes: purpose)); if (options.CheckGalaxyHostPipe) checks.Add(await NamedPipeProbe.CheckGalaxyHostPipeAsync(options.GalaxyHostPipeName, ct)); // AvevaSoftService foreach (var (name, purpose) in SoftServices) checks.Add(ServiceProbe.Check(name, PrerequisiteCategory.AvevaSoftService, hardRequired: false, whatItDoes: purpose)); // AvevaHistorian if (options.CheckHistorian) { foreach (var (name, purpose) in HistorianServices) checks.Add(ServiceProbe.Check(name, PrerequisiteCategory.AvevaHistorian, hardRequired: false, whatItDoes: purpose)); } return new PrerequisiteReport(checks); } /// /// Narrower check for tests that only need the Galaxy Repository (SQL) path — don't /// pay the cost of probing every aa* service when the test only reads gobject rows. /// public static async Task CheckRepositoryOnlyAsync( string? sqlConnectionString = null, CancellationToken ct = default) { var checks = new List { await SqlProbe.CheckZbDatabaseAsync(sqlConnectionString, ct), }; if (checks[0].Status == PrerequisiteStatus.Pass) checks.Add(await SqlProbe.CheckDeployedObjectCountAsync(sqlConnectionString, ct)); return new PrerequisiteReport(checks); } /// /// Narrower check for the named-pipe endpoint — tests that drive the full Proxy /// against a live Galaxy.Host service don't need the SQL or AVEVA-internal probes /// (the Host does that work internally; we just need the pipe to accept). /// public static async Task CheckGalaxyHostPipeOnlyAsync( string? pipeName = null, CancellationToken ct = default) { var checks = new List { await NamedPipeProbe.CheckGalaxyHostPipeAsync(pipeName, ct), }; return new PrerequisiteReport(checks); } /// Knobs for . public sealed class Options { /// SQL Server connection string — defaults to Windows-auth localhost\ZB. public string? SqlConnectionString { get; init; } /// Named-pipe endpoint for OtOpcUaGalaxyHost — defaults to OtOpcUaGalaxy. public string? GalaxyHostPipeName { get; init; } /// Include the named-pipe probe. Off by default — it's a seconds-long TCP-like probe and some tests don't need it. public bool CheckGalaxyHostPipe { get; init; } = true; /// Include Historian service probes. Off by default — Historian is optional. public bool CheckHistorian { get; init; } = false; } }