Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/AlertSignalRBridge.cs
T

72 lines
2.9 KiB
C#

using Akka.Actor;
using Akka.Cluster.Tools.PublishSubscribe;
using Akka.Event;
using Microsoft.AspNetCore.SignalR;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Alerts;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
/// <summary>
/// Akka actor that subscribes to the <c>alerts</c> DistributedPubSub topic and forwards each
/// <see cref="AlarmTransitionEvent"/> to every SignalR client connected to <see cref="AlertHub"/>.
/// Mirrors <c>FleetStatusSignalRBridge</c>'s design — one bridge per admin node, hub fan-out is
/// per-node, no cluster-singleton needed.
/// </summary>
public sealed class AlertSignalRBridge : ReceiveActor
{
public const string TopicName = "alerts";
private readonly IHubContext<AlertHub> _hub;
private readonly IInProcessBroadcaster<AlarmTransitionEvent> _broadcaster;
private readonly ILoggingAdapter _log = Context.GetLogger();
/// <summary>
/// Creates actor props for the AlertSignalRBridge.
/// </summary>
/// <param name="hub">The SignalR hub context to send alerts to.</param>
/// <param name="broadcaster">In-process fan-out read directly by the Blazor Server Alerts page.</param>
public static Props Props(IHubContext<AlertHub> hub, IInProcessBroadcaster<AlarmTransitionEvent> broadcaster) =>
Akka.Actor.Props.Create(() => new AlertSignalRBridge(hub, broadcaster));
/// <summary>
/// Initializes a new instance of the AlertSignalRBridge actor.
/// </summary>
/// <param name="hub">The SignalR hub context to send alerts to.</param>
/// <param name="broadcaster">In-process fan-out read directly by the Blazor Server Alerts page.</param>
public AlertSignalRBridge(IHubContext<AlertHub> hub, IInProcessBroadcaster<AlarmTransitionEvent> broadcaster)
{
_hub = hub;
_broadcaster = broadcaster;
ReceiveAsync<AlarmTransitionEvent>(ForwardAsync);
// DPS subscription is now live — mark the feed connected so the Blazor "live" pill lights up.
Receive<SubscribeAck>(_ => _broadcaster.SetConnected(true));
}
/// <inheritdoc />
protected override void PreStart() =>
DistributedPubSub.Get(Context.System).Mediator.Tell(new Subscribe(TopicName, Self));
/// <inheritdoc />
protected override void PostStop()
{
// Bridge stopping — the feed is no longer live, drop the "live" pill.
_broadcaster.SetConnected(false);
base.PostStop();
}
private async Task ForwardAsync(AlarmTransitionEvent msg)
{
// In-process fan-out first — this is what the Blazor Server Alerts page reads. The hub push
// is kept for any out-of-process (e.g. WASM) SignalR client.
_broadcaster.Publish(msg);
try
{
await _hub.Clients.All.SendAsync(AlertHub.MethodName, msg);
}
catch (Exception ex)
{
_log.Warning(ex, "AlertSignalRBridge: SignalR push failed for {AlarmId}", msg.AlarmId);
}
}
}