Add FanControl.ChromeboxEC plugin, project background, ectool docs
- Add FanControl.ChromeboxEC plugin for Fan Control - Add project background/缘由 in README (EN/zh) - Add docs/ectool-commands-zh.md - Update sln, appsettings, gitignore Made-with: Cursor
This commit is contained in:
107
FanControl.ChromeboxEC/EctoolRunner.cs
Normal file
107
FanControl.ChromeboxEC/EctoolRunner.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
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 输出中的转速 (500–50000 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 输出为摄氏温度。支持 "315 K (= 42 C)" 等格式,多传感器时取最高值。</summary>
|
||||
public static float? TryParseTemp(string stdout)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stdout))
|
||||
return null;
|
||||
|
||||
float? best = null;
|
||||
// 匹配 "315 K" 或 "315 K (= 42 C)"
|
||||
foreach (Match m in Regex.Matches(stdout, @"(\d{2,3})\s*K\b", 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;
|
||||
}
|
||||
}
|
||||
// 匹配 "= 42 C" 或 "42 C"
|
||||
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;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user