using Akka.Cluster.Hosting; using Akka.Hosting; using Akka.Remote.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using ZB.MOM.WW.OtOpcUa.Commons.Interfaces; namespace ZB.MOM.WW.OtOpcUa.Cluster; public static class ServiceCollectionExtensions { /// /// Binds and registers . The /// actual ActorSystem + cluster bootstrap is layered on inside the host's AddAkka(...) /// configurator via — keeping the entire Akka graph /// under Akka.Hosting's management so cluster singletons land on the same ActorSystem. /// /// The service collection to configure. /// The application configuration containing cluster options. public static IServiceCollection AddOtOpcUaCluster(this IServiceCollection services, IConfiguration configuration) { services.AddOptions() .Bind(configuration.GetSection(AkkaClusterOptions.SectionName)); services.AddSingleton(); return services; } /// /// Configures the Akka.Hosting builder with the embedded OtOpcUa HOCON (split-brain resolver, /// pinned dispatcher, failure detector tuning) + remote endpoint + cluster bootstrap derived /// from . /// /// Wire from Program.cs: /// /// services.AddAkka("otopcua", (ab, sp) => /// { /// ab.WithOtOpcUaClusterBootstrap(sp); /// if (hasAdmin) ab.WithOtOpcUaControlPlaneSingletons(); /// if (hasDriver) ab.WithOtOpcUaRuntimeActors(); /// }); /// /// /// The Akka configuration builder to configure. /// The service provider for resolving cluster options. public static AkkaConfigurationBuilder WithOtOpcUaClusterBootstrap( this AkkaConfigurationBuilder builder, IServiceProvider serviceProvider) { var options = serviceProvider.GetRequiredService>().Value; builder.AddHocon(HoconLoader.LoadBaseConfig(), HoconAddMode.Append); builder.WithRemoting(new RemoteOptions { HostName = options.Hostname, Port = options.Port, PublicHostName = options.PublicHostname, }); builder.WithClustering(new ClusterOptions { SeedNodes = options.SeedNodes, Roles = options.Roles, }); return builder; } }