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 { /// /// gRPC 指令接收后台服务 /// 负责:1. 逻辑注册 2. 维持指令长连接 3. 指令分发 /// public class GrpcCommandReceiverWorker : BackgroundService { private readonly ILogger _logger; private readonly ServiceConfig _config; private readonly IEnumerable _handlers; // 自动注入所有指令处理器 public GrpcCommandReceiverWorker( ILogger logger, ServiceConfig config, IEnumerable 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); } } } /// /// 指令分发逻辑 /// 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); } } } }