视频SDK新协议签入

This commit is contained in:
2026-01-15 09:31:57 +08:00
parent 3f8e42e560
commit 81580a8f55
14 changed files with 844 additions and 472 deletions

View File

@@ -0,0 +1,123 @@
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using SHH.CameraSdk;
using SHH.Contracts;
using SHH.Contracts.Grpc; // 引用 Proto 生成的命名空间
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SHH.CameraService
{
/// <summary>
/// gRPC 指令接收后台服务
/// 负责1. 逻辑注册 2. 维持指令长连接 3. 指令分发
/// </summary>
public class GrpcCommandReceiverWorker : BackgroundService
{
private readonly ILogger<GrpcCommandReceiverWorker> _logger;
private readonly ServiceConfig _config;
private readonly IEnumerable<ICommandHandler> _handlers; // 自动注入所有指令处理器
public GrpcCommandReceiverWorker(
ILogger<GrpcCommandReceiverWorker> logger,
ServiceConfig config,
IEnumerable<ICommandHandler> handlers)
{
_logger = logger;
_config = config;
_handlers = handlers;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// 给 SDK 和数据库留出几秒钟的加载时间
_logger.LogInformation("[gRPC Bus] 后台 Worker 准备就绪3秒后发起连接...");
await Task.Delay(3000, stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
try
{
// 1. 地址预处理 (将 127.0.0.1 强制转换为 localhost 解决 Unimplemented 问题)
var ep = _config.CommandEndpoints.First();
string targetUrl = ep.Uri.Replace("tcp://", "http://").Replace("127.0.0.1", "localhost");
using var channel = GrpcChannel.ForAddress(targetUrl);
var client = new GatewayProvider.GatewayProviderClient(channel);
// --- 第一步:发起逻辑注册 (Unary) ---
_logger.LogInformation("[gRPC Bus] 正在发起逻辑注册: {Url}", targetUrl);
var regResp = await client.RegisterInstanceAsync(new RegisterRequest
{
InstanceId = _config.AppId,
Version = "2.0.0-grpc",
ServerIp = "127.0.0.1",
StartTimeTicks = DateTime.Now.Ticks
}, cancellationToken: stoppingToken);
if (regResp.Success)
{
_logger.LogInformation("[gRPC Bus] 逻辑注册成功。正在开启长连接指令通道...");
// --- 第二步:开启物理指令流 (Server Streaming) ---
using var call = client.OpenCommandChannel(new CommandStreamRequest
{
InstanceId = _config.AppId
}, cancellationToken: stoppingToken);
// --- 第三步:阻塞式监听服务端推送 ---
// 只要服务端通过 responseStream.WriteAsync 发消息,这里就会命中
while (await call.ResponseStream.MoveNext(stoppingToken))
{
var protoMsg = call.ResponseStream.Current;
_logger.LogInformation("[gRPC Bus] 收到远程指令: {CmdCode}", protoMsg.CmdCode);
// 异步分发,不阻塞接收循环
_ = DispatchCommandAsync(protoMsg);
}
}
}
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
_logger.LogError("[gRPC Bus] 链路异常5秒后重试: {Msg}", ex.Message);
await Task.Delay(5000, stoppingToken);
}
}
}
/// <summary>
/// 指令分发逻辑
/// </summary>
private async Task DispatchCommandAsync(CommandPayloadProto msg)
{
try
{
// 1. 寻找匹配的处理器 (SyncCameraHandler / RemoveCameraHandler)
var handler = _handlers.FirstOrDefault(h => h.ActionName == msg.CmdCode);
if (handler != null)
{
// 2. 将 Proto 的参数转为 JToken保持与原有处理器兼容
var jsonParams = JToken.Parse(msg.JsonParams);
await handler.ExecuteAsync(jsonParams);
_logger.LogInformation("[gRPC Bus] 指令 {CmdCode} 执行完成", msg.CmdCode);
}
else
{
_logger.LogWarning("[gRPC Bus] 未找到处理 {CmdCode} 的处理器", msg.CmdCode);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "[gRPC Bus] 指令执行失败: {CmdCode}", msg.CmdCode);
}
}
}
}