diff --git a/UIShare/GlobalVariable/HardwareDataBroadcaster.cs b/UIShare/GlobalVariable/HardwareDataBroadcaster.cs
index a5e0fbf..2380403 100644
--- a/UIShare/GlobalVariable/HardwareDataBroadcaster.cs
+++ b/UIShare/GlobalVariable/HardwareDataBroadcaster.cs
@@ -1,8 +1,10 @@
using Common.Attributes;
using DeviceCommand.Base;
+using Logger;
using Model.Models;
using Prism.Events;
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
@@ -41,6 +43,18 @@ namespace UIShare.GlobalVariable
private CancellationTokenSource? _cts;
private bool _disposed;
+ /// 是否已完成过至少一次 Discover(幂等标记,避免多作用域重复调用时 Clear + 重扫)
+ private bool _discovered;
+
+ /// 连续失败次数达到此阈值后,暂停该通道采样并发布报警
+ private const int FailureThreshold = 5;
+
+ /// 每通道连续失败计数(key = fingerprint + "|" + methodName)
+ private readonly ConcurrentDictionary _failureCounts = new();
+
+ /// 已因连续失败而被暂停的通道(key 同上)
+ private readonly ConcurrentDictionary _suspendedChannels = new();
+
/// 采样间隔(默认 1000ms)
public TimeSpan SampleInterval
{
@@ -89,9 +103,12 @@ namespace UIShare.GlobalVariable
///
/// 扫描 GlobalInfo.HardwarePool 中所有已创建的物理设备,
/// 反查可监测方法并编译为委托。每个指纹只注册一次,反射只执行一次。
+ /// 幂等:首次调用后再次调用不会 Clear 重扫,避免多作用域重复 Discover 导致短暂采样中断。
///
public void Discover()
{
+ if (_discovered) return;
+ _discovered = true;
_registeredMethods.Clear();
foreach (var poolEntry in _globalInfo.HardwarePool)
@@ -157,6 +174,11 @@ namespace UIShare.GlobalVariable
foreach (var entry in _registeredMethods)
{
+ string channelKey = entry.Fingerprint + "|" + entry.MethodName;
+
+ // 已暂停的通道跳过采样
+ if (_suspendedChannels.ContainsKey(channelKey)) continue;
+
// fire-and-forget:每个通道独立采样,完成后自行广播
_ = Task.Run(async () =>
{
@@ -167,6 +189,9 @@ namespace UIShare.GlobalVariable
if (!double.TryParse(raw, out double value)) return;
+ // 采样成功,重置失败计数
+ _failureCounts.TryRemove(channelKey, out _);
+
// 向所有引用该物理设备的作用域分别广播
var scopes = GetScopesForFingerprint(entry.Fingerprint);
foreach (var scope in scopes)
@@ -188,9 +213,24 @@ namespace UIShare.GlobalVariable
}
}
}
- catch
+ catch (Exception ex)
{
- // 单个通道故障不干扰其他通道
+ int count = _failureCounts.AddOrUpdate(channelKey, 1, (_, c) => c + 1);
+ LoggerHelper.Warn($"采样通道 [{entry.Fingerprint}/{entry.MethodName}] 第 {count} 次失败: {ex.Message}");
+
+ if (count >= FailureThreshold)
+ {
+ _suspendedChannels.TryAdd(channelKey, true);
+ LoggerHelper.Error($"采样通道 [{entry.Fingerprint}/{entry.MethodName}] 连续失败 {count} 次,已暂停采样");
+
+ // 向所有引用该设备的作用域发布报警
+ var scopes = GetScopesForFingerprint(entry.Fingerprint);
+ foreach (var scope in scopes)
+ {
+ _eventAggregator.GetEvent().Publish(
+ (scope, entry.Fingerprint, $"通道 {entry.MethodName} 连续失败 {count} 次,已暂停"));
+ }
+ }
}
}, token);
}
@@ -209,6 +249,8 @@ namespace UIShare.GlobalVariable
_cts?.Dispose();
_cts = null;
_registeredMethods.Clear();
+ _failureCounts.Clear();
+ _suspendedChannels.Clear();
}
}
}