架构增加文件存储服务的支持

This commit is contained in:
2025-12-26 21:19:43 +08:00
parent 6ab500724f
commit 71856b483e
4 changed files with 170 additions and 56 deletions

View File

@@ -6,7 +6,7 @@ using Microsoft.OpenApi.Models;
namespace SHH.CameraSdk;
/// <summary>
/// A 方案:标准控制台结构 (动态窗口版)
/// A 方案:标准控制台结构 (框架搭建版:支持动态端口与依赖注入)
/// </summary>
public class Program
{
@@ -14,41 +14,65 @@ public class Program
public static async Task Main(string[] args)
{
// ==============================================================================
// 1. 基础设施初始化
// 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. 基础设施初始化
// ==============================================================================
InitHardwareEnv();
// B. 【核心】创建独立的文件存储服务 (此时只建立目录,不进行具体读写)
IStorageService storageService = new FileStorageService(processId);
// 核心设备管理器
// 注意:暂时保持无参构造,后续我们在改造 CameraManager 时再注入 storageService
using var cameraManager = new CameraManager();
// [新增] 动态窗口管理器 (不再直接 new FrameConsumer)
// 这是一个单例服务,负责在运行期间管理所有弹出的窗口
// 动态窗口管理器
var displayManager = new DisplayWindowManager();
// ==============================================================================
// 2. 启动 Web 监控与诊断服务 (注入两个管理器)
// 3. 启动 Web 监控与诊断服务 (注入服务与端口)
// ==============================================================================
var app = await StartWebMonitoring(cameraManager, displayManager);
var app = await StartWebMonitoring(cameraManager, displayManager, storageService, port);
// 启动网络哨兵 (后台 Ping)
// 启动网络哨兵
var sentinel = new ConnectivitySentinel(cameraManager);
// ==============================================================================
// 3. 业务编排:仅配置设备,不配置窗口
// 4. 业务编排
// ==============================================================================
await ConfigureBusinessLogic(cameraManager);
// ==============================================================================
// 4. 启动引擎与交互
// 5. 启动引擎与交互
// ==============================================================================
Console.WriteLine("\n[系统] 正在启动全局管理引擎...");
await cameraManager.StartAsync();
Console.WriteLine(">> 系统就绪。");
Console.WriteLine(">>当前无播放窗口。请通过 Web 界面 '新增订阅' -> 模式选 'UI_Preview' 来动态打开。");
Console.WriteLine($">> 系统就绪。Web 管理地址: http://localhost:{port}");
Console.WriteLine($">> 数据存储路径: App_Data/Process_{processId}/");
Console.WriteLine(">> 按 'S' 键退出...");
// 阻塞主线程,保持程序运行
// 阻塞主线程
while (Console.ReadKey(true).Key != ConsoleKey.S)
{
Thread.Sleep(100);
@@ -64,33 +88,43 @@ public class Program
static void InitHardwareEnv()
{
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.3 动态窗口版) ===");
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ===");
Console.WriteLine("[硬件] 海康驱动预热中...");
HikNativeMethods.NET_DVR_Init();
HikSdkManager.ForceWarmUp();
Console.WriteLine("[硬件] 预热完成。");
}
// [修改] 签名增加 DisplayWindowManager 参数
static async Task<WebApplication> StartWebMonitoring(CameraManager manager, DisplayWindowManager displayMgr)
static async Task<WebApplication> StartWebMonitoring(
CameraManager manager,
DisplayWindowManager displayMgr,
IStorageService storage, // 接收存储服务实例
int port) // 接收动态端口
{
var builder = WebApplication.CreateBuilder();
// 1. 配置 CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
});
});
// 日志屏蔽
builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
builder.Logging.AddFilter("System", LogLevel.Warning);
// 2. 日志降噪
builder.Logging.SetMinimumLevel(LogLevel.Warning);
builder.Logging.AddFilter("Microsoft.AspNetCore.Hosting.Diagnostics", LogLevel.Warning);
// 3. 【核心】依赖注入注册
// 将 storageService 注册为单例,这样 UserActionFilter 和 MonitorController 就能拿到它了
builder.Services.AddSingleton<IStorageService>(storage);
builder.Services.AddSingleton(manager);
builder.Services.AddSingleton(displayMgr);
// 显式注册过滤器 (这是防止 500 错误的关键)
builder.Services.AddScoped<UserActionFilter>();
builder.Services.AddControllers(options =>
{
// 注册全局操作日志过滤器
@@ -100,39 +134,38 @@ public class Program
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "SHH Camera Diagnostics", Version = "v1" });
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{processIdFromPort(port)}", Version = "v1" });
});
builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
// [关键] 注入两个单例服务,让 Controller 能调用它们
builder.Services.AddSingleton(manager);
builder.Services.AddSingleton(displayMgr);
var webApp = builder.Build();
// 4. 配置中间件
webApp.UseSwagger();
webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1"));
webApp.UseCors("AllowAll");
webApp.MapControllers();
_ = webApp.RunAsync("http://0.0.0.0:5000");
Console.WriteLine("[Web] 监控API已启动: http://localhost:5000");
// 5. 启动监听 (使用动态端口)
_ = webApp.RunAsync($"http://0.0.0.0:{port}");
Console.WriteLine($"[Web] 监控API已启动: http://localhost:{port}");
return webApp;
}
// [修改] 移除 FrameConsumer 参数,不再进行硬编码订阅
// 辅助方法:从端口反推 ID仅用于 Swagger 标题显示
static int processIdFromPort(int port) => port - 5000 + 1;
static async Task ConfigureBusinessLogic(CameraManager manager)
{
// 1. 添加设备配置
// 1. 添加测试设备
var config = new VideoSourceConfig
{
Id = 101,
Brand = DeviceBrand.HikVision,
IpAddress = "172.16.41.206",
IpAddress = "192.168.5.9",
Port = 8000,
Username = "admin",
Password = "abcd1234",
Password = "RRYFOA",
StreamType = 0
};
manager.AddDevice(config);
@@ -148,8 +181,5 @@ public class Program
StreamType = 0
};
manager.AddDevice(config2);
// 注意:此处不再调用 Register 或 Subscribe
// 所有的播放请求都将由 WebAPI 收到前端指令后,调用 DisplayWindowManager 来动态发起
}
}