Files
chromebox-fan-control-win/FanControl.ChromeboxEC/EctoolRunner.cs
2026-04-02 18:32:43 +08:00

111 lines
3.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace FanControl.ChromeboxEC;
/// <summary>ectool 命令封装,参见 docs/ectool-commands-zh.md</summary>
internal static class EctoolRunner
{
private static readonly Regex NumberRegex = new(@"\d+", RegexOptions.Compiled);
/// <summary>执行 ectool 命令,返回 (成功, stdout, stderr)</summary>
public static (bool ok, string stdout, string stderr) Run(
string exePath,
IReadOnlyList<string> args,
int timeoutMs = 15_000)
{
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
return (false, "", "ectool 未找到");
var psi = new ProcessStartInfo
{
FileName = exePath,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
foreach (var a in args)
psi.ArgumentList.Add(a);
using var proc = new Process { StartInfo = psi };
try
{
proc.Start();
}
catch (Exception ex)
{
return (false, "", ex.Message);
}
var stdout = proc.StandardOutput.ReadToEnd();
var stderr = proc.StandardError.ReadToEnd();
if (!proc.WaitForExit(timeoutMs))
{
try { proc.Kill(entireProcessTree: true); } catch { /* ignore */ }
return (false, "", "ectool 超时");
}
return (proc.ExitCode == 0, stdout, stderr);
}
/// <summary>解析 pwmgetfanrpm 输出中的转速 (50050000 RPM)</summary>
public static int? TryParseFanRpm(string stdout)
{
if (string.IsNullOrWhiteSpace(stdout))
return null;
int? best = null;
foreach (Match m in NumberRegex.Matches(stdout))
{
if (!int.TryParse(m.Value, out var n))
continue;
if (n is < 400 or > 50_000)
continue;
if (n >= 800 && (best == null || n > best))
best = n;
}
if (best != null)
return best;
foreach (Match m in NumberRegex.Matches(stdout))
{
if (int.TryParse(m.Value, out var n) && n is >= 400 and <= 50_000)
return n;
}
return null;
}
/// <summary>解析 temps 输出为摄氏温度。支持 "329 K (= 56 C)" 等格式,多传感器时取最高值。排除 ratio 行中的 (313 K and 333 K) 等阈值。</summary>
public static float? TryParseTemp(string stdout)
{
if (string.IsNullOrWhiteSpace(stdout))
return null;
float? best = null;
// 匹配 "= 56 C":最可靠,且不会被 ratio 行的 (313 K and 333 K) 干扰
foreach (Match m in Regex.Matches(stdout, @"=\s*(\d{1,3})\s*[Cc]\b"))
{
if (int.TryParse(m.Groups[1].Value, out var c) && c is >= 0 and <= 120)
{
if (best == null || c > best)
best = c;
}
}
if (best != null)
return best;
// 若无 "= X C",再匹配 "329 K",排除 ratio 中的 "313 K and" 与 "and 333 K"
foreach (Match m in Regex.Matches(stdout, @"(?<!and\s)(\d{2,3})\s*K\b(?!\s+and)", RegexOptions.IgnoreCase))
{
if (int.TryParse(m.Groups[1].Value, out var k) && k is >= 250 and <= 400)
{
var c = k - 273.15f;
if (best == null || c > best)
best = c;
}
}
return best;
}
}