fb57717f6f
Closes #313
90 lines
3.8 KiB
C#
90 lines
3.8 KiB
C#
using CliFx.Attributes;
|
|
using CliFx.Infrastructure;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Cli.Common;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
|
|
|
|
/// <summary>
|
|
/// Watch a TwinCAT symbol until Ctrl+C. Native ADS notifications by default (TwinCAT
|
|
/// pushes on its own cycle); pass <c>--poll-only</c> to fall through to PollGroupEngine.
|
|
/// </summary>
|
|
[Command("subscribe", Description = "Watch a TwinCAT symbol via ADS notification or poll, until Ctrl+C.")]
|
|
public sealed class SubscribeCommand : TwinCATCommandBase
|
|
{
|
|
[CommandOption("symbol", 's', Description = "Symbol path — same format as `read`.", IsRequired = true)]
|
|
public string SymbolPath { get; init; } = default!;
|
|
|
|
[CommandOption("type", 't', Description =
|
|
"Bool / SInt / USInt / Int / UInt / DInt / UDInt / LInt / ULInt / Real / LReal / " +
|
|
"String / WString / Time / Date / DateTime / TimeOfDay (default DInt).")]
|
|
public TwinCATDataType DataType { get; init; } = TwinCATDataType.DInt;
|
|
|
|
[CommandOption("interval-ms", 'i', Description =
|
|
"Cycle time ms — minimum interval between change checks the PLC runtime applies " +
|
|
"(default 1000). Different from --max-delay-ms; see help for that flag.")]
|
|
public int IntervalMs { get; init; } = 1000;
|
|
|
|
[CommandOption("max-delay-ms", Description =
|
|
"Per-tag MaxDelay in ms (PR 3.1, issue #313) — upper bound on how long the runtime " +
|
|
"can buffer / coalesce change events before dispatch. 0 (default) = fire ASAP, no " +
|
|
"coalescing. Larger values (e.g. 500) reduce event rate on bursty signals so the " +
|
|
"OPC UA queue downstream doesn't flood. Distinct from --interval-ms (the cycle).")]
|
|
public int MaxDelayMs { get; init; } = 0;
|
|
|
|
public override async ValueTask ExecuteAsync(IConsole console)
|
|
{
|
|
ConfigureLogging();
|
|
var ct = console.RegisterCancellationHandler();
|
|
|
|
var tagName = ReadCommand.SynthesiseTagName(SymbolPath, DataType);
|
|
var tag = new TwinCATTagDefinition(
|
|
Name: tagName,
|
|
DeviceHostAddress: Gateway,
|
|
SymbolPath: SymbolPath,
|
|
DataType: DataType,
|
|
Writable: false,
|
|
MaxDelayMs: MaxDelayMs > 0 ? MaxDelayMs : null);
|
|
var options = BuildOptions([tag]);
|
|
|
|
await using var driver = new TwinCATDriver(options, DriverInstanceId);
|
|
ISubscriptionHandle? handle = null;
|
|
try
|
|
{
|
|
await driver.InitializeAsync("{}", ct);
|
|
|
|
driver.OnDataChange += (_, e) =>
|
|
{
|
|
var line = $"[{DateTime.UtcNow:HH:mm:ss.fff}] " +
|
|
$"{e.FullReference} = {SnapshotFormatter.FormatValue(e.Snapshot.Value)} " +
|
|
$"({SnapshotFormatter.FormatStatus(e.Snapshot.StatusCode)})";
|
|
console.Output.WriteLine(line);
|
|
};
|
|
|
|
handle = await driver.SubscribeAsync([tagName], TimeSpan.FromMilliseconds(IntervalMs), ct);
|
|
|
|
var mode = PollOnly ? "polling" : "ADS notification";
|
|
var coalesce = MaxDelayMs > 0 ? $", max-delay {MaxDelayMs}ms" : "";
|
|
await console.Output.WriteLineAsync(
|
|
$"Subscribed to {SymbolPath} @ {IntervalMs}ms ({mode}{coalesce}). Ctrl+C to stop.");
|
|
try
|
|
{
|
|
await Task.Delay(System.Threading.Timeout.InfiniteTimeSpan, ct);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Expected on Ctrl+C.
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (handle is not null)
|
|
{
|
|
try { await driver.UnsubscribeAsync(handle, CancellationToken.None); }
|
|
catch { /* teardown best-effort */ }
|
|
}
|
|
await driver.ShutdownAsync(CancellationToken.None);
|
|
}
|
|
}
|
|
}
|