using CliFx.Attributes; using CliFx.Infrastructure; using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common; namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands; /// /// Probes a TwinCAT runtime: opens an ADS session, reads one symbol, prints driver health. /// Use this first after configuring a new AMS route — it'll surface "no route" / /// "port unreachable" / "AMS router down" errors up-front before you bring the OtOpcUa /// server near the endpoint. /// [Command("probe", Description = "Verify the TwinCAT runtime is reachable and a sample symbol reads.")] public sealed class ProbeCommand : TwinCATCommandBase { [CommandOption("symbol", 's', Description = "Symbol path to probe. System-global examples: " + "'TwinCAT_SystemInfoVarList._AppInfo.OnlineChangeCnt', 'MAIN.bRunning'. " + "User-project: a GVL or program variable.", IsRequired = true)] public string SymbolPath { get; init; } = default!; [CommandOption("type", Description = "Data type (default DInt — TwinCAT DINT maps to int32).")] public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt; public override async ValueTask ExecuteAsync(IConsole console) { ConfigureLogging(); var ct = console.RegisterCancellationHandler(); var probeTag = new TwinCATTagDefinition( Name: "__probe", DeviceHostAddress: Gateway, SymbolPath: SymbolPath, DataType: DataType, Writable: false); var options = BuildOptions([probeTag]); await using var driver = new TwinCATDriver(options, DriverInstanceId); try { await driver.InitializeAsync("{}", ct); var snapshot = await driver.ReadAsync(["__probe"], ct); var health = driver.GetHealth(); await console.Output.WriteLineAsync($"AMS: {AmsNetId}:{AmsPort}"); await console.Output.WriteLineAsync($"Health: {health.State}"); if (health.LastError is { } err) await console.Output.WriteLineAsync($"Last error: {err}"); await console.Output.WriteLineAsync(); await console.Output.WriteLineAsync(SnapshotFormatter.Format(SymbolPath, snapshot[0])); } finally { await driver.ShutdownAsync(CancellationToken.None); } } }