using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; using SHH.CameraSdk; namespace SHH.CameraService; public class Program { public static async Task Main(string[] args) { // 1. 理由:缓冲时间 10 秒, 供附加调试工具使用 for (var i = 1; i < 10; i++) Thread.Sleep(1000); // ============================================================= // 2. 基础环境与配置 (理由:明确身份 ID 和 监听端口) // ============================================================= var config = ServiceConfig.BuildFromArgs(args); // 硬件预热 (理由:确保底层驱动库在 Web 容器启动前完全就绪) HikNativeMethods.NET_DVR_Init(); HikSdkManager.ForceWarmUp(); var builder = WebApplication.CreateBuilder(args); // ============================================================= // 3. 依赖注入注册 (DI) // ============================================================= builder.Services.AddSingleton(config); // 注册缩放与增亮业务(不注册则不实现) builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => new ImageScaleCluster(4, sp.GetRequiredService())); builder.Services.AddSingleton(sp => new ImageEnhanceCluster(4, sp.GetRequiredService())); builder.Services.AddHostedService(); // 接入 SDK 核心逻辑 builder.Services.AddCameraSdk(config.NumericId); // 注册后台引擎 (理由:托管长周期的硬件状态监控) builder.Services.AddHostedService(); builder.Services.AddHostedService(); // 配置 Web 相关的服务 ConfigureWebServices(builder, config); // 配置进程守护 builder.Services.AddHostedService(); // ============================================================= // 4. 接受启动传参, 并支持将视频进行网络广播 // ============================================================= // 1. 读取配置创建 targets (可以是 1 个,也可以是 10 个) var netTargets = new List(); if (config.VideoEndpoints != null) { foreach(var cfgVideo in config.VideoEndpoints) { netTargets.Add(new StreamTarget(new PushTargetConfig { Name = cfgVideo.Description, Endpoint = cfgVideo.Uri, QueueCapacity = 10, })); } } // 2. 注册 Targets (供采集者用) builder.Services.AddSingleton>(netTargets); // 3. 注册采集者 (它会注入上面的 targets,进行编码和分发) builder.Services.AddHostedService(); // 5. 为每个 Target 注册一个独立的发送者 foreach (var target in netTargets) { builder.Services.AddHostedService(sp => new NetMqSenderWorker(target)); } // ============================================================= // 5. 命令管道配置 // ============================================================= // 2. 注册管道管理器 builder.Services.AddSingleton(); // 负责连接 Dashboard,注册身份,接收重启/控制指令 builder.Services.AddHostedService(); // 1. 注册分发器 builder.Services.AddSingleton(); // 2. 注册具体的指令处理器 (每写一个新的 Handler,就在这里注册一下,或者用反射批量注册) builder.Services.AddSingleton(); builder.Services.AddSingleton(); // ============================================================= // 6. 构建与管道配置 // ============================================================= var app = builder.Build(); // 核心修复:同步点火逻辑 (理由:在 Web 开启前完成设备池的初步构建) await StartBusinessLogic(app); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", $"SHH Gateway #{config.AppId}"); }); app.MapGet("/", () => $"SHH Gateway {config.AppId} is running."); app.UseCors("AllowAll"); // 理由:正式映射控制器路由 app.MapControllers(); // ============================================================= // 5. 正式启动 // ============================================================= await app.RunAsync($"http://0.0.0.0:{config.BasePort}"); } /// /// 对齐业务启动:激活单例并启动相机管理器 /// static async Task StartBusinessLogic(WebApplication app) { var manager = app.Services.GetRequiredService(); // 激活哨兵逻辑 (理由:显式 Get 触发单例构造,否则不工作) _ = app.Services.GetRequiredService(); // 启动相机任务加载 await manager.StartAsync(); Console.WriteLine("[System] 核心业务逻辑已激活。"); } /// /// 注册 Web API 支持 /// static void ConfigureWebServices(WebApplicationBuilder builder, ServiceConfig cfg) { builder.Services.AddCors(options => { options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()); }); // ★★★★★ 补全点:跨项目控制器加载 ★★★★★ // 理由:Controller 定义在 SDK 项目中,必须通过 AddApplicationPart 显式挂载 builder.Services.AddControllers(options => { options.Filters.Add(); }) .AddApplicationPart(typeof(CamerasController).Assembly) // 必备:加载相机控制接口 .AddApplicationPart(typeof(MonitorController).Assembly); // 必备:加载监控接口 builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{cfg.AppId}", Version = "v1" }); }); } }