Files
BQP/srcs/RobotNet10/FleetManager/RobotNet10.FleetManager/Program.cs
2026-07-13 09:25:40 +07:00

239 lines
10 KiB
C#

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<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(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<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, 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<IRobotEventBus, RobotEventBus>();
// Register StorageConfig for RobotModelImages
builder.Services.Configure<StorageConfig>("RobotModelImages", options =>
{
builder.Configuration.GetSection("RobotModelImage").Bind(options);
});
// Register Robot Management Services (with event bus for cache invalidation)
builder.Services.AddScoped<IRobotModelService, RobotModelService>();
builder.Services.AddScoped<IRobotService, RobotService>();
builder.Services.AddScoped<IRobotModelImageStorageService, RobotModelImageStorageService>();
builder.Services.AddScoped<IRobotModelMapService, RobotModelMapService>();
// Register SignalR Hub Context as BackgroundService (broadcasts at 1Hz)
builder.Services.AddSingleton<RobotStateHubContext>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<RobotStateHubContext>());
// Register Logger service (factory pattern)
builder.Services.AddSingleton(typeof(RobotNet10.FleetManager.Services.Logger<>));
// Register ConfigManager
builder.Services.AddSingleton<IConnectionConfig, ConnectionConfig>();
builder.Services.AddSingleton<ITrafficConfig, TrafficConfig>();
builder.Services.AddSingleton<IACSTrafficConfig, ACSTrafficConfig>();
// Register OpenACS Services
builder.Services.AddSingleton<TrafficACS>();
builder.Services.AddSingleton<OpenACSPublisher>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<OpenACSPublisher>());
// Register ACS Order Control
builder.Services.AddSingleton<IOrderControlService, OrderACSControl>();
// Note: IRobotEventBus is registered above with Robot Management Services
// Register VDA5050 RobotConnections Service
builder.Services.AddSingleton<IRobotConnectionsService, RobotConnectionsService>();
// Register VDA5050 RobotManager Service
builder.Services.AddSingleton<RobotManagerService>();
builder.Services.AddSingleton<IRobotManagerService>(sp => sp.GetRequiredService<RobotManagerService>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<RobotManagerService>());
// Register Path Planner Factory
builder.Services.AddSingleton<IPathPlannerFactory, PathPlannerFactory>();
// Register TrafficControl Sub-Services (order matters due to dependencies)
// 1. RouteStorageService - No dependencies
builder.Services.AddSingleton<IRouteStorageService, RouteStorageService>();
// 2. PriorityService - Only needs Logger
builder.Services.AddSingleton<IPriorityService, PriorityService>();
// 3. RobotInfoService - Needs IServiceScopeFactory, IRobotManagerService, IRobotEventBus
builder.Services.AddSingleton<IRobotInfoService, RobotInfoService>();
// 4. EdgeReservationService - Needs INodeService, IEdgeService, IServiceScopeFactory, IRobotInfoService
builder.Services.AddSingleton<IEdgeReservationService, EdgeReservationService>();
// 5. OrderUpdateService - Needs IRobotManagerService
builder.Services.AddSingleton<IOrderUpdateService, OrderUpdateService>();
// 6. ConflictDetectionService - Needs many services
builder.Services.AddSingleton<IConflictDetectionService, ConflictDetectionService>();
// 7. ConflictResolutionService - Needs many services
builder.Services.AddSingleton<IConflictResolutionService, ConflictResolutionService>();
// 8. BaseHorizonManagementService - Needs many services
builder.Services.AddSingleton<IBaseHorizonManagementService, BaseHorizonManagementService>();
// 9. RoutePlanningService - Needs many services
builder.Services.AddSingleton<IRoutePlanningService, RoutePlanningService>();
// Register TrafficControl Service (Orchestrator) - Must be registered after all sub-services
builder.Services.AddSingleton<TrafficControlService>();
builder.Services.AddSingleton<ITrafficControlService , TrafficControlService>(sp => sp.GetRequiredService<TrafficControlService>());
//builder.Services.AddHostedService(sp => sp.GetRequiredService<TrafficControlService>());
// 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<ScriptEngineResource>(options => options.UseSqlServer(scriptConnectionString, b => b.MigrationsAssembly("RobotNet10.FleetManager")));
// Add Script Services
builder.Services.AddSingleton<IRobotManager, ScriptRobotmanager>();
builder.Services.AddSingleton<ILayoutManager, ScriptLayoutManager>();
#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<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(RobotNet10.FleetManager.Client._Imports).Assembly);
// Map API controllers
app.MapControllers();
// Map SignalR Hubs
app.MapHub<RobotStateHub>("/hubs/robot-state");
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.MapScriptEngineHubs();
app.Run();