c88e0b6bed
Closes #316
95 lines
3.9 KiB
C#
95 lines
3.9 KiB
C#
using CliFx.Attributes;
|
|
using CliFx.Infrastructure;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT.Cli.Commands;
|
|
|
|
/// <summary>
|
|
/// PR 5.1 / #316 — stream TC3 EventLogger alarms to the terminal until Ctrl+C.
|
|
/// Mirrors the OPC UA Client CLI <c>alarms</c> verb shape: subscribe + print every
|
|
/// incoming <see cref="AlarmEventArgs"/> with timestamp, source, severity, and
|
|
/// message text. Requires <c>EnableAlarms=true</c> on the driver — set via the
|
|
/// base options builder when the verb is selected.
|
|
/// </summary>
|
|
[Command("alarms", Description =
|
|
"Subscribe to TC3 EventLogger alarms via the driver's IAlarmSource bridge and " +
|
|
"stream events to stdout until Ctrl+C.")]
|
|
public sealed class AlarmsCommand : TwinCATCommandBase
|
|
{
|
|
[CommandOption("source", Description =
|
|
"Optional alarm source filter (matched case-insensitively against the event's " +
|
|
"Source field). Repeat the flag to match multiple sources; omit to subscribe to " +
|
|
"every event the EventLogger surfaces.")]
|
|
public IReadOnlyList<string> Sources { get; init; } = Array.Empty<string>();
|
|
|
|
public override async ValueTask ExecuteAsync(IConsole console)
|
|
{
|
|
ConfigureLogging();
|
|
var ct = console.RegisterCancellationHandler();
|
|
|
|
// Empty Tags + EnableAlarms=true builds a driver that opens only the alarm path.
|
|
// TwinCATDriverOptions is a regular class with init-only properties — rebuild
|
|
// the instance instead of using a record-style `with` clone.
|
|
var baseOptions = BuildOptions([]);
|
|
var options = new TwinCATDriverOptions
|
|
{
|
|
Devices = baseOptions.Devices,
|
|
Tags = baseOptions.Tags,
|
|
Probe = baseOptions.Probe,
|
|
Timeout = baseOptions.Timeout,
|
|
UseNativeNotifications = baseOptions.UseNativeNotifications,
|
|
EnableControllerBrowse = baseOptions.EnableControllerBrowse,
|
|
MaxArrayExpansion = baseOptions.MaxArrayExpansion,
|
|
EnableAlarms = true,
|
|
};
|
|
|
|
await using var driver = new TwinCATDriver(options, DriverInstanceId);
|
|
IAlarmSubscriptionHandle? handle = null;
|
|
try
|
|
{
|
|
await driver.InitializeAsync("{}", ct);
|
|
|
|
driver.OnAlarmEvent += (_, e) =>
|
|
{
|
|
var line =
|
|
$"[{e.SourceTimestampUtc:HH:mm:ss.fff}] " +
|
|
$"{e.SourceNodeId} " +
|
|
$"sev={e.Severity} " +
|
|
$"type={e.AlarmType} " +
|
|
$"cond={e.ConditionId} " +
|
|
$"\"{e.Message}\"";
|
|
console.Output.WriteLine(line);
|
|
};
|
|
|
|
handle = await driver.SubscribeAlarmsAsync(Sources, ct);
|
|
|
|
var filterDesc = Sources.Count == 0
|
|
? "all sources"
|
|
: $"sources [{string.Join(", ", Sources)}]";
|
|
await console.Output.WriteLineAsync(
|
|
$"Subscribed to TC3 EventLogger alarms on {AmsNetId}:{AmsPort} ({filterDesc}). Ctrl+C to stop.");
|
|
await console.Output.WriteLineAsync(
|
|
"Note: Beckhoff doesn't ship a managed TcEventLogger wrapper; the driver " +
|
|
"uses a best-effort decode of AMS port 110 notifications. Some fields may " +
|
|
"surface as 'Unknown' until a binary-protocol decoder lands.");
|
|
try
|
|
{
|
|
await Task.Delay(System.Threading.Timeout.InfiniteTimeSpan, ct);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// Expected on Ctrl+C.
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (handle is not null)
|
|
{
|
|
try { await driver.UnsubscribeAlarmsAsync(handle, CancellationToken.None); }
|
|
catch { /* teardown best-effort */ }
|
|
}
|
|
await driver.ShutdownAsync(CancellationToken.None);
|
|
}
|
|
}
|
|
}
|