fix: HardwareDataBroadcaster幂等Discover与静默异常增强(失败计数+通道暂停+日志)

This commit is contained in:
2026-09-14 13:50:14 +08:00
parent 8b53d8e921
commit 8d8df56544
@@ -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;
/// <summary>是否已完成过至少一次 Discover(幂等标记,避免多作用域重复调用时 Clear + 重扫)</summary>
private bool _discovered;
/// <summary>连续失败次数达到此阈值后,暂停该通道采样并发布报警</summary>
private const int FailureThreshold = 5;
/// <summary>每通道连续失败计数(key = fingerprint + "|" + methodName</summary>
private readonly ConcurrentDictionary<string, int> _failureCounts = new();
/// <summary>已因连续失败而被暂停的通道(key 同上)</summary>
private readonly ConcurrentDictionary<string, bool> _suspendedChannels = new();
/// <summary>采样间隔(默认 1000ms</summary>
public TimeSpan SampleInterval
{
@@ -89,9 +103,12 @@ namespace UIShare.GlobalVariable
/// <summary>
/// 扫描 GlobalInfo.HardwarePool 中所有已创建的物理设备,
/// 反查可监测方法并编译为委托。每个指纹只注册一次,反射只执行一次。
/// <para>幂等:首次调用后再次调用不会 Clear 重扫,避免多作用域重复 Discover 导致短暂采样中断。</para>
/// </summary>
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<AlarmEvent>().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();
}
}
}