using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; namespace SHH.CameraSdk; /// /// A 方案:标准控制台结构 (框架搭建版:支持动态端口与依赖注入) /// public class Program { [STAThread] public static async Task Main(string[] args) { // ============================================================================== // 1. 身份识别与端口计算 // ============================================================================== // 默认 1 号进程 int processId = 1; // 如果命令行传了参数 (例如: dotnet run 2),则覆盖为 2 号进程 if (args.Length > 0 && int.TryParse(args[0], out int pid)) { processId = pid; } // 端口计算公式:5000 + (ID - 1) // ID=1 -> 5000 // ID=2 -> 5001 int port = 5000 + (processId - 1); Console.Title = $"SHH Gateway - Instance #{processId} (Port: {port})"; Console.WriteLine($"[System] 正在初始化实例 #{processId}..."); // ============================================================================== // 2. 基础设施初始化 // ============================================================================== InitHardwareEnv(); // B. 【核心】创建独立的文件存储服务 (此时只建立目录,不进行具体读写) IStorageService storageService = new FileStorageService(processId); // 核心设备管理器 // 注意:暂时保持无参构造,后续我们在改造 CameraManager 时再注入 storageService using var cameraManager = new CameraManager(storageService); // 动态窗口管理器 var displayManager = new DisplayWindowManager(cameraManager); // ============================================================================== // 3. 启动 Web 监控与诊断服务 (注入服务与端口) // ============================================================================== var app = await StartWebMonitoring(cameraManager, displayManager, storageService, port); // 启动网络哨兵 var sentinel = new ConnectivitySentinel(cameraManager); // ============================================================================== // 4. 业务编排 // ============================================================================== // 【关键修复 1】先 StartAsync,让它先从文件把 999 号设备读进内存 await cameraManager.StartAsync(); // 【关键修复 2】文件加载完后,再决定要不要加默认设备 await ConfigureBusinessLogic(cameraManager); // ============================================================================== // 5. 启动引擎与交互 // ============================================================================== Console.WriteLine("\n[系统] 正在启动全局管理引擎..."); Console.WriteLine($">> 系统就绪。Web 管理地址: http://localhost:{port}"); Console.WriteLine($">> 数据存储路径: App_Data/Process_{processId}/"); Console.WriteLine(">> 按 'S' 键退出..."); // 阻塞主线程 while (Console.ReadKey(true).Key != ConsoleKey.S) { Thread.Sleep(100); } Console.WriteLine("[系统] 正在停机..."); await app.StopAsync(); } // ============================================================================== // Static Methods // ============================================================================== static void InitHardwareEnv() { Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ==="); Console.WriteLine("[硬件] 海康驱动预热中..."); HikNativeMethods.NET_DVR_Init(); HikSdkManager.ForceWarmUp(); Console.WriteLine("[硬件] 预热完成。"); } static async Task StartWebMonitoring( CameraManager manager, DisplayWindowManager displayMgr, IStorageService storage, // 接收存储服务实例 int port) // 接收动态端口 { var builder = WebApplication.CreateBuilder(); // 注册缩放集群服务 (建议 Worker 数 = CPU 核心数,这里设为 4) var scaleService = new ImageScaleCluster(4); // 2. [核心] 将缩放服务“挂载”到全局路由上 // 从此刻起,所有驱动层的帧都会先流经 scaleService GlobalPipelineRouter.SetProcessor(scaleService); // 3. 注册到 DI 容器 (以便 Controller 或其他服务可以管理它,例如动态调整并行度) builder.Services.AddSingleton(scaleService); // 1. 配置 CORS builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); }); }); //// 2. 日志降噪 //builder.Logging.SetMinimumLevel(LogLevel.Warning); //builder.Logging.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.Warning); // 3. 【核心】依赖注入注册 // 将 storageService 注册为单例,这样 UserActionFilter 和 MonitorController 就能拿到它了 builder.Services.AddSingleton(storage); builder.Services.AddSingleton(manager); builder.Services.AddSingleton(displayMgr); // 显式注册过滤器 (这是防止 500 错误的关键) builder.Services.AddScoped(); builder.Services.AddControllers(options => { // 注册全局操作日志过滤器 options.Filters.Add(); }); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{processIdFromPort(port)}", Version = "v1" }); }); var webApp = builder.Build(); // 4. 配置中间件 webApp.UseSwagger(); webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1")); webApp.UseCors("AllowAll"); webApp.MapControllers(); // 5. 启动监听 (使用动态端口) _ = webApp.RunAsync($"http://0.0.0.0:{port}"); Console.WriteLine($"[Web] 监控API已启动: http://localhost:{port}"); return webApp; } // 辅助方法:从端口反推 ID,仅用于 Swagger 标题显示 static int processIdFromPort(int port) => port - 5000 + 1; static async Task ConfigureBusinessLogic(CameraManager manager) { try { // 1. 添加测试设备 var config = new VideoSourceConfig { Id = 101, Brand = DeviceBrand.HikVision, IpAddress = "192.168.5.9", Port = 8000, Username = "admin", Password = "RRYFOA", StreamType = 0 }; manager.AddDevice(config); var config2 = new VideoSourceConfig { Id = 102, Brand = DeviceBrand.HikVision, IpAddress = "172.16.41.20", Port = 8000, Username = "admin", Password = "abcd1234", StreamType = 0 }; manager.AddDevice(config2); } catch { } } }