using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using MudBlazor.Services; using NLog.Web; using RobotNet10.CustomConfiguration.Extensions; using RobotNet10.FleetManager.Client; using RobotNet10.FleetManager.Components; using RobotNet10.FleetManager.Components.Account; using RobotNet10.FleetManager.Data; using RobotNet10.FleetManager.Events; using RobotNet10.FleetManager.Hubs; using RobotNet10.FleetManager.Script; using RobotNet10.FleetManager.Services; using RobotNet10.FleetManager.Services.ConfigManager; using RobotNet10.FleetManager.Services.OpenACS; using RobotNet10.FleetManager.Services.RobotConnections; using RobotNet10.FleetManager.Services.RobotManager; using RobotNet10.FleetManager.Services.Script; using RobotNet10.FleetManager.Services.TrafficControl; using RobotNet10.FleetManager.Services.TrafficControl.ACS; using RobotNet10.FleetManager.Services.TrafficControl.Services; using RobotNet10.GlobalPathPlanner; using RobotNet10.MapManager.Extensions; using RobotNet10.ScriptEngine; using RobotNet10.ScriptEngine.Helpers; using RobotNet10.StorageManager; using System.Diagnostics; using System.Globalization; CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); var builder = WebApplication.CreateBuilder(args); builder.Host.UseNLog(); // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents() .AddAuthenticationStateSerialization(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddAuthentication() .AddBearerToken(IdentityConstants.BearerScheme); var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddIdentity(options => { options.SignIn.RequireConfirmedAccount = true; options.Lockout.AllowedForNewUsers = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequireLowercase = false; options.Password.RequireDigit = false; }) .AddEntityFrameworkStores() .AddSignInManager() .AddDefaultTokenProviders(); builder.Services.AddSingleton, IdentityNoOpEmailSender>(); var mapConnectionString = builder.Configuration.GetConnectionString("MapEditorConnection") ?? throw new InvalidOperationException("Connection string 'MapEditorConnection' not found."); builder.Services.AddMapManager(builder.Configuration, options => options.UseSqlServer(mapConnectionString, b => b.MigrationsAssembly("RobotNet10.FleetManager").UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)), "LayoutImage"); builder.Services.AddMudServices(); builder.Services.AddNavigationMenu(builder.Configuration["APP_VERSION"] ?? "dev"); builder.Services.AddControllers(); // Register VDA5050 Event Bus (must be registered before services that use it) builder.Services.AddSingleton(); // Register StorageConfig for RobotModelImages builder.Services.Configure("RobotModelImages", options => { builder.Configuration.GetSection("RobotModelImage").Bind(options); }); // Register Robot Management Services (with event bus for cache invalidation) builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // Register SignalR Hub Context as BackgroundService (broadcasts at 1Hz) builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Register Logger service (factory pattern) builder.Services.AddSingleton(typeof(RobotNet10.FleetManager.Services.Logger<>)); // Register ConfigManager builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // Register OpenACS Services builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Register ACS Order Control builder.Services.AddSingleton(); // Note: IRobotEventBus is registered above with Robot Management Services // Register VDA5050 RobotConnections Service builder.Services.AddSingleton(); // Register VDA5050 RobotManager Service builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => sp.GetRequiredService()); builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Register Path Planner Factory builder.Services.AddSingleton(); // Register TrafficControl Sub-Services (order matters due to dependencies) // 1. RouteStorageService - No dependencies builder.Services.AddSingleton(); // 2. PriorityService - Only needs Logger builder.Services.AddSingleton(); // 3. RobotInfoService - Needs IServiceScopeFactory, IRobotManagerService, IRobotEventBus builder.Services.AddSingleton(); // 4. EdgeReservationService - Needs INodeService, IEdgeService, IServiceScopeFactory, IRobotInfoService builder.Services.AddSingleton(); // 5. OrderUpdateService - Needs IRobotManagerService builder.Services.AddSingleton(); // 6. ConflictDetectionService - Needs many services builder.Services.AddSingleton(); // 7. ConflictResolutionService - Needs many services builder.Services.AddSingleton(); // 8. BaseHorizonManagementService - Needs many services builder.Services.AddSingleton(); // 9. RoutePlanningService - Needs many services builder.Services.AddSingleton(); // Register TrafficControl Service (Orchestrator) - Must be registered after all sub-services builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => sp.GetRequiredService()); //builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Add SignalR builder.Services.AddSignalR(); // Add Custom Configuration builder.Services.AddCustomConfiguration(builder.Configuration, "StorageConfig"); var scriptConnectionString = builder.Configuration.GetConnectionString("ScriptEngineConnection") ?? throw new InvalidOperationException("Connection string 'ScriptEngineConnection' not found."); builder.Services.AddScriptEngine(options => options.UseSqlServer(scriptConnectionString, b => b.MigrationsAssembly("RobotNet10.FleetManager"))); // Add Script Services builder.Services.AddSingleton(); builder.Services.AddSingleton(); #if DEBUG var dllPath = builder.Configuration["ScriptEngine:RuntimeDllFolder"] ?? "dlls"; if (!Directory.Exists(dllPath)) { Directory.CreateDirectory(dllPath); } var netCoreDir = Path.GetDirectoryName(typeof(object).Assembly.Location); if (!string.IsNullOrEmpty(netCoreDir)) { var coreSrc = Path.Combine(netCoreDir, "System.Private.CoreLib.dll"); var runtimeSrc = Path.Combine(netCoreDir, "System.Runtime.dll"); var linqSrc = Path.Combine(netCoreDir, "System.Linq.Expressions.dll"); var scriptSrc = typeof(RobotNet10.Script.ILogger).Assembly.Location; var fleetSrc = typeof(RobotNet10.FleetManager.Script.IRobot).Assembly.Location; var coreDest = Path.Combine(dllPath, "System.Private.CoreLib.dll"); var runtimeDest = Path.Combine(dllPath, "System.Runtime.dll"); var linqDest = Path.Combine(dllPath, "System.Linq.Expressions.dll"); var scriptDest = Path.Combine(dllPath, "RobotNet10.Script.dll"); var fleetDest = Path.Combine(dllPath, "RobotNet10.FleetManager.Script.dll"); if (!File.Exists(coreDest)) File.Copy(coreSrc, coreDest); if (!File.Exists(runtimeDest)) File.Copy(runtimeSrc, runtimeDest); if (!File.Exists(linqDest)) File.Copy(linqSrc, linqDest); File.Copy(scriptSrc, scriptDest, true); File.Copy(fleetSrc, fleetDest, true); } #endif var app = builder.Build(); await app.Services.SeedApplicationDbAsync(); await app.Services.SeedScriptEngineDbAsync(); await app.Services.SeedMapManagerAsync(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error", createScopeForErrors: true); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true); app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapStaticAssets(); app.MapRazorComponents() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(RobotNet10.FleetManager.Client._Imports).Assembly); // Map API controllers app.MapControllers(); // Map SignalR Hubs app.MapHub("/hubs/robot-state"); // Add additional endpoints required by the Identity /Account Razor components. app.MapAdditionalIdentityEndpoints(); app.MapScriptEngineHubs(); app.Run();