Files
Ayay/SHH.CameraService/Program.cs

250 lines
9.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<IStorageService>(new FileStorageService(processId));
// CameraManager 注册为单例,生命周期由 CameraEngineWorker 管理
builder.Services.AddSingleton<CameraManager>();
builder.Services.AddSingleton<ProcessingConfigManager>();
builder.Services.AddSingleton<DisplayWindowManager>();
// --- C. 注册图像处理集群 (修复版) ---
// 我们需要确保 ImageScaleCluster 和 ImageEnhanceCluster 都能被独立注入,
// 同时它们之间又要建立链式关系。我们使用一个专门的 HostedService 来做连接。
// 1. 注册 Scale 实例
builder.Services.AddSingleton<ImageScaleCluster>(sp =>
{
var config = sp.GetRequiredService<ProcessingConfigManager>();
return new ImageScaleCluster(4, config);
});
// 2. 注册 Enhance 实例
builder.Services.AddSingleton<ImageEnhanceCluster>(sp =>
{
var config = sp.GetRequiredService<ProcessingConfigManager>();
return new ImageEnhanceCluster(4, config);
});
// 3. 注册一个启动服务来连接这两个集群 (Chain of Responsibility)
builder.Services.AddHostedService<PipelineConfigurator>();
// --- D. 注册 Web 基础服务 ---
builder.Services.AddControllers()
.AddApplicationPart(typeof(CamerasController).Assembly) // 加载 SDK 中的控制器
.AddApplicationPart(typeof(MonitorController).Assembly)
.AddControllersAsServices();
// 注册全局操作日志过滤器 (防止 500 错误)
builder.Services.AddScoped<UserActionFilter>();
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<CameraEngineWorker>();
// 2. 网络哨兵 (负责断线重连)
// 假设 ConnectivitySentinel 实现了 IHostedService 或者它是一个简单的类
// 如果它实现了 IHostedService:
// builder.Services.AddHostedService<ConnectivitySentinel>();
// 如果它只是一个普通类,需要在 CameraEngineWorker 里启动它,或者注册为单例并手动启动
// 这里假设我们需要显式注册它以便让它工作:
builder.Services.AddSingleton<ConnectivitySentinel>(); // 注册单例
// 注意ConnectivitySentinel 的启动逻辑我们放到 CameraEngineWorker 里去调用
// 3. ZeroMQ 桥梁
builder.Services.AddHostedService<ZeroMqBridgeService>();
// 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 环境调试)
}
}
}
/// <summary>
/// 负责图像处理管道的组装 (Scale -> Enhance -> Global)
/// </summary>
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;
}
/// <summary>
/// 负责 CameraManager 的生命周期管理和业务初始化
/// </summary>
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}");
}
}
}