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;
}
}