Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Hubs/HubServiceCollectionExtensions.cs
T
2026-07-13 10:43:47 -04:00

84 lines
4.5 KiB
C#

using Akka.Actor;
using Akka.Hosting;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
public static class HubServiceCollectionExtensions
{
public const string FleetStatusSignalRBridgeName = "fleet-status-signalr-bridge";
public const string AlertSignalRBridgeName = "alert-signalr-bridge";
public const string ScriptLogSignalRBridgeName = "script-log-signalr-bridge";
public const string DriverStatusSignalRBridgeName = "driver-status-signalr-bridge";
/// <summary>
/// Registers the in-process live-push services the AdminUI's Blazor Server panels read
/// directly (instead of self-connecting a SignalR <c>HubConnection</c>, which fails behind a
/// reverse proxy — see <see cref="IInProcessBroadcaster{T}"/>):
/// <list type="bullet">
/// <item><see cref="IDriverStatusSnapshotStore"/> — last-value snapshot per driver.</item>
/// <item><see cref="IInProcessBroadcaster{T}"/> — append-stream fan-out (alarm
/// transitions, script-log lines). Registered as an open generic so each closed type
/// resolves to its own singleton shared by the bridge actor and the consuming component.</item>
/// </list>
/// </summary>
/// <param name="services">The service collection.</param>
/// <returns>The same service collection, for chaining.</returns>
public static IServiceCollection AddOtOpcUaDriverStatusServices(this IServiceCollection services)
{
services.AddSingleton<IDriverStatusSnapshotStore, InMemoryDriverStatusSnapshotStore>();
services.AddSingleton<IDriverResilienceStatusStore, InMemoryDriverResilienceStatusStore>();
services.AddSingleton(typeof(IInProcessBroadcaster<>), typeof(InProcessBroadcaster<>));
return services;
}
/// <summary>
/// Spawns the SignalR bridge actors that forward DPS messages to browser-facing SignalR
/// hubs: <c>fleet-status</c> → <see cref="FleetStatusHub"/>, <c>alerts</c> →
/// <see cref="AlertHub"/>, <c>script-logs</c> → <see cref="ScriptLogHub"/>.
///
/// Call inside the admin-role configurator on the shared <see cref="AkkaConfigurationBuilder"/>:
/// <code>
/// if (hasAdmin)
/// {
/// ab.WithOtOpcUaControlPlaneSingletons();
/// ab.WithOtOpcUaSignalRBridges();
/// }
/// </code>
/// </summary>
/// <param name="builder">The Akka configuration builder.</param>
/// <returns>The same builder, for chaining.</returns>
public static AkkaConfigurationBuilder WithOtOpcUaSignalRBridges(this AkkaConfigurationBuilder builder)
{
builder.WithActors((system, registry, resolver) =>
{
var fleetHub = resolver.GetService<IHubContext<FleetStatusHub>>();
var fleetBridge = system.ActorOf(FleetStatusSignalRBridge.Props(fleetHub), FleetStatusSignalRBridgeName);
registry.Register<FleetStatusSignalRBridgeKey>(fleetBridge);
var alertHub = resolver.GetService<IHubContext<AlertHub>>();
var alertBroadcaster = resolver.GetService<IInProcessBroadcaster<Commons.Messages.Alerts.AlarmTransitionEvent>>();
var alertBridge = system.ActorOf(AlertSignalRBridge.Props(alertHub, alertBroadcaster), AlertSignalRBridgeName);
registry.Register<AlertSignalRBridgeKey>(alertBridge);
var scriptLogHub = resolver.GetService<IHubContext<ScriptLogHub>>();
var scriptLogBroadcaster = resolver.GetService<IInProcessBroadcaster<Commons.Messages.Logging.ScriptLogEntry>>();
var scriptLogBridge = system.ActorOf(ScriptLogSignalRBridge.Props(scriptLogHub, scriptLogBroadcaster), ScriptLogSignalRBridgeName);
registry.Register<ScriptLogSignalRBridgeKey>(scriptLogBridge);
var driverStatusHub = resolver.GetService<IHubContext<DriverStatusHub>>();
var driverStatusStore = resolver.GetService<IDriverStatusSnapshotStore>();
var driverStatusBridge = system.ActorOf(DriverStatusSignalRBridge.Props(driverStatusHub, driverStatusStore), DriverStatusSignalRBridgeName);
registry.Register<DriverStatusSignalRBridgeKey>(driverStatusBridge);
});
return builder;
}
}
/// <summary>Marker keys for <see cref="ActorRegistry"/> lookup of the SignalR bridge actors.</summary>
public sealed class FleetStatusSignalRBridgeKey { }
public sealed class AlertSignalRBridgeKey { }
public sealed class ScriptLogSignalRBridgeKey { }
public sealed class DriverStatusSignalRBridgeKey { }