using System.Net.NetworkInformation; namespace SHH.CameraSdk; /// /// [状态代理] 网络连通性哨兵 /// 特性: /// 1. 低耦合:不依赖具体驱动,只依赖接口 /// 2. 高性能:使用 Parallel.ForEachAsync 实现受控并行 /// 3. 智能策略:播放中不Ping,空闲时才Ping /// public class ConnectivitySentinel { private readonly CameraManager _manager; // [cite: 329] private readonly PeriodicTimer _timer; private readonly CancellationTokenSource _cts = new(); // [关键配置] 最大并发度 // 建议值:CPU 核心数 * 4,或者固定 16-32 // 50 个摄像头,设为 16,意味着分 4 批完成,总耗时极短 private const int MAX_PARALLELISM = 16; public ConnectivitySentinel(CameraManager manager) { _manager = manager; // 每 3 秒执行一轮全量巡检 _timer = new PeriodicTimer(TimeSpan.FromSeconds(3)); // 启动后台任务(不阻塞主线程) _ = RunLoopAsync(); } private async Task RunLoopAsync() { try { // 等待下一个 3秒 周期 while (await _timer.WaitForNextTickAsync(_cts.Token)) { // 1. 获取当前所有设备的快照 // CameraManager.GetAllDevices() 返回的是 BaseVideoSource,它实现了 IDeviceConnectivity var devices = _manager.GetAllDevices().Cast(); // 2. [核心回答] 受控并行执行 // .NET 6+ 提供的超级 API,专门解决“一下子 50 个”的问题 await Parallel.ForEachAsync(devices, new ParallelOptions { MaxDegreeOfParallelism = MAX_PARALLELISM, CancellationToken = _cts.Token }, async (device, token) => { // 对每个设备执行独立检查 await CheckSingleDeviceAsync(device); }); } } catch (OperationCanceledException) { /* 正常停止 */ } } private async Task CheckSingleDeviceAsync(IDeviceConnectivity device) { bool isAlive = false; // [智能策略]:如果设备正在取流,直接检查帧心跳(省流模式) if (device.Status == VideoSourceStatus.Playing || device.Status == VideoSourceStatus.Streaming) { long now = Environment.TickCount64; // 5秒内有帧,就算在线 isAlive = (now - device.LastFrameTick) < 5000; } else { // [主动探测]:空闲或离线时,发射 ICMP Ping isAlive = await PingAsync(device.IpAddress); } // [状态注入]:将探测结果“注入”回设备 device.SetNetworkStatus(isAlive); } // 纯粹的 Ping 逻辑 private async Task PingAsync(string ip) { try { using var ping = new Ping(); // 超时设为 800ms,快速失败,避免拖慢整体批次 var reply = await ping.SendPingAsync(ip, 800); return reply.Status == IPStatus.Success; } catch { return false; } } public void Stop() { _cts.Cancel(); _timer.Dispose(); } }