using Akka.Actor; using Akka.Cluster.Hosting; using Akka.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using ZB.MOM.WW.OtOpcUa.Configuration; using ZB.MOM.WW.OtOpcUa.ControlPlane.AdminOperations; using ZB.MOM.WW.OtOpcUa.ControlPlane.Audit; using ZB.MOM.WW.OtOpcUa.ControlPlane.Coordinators; using ZB.MOM.WW.OtOpcUa.ControlPlane.Fleet; using ZB.MOM.WW.OtOpcUa.ControlPlane.Redundancy; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; namespace ZB.MOM.WW.OtOpcUa.ControlPlane; public static class ServiceCollectionExtensions { public const string AdminRole = "admin"; public const string ConfigPublishSingletonName = "config-publish"; public const string AdminOperationsSingletonName = "admin-operations"; public const string AuditWriterSingletonName = "audit-writer"; public const string FleetStatusSingletonName = "fleet-status"; public const string RedundancyStateSingletonName = "redundancy-state"; /// /// Registers all five admin-role cluster singletons + their proxies on the AkkaConfigurationBuilder. /// Must be called against the same builder used by AkkaHostedService so the singletons /// share the host's ActorSystem. /// /// The Akka configuration builder. /// The builder for fluent chaining. /// /// Wire from the fused Host's Program.cs: /// /// builder.Services.AddAkka("otopcua", (ab, sp) => /// { /// ab.WithRemoting(/* ... */).WithClustering(/* ... */); /// ab.WithOtOpcUaControlPlaneSingletons(); /// }); /// /// public static AkkaConfigurationBuilder WithOtOpcUaControlPlaneSingletons(this AkkaConfigurationBuilder builder) { var singletonOptions = new ClusterSingletonOptions { Role = AdminRole }; var proxyOptions = new ClusterSingletonOptions { Role = AdminRole }; builder.WithSingleton( ConfigPublishSingletonName, (system, registry, resolver) => { var dbFactory = resolver.GetService>(); return ConfigPublishCoordinator.Props(dbFactory); }, singletonOptions); builder.WithSingleton( AdminOperationsSingletonName, (system, registry, resolver) => { var dbFactory = resolver.GetService>(); var coordinator = registry.Get(); var probes = resolver.GetService>() ?? Enumerable.Empty(); return AdminOperationsActor.Props(dbFactory, coordinator, probes); }, singletonOptions); builder.WithSingleton( AuditWriterSingletonName, (system, registry, resolver) => { var dbFactory = resolver.GetService>(); return AuditWriterActor.Props(dbFactory); }, singletonOptions); builder.WithSingleton( FleetStatusSingletonName, (system, registry, resolver) => FleetStatusBroadcaster.Props(), singletonOptions); builder.WithSingleton( RedundancyStateSingletonName, (system, registry, resolver) => RedundancyStateActor.Props(), singletonOptions); return builder; } } /// Marker key types used by Akka.Hosting to resolve singletons from the registry. public sealed class ConfigPublishCoordinatorKey { } public sealed class AdminOperationsActorKey { } public sealed class AuditWriterActorKey { } public sealed class FleetStatusBroadcasterKey { } public sealed class RedundancyStateActorKey { }