2025-12-26 03:18:21 +08:00
|
|
|
|
using Microsoft.AspNetCore.Builder;
|
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2025-12-26 16:58:12 +08:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2025-12-26 03:18:21 +08:00
|
|
|
|
using Microsoft.OpenApi.Models;
|
|
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
namespace SHH.CameraSdk;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-12-26 21:19:43 +08:00
|
|
|
|
/// A 方案:标准控制台结构 (框架搭建版:支持动态端口与依赖注入)
|
2025-12-26 17:28:07 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class Program
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 17:28:07 +08:00
|
|
|
|
[STAThread]
|
|
|
|
|
|
public static async Task Main(string[] args)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// ==============================================================================
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 1. 身份识别与端口计算
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
// 默认 1 号进程
|
|
|
|
|
|
int processId = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果命令行传了参数 (例如: dotnet run 2),则覆盖为 2 号进程
|
|
|
|
|
|
if (args.Length > 0 && int.TryParse(args[0], out int pid))
|
|
|
|
|
|
{
|
|
|
|
|
|
processId = pid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 端口计算公式:5000 + (ID - 1)
|
|
|
|
|
|
// ID=1 -> 5000
|
|
|
|
|
|
// ID=2 -> 5001
|
|
|
|
|
|
int port = 5000 + (processId - 1);
|
|
|
|
|
|
|
|
|
|
|
|
Console.Title = $"SHH Gateway - Instance #{processId} (Port: {port})";
|
|
|
|
|
|
Console.WriteLine($"[System] 正在初始化实例 #{processId}...");
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// 2. 基础设施初始化
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
InitHardwareEnv();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// B. 【核心】创建独立的文件存储服务 (此时只建立目录,不进行具体读写)
|
|
|
|
|
|
IStorageService storageService = new FileStorageService(processId);
|
|
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// 核心设备管理器
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 注意:暂时保持无参构造,后续我们在改造 CameraManager 时再注入 storageService
|
2025-12-26 22:24:12 +08:00
|
|
|
|
using var cameraManager = new CameraManager(storageService);
|
2025-12-26 17:28:07 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 动态窗口管理器
|
2025-12-27 05:05:06 +08:00
|
|
|
|
var displayManager = new DisplayWindowManager(cameraManager);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// ==============================================================================
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 3. 启动 Web 监控与诊断服务 (注入服务与端口)
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// ==============================================================================
|
2025-12-26 21:19:43 +08:00
|
|
|
|
var app = await StartWebMonitoring(cameraManager, displayManager, storageService, port);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 启动网络哨兵
|
2025-12-26 17:28:07 +08:00
|
|
|
|
var sentinel = new ConnectivitySentinel(cameraManager);
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 4. 业务编排
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// ==============================================================================
|
2025-12-26 22:24:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 【关键修复 1】先 StartAsync,让它先从文件把 999 号设备读进内存
|
|
|
|
|
|
await cameraManager.StartAsync();
|
|
|
|
|
|
|
|
|
|
|
|
// 【关键修复 2】文件加载完后,再决定要不要加默认设备
|
2025-12-26 17:28:07 +08:00
|
|
|
|
await ConfigureBusinessLogic(cameraManager);
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 5. 启动引擎与交互
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
Console.WriteLine("\n[系统] 正在启动全局管理引擎...");
|
|
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
Console.WriteLine($">> 系统就绪。Web 管理地址: http://localhost:{port}");
|
|
|
|
|
|
Console.WriteLine($">> 数据存储路径: App_Data/Process_{processId}/");
|
2025-12-26 17:28:07 +08:00
|
|
|
|
Console.WriteLine(">> 按 'S' 键退出...");
|
|
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 阻塞主线程
|
2025-12-26 17:28:07 +08:00
|
|
|
|
while (Console.ReadKey(true).Key != ConsoleKey.S)
|
2025-12-26 06:14:55 +08:00
|
|
|
|
{
|
2025-12-26 17:28:07 +08:00
|
|
|
|
Thread.Sleep(100);
|
2025-12-26 06:14:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
Console.WriteLine("[系统] 正在停机...");
|
|
|
|
|
|
await app.StopAsync();
|
|
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// Static Methods
|
|
|
|
|
|
// ==============================================================================
|
2025-12-26 16:58:12 +08:00
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
static void InitHardwareEnv()
|
|
|
|
|
|
{
|
2025-12-26 21:19:43 +08:00
|
|
|
|
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ===");
|
2025-12-26 17:28:07 +08:00
|
|
|
|
Console.WriteLine("[硬件] 海康驱动预热中...");
|
|
|
|
|
|
HikNativeMethods.NET_DVR_Init();
|
|
|
|
|
|
HikSdkManager.ForceWarmUp();
|
|
|
|
|
|
Console.WriteLine("[硬件] 预热完成。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
static async Task<WebApplication> StartWebMonitoring(
|
|
|
|
|
|
CameraManager manager,
|
|
|
|
|
|
DisplayWindowManager displayMgr,
|
|
|
|
|
|
IStorageService storage, // 接收存储服务实例
|
|
|
|
|
|
int port) // 接收动态端口
|
2025-12-26 17:28:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder();
|
|
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 1. 注册配置管理器(指挥部)
|
|
|
|
|
|
var configManager = new ProcessingConfigManager();
|
|
|
|
|
|
builder.Services.AddSingleton(configManager);
|
2025-12-27 07:25:32 +08:00
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 2. 初始化预处理流水线环节
|
|
|
|
|
|
// 建议:此处直接手动创建实例,以便精确控制链条顺序
|
|
|
|
|
|
var scaleService = new ImageScaleCluster(4, configManager); // 环节一
|
|
|
|
|
|
var enhanceService = new ImageEnhanceCluster(4, configManager); // 环节二
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 编排流水线:缩放 -> 增亮 -> 终点(GlobalProcessingCenter)
|
2025-12-27 07:25:32 +08:00
|
|
|
|
scaleService.SetNext(enhanceService);
|
2025-12-27 07:05:07 +08:00
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 4. 将流水线入口挂载到全局路由(驱动层改道)
|
2025-12-27 07:05:07 +08:00
|
|
|
|
GlobalPipelineRouter.SetProcessor(scaleService);
|
|
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 5. 【修复点】将具体实例注册到 DI 容器
|
|
|
|
|
|
// 这样 Controller 可以通过构造函数拿到具体的实例进行动态管理
|
|
|
|
|
|
builder.Services.AddSingleton(scaleService);
|
|
|
|
|
|
builder.Services.AddSingleton(enhanceService);
|
2025-12-27 07:05:07 +08:00
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 6. 配置 CORS
|
2025-12-26 17:28:07 +08:00
|
|
|
|
builder.Services.AddCors(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
options.AddPolicy("AllowAll", policy =>
|
2025-12-26 06:14:55 +08:00
|
|
|
|
{
|
2025-12-26 21:19:43 +08:00
|
|
|
|
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
|
2025-12-26 06:14:55 +08:00
|
|
|
|
});
|
2025-12-26 17:28:07 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-12-27 14:16:50 +08:00
|
|
|
|
// 7. 依赖注入注册
|
|
|
|
|
|
builder.Services.AddSingleton<IStorageService>(storage);
|
|
|
|
|
|
builder.Services.AddSingleton(manager);
|
|
|
|
|
|
builder.Services.AddSingleton(displayMgr);
|
|
|
|
|
|
|
2025-12-26 22:24:12 +08:00
|
|
|
|
//// 2. 日志降噪
|
|
|
|
|
|
//builder.Logging.SetMinimumLevel(LogLevel.Warning);
|
|
|
|
|
|
//builder.Logging.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.Warning);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 显式注册过滤器 (这是防止 500 错误的关键)
|
|
|
|
|
|
builder.Services.AddScoped<UserActionFilter>();
|
|
|
|
|
|
|
2025-12-26 18:59:27 +08:00
|
|
|
|
builder.Services.AddControllers(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 注册全局操作日志过滤器
|
|
|
|
|
|
options.Filters.Add<UserActionFilter>();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
|
builder.Services.AddSwaggerGen(c =>
|
|
|
|
|
|
{
|
2025-12-26 21:19:43 +08:00
|
|
|
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{processIdFromPort(port)}", Version = "v1" });
|
2025-12-26 17:28:07 +08:00
|
|
|
|
});
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
var webApp = builder.Build();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 4. 配置中间件
|
2025-12-26 17:28:07 +08:00
|
|
|
|
webApp.UseSwagger();
|
|
|
|
|
|
webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1"));
|
|
|
|
|
|
webApp.UseCors("AllowAll");
|
|
|
|
|
|
webApp.MapControllers();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 5. 启动监听 (使用动态端口)
|
|
|
|
|
|
_ = webApp.RunAsync($"http://0.0.0.0:{port}");
|
|
|
|
|
|
Console.WriteLine($"[Web] 监控API已启动: http://localhost:{port}");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
return webApp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 21:19:43 +08:00
|
|
|
|
// 辅助方法:从端口反推 ID,仅用于 Swagger 标题显示
|
|
|
|
|
|
static int processIdFromPort(int port) => port - 5000 + 1;
|
|
|
|
|
|
|
2025-12-26 17:28:07 +08:00
|
|
|
|
static async Task ConfigureBusinessLogic(CameraManager manager)
|
|
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
try
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
// 1. 添加测试设备
|
|
|
|
|
|
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
|
2025-12-26 17:28:07 +08:00
|
|
|
|
{
|
2025-12-26 22:24:12 +08:00
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|