using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using SHH.CameraSdk; // 引用你的业务核心 using SHH.NetMQ; namespace SHH.CameraService; public class Program { public static async Task Main(string[] args) { // ============================================================= // 1. 端口与身份计算 // ============================================================= int processId = 1; if (args.Length > 0 && int.TryParse(args[0], out int pid)) processId = pid; int port = 5000 + (processId - 1); Console.Title = $"SHH Gateway - Instance #{processId} (Port: {port})"; // ============================================================= // 2. 硬件环境预热 (【重要】必须在一切开始前调用) // ============================================================= InitHardwareEnv(); // ============================================================= // 3. 构建 WebHost // ============================================================= var builder = WebApplication.CreateBuilder(args); // --- A. 注册 ZeroMQ 组件 (传输层) --- string zmqBind = $"tcp://*:{5555 + (processId - 1)}"; string zmqTarget = "tcp://127.0.0.1:6000"; builder.Services.AddSingleton(new DistributorServer(zmqBind)); builder.Services.AddSingleton(new ForwarderClient(zmqTarget)); // --- B. 注册核心业务服务 --- builder.Services.AddSingleton(new FileStorageService(processId)); // CameraManager 注册为单例,生命周期由 CameraEngineWorker 管理 builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // --- C. 注册图像处理集群 (修复版) --- // 我们需要确保 ImageScaleCluster 和 ImageEnhanceCluster 都能被独立注入, // 同时它们之间又要建立链式关系。我们使用一个专门的 HostedService 来做连接。 // 1. 注册 Scale 实例 builder.Services.AddSingleton(sp => { var config = sp.GetRequiredService(); return new ImageScaleCluster(4, config); }); // 2. 注册 Enhance 实例 builder.Services.AddSingleton(sp => { var config = sp.GetRequiredService(); return new ImageEnhanceCluster(4, config); }); // 3. 注册一个启动服务来连接这两个集群 (Chain of Responsibility) builder.Services.AddHostedService(); // --- D. 注册 Web 基础服务 --- builder.Services.AddControllers() .AddApplicationPart(typeof(CamerasController).Assembly) // 加载 SDK 中的控制器 .AddApplicationPart(typeof(MonitorController).Assembly) .AddControllersAsServices(); // 注册全局操作日志过滤器 (防止 500 错误) builder.Services.AddScoped(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"Gateway #{processId}", Version = "v1" }); }); // --- E. 注册后台服务 (Worker) --- // 1. 核心引擎工作者 (负责 StartAsync 和 ConfigureBusinessLogic) builder.Services.AddHostedService(); // 2. 网络哨兵 (负责断线重连) // 假设 ConnectivitySentinel 实现了 IHostedService 或者它是一个简单的类 // 如果它实现了 IHostedService: // builder.Services.AddHostedService(); // 如果它只是一个普通类,需要在 CameraEngineWorker 里启动它,或者注册为单例并手动启动 // 这里假设我们需要显式注册它以便让它工作: builder.Services.AddSingleton(); // 注册单例 // 注意:ConnectivitySentinel 的启动逻辑我们放到 CameraEngineWorker 里去调用 // 3. ZeroMQ 桥梁 builder.Services.AddHostedService(); // 4. 配置 CORS builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); }); }); // ============================================================= // 4. 启动应用 // ============================================================= var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.UseCors("AllowAll"); // 启用 CORS app.MapControllers(); Console.WriteLine($"[System] 绑定 Web 端口: {port}"); Console.WriteLine($"[System] 绑定 ZMQ 端口: {zmqBind}"); await app.RunAsync($"http://0.0.0.0:{port}"); } static void InitHardwareEnv() { Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ==="); Console.WriteLine("[硬件] 海康驱动预热中..."); try { HikNativeMethods.NET_DVR_Init(); HikSdkManager.ForceWarmUp(); Console.WriteLine("[硬件] 预热完成。"); } catch (Exception ex) { Console.WriteLine($"[硬件] 预热失败: {ex.Message}"); // 不抛出异常,允许程序继续尝试启动(可能是在无 DLL 环境调试) } } } /// /// 负责图像处理管道的组装 (Scale -> Enhance -> Global) /// public class PipelineConfigurator : IHostedService { private readonly ImageScaleCluster _scale; private readonly ImageEnhanceCluster _enhance; public PipelineConfigurator(ImageScaleCluster scale, ImageEnhanceCluster enhance) { _scale = scale; _enhance = enhance; } public Task StartAsync(CancellationToken cancellationToken) { // 建立责任链: Scale -> Enhance _scale.SetNext(_enhance); // 挂载到全局路由 (驱动层回调会把流推给 Scale) GlobalPipelineRouter.SetProcessor(_scale); Console.WriteLine("[Pipeline] 图像处理链组装完成: Scale -> Enhance"); return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } /// /// 负责 CameraManager 的生命周期管理和业务初始化 /// public class CameraEngineWorker : BackgroundService { private readonly CameraManager _manager; private readonly ConnectivitySentinel _sentinel; // 注入哨兵 public CameraEngineWorker(CameraManager manager, ConnectivitySentinel sentinel) { _manager = manager; _sentinel = sentinel; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { Console.WriteLine("[Engine] 正在启动摄像头管理器..."); // 1. 启动管理器 (加载文件配置) await _manager.StartAsync(); // 2. 启动哨兵 (开始监控断线) // 假设 ConnectivitySentinel 有一个 Start 或类似的方法,如果没有,说明它在构造函数里就启动了 timers // _sentinel.Start(); // 3. 加载默认业务逻辑 (添加测试设备) await ConfigureBusinessLogic(_manager); Console.WriteLine("[Engine] 业务逻辑加载完成。"); } public override async Task StopAsync(CancellationToken cancellationToken) { Console.WriteLine("[Engine] 正在停止..."); await _manager.DisposeAsync(); await base.StopAsync(cancellationToken); } // 以前 Program 类里的静态方法,现在移到这里 private async Task ConfigureBusinessLogic(CameraManager manager) { try { //// 检查是否已经有设备了,如果没有才添加默认的 //if (manager.GetAllCameras().Any()) return; Console.WriteLine("[Engine] 检测到空配置,正在添加默认测试设备..."); 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 (Exception ex) { Console.WriteLine($"[Engine] 添加默认设备失败: {ex.Message}"); } } }