阶段性批量提交
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using SHH.CameraSdk; // 引用你的业务核心
|
||||
using SHH.NetMQ;
|
||||
|
||||
namespace SHH.CameraService;
|
||||
|
||||
@@ -10,22 +9,26 @@ public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
#region --- 1. 端口与身份计算 ---
|
||||
|
||||
int processId = 1;
|
||||
// 从命令行参数解析进程ID(默认1)
|
||||
if (args.Length > 0 && int.TryParse(args[0], out int pid))
|
||||
processId = pid;
|
||||
|
||||
// 计算 Web 服务端口(基础5000 + 进程ID偏移)
|
||||
int port = 5000 + (processId - 1);
|
||||
// 缓冲时间 (您之前写了20000ms即20秒,可能是为了附加调试器。如果觉得太慢可以改回 2000)
|
||||
for(var i=1; i<10; i++)
|
||||
Thread.Sleep(1000);
|
||||
|
||||
Console.Title = $"SHH Gateway - Instance #{processId} (Port: {port})";
|
||||
// 1. 解析配置
|
||||
var config = ServiceConfig.BuildFromArgs(args);
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2. 硬件环境预热 (【重要】必须在一切开始前调用) ---
|
||||
// ---【补全变量定义】---
|
||||
|
||||
// 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
|
||||
@@ -34,161 +37,135 @@ public class Program
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
#region --- A. 注册 ZeroMQ 组件 (传输层) ---
|
||||
// ★★★ 核心:注入全局配置 ★★★
|
||||
builder.Services.AddSingleton(config);
|
||||
|
||||
// 注册转发客户端(定向推送)
|
||||
string zmqBind = $"tcp://*:{5555 + (processId - 1)}";
|
||||
// -------------------------------------------------------------
|
||||
// A. 注册新架构组件
|
||||
// -------------------------------------------------------------
|
||||
builder.Services.AddSingleton<VideoDataChannel>();
|
||||
|
||||
// ★★★ 新增:注册指令总线服务 ★★★
|
||||
string zmqTarget = "tcp://127.0.0.1:6000";
|
||||
// 推流服务 (连接 config.TargetClients 里的 :6002)
|
||||
builder.Services.AddHostedService<ZeroMQBridgeWorker>();
|
||||
|
||||
// 注册转发客户端(定向推送)
|
||||
builder.Services.AddSingleton(new ForwarderClient(zmqTarget));
|
||||
// 指令客户端 (连接 config.TargetClients 里的 :6001)
|
||||
builder.Services.AddHostedService<CommandClientWorker>();
|
||||
|
||||
// ★★★ 新增:注册指令总线服务 ★★★
|
||||
builder.Services.AddHostedService<CommandBusService>();
|
||||
// 进程守护
|
||||
builder.Services.AddHostedService<ParentProcessSentinel>();
|
||||
|
||||
// 注册分发服务器(广播)
|
||||
builder.Services.AddSingleton(new DistributorServer(zmqBind));
|
||||
// -------------------------------------------------------------
|
||||
// B. 注册 SDK 业务服务
|
||||
// -------------------------------------------------------------
|
||||
// 使用刚刚补全的 processIdInt
|
||||
builder.Services.AddSingleton<IStorageService>(new FileStorageService(processIdInt));
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- B. 注册核心业务服务 ---
|
||||
|
||||
// 注册文件存储服务(进程隔离)
|
||||
builder.Services.AddSingleton<IStorageService>(new FileStorageService(processId));
|
||||
|
||||
// CameraManager 注册为单例,生命周期由 CameraEngineWorker 管理
|
||||
builder.Services.AddSingleton<CameraManager>();
|
||||
|
||||
// 图像处理配置管理器(单例)
|
||||
builder.Services.AddSingleton<ProcessingConfigManager>();
|
||||
|
||||
// 显示窗口管理器(单例)
|
||||
builder.Services.AddSingleton<DisplayWindowManager>();
|
||||
builder.Services.AddSingleton<NetworkStreamManager>();
|
||||
|
||||
#endregion
|
||||
builder.Services.AddSingleton<ImageScaleCluster>(sp => new ImageScaleCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
|
||||
builder.Services.AddSingleton<ImageEnhanceCluster>(sp => new ImageEnhanceCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
|
||||
|
||||
#region --- C. 注册图像处理集群 (修复版) ---
|
||||
|
||||
// 说明:通过责任链模式组装 Scale → Enhance 处理流程,确保顺序执行
|
||||
// 1. 注册图像缩容集群(并行度4)
|
||||
builder.Services.AddSingleton<ImageScaleCluster>(sp =>
|
||||
{
|
||||
var configManager = sp.GetRequiredService<ProcessingConfigManager>();
|
||||
return new ImageScaleCluster(4, configManager);
|
||||
});
|
||||
|
||||
// 2. 注册图像增强集群(并行度4)
|
||||
builder.Services.AddSingleton<ImageEnhanceCluster>(sp =>
|
||||
{
|
||||
var configManager = sp.GetRequiredService<ProcessingConfigManager>();
|
||||
return new ImageEnhanceCluster(4, configManager);
|
||||
});
|
||||
|
||||
// 3. 注册管道配置服务(组装责任链)
|
||||
builder.Services.AddHostedService<PipelineConfigurator>();
|
||||
|
||||
#endregion
|
||||
// 使用补全的 processIdInt
|
||||
builder.Services.AddCameraSdk(processIdInt);
|
||||
|
||||
#region --- D. 注册 Web 基础服务 ---
|
||||
builder.Services.AddHostedService<CameraEngineWorker>();
|
||||
builder.Services.AddSingleton<ConnectivitySentinel>();
|
||||
|
||||
// 注册控制器(加载 SDK 中的 CamerasController、MonitorController)
|
||||
builder.Services.AddControllers()
|
||||
.AddApplicationPart(typeof(CamerasController).Assembly) // 加载 SDK 中的控制器
|
||||
.AddApplicationPart(typeof(MonitorController).Assembly)
|
||||
.AddControllersAsServices();
|
||||
builder.Services.AddControllers().AddApplicationPart(typeof(CamerasController).Assembly);
|
||||
builder.Services.AddControllers().AddApplicationPart(typeof(MonitorController).Assembly);
|
||||
|
||||
// 注册全局操作日志过滤器(捕获 API 操作日志)
|
||||
builder.Services.AddScoped<UserActionFilter>();
|
||||
|
||||
// 注册 Swagger 文档(区分实例ID)
|
||||
// -------------------------------------------------------------
|
||||
// C. Web API 基础
|
||||
// -------------------------------------------------------------
|
||||
builder.Services.AddControllers().AddControllersAsServices();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo
|
||||
{
|
||||
Title = $"Gateway #{processId}",
|
||||
Version = "v1"
|
||||
});
|
||||
// 【修正】使用 config.AppId
|
||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"Gateway {config.AppId}", Version = "v1" });
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- E. 注册后台服务 (Worker) ---
|
||||
|
||||
// 1. 核心引擎工作者 (负责 StartAsync 和 ConfigureBusinessLogic)
|
||||
builder.Services.AddHostedService<CameraEngineWorker>();
|
||||
|
||||
// 2.网络哨兵(负责断线重连)(监控设备断线重连,注册为单例)
|
||||
builder.Services.AddSingleton<ConnectivitySentinel>();
|
||||
|
||||
// 3. ZeroMQ 桥梁服务(转发帧数据到外部系统)
|
||||
builder.Services.AddHostedService<ZeroMqBridgeService>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- F. 配置 CORS(允许所有跨域请求) ---
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 4. 启动应用 ---
|
||||
builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// 启用 Swagger 文档
|
||||
//// =======================================================================
|
||||
//// ★★★ 核心接入点:连接 [现有分发器] 与 [新推流通道] ★★★
|
||||
//// =======================================================================
|
||||
|
||||
//// 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();
|
||||
|
||||
// 启用 CORS 策略
|
||||
app.UseCors("AllowAll");
|
||||
|
||||
// 映射控制器路由
|
||||
app.MapControllers();
|
||||
|
||||
// 输出启动信息
|
||||
Console.WriteLine($"[System] 绑定 Web 端口: {port}");
|
||||
Console.WriteLine($"[System] 绑定 ZMQ 端口: {zmqBind}");
|
||||
// 【修正】使用 webPort
|
||||
Console.WriteLine($"[System] Web API 已启动: http://0.0.0.0:{webPort}");
|
||||
|
||||
// 启动 Web 应用
|
||||
await app.RunAsync($"http://0.0.0.0:{port}");
|
||||
await app.RunAsync($"http://0.0.0.0:{webPort}");
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region --- 辅助方法:硬件环境预热 ---
|
||||
|
||||
/// <summary>
|
||||
/// 初始化硬件环境(海康 SDK 预热)
|
||||
/// </summary>
|
||||
static void InitHardwareEnv()
|
||||
{
|
||||
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ===");
|
||||
Console.WriteLine("[硬件] 海康驱动预热中...");
|
||||
Console.WriteLine("=== 工业级视频接入服务启动 ===");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内存转码:Mat -> Jpg Bytes
|
||||
/// </summary>
|
||||
static byte[] EncodeToJpg(SmartFrame frame)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化海康 SDK
|
||||
HikNativeMethods.NET_DVR_Init();
|
||||
// 强制预热播放库(避免首次取流延迟)
|
||||
HikSdkManager.ForceWarmUp();
|
||||
Console.WriteLine("[硬件] 预热完成。");
|
||||
// 假设 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 (Exception ex)
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"[硬件] 预热失败: {ex.Message}");
|
||||
// 不抛出异常,允许程序在无 DLL 环境下调试
|
||||
// 容错处理,防止一帧损坏导致程序崩溃
|
||||
}
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user