Files
Ayay/SHH.CameraSdk/Program.cs

205 lines
7.6 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.Extensions.Logging;
using Microsoft.OpenApi.Models;
namespace SHH.CameraSdk;
/// <summary>
/// A 方案:标准控制台结构 (框架搭建版:支持动态端口与依赖注入)
/// </summary>
public class Program
{
[STAThread]
public static async Task Main(string[] args)
{
// ==============================================================================
// 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(storageService);
// 动态窗口管理器
var displayManager = new DisplayWindowManager(cameraManager);
// ==============================================================================
// 3. 启动 Web 监控与诊断服务 (注入服务与端口)
// ==============================================================================
var app = await StartWebMonitoring(cameraManager, displayManager, storageService, port);
// 启动网络哨兵
var sentinel = new ConnectivitySentinel(cameraManager);
// ==============================================================================
// 4. 业务编排
// ==============================================================================
// 【关键修复 1】先 StartAsync让它先从文件把 999 号设备读进内存
await cameraManager.StartAsync();
// 【关键修复 2】文件加载完后再决定要不要加默认设备
await ConfigureBusinessLogic(cameraManager);
// ==============================================================================
// 5. 启动引擎与交互
// ==============================================================================
Console.WriteLine("\n[系统] 正在启动全局管理引擎...");
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);
}
Console.WriteLine("[系统] 正在停机...");
await app.StopAsync();
}
// ==============================================================================
// Static Methods
// ==============================================================================
static void InitHardwareEnv()
{
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.5 框架版) ===");
Console.WriteLine("[硬件] 海康驱动预热中...");
HikNativeMethods.NET_DVR_Init();
HikSdkManager.ForceWarmUp();
Console.WriteLine("[硬件] 预热完成。");
}
static async Task<WebApplication> StartWebMonitoring(
CameraManager manager,
DisplayWindowManager displayMgr,
IStorageService storage, // 接收存储服务实例
int port) // 接收动态端口
{
var builder = WebApplication.CreateBuilder();
// 注册缩放集群服务 (建议 Worker 数 = CPU 核心数,这里设为 4)
var scaleService = new ImageScaleCluster(4);
// 2. [核心] 将缩放服务“挂载”到全局路由上
// 从此刻起,所有驱动层的帧都会先流经 scaleService
GlobalPipelineRouter.SetProcessor(scaleService);
// 3. 注册到 DI 容器 (以便 Controller 或其他服务可以管理它,例如动态调整并行度)
builder.Services.AddSingleton<IFrameProcessor>(scaleService);
// 1. 配置 CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
});
});
//// 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 =>
{
// 注册全局操作日志过滤器
options.Filters.Add<UserActionFilter>();
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{processIdFromPort(port)}", Version = "v1" });
});
var webApp = builder.Build();
// 4. 配置中间件
webApp.UseSwagger();
webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1"));
webApp.UseCors("AllowAll");
webApp.MapControllers();
// 5. 启动监听 (使用动态端口)
_ = webApp.RunAsync($"http://0.0.0.0:{port}");
Console.WriteLine($"[Web] 监控API已启动: http://localhost:{port}");
return webApp;
}
// 辅助方法:从端口反推 ID仅用于 Swagger 标题显示
static int processIdFromPort(int port) => port - 5000 + 1;
static async Task ConfigureBusinessLogic(CameraManager manager)
{
try
{
// 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
{
}
}
}