- Add RampUpSteps and RampUpMinDeltaPercent config - Gradual ramp in both directions (up and down) to reduce fan oscillation - Fix Advanced tab layout with RowStyles and label alignment Made-with: Cursor
137 lines
5.7 KiB
C#
137 lines
5.7 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace ChromeboxFanControl;
|
|
|
|
public sealed class AppConfig
|
|
{
|
|
/// <summary>UI language: "auto" (system), "en", "zh-Hans", "zh-Hant". Restart to apply.</summary>
|
|
public string Language { get; set; } = "auto";
|
|
public string EctoolPath { get; set; } = @"C:\Program Files\crosec\ectool.exe";
|
|
public int PollIntervalMs { get; set; } = 1500;
|
|
/// <summary>Call ectool for RPM every N control cycles (1 = every cycle).</summary>
|
|
public int FanRpmPollEveryNCycles { get; set; } = 1;
|
|
public string[] FanRpmArgs { get; set; } = ["pwmgetfanrpm", "0"];
|
|
/// <summary>Ectool args to read fan duty (0-100). Empty = skip, use commanded duty.</summary>
|
|
public string[]? FanDutyArgs { get; set; }
|
|
public string[] AutoFanCtrlArgs { get; set; } = ["autofanctrl"];
|
|
public string TempSource { get; set; } = "AverageCore";
|
|
public int RampUpSteps { get; set; } = 3;
|
|
public int RampUpMinDeltaPercent { get; set; } = 20;
|
|
public int FailSafeAfterConsecutiveErrors { get; set; } = 5;
|
|
public int FailSafeFanPercent { get; set; } = 100;
|
|
public bool FailSafeRestoreAutoFan { get; set; }
|
|
public int ChartHistoryMinutes { get; set; } = 15;
|
|
public int ChartMaxPoints { get; set; } = 900;
|
|
public int[] CurvePoints { get; set; } =
|
|
[
|
|
0, 0, 10, 25, 40, 55, 70, 80, 90, 95, 97, 98, 99, 100
|
|
];
|
|
|
|
public static JsonSerializerOptions JsonOptions { get; } = new()
|
|
{
|
|
WriteIndented = true,
|
|
PropertyNameCaseInsensitive = true,
|
|
Converters = { new JsonStringEnumConverter() }
|
|
};
|
|
|
|
public static string UserConfigPath =>
|
|
Path.Combine(
|
|
Environment.UserInteractive
|
|
? Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
|
|
: Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
|
|
"ChromeboxFanControl",
|
|
"config.json");
|
|
|
|
public static AppConfig LoadMerged()
|
|
{
|
|
var cfg = new AppConfig();
|
|
var baseDir = AppContext.BaseDirectory;
|
|
var appSettings = Path.Combine(baseDir, "appsettings.json");
|
|
if (File.Exists(appSettings))
|
|
ApplyJson(File.ReadAllText(appSettings), cfg);
|
|
|
|
if (File.Exists(UserConfigPath))
|
|
{
|
|
try
|
|
{
|
|
ApplyJson(File.ReadAllText(UserConfigPath), cfg);
|
|
}
|
|
catch
|
|
{
|
|
/* keep base */
|
|
}
|
|
}
|
|
|
|
return cfg;
|
|
}
|
|
|
|
private static void ApplyJson(string json, AppConfig dst)
|
|
{
|
|
var src = JsonSerializer.Deserialize<AppConfig>(json, JsonOptions);
|
|
if (src == null)
|
|
return;
|
|
|
|
if (!string.IsNullOrEmpty(src.Language))
|
|
dst.Language = src.Language;
|
|
dst.EctoolPath = src.EctoolPath;
|
|
dst.PollIntervalMs = Math.Clamp(src.PollIntervalMs, 250, 60_000);
|
|
dst.FanRpmPollEveryNCycles = Math.Max(1, src.FanRpmPollEveryNCycles);
|
|
if (src.FanRpmArgs is { Length: > 0 })
|
|
dst.FanRpmArgs = src.FanRpmArgs;
|
|
dst.FanDutyArgs = src.FanDutyArgs;
|
|
if (src.AutoFanCtrlArgs is { Length: > 0 })
|
|
dst.AutoFanCtrlArgs = src.AutoFanCtrlArgs;
|
|
dst.TempSource = string.Equals(src.TempSource, "MaxCore", StringComparison.OrdinalIgnoreCase) ? "MaxCore" : "AverageCore";
|
|
dst.RampUpSteps = Math.Clamp(src.RampUpSteps, 1, 10);
|
|
dst.RampUpMinDeltaPercent = Math.Clamp(src.RampUpMinDeltaPercent, 0, 50);
|
|
dst.FailSafeAfterConsecutiveErrors = Math.Max(1, src.FailSafeAfterConsecutiveErrors);
|
|
dst.FailSafeFanPercent = Math.Clamp(src.FailSafeFanPercent, 0, 100);
|
|
dst.FailSafeRestoreAutoFan = src.FailSafeRestoreAutoFan;
|
|
dst.ChartHistoryMinutes = Math.Clamp(src.ChartHistoryMinutes, 1, 120);
|
|
dst.ChartMaxPoints = src.ChartMaxPoints > 0 ? src.ChartMaxPoints : 900;
|
|
if (src.CurvePoints is { Length: 14 })
|
|
dst.CurvePoints = (int[])src.CurvePoints.Clone();
|
|
else if (src.CurvePoints is { Length: 13 } old13)
|
|
dst.CurvePoints = MigrateCurveTo14(old13);
|
|
else if (src.CurvePoints is { Length: 20 } old20)
|
|
dst.CurvePoints = MigrateCurveTo14(old20, new[] { 0, 5, 11, 16, 21, 26, 32, 37, 42, 47, 53, 58, 63, 68, 74, 79, 84, 90, 95, 100 });
|
|
}
|
|
|
|
private static int[] MigrateCurveTo14(int[] oldCurve, int[]? oldTemps = null)
|
|
{
|
|
var temps = oldTemps ?? (oldCurve.Length == 13 ? new[] { 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100 } : new[] { 0, 5, 11, 16, 21, 26, 32, 37, 42, 47, 53, 58, 63, 68, 74, 79, 84, 90, 95, 100 });
|
|
var newTemps = FanCurve.TempBreakpoints;
|
|
var result = new int[14];
|
|
for (var i = 0; i < 14; i++)
|
|
{
|
|
var t = newTemps[i];
|
|
if (t <= temps[0])
|
|
result[i] = Math.Clamp(oldCurve[0], 0, 100);
|
|
else if (t >= temps[^1])
|
|
result[i] = Math.Clamp(oldCurve[^1], 0, 100);
|
|
else
|
|
{
|
|
for (var j = 0; j < temps.Length - 1; j++)
|
|
{
|
|
if (t < temps[j + 1])
|
|
{
|
|
var frac = (t - temps[j]) / (double)(temps[j + 1] - temps[j]);
|
|
result[i] = (int)Math.Round(Math.Clamp(oldCurve[j], 0, 100) + (Math.Clamp(oldCurve[j + 1], 0, 100) - Math.Clamp(oldCurve[j], 0, 100)) * frac);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public void SaveUser()
|
|
{
|
|
var dir = Path.GetDirectoryName(UserConfigPath);
|
|
if (!string.IsNullOrEmpty(dir))
|
|
Directory.CreateDirectory(dir);
|
|
File.WriteAllText(UserConfigPath, JsonSerializer.Serialize(this, JsonOptions));
|
|
}
|
|
}
|