Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,313 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using NLog.Web;
using RobotNet10.CANOpen;
using RobotNet10.CustomConfiguration.Extensions;
using RobotNet10.MapManager.Extensions;
using RobotNet10.NavigationTune.Data;
using RobotNet10.NavigationTune.Execution;
using RobotNet10.NavigationTune.Extensions;
using RobotNet10.NavigationTune.Hubs;
using RobotNet10.RobotApp.Client;
using RobotNet10.RobotApp.Components;
using RobotNet10.RobotApp.Components.Account;
using RobotNet10.RobotApp.Data;
using RobotNet10.RobotApp.Detection;
using RobotNet10.RobotApp.Devices;
using RobotNet10.RobotApp.Hubs;
using RobotNet10.RobotApp.Interfaces;
using RobotNet10.RobotApp.MarkerDetection;
using RobotNet10.RobotApp.Modules;
using RobotNet10.RobotApp.Motion;
using RobotNet10.RobotApp.Services;
using RobotNet10.RobotApp.Services.Navigation;
using RobotNet10.RobotApp.Services.Robot;
using RobotNet10.RobotApp.Services.NavigationMonitor;
using RobotNet10.RobotApp.Services.Simulation;
using RobotNet10.RobotApp.Navigation;
using RobotNet10.RobotApp.TF3;
using RobotNet10.RobotApp.Xloc;
using RobotNet10.ScriptEngine;
using RobotNet10.ScriptEngine.Helpers;
using Microsoft.AspNetCore.Http.Features;
// Print Ceres library information at startup
//CeresLibraryInfo.PrintLibraryInfo();
var builder = WebApplication.CreateBuilder(args);
var robotConfig = builder.Configuration["RobotConfig"];
if(string.IsNullOrEmpty(robotConfig) || !File.Exists(robotConfig))
{
throw new Exception("RobotConfig not found");
}
builder.Configuration.AddJsonFile(robotConfig);
// Add NLog
//builder.Logging.ClearProviders();
//builder.Logging.SetMinimumLevel(LogLevel.Trace);
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.UseSqlite(connectionString, b => b.MigrationsAssembly("RobotNet10.RobotApp").UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)));
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>();
builder.Services.AddMudServices();
builder.Services.AddNavigationMenu();
builder.Services.Configure<FormOptions>(o =>
{
o.MultipartBodyLengthLimit = 200L * 1024 * 1024;
});
builder.WebHost.ConfigureKestrel(o =>
{
o.Limits.MaxRequestBodySize = 200L * 1024 * 1024;
});
// Add HttpClient for Blazor Server components (server-side calls into the same Kestrel app).
// BaseAddress must be loopback: using the browser's WAN/MOXA host from this machine often fails (hairpin NAT).
builder.Services.AddScoped(sp =>
{
var navigationManager = sp.GetRequiredService<Microsoft.AspNetCore.Components.NavigationManager>();
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true
};
var publicBase = new Uri(navigationManager.BaseUri);
var loopbackBase = new UriBuilder(publicBase) { Host = "127.0.0.1" }.Uri;
return new HttpClient(handler) { BaseAddress = loopbackBase };
});
builder.Services.AddControllers();
builder.Services.AddSignalR();
builder.Services.AddSingleton<DeviceHubContext>();
builder.Services.AddSingleton<OdometryHubContext>();
var scriptConnectionString = builder.Configuration.GetConnectionString("ScriptEngineConnection") ?? throw new InvalidOperationException("Connection string 'ScriptEngineConnection' not found.");
builder.Services.AddScriptEngine<ScriptEngineResource>(options => options.UseSqlite(scriptConnectionString, b => b.MigrationsAssembly("RobotNet10.RobotApp")));
var mapConnectionString = builder.Configuration.GetConnectionString("MapEditorConnection") ?? throw new InvalidOperationException("Connection string 'MapEditorConnection' not found.");
builder.Services.AddMapManager(builder.Configuration, options => options.UseSqlite(mapConnectionString, b => b.MigrationsAssembly("RobotNet10.RobotApp").UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)), "LayoutImage");
// var navTuneConnectionString = builder.Configuration.GetConnectionString("NavigationTuneConnection") ?? throw new InvalidOperationException("Connection string 'NavigationTuneConnection' not found.");
// builder.Services.AddNavigationTuningWithRobot(options => options.UseSqlite(navTuneConnectionString, b => b.MigrationsAssembly("RobotNet10.RobotApp").UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)));
// Register adapters for Navigation Tuning
// builder.Services.AddScoped<ILocalizationProvider>(sp =>
// {
// var localization = sp.GetRequiredService<RobotNet10.RobotApp.Interfaces.ILocalization>();
// return new LocalizationAdapter(
// () => localization.X,
// () => localization.Y,
// () => localization.Theta
// );
// });
builder.Services.AddScoped<IVelocityProvider>(sp =>
{
var velocityController = sp.GetRequiredService<IVelocityController>();
return new VelocityControllerAdapter(
() => velocityController.RawVelocity,
(linear, angular) => velocityController.SetVelocity(linear, angular),
() => velocityController.GetModelConfidence(),
acc => velocityController.SetAcceleration(acc),
dec => velocityController.SetDeceleration(dec)
);
});
// Add Custom Configuration
builder.Services.AddCustomConfiguration(builder.Configuration, "StorageConfig");
builder.Services.AddCanOpenManager();
builder.Services.AddSingleton<DeviceProvider>();
builder.Services.AddSingleton<IDeviceProvider>(sp => sp.GetRequiredService<DeviceProvider>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<DeviceProvider>());
builder.Services.AddSingleton(typeof(RobotNet10.RobotApp.Services.Logger<>));
builder.Services.AddRobotSimulation();
// Core C API services (TF3 -> XLOC -> Navigation)
builder.Services.AddSingleton<TF3BufferManager>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<TF3BufferManager>());
// XLOC integration is required by RobotLocalization.
builder.Services.AddSingleton<XlocIntegrationService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<XlocIntegrationService>());
builder.Services.AddHostedService<XlocAutoLocalizationHostedService>();
builder.Services.AddSingleton<NavigationIntegrationService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<NavigationIntegrationService>());
builder.Services.AddRobot();
// Motion services
builder.Services.AddSingleton<DifferentialDrive>();
builder.Services.AddSingleton<IInverseKinematics>(sp => sp.GetRequiredService<DifferentialDrive>());
builder.Services.AddSingleton<IOdometryEstimator>(sp => sp.GetRequiredService<DifferentialDrive>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<DifferentialDrive>());
builder.Services.AddSingleton<OdometryService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<OdometryService>());
builder.Services.AddHostedService<OdometryBroadcastService>();
// Rotation Module services
// builder.Services.AddSingleton<RotationModuleService>();
// builder.Services.AddSingleton<IRotationModule>(sp => sp.GetRequiredService<RotationModuleService>());
// builder.Services.AddHostedService(sp => sp.GetRequiredService<RotationModuleService>());
// Lift Module: bật khi Modules:LiftModule:Enable = true
var liftModuleEnabled = builder.Configuration.GetValue<bool>("Modules:LiftModule:Enable");
if (liftModuleEnabled)
{
builder.Services.AddSingleton<LiftModuleService>();
builder.Services.AddSingleton<ILiftModule>(sp => sp.GetRequiredService<LiftModuleService>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<LiftModuleService>());
}
else
{
builder.Services.AddSingleton<ILiftModule, NoOpLiftModule>();
}
// Rotation Module (vẫn dùng NoOp khi chưa bật)
builder.Services.AddSingleton<IRotationModule, NoOpRotationModule>();
builder.Services.AddSingleton<ManualControlService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<ManualControlService>());
builder.Services.AddSingleton<PS5ControllerService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<PS5ControllerService>());
// builder.Services.AddCartographer(builder.Configuration.GetSection("Cartographer"));
// builder.Services.AddSingleton<IMarkerDetector, MarkerDetector>();
// Navigation Monitor (Telemetry & Safety)
// builder.Services.AddSingleton<NavigationMonitorService>();
// builder.Services.AddHostedService(sp => sp.GetRequiredService<NavigationMonitorService>());
#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 robotAppSrc = typeof(RobotNet10.RobotApp.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 robotAppDest = Path.Combine(dllPath, "RobotNet10.RobotApp.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(robotAppSrc, robotAppDest, true);
}
#endif
var app = builder.Build();
await app.Services.SeedApplicationDbAsync();
await app.Services.SeedScriptEngineDbAsync();
await app.Services.SeedMapManagerAsync();
var serviceProbe = app.Services.GetService<IServiceProviderIsService>();
if (serviceProbe?.IsService(typeof(TuningDbContext)) == true)
{
await app.Services.SeedTuningDbAsync();
}
// 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.MapControllers();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(RobotNet10.RobotApp.Client._Imports).Assembly);
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.MapScriptEngineHubs();
app.MapHub<DeviceHub>("/hubs/devices");
app.MapHub<BatteryHub>("/hubs/devices/battery");
app.MapHub<InertialMeasurementUnitHub>("/hubs/devices/inertialmeasurementunit");
app.MapHub<LidarHub>("/hubs/devices/lidar");
app.MapHub<ModbusTcpHub>("/hubs/devices/modbustcp");
// app.MapHub<RfHandleHub>("/hubs/devices/rfhandle");
app.MapHub<CameraQrHub>("/hubs/devices/cameraqr");
app.MapHub<CiA402ServoHub>("/hubs/devices/cia402servo");
app.MapHub<MotionHub>("/hubs/motion");
app.MapHub<OdometryHub>("/hubs/odometry");
app.MapHub<XlocPoseHub>("/hubs/xloc/pose");
// app.MapHub<SLAMHub>("/hubs/slam");
// app.MapHub<TuningHub>("/tuninghub");
app.MapHub<PlcControllerHub>("/hubs/plc/controller");
// app.MapHub<MarkerDetectorHub>("hubs/marker-detect");
// app.MapHub<NavigationMonitorHub>("/hubs/motion/navigation-monitor");
// app.MapHub<RobotNet10.RobotApp.Hubs.XlocHub>("/hubs/xloc");
app.MapMotionApiEndpoints();
app.MapPlcApiEndpoints();
app.MapXlocApiEndpoints();
app.MapNavigationApiEndpoints();
app.MapTf3ApiEndpoints();
app.MapMarkerDetectionApiEndpoints();
app.Run();