2026-01-15 09:31:57 +08:00
|
|
|
|
using Grpc.Net.Client;
|
|
|
|
|
|
using Microsoft.AspNetCore.Builder;
|
2025-12-29 08:09:14 +08:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Microsoft.OpenApi.Models;
|
2026-01-07 10:59:03 +08:00
|
|
|
|
using SHH.CameraSdk;
|
2026-01-15 09:31:57 +08:00
|
|
|
|
using SHH.Contracts.Grpc;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
2025-12-29 08:09:14 +08:00
|
|
|
|
|
|
|
|
|
|
namespace SHH.CameraService;
|
|
|
|
|
|
|
|
|
|
|
|
public class Program
|
|
|
|
|
|
{
|
|
|
|
|
|
public static async Task Main(string[] args)
|
|
|
|
|
|
{
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 2. 硬件预热 (静态方法保留)
|
2026-01-07 10:59:03 +08:00
|
|
|
|
HikNativeMethods.NET_DVR_Init();
|
|
|
|
|
|
HikSdkManager.ForceWarmUp();
|
2026-01-05 14:54:06 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 1. [核心环境] 必须在所有网络操作前开启
|
|
|
|
|
|
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 模拟/解析配置
|
|
|
|
|
|
if (args.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
string serviceArgs = "--appid CameraApp_01 " +
|
|
|
|
|
|
"--uris localhost,9001,video,调试PC; " +
|
|
|
|
|
|
"--uris localhost,9001,command,调试PC; " +
|
|
|
|
|
|
"--mode 1 --ports 5000,100";
|
|
|
|
|
|
args = serviceArgs.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
}
|
|
|
|
|
|
var config = ServiceConfig.BuildFromArgs(args);
|
2026-01-05 14:54:06 +08:00
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 3. 【强行复刻成功逻辑】 在 Web 容器启动前直接执行注册
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
2026-01-15 09:31:57 +08:00
|
|
|
|
if (config.CommandEndpoints.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 将 tcp:// 转换为 http:// 以适配 gRPC
|
|
|
|
|
|
string targetUrl = config.CommandEndpoints.First().Uri.Replace("tcp://", "http://");
|
|
|
|
|
|
|
|
|
|
|
|
using var channel = GrpcChannel.ForAddress(targetUrl);
|
|
|
|
|
|
var client = new GatewayProvider.GatewayProviderClient(channel);
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"[gRPC] 正在执行预注册 (环境: 纯净): {targetUrl}");
|
|
|
|
|
|
var resp = await client.RegisterInstanceAsync(new RegisterRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
InstanceId = config.AppId,
|
|
|
|
|
|
Version = "2.0.0-grpc",
|
|
|
|
|
|
ServerIp = "127.0.0.1",
|
|
|
|
|
|
WebApiPort = config.BasePort,
|
|
|
|
|
|
StartTimeTicks = DateTime.Now.Ticks,
|
|
|
|
|
|
ProcessId = Environment.ProcessId,
|
|
|
|
|
|
Description = "Camera Service"
|
|
|
|
|
|
});
|
|
|
|
|
|
Console.WriteLine($"[gRPC] 预注册成功: {resp.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"[gRPC] 预注册尝试失败 (不影响启动): {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-05 14:54:06 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
// 4. 构建 Web 主机环境
|
|
|
|
|
|
// =============================================================
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
2025-12-29 08:09:14 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 基础业务单例注册
|
|
|
|
|
|
builder.Services.AddSingleton(config);
|
|
|
|
|
|
builder.Services.AddSingleton<ProcessingConfigManager>();
|
2026-01-07 10:59:03 +08:00
|
|
|
|
builder.Services.AddSingleton(sp => new ImageScaleCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
|
|
|
|
|
|
builder.Services.AddSingleton(sp => new ImageEnhanceCluster(4, sp.GetRequiredService<ProcessingConfigManager>()));
|
|
|
|
|
|
builder.Services.AddHostedService<PipelineConfigurator>();
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// 接入 SDK 核心逻辑
|
|
|
|
|
|
builder.Services.AddCameraSdk(config.NumericId);
|
|
|
|
|
|
builder.Services.AddHostedService<CameraEngineWorker>();
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// ★ 注册 gRPC 版本的状态监控工作者 (不讲道理,直接注册)
|
|
|
|
|
|
builder.Services.AddHostedService<DeviceStateMonitorWorker>();
|
2026-01-07 10:59:03 +08:00
|
|
|
|
builder.Services.AddHostedService<ParentProcessSentinel>();
|
2026-01-15 09:31:57 +08:00
|
|
|
|
builder.Services.AddHostedService<GrpcCommandReceiverWorker>();
|
2025-12-29 08:09:14 +08:00
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 5. 视频流 Target 注册 (gRPC 模式)
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
var netTargets = new List<StreamTarget>();
|
|
|
|
|
|
if (config.VideoEndpoints != null)
|
|
|
|
|
|
{
|
2026-01-15 09:31:57 +08:00
|
|
|
|
foreach (var cfgVideo in config.VideoEndpoints)
|
2026-01-07 10:59:03 +08:00
|
|
|
|
{
|
|
|
|
|
|
netTargets.Add(new StreamTarget(new PushTargetConfig
|
|
|
|
|
|
{
|
2026-01-15 09:31:57 +08:00
|
|
|
|
Name = cfgVideo.Description,
|
|
|
|
|
|
Endpoint = cfgVideo.Uri,
|
|
|
|
|
|
QueueCapacity = 10,
|
2026-01-07 10:59:03 +08:00
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
builder.Services.AddSingleton<IEnumerable<StreamTarget>>(netTargets);
|
|
|
|
|
|
builder.Services.AddHostedService<NetworkStreamingWorker>();
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 为每个 Target 绑定一个 gRPC 流发送者
|
2026-01-07 10:59:03 +08:00
|
|
|
|
foreach (var target in netTargets)
|
|
|
|
|
|
{
|
2026-01-15 09:31:57 +08:00
|
|
|
|
builder.Services.AddHostedService(sp =>
|
|
|
|
|
|
new GrpcSenderWorker(target, sp.GetRequiredService<ILogger<GrpcSenderWorker>>()));
|
2026-01-07 10:59:03 +08:00
|
|
|
|
}
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 注册指令分发 (不再使用 NetMQ 的 CommandClientWorker)
|
2026-01-09 12:30:36 +08:00
|
|
|
|
builder.Services.AddSingleton<InterceptorPipeline>();
|
2026-01-07 10:59:03 +08:00
|
|
|
|
builder.Services.AddSingleton<CommandDispatcher>();
|
|
|
|
|
|
builder.Services.AddSingleton<ICommandHandler, SyncCameraHandler>();
|
2026-01-12 18:27:58 +08:00
|
|
|
|
builder.Services.AddSingleton<ICommandHandler, RemoveCameraHandler>();
|
2025-12-29 08:09:14 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
ConfigureWebServices(builder, config);
|
|
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 6. 启动服务
|
2026-01-07 10:59:03 +08:00
|
|
|
|
// =============================================================
|
|
|
|
|
|
var app = builder.Build();
|
2025-12-29 08:09:14 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
// 激活 SDK 管理器并启动业务点火
|
2026-01-07 10:59:03 +08:00
|
|
|
|
await StartBusinessLogic(app);
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
app.UseSwagger();
|
2026-01-15 09:31:57 +08:00
|
|
|
|
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", $"SHH Gateway #{config.AppId}"));
|
2026-01-05 14:54:06 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
app.MapGet("/", () => $"SHH Gateway {config.AppId} is running (gRPC Mode).");
|
2026-01-03 00:16:28 +08:00
|
|
|
|
app.UseCors("AllowAll");
|
2026-01-07 10:59:03 +08:00
|
|
|
|
app.MapControllers();
|
2026-01-03 00:16:28 +08:00
|
|
|
|
|
2026-01-15 09:31:57 +08:00
|
|
|
|
Console.WriteLine($"[System] 正在启动 Web 服务,监听端口: {config.BasePort}");
|
2026-01-07 10:59:03 +08:00
|
|
|
|
await app.RunAsync($"http://0.0.0.0:{config.BasePort}");
|
2025-12-29 08:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-07 10:59:03 +08:00
|
|
|
|
/// <summary>
|
2026-01-15 09:31:57 +08:00
|
|
|
|
/// 激活单例并启动相机管理器
|
2026-01-07 10:59:03 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
static async Task StartBusinessLogic(WebApplication app)
|
2026-01-05 14:54:06 +08:00
|
|
|
|
{
|
2026-01-07 10:59:03 +08:00
|
|
|
|
var manager = app.Services.GetRequiredService<CameraManager>();
|
|
|
|
|
|
_ = app.Services.GetRequiredService<ConnectivitySentinel>();
|
|
|
|
|
|
await manager.StartAsync();
|
|
|
|
|
|
Console.WriteLine("[System] 核心业务逻辑已激活。");
|
2026-01-05 14:54:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-03 00:16:28 +08:00
|
|
|
|
/// <summary>
|
2026-01-07 10:59:03 +08:00
|
|
|
|
/// 注册 Web API 支持
|
2026-01-03 00:16:28 +08:00
|
|
|
|
/// </summary>
|
2026-01-07 10:59:03 +08:00
|
|
|
|
static void ConfigureWebServices(WebApplicationBuilder builder, ServiceConfig cfg)
|
2025-12-29 08:09:14 +08:00
|
|
|
|
{
|
2026-01-07 10:59:03 +08:00
|
|
|
|
builder.Services.AddCors(options =>
|
2025-12-29 08:09:14 +08:00
|
|
|
|
{
|
2026-01-07 10:59:03 +08:00
|
|
|
|
options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
builder.Services.AddControllers(options =>
|
2025-12-29 08:09:14 +08:00
|
|
|
|
{
|
2026-01-07 10:59:03 +08:00
|
|
|
|
options.Filters.Add<UserActionFilter>();
|
|
|
|
|
|
})
|
2026-01-15 09:31:57 +08:00
|
|
|
|
.AddApplicationPart(typeof(CamerasController).Assembly)
|
|
|
|
|
|
.AddApplicationPart(typeof(MonitorController).Assembly);
|
2026-01-07 10:59:03 +08:00
|
|
|
|
|
|
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
|
builder.Services.AddSwaggerGen(c =>
|
|
|
|
|
|
{
|
|
|
|
|
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = $"SHH Gateway #{cfg.AppId}", Version = "v1" });
|
|
|
|
|
|
});
|
2025-12-29 08:09:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|