diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..6f56dfa --- /dev/null +++ b/src/Server/ZB.MOM.WW.OtOpcUa.ControlPlane/ServiceCollectionExtensions.cs @@ -0,0 +1,91 @@ +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; + +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. + /// + /// 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(); + return AdminOperationsActor.Props(dbFactory, coordinator); + }, + 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 { }