using Akka.Actor; using Akka.Cluster.Tools.PublishSubscribe; using ZB.MOM.WW.OtOpcUa.Commons.Interfaces; using ZB.MOM.WW.OtOpcUa.Commons.Messages.Drivers; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Runtime.Telemetry; namespace ZB.MOM.WW.OtOpcUa.Runtime.Drivers; /// /// Forwards transitions to the cluster-wide /// driver-health DistributedPubSub topic. Consumed by the AdminUI /// DriverStatusSignalRBridge. /// public sealed class AkkaDriverHealthPublisher : IDriverHealthPublisher { /// The DistributedPubSub topic name for driver-health snapshots — single source /// of truth on the message contract itself. public const string TopicName = DriverHealthChanged.TopicName; private readonly ActorSystem _system; private readonly ITelemetryLocalHub _hub; private readonly string? _localNodeId; /// Initializes a new instance of . /// The Akka actor system used to resolve the DPS mediator. /// The node-local live-telemetry hub each snapshot is also emitted into (Phase 5). /// This node's role info; its LocalNode stamps every snapshot's /// (lmxopcua#847). Optional for harnesses. public AkkaDriverHealthPublisher(ActorSystem system, ITelemetryLocalHub hub, IClusterRoleInfo? roleInfo = null) { _system = system; _hub = hub; var local = roleInfo?.LocalNode.Value; _localNodeId = string.IsNullOrWhiteSpace(local) ? null : local; } /// public void Publish( string clusterId, string driverInstanceId, DriverHealth health, int errorCount5Min, DateTime? rediscoveryNeededUtc = null, string? rediscoveryReason = null, IReadOnlyList? hostStatuses = null, SkippedTagReport? skippedTags = null, IReadOnlyDictionary? diagnostics = null) { var msg = new DriverHealthChanged( clusterId, driverInstanceId, health.State.ToString(), health.LastSuccessfulRead, health.LastError, errorCount5Min, DateTime.UtcNow, rediscoveryNeededUtc, rediscoveryReason, hostStatuses, skippedTags, diagnostics, _localNodeId); DistributedPubSub.Get(_system).Mediator.Tell(new Publish(TopicName, msg)); // Phase 5: fan the same snapshot into the node-local live-telemetry hub (no-op until a gRPC // client subscribes). The DPS publish above is unchanged — the hub is a strictly additive tap. _hub.Emit(new TelemetryItem.Health(msg)); } }