using Microsoft.AspNetCore.SignalR; using SHH.Contracts; using System.Collections.Concurrent; namespace SHH.CameraDashboard { /// /// [SignalR 中心] /// 职责:负责物理链路的维护,将网络消息路由到业务层 (CommandServer) /// public class CameraHub : Hub { // 静态映射表:InstanceId -> ConnectionId (用于定向发指令) private static readonly ConcurrentDictionary _instanceMapping = new(); // ================================================================= // 1. 注册逻辑 (替代原 CommandServer.HandleRegistration) // ================================================================= /// /// 供 CameraService 调用 /// public async Task Register(RegisterPayload payload) { string connectionId = Context.ConnectionId; // 1. 记录映射关系 _instanceMapping[payload.InstanceId] = connectionId; // 2. 将连接加入组 (按 InstanceId 组队,方便 CommandServer 发指令) await Groups.AddToGroupAsync(connectionId, payload.InstanceId); // 3. 回调业务单例处理 UI 逻辑 CommandServer.Instance.HandleClientRegister(payload); Console.WriteLine($"[Hub] 收到注册: {payload.InstanceId}, 连接ID: {connectionId}"); } // ================================================================= // 2. 指令回执 // ================================================================= /// /// 供 CameraService 执行完指令后调用 /// public void ReportCommandResult(CommandResult result) { CommandServer.Instance.HandleCommandResult(result); } // ================================================================= // 3. 视频流中转 // ================================================================= /// /// 供 CameraService 上传图片 /// public async Task UploadFrame(VideoPayload payload) { // 转发给订阅了该摄像头的组 (例如 Dashboard UI 订阅了该组) await Clients.Group($"Watch_{payload.CameraId}").SendAsync("OnFrameRecv", payload); } // ================================================================= // 生命周期管理 // ================================================================= public override async Task OnDisconnectedAsync(Exception? exception) { // 清理映射逻辑可以在这里扩展 await base.OnDisconnectedAsync(exception); } } }