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