using System.Collections.Concurrent; using System.Diagnostics; using System.Threading.Channels; using LibreHardwareMonitor.Hardware; namespace ThingHK; /// /// 硬件访问层:封装 LibreHardwareMonitor Computer, /// 负责硬件发现、分层 Update(快/慢通道)、快照构建。 /// /// 设计要点: /// - Computer 非 IDisposable(LHB 0.9.5),用 Close() 显式释放 /// - 传感器按 SensorType 分快/慢通道: /// * 快通道(temp/load/clock/power/voltage/throughput):高频,1s /// * 慢通道(data/smalldata/factor/level/control/timing/energy 等):低频,5s /// 原因:SMART 查询会阻塞,混在一起会拖慢整体;data 类(如内存使用量)变化缓慢 /// - Update 只作用于硬件设备,传感器读取由 visitor 遍历收集(LHB 设计) /// internal sealed class HardwareManager : IDisposable { private readonly Computer _computer; private readonly SnapshotVisitor _visitor = new(); private readonly bool _isAdmin; private readonly double _coldStartMs; private readonly Stopwatch _startupSw; private bool _ready; private bool _closed; private HardwareConfig _config; // 缓存"哪个硬件属于快通道"——按 hardware type 判断更稳定(不同机型传感器命名不一致) // 实际分频逻辑见 SamplingScheduler,此处只暴露硬件列表给调度器 public IReadOnlyList AllHardware => _visitor.AllHardware; public bool IsAdmin => _isAdmin; public double ColdStartMs => _coldStartMs; public bool Ready => _ready; public HardwareConfig Config => _config; public HardwareManager(HardwareConfig config) { _config = config ?? HardwareConfig.Default; _isAdmin = IsRunningAsAdmin(); _startupSw = Stopwatch.StartNew(); // LHB 行为:主板的传感器实际通过 SubHardware(SuperIO/EC)暴露, // 而 SuperIO 作为顶层硬件时需 IsControllerEnabled 才能枚举。 // 联动策略:启用 motherboard 时自动启用 controller,避免主板勾选后无数据。 bool motherboardEnabled = _config.IsHardwareEnabled("motherboard"); bool controllerEnabled = _config.IsHardwareEnabled("controller") || motherboardEnabled; _computer = new Computer { IsCpuEnabled = _config.IsHardwareEnabled("cpu"), IsGpuEnabled = _config.IsHardwareEnabled("gpu"), IsMemoryEnabled = _config.IsHardwareEnabled("memory"), IsStorageEnabled = _config.IsHardwareEnabled("storage"), IsMotherboardEnabled = motherboardEnabled, IsControllerEnabled = controllerEnabled, IsBatteryEnabled = _config.IsHardwareEnabled("battery"), IsNetworkEnabled = _config.IsHardwareEnabled("network"), IsPsuEnabled = _config.IsHardwareEnabled("psu"), }; _computer.Open(); // 首轮扫描:发现所有硬件(Update 前的 Sensors 通常为空,但 Hardware 列表就绪) _computer.Accept(_visitor); // 首轮 Update:填充传感器值 // 注意:部分传感器第二轮才有值,调度器会持续 Update UpdateAll(); _coldStartMs = _startupSw.Elapsed.TotalMilliseconds; _startupSw.Stop(); _ready = true; } /// 热更新传感器类型过滤(无需重启 Kernel,下次 BuildSnapshot 生效) public void UpdateConfig(HardwareConfig newConfig) { _config = newConfig; } /// /// 全量 Update 所有硬件。 /// 由 SamplingScheduler 按通道分频调用。 /// public void UpdateAll() { foreach (var hw in _visitor.AllHardware) { try { hw.Update(); } catch { /* 单个硬件 Update 失败不影响整体 */ } } } /// /// 仅 Update 慢通道硬件(Storage/PSU/Battery 等)。 /// 快通道硬件(CPU/GPU/Memory/Network)由调度器更高频调用 UpdateAll。 /// public void UpdateSlowOnly() { foreach (var hw in _visitor.AllHardware) { if (IsSlowHardware(hw.HardwareType)) { try { hw.Update(); } catch { /* ignore */ } } } } /// /// 构建 SensorSnapshot:遍历当前所有传感器,不触发 Update。 /// 调用者应先 Update 再 Snapshot,避免读到旧值。 /// public SensorSnapshot BuildSnapshot() { var snap = new SensorSnapshot { SchemaVersion = 1, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), IsAdmin = _isAdmin, Ready = _ready, }; // 首个快照带上冷启动耗时,后续为 0 if (_coldStartMs > 0 && snap.Timestamp > 0) { snap.ColdStartMs = Math.Round(_coldStartMs, 1); } // 重新遍历以读取最新传感器值(visitor 缓存的是 hardware 引用,sensor 值实时) var groups = new Dictionary(); foreach (var hw in _visitor.AllHardware) { string groupId = hw.HardwareType.ToString().ToLowerInvariant(); string groupName = hw.HardwareType.ToString(); if (!groups.TryGetValue(groupId, out var g)) { g = new SensorGroup { Id = groupId, Name = groupName }; groups[groupId] = g; snap.Groups.Add(g); } foreach (var s in hw.Sensors) { // 按配置过滤传感器类型:关闭的类型不加入快照,减少传输数据量 string sensorType = s.SensorType.ToString().ToLowerInvariant(); if (!_config.IsSensorTypeEnabled(sensorType)) continue; g.Sensors.Add(new SensorEntry { Id = $"{groupId}/{hw.Name}/{s.SensorType}/{s.Name}".Replace(' ', '_').ToLowerInvariant(), Name = s.Name, Type = sensorType, Value = s.Value, Unit = UnitFor(s.SensorType), HardwareName = hw.Name, }); } } return snap; } /// /// 判断硬件是否属于慢通道(低频 Update 即可)。 /// 快通道:CPU/GPU/Memory/Network(变化快、查询轻量) /// 慢通道:Storage/PSU/Motherboard/Battery/SuperIO/EmbeddedController(查询重或变化慢) /// public static bool IsSlowHardware(HardwareType t) => t switch { HardwareType.Storage => true, HardwareType.Psu => true, HardwareType.Motherboard => true, HardwareType.Battery => true, HardwareType.SuperIO => true, HardwareType.EmbeddedController => true, _ => false, }; private static string UnitFor(SensorType t) => t switch { SensorType.Temperature => "°C", SensorType.Load => "%", SensorType.Power => "W", SensorType.Voltage => "V", SensorType.Fan => "RPM", SensorType.Clock => "MHz", SensorType.Data => "GB", SensorType.SmallData => "MB", SensorType.Frequency => "Hz", SensorType.Throughput => "B/s", SensorType.Level => "%", SensorType.Factor => "", SensorType.Control => "%", SensorType.Flow => "L/h", SensorType.TimeSpan => "s", SensorType.Energy => "mWh", SensorType.Noise => "dBA", SensorType.Conductivity => "µS/cm", SensorType.Humidity => "%", _ => "", }; private static bool IsRunningAsAdmin() { try { using var identity = System.Security.Principal.WindowsIdentity.GetCurrent(); var principal = new System.Security.Principal.WindowsPrincipal(identity); return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); } catch { return false; } } public void Dispose() { if (_closed) return; _closed = true; try { _computer.Close(); } catch { /* Close 在 AOT 下偶有反射清理异常 */ } } } /// /// 遍历 Computer,收集所有 Hardware(含 SubHardware)。 /// LHB 的 Visit 会递归到 SubHardware。 /// 引用缓存:visitor 持有 hardware 引用,传感器值通过 hardware.Sensors 实时读取。 /// internal sealed class SnapshotVisitor : IVisitor { public List AllHardware { get; } = new(); public void VisitComputer(IComputer computer) { foreach (var hw in computer.Hardware) hw.Accept(this); } public void VisitHardware(IHardware hardware) { AllHardware.Add(hardware); foreach (var sub in hardware.SubHardware) sub.Accept(this); } public void VisitSensor(ISensor sensor) { } public void VisitParameter(IParameter parameter) { } } /// /// 采样调度器:按快/慢通道分频驱动 HardwareManager.UpdateAll。 /// 使用 Channel 向 SSE 推送层广播快照(解耦:调度器不关心有几个订阅者)。 /// /// 调度策略: /// - 快通道 tick:UpdateAll(含慢通道硬件,因 UpdateAll 成本主要在 SMART,已通过慢通道分频减少调用频率) /// 实际优化:快通道 tick 只 Update 快通道硬件(UpdateFastOnly),慢通道单独按慢节奏 Update /// - 慢通道 tick:UpdateSlowOnly(仅 Storage/PSU/Motherboard 等) /// - 每个 tick 结束后构建快照并广播 /// internal sealed class SamplingScheduler : IDisposable { private readonly HardwareManager _hw; private readonly CancellationTokenSource _cts = new(); private readonly Channel _broadcast; private readonly SnapshotCache _cache; private int _fastIntervalMs = 1000; private int _slowIntervalMs = 5000; public SnapshotCache Cache => _cache; public int FastIntervalMs => _fastIntervalMs; public int SlowIntervalMs => _slowIntervalMs; public SamplingScheduler(HardwareManager hw, int fastIntervalMs, int slowIntervalMs) { _hw = hw; _fastIntervalMs = Math.Max(200, fastIntervalMs); _slowIntervalMs = Math.Max(_fastIntervalMs, slowIntervalMs); // unbounded channel:快照丢失风险 < 内存爆涨风险(消费慢时丢弃最旧的) // 这里用 bounded + DropOldest:保证 SSE 慢消费者不阻塞调度器 _broadcast = Channel.CreateBounded(new BoundedChannelOptions(8) { FullMode = BoundedChannelFullMode.DropOldest, SingleReader = false, SingleWriter = true, }); _cache = new SnapshotCache(); } /// /// 启动调度循环。立即推送首个快照(已由 HardwareManager 构造时 Update 过)。 /// public Task StartAsync() { // 首个快照(含 ColdStartMs) var first = _hw.BuildSnapshot(); _cache.Update(first); _broadcast.Writer.TryWrite(first); // 快/慢通道并行循环 _ = Task.Run(() => FastLoopAsync(_cts.Token)); _ = Task.Run(() => SlowLoopAsync(_cts.Token)); return Task.CompletedTask; } private async Task FastLoopAsync(CancellationToken ct) { using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(_fastIntervalMs)); while (await timer.WaitForNextTickAsync(ct).ConfigureAwait(false)) { try { _hw.UpdateAll(); var snap = _hw.BuildSnapshot(); _cache.Update(snap); _broadcast.Writer.TryWrite(snap); } catch (Exception ex) { Console.Error.WriteLine($"[Scheduler] 快通道异常: {ex.Message}"); } } } private async Task SlowLoopAsync(CancellationToken ct) { using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(_slowIntervalMs)); while (await timer.WaitForNextTickAsync(ct).ConfigureAwait(false)) { try { // 慢通道单独 Update,避免依赖快通道的 UpdateAll // 注:下一次快通道 tick 构建的快照会包含本次慢通道更新后的值 _hw.UpdateSlowOnly(); } catch (Exception ex) { Console.Error.WriteLine($"[Scheduler] 慢通道异常: {ex.Message}"); } } } /// /// 订阅快照流。每个 SSE 客户端调用一次。 /// 返回的 IAsyncEnumerable 会在调度器停止或订阅者取消时结束。 /// public IAsyncEnumerable SubscribeAsync(CancellationToken ct) => _broadcast.Reader.ReadAllAsync(ct); public void UpdateIntervals(int? fastMs, int? slowMs) { // 注意:PeriodicTimer 已启动后无法修改间隔,下次重启 Kernel 才生效。 // 阶段二简化处理:记录新值,实际生效需重启。阶段三若需热更新可重建 timer。 if (fastMs.HasValue && fastMs.Value >= 200) _fastIntervalMs = fastMs.Value; if (slowMs.HasValue && slowMs.Value >= _fastIntervalMs) _slowIntervalMs = slowMs.Value; } public void Dispose() { _cts.Cancel(); _broadcast.Writer.TryComplete(); _cts.Dispose(); } } /// /// 快照缓存:存储最新快照,供 GET /snapshot 直接返回,避免触发底层重扫描。 /// 线程安全:读写均加锁,快照对象本身不可变(每次 Update 替换引用)。 /// internal sealed class SnapshotCache { private readonly object _lock = new(); private SensorSnapshot? _latest; private int _sensorCount; public void Update(SensorSnapshot snap) { lock (_lock) { _latest = snap; _sensorCount = snap.Groups.Sum(g => g.Sensors.Count); } } public SensorSnapshot? GetLatest() { lock (_lock) { return _latest; } } public int SensorCount { get { lock (_lock) return _sensorCount; } } }