Files
Ayay/SHH.CameraService/Program.cs
2026-01-05 14:54:06 +08:00

171 lines
6.7 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.OpenApi.Models;
using SHH.CameraSdk; // 引用你的业务核心
namespace SHH.CameraService;
public class Program
{
public static async Task Main(string[] args)
{
// 缓冲时间 (您之前写了20000ms即20秒可能是为了附加调试器。如果觉得太慢可以改回 2000)
for(var i=1; i<10; i++)
Thread.Sleep(1000);
// 1. 解析配置
var config = ServiceConfig.BuildFromArgs(args);
// ---【补全变量定义】---
// A. 补全 webPort (统一使用 config.BasePort)
int webPort = config.BasePort;
// B. 补全 processIdInt (用于 FileStorage 和 CameraSdk)
// 逻辑:尝试将 AppId 解析为数字;如果 AppId 是字符串(如"CameraApp_01"),则默认给 1或者根据 BasePort 推算
int processIdInt = config.NumericId;
Console.Title = $"SHH Gateway - {config.AppId} (Web: {webPort})";
#region --- 2. ---
InitHardwareEnv();
#endregion
#region --- 3. WebHost ---
var builder = WebApplication.CreateBuilder(args);
// ★★★ 核心:注入全局配置 ★★★
builder.Services.AddSingleton(config);
// -------------------------------------------------------------
// A. 注册新架构组件
// -------------------------------------------------------------
builder.Services.AddSingleton<VideoDataChannel>();
// 推流服务 (连接 config.TargetClients 里的 :6002)
builder.Services.AddHostedService<ZeroMQBridgeWorker>();
// 指令客户端 (连接 config.TargetClients 里的 :6001)
builder.Services.AddHostedService<CommandClientWorker>();
// 进程守护
builder.Services.AddHostedService<ParentProcessSentinel>();
// -------------------------------------------------------------
// B. 注册 SDK 业务服务
// -------------------------------------------------------------
// 使用刚刚补全的 processIdInt
builder.Services.AddSingleton<IStorageService>(new FileStorageService(processIdInt));
builder.Services.AddSingleton<CameraManager>();
builder.Services.AddSingleton<ProcessingConfigManager>();
builder.Services.AddSingleton<DisplayWindowManager>();
builder.Services.AddSingleton<NetworkStreamManager>();
builder.Services.AddSingleton<ImageScaleCluster>(sp => new ImageScaleCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
builder.Services.AddSingleton<ImageEnhanceCluster>(sp => new ImageEnhanceCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
builder.Services.AddHostedService<PipelineConfigurator>();
// 使用补全的 processIdInt
builder.Services.AddCameraSdk(processIdInt);
builder.Services.AddHostedService<CameraEngineWorker>();
builder.Services.AddSingleton<ConnectivitySentinel>();
builder.Services.AddControllers().AddApplicationPart(typeof(CamerasController).Assembly);
builder.Services.AddControllers().AddApplicationPart(typeof(MonitorController).Assembly);
// -------------------------------------------------------------
// C. Web API 基础
// -------------------------------------------------------------
builder.Services.AddControllers().AddControllersAsServices();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
// 【修正】使用 config.AppId
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"Gateway {config.AppId}", Version = "v1" });
});
builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
var app = builder.Build();
//// =======================================================================
//// ★★★ 核心接入点:连接 [现有分发器] 与 [新推流通道] ★★★
//// =======================================================================
//// 1. 获取刚刚注册的数据通道
//var videoChannel = app.Services.GetRequiredService<VideoDataChannel>();
////var config = app.Services.GetRequiredService<ServiceConfig>();
//// 2. 订阅你现有的全局事件 (这里就是“取货”的地方)
//// 每当 HikVideoSource 采集到一帧并调用 Dispatch 时,这里就会触发
//GlobalStreamDispatcher.OnGlobalFrame += (deviceId, smartFrame) =>
//{
// // 3. 数据处理:将 OpenCvSharp Mat 转为 JPG 字节流 (网络传输必须压缩)
// byte[] jpgData = EncodeToJpg(smartFrame);
// if (jpgData != null && jpgData.Length > 0)
// {
// // 4. 封装载荷
// var payload = new VideoPayload
// {
// // 使用 AppId 或 DeviceId 作为标识
// CameraId = config.AppId,
// OriginalImageBytes = jpgData,
// CaptureTime = DateTime.Now,
// OriginalWidth = smartFrame.TargetWidth,
// OriginalHeight = smartFrame.TargetHeight
// };
// // 5. 扔进通道 (Fire-and-Forget不阻塞你原来的显示逻辑)
// // WriteAsync 是 ValueTask这里忽略等待追求最高吞吐
// _ = videoChannel.WriteAsync(payload);
// }
//};
//Console.WriteLine("[System] 全局流已桥接到 ZeroMQ 推流通道");
app.UseSwagger();
app.UseSwaggerUI();
app.UseCors("AllowAll");
app.MapControllers();
// 【修正】使用 webPort
Console.WriteLine($"[System] Web API 已启动: http://0.0.0.0:{webPort}");
await app.RunAsync($"http://0.0.0.0:{webPort}");
#endregion
}
static void InitHardwareEnv()
{
Console.WriteLine("=== 工业级视频接入服务启动 ===");
}
/// <summary>
/// 内存转码Mat -> Jpg Bytes
/// </summary>
static byte[] EncodeToJpg(SmartFrame frame)
{
try
{
// 假设 SmartFrame 内部持有 OpenCvSharp.Mat 类型的 InternalMat
if (frame != null && frame.InternalMat != null && !frame.InternalMat.Empty())
{
// 80 是 JPG 质量参数,平衡画质与带宽
return frame.InternalMat.ImEncode(".jpg", new int[] { 1, 80 });
}
}
catch
{
// 容错处理,防止一帧损坏导致程序崩溃
}
return Array.Empty<byte>();
}
}