using Akka.Actor; using Microsoft.Extensions.Options; using ZB.MOM.WW.OtOpcUa.Cluster; using ZB.MOM.WW.OtOpcUa.Commons.Interfaces; using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet; using ZB.MOM.WW.OtOpcUa.Commons.Types; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Clients; /// /// backed by an Akka against /// the target node's DriverHostActor at akka.tcp://otopcua@<host>:<port>/user/driver-host. /// Sends ; expects a reply. /// /// On timeout or any other failure (peer down, GetDiagnostics handler missing) returns an /// empty snapshot so the UI degrades to "no data" instead of throwing. /// public sealed class FleetDiagnosticsClient : IFleetDiagnosticsClient { private static readonly TimeSpan AskTimeout = TimeSpan.FromSeconds(3); private readonly ActorSystem _system; private readonly string _systemName; /// Initializes a new FleetDiagnosticsClient with the given actor system and cluster options. /// The Akka actor system. /// Cluster configuration options. public FleetDiagnosticsClient(ActorSystem system, IOptions options) { _system = system; _systemName = options.Value.SystemName; } /// Gets diagnostics for a cluster node. /// The node identifier to query. /// Cancellation token. /// Diagnostics snapshot for the node, or an empty snapshot if the query fails. public async Task GetDiagnosticsAsync(NodeId nodeId, CancellationToken ct) { var selection = _system.ActorSelection($"akka.tcp://{_systemName}@{nodeId.Value}/user/driver-host"); try { using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct); linked.CancelAfter(AskTimeout); return await selection.Ask( new GetDiagnostics(CorrelationId.NewId()), AskTimeout, linked.Token); } catch (Exception) { return new NodeDiagnosticsSnapshot( nodeId, CurrentRevision: null, Drivers: Array.Empty(), AsOfUtc: DateTime.UtcNow); } } }