日常更新
This commit is contained in:
@@ -1,115 +1,115 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ChromeboxFanControl;
|
||||
|
||||
public sealed class EctoolRunner
|
||||
{
|
||||
private static readonly Regex NumberRegex = new(@"\d+", RegexOptions.Compiled);
|
||||
|
||||
public static (bool ok, string stdout, string stderr) RunSync(
|
||||
string exePath,
|
||||
IReadOnlyList<string> args,
|
||||
int timeoutMs = 15_000) =>
|
||||
RunAsync(exePath, args, CancellationToken.None, timeoutMs).GetAwaiter().GetResult();
|
||||
|
||||
public static async Task<(bool ok, string stdout, string stderr)> RunAsync(
|
||||
string exePath,
|
||||
IReadOnlyList<string> args,
|
||||
CancellationToken ct = default,
|
||||
int timeoutMs = 15_000)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||
return (false, "", "ectool executable not found.");
|
||||
|
||||
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 stdoutTask = proc.StandardOutput.ReadToEndAsync(ct);
|
||||
var stderrTask = proc.StandardError.ReadToEndAsync(ct);
|
||||
var waitTask = proc.WaitForExitAsync(ct);
|
||||
var completed = await Task.WhenAny(waitTask, Task.Delay(timeoutMs, ct)).ConfigureAwait(false);
|
||||
if (completed != waitTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Kill(entireProcessTree: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return (false, "", "ectool timed out.");
|
||||
}
|
||||
|
||||
await waitTask.ConfigureAwait(false);
|
||||
var stdout = await stdoutTask.ConfigureAwait(false);
|
||||
var stderr = await stderrTask.ConfigureAwait(false);
|
||||
var ok = proc.ExitCode == 0;
|
||||
return (ok, stdout, stderr);
|
||||
}
|
||||
|
||||
/// <summary>Pick the most plausible RPM (typically 500–20000) from ectool text output.</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;
|
||||
// Prefer larger numbers in typical fan range (avoid fan index "0")
|
||||
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 >= 0 and <= 50_000)
|
||||
return n;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Pick duty percent (0-100) from ectool text output.</summary>
|
||||
public static int? TryParseFanDuty(string stdout)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stdout))
|
||||
return null;
|
||||
|
||||
foreach (Match m in NumberRegex.Matches(stdout))
|
||||
{
|
||||
if (int.TryParse(m.Value, out var n) && n is >= 0 and <= 100)
|
||||
return n;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ChromeboxFanControl;
|
||||
|
||||
public sealed class EctoolRunner
|
||||
{
|
||||
private static readonly Regex NumberRegex = new(@"\d+", RegexOptions.Compiled);
|
||||
|
||||
public static (bool ok, string stdout, string stderr) RunSync(
|
||||
string exePath,
|
||||
IReadOnlyList<string> args,
|
||||
int timeoutMs = 15_000) =>
|
||||
RunAsync(exePath, args, CancellationToken.None, timeoutMs).GetAwaiter().GetResult();
|
||||
|
||||
public static async Task<(bool ok, string stdout, string stderr)> RunAsync(
|
||||
string exePath,
|
||||
IReadOnlyList<string> args,
|
||||
CancellationToken ct = default,
|
||||
int timeoutMs = 15_000)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(exePath) || !File.Exists(exePath))
|
||||
return (false, "", "ectool executable not found.");
|
||||
|
||||
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 stdoutTask = proc.StandardOutput.ReadToEndAsync(ct);
|
||||
var stderrTask = proc.StandardError.ReadToEndAsync(ct);
|
||||
var waitTask = proc.WaitForExitAsync(ct);
|
||||
var completed = await Task.WhenAny(waitTask, Task.Delay(timeoutMs, ct)).ConfigureAwait(false);
|
||||
if (completed != waitTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.Kill(entireProcessTree: true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
return (false, "", "ectool timed out.");
|
||||
}
|
||||
|
||||
await waitTask.ConfigureAwait(false);
|
||||
var stdout = await stdoutTask.ConfigureAwait(false);
|
||||
var stderr = await stderrTask.ConfigureAwait(false);
|
||||
var ok = proc.ExitCode == 0;
|
||||
return (ok, stdout, stderr);
|
||||
}
|
||||
|
||||
/// <summary>Pick the most plausible RPM (typically 500–20000) from ectool text output.</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;
|
||||
// Prefer larger numbers in typical fan range (avoid fan index "0")
|
||||
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 >= 0 and <= 50_000)
|
||||
return n;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Pick duty percent (0-100) from ectool text output.</summary>
|
||||
public static int? TryParseFanDuty(string stdout)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(stdout))
|
||||
return null;
|
||||
|
||||
foreach (Match m in NumberRegex.Matches(stdout))
|
||||
{
|
||||
if (int.TryParse(m.Value, out var n) && n is >= 0 and <= 100)
|
||||
return n;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user