571 lines
20 KiB
C#
571 lines
20 KiB
C#
using ChromeboxFanControl.Properties;
|
|
using ScottPlot;
|
|
using ScottPlot.WinForms;
|
|
|
|
namespace ChromeboxFanControl;
|
|
|
|
public sealed class MainForm : Form
|
|
{
|
|
private AppConfig _config = AppConfig.LoadMerged();
|
|
private FanController _fan = null!;
|
|
private readonly FormsPlot _chart = new() { Dock = DockStyle.Fill };
|
|
private readonly FormsPlot _curveChart = new() { Dock = DockStyle.Fill };
|
|
private readonly System.Windows.Forms.Label _lblStatus = new();
|
|
private readonly DataGridView _gridCurve = new();
|
|
private readonly TextBox _txtEctool = new();
|
|
private readonly NumericUpDown _nudPoll = new();
|
|
private readonly NumericUpDown _nudRpmEvery = new();
|
|
private readonly TextBox _txtFanRpmArgs = new();
|
|
private readonly TextBox _txtFanDutyArgs = new();
|
|
private readonly TextBox _txtAutoFanArgs = new();
|
|
private readonly ComboBox _cmbTempSource = new();
|
|
private readonly ComboBox _cmbLanguage = new();
|
|
private readonly NumericUpDown _nudFailCount = new();
|
|
private readonly NumericUpDown _nudFailPct = new();
|
|
private readonly CheckBox _chkFailAuto = new();
|
|
private readonly NumericUpDown _nudChartMin = new();
|
|
private readonly NumericUpDown _nudChartPoints = new();
|
|
private readonly NotifyIcon _tray = new();
|
|
private readonly ContextMenuStrip _trayMenu = new();
|
|
private readonly ToolStripMenuItem _miPause;
|
|
private readonly object _seriesLock = new();
|
|
private readonly List<double> _tSec = new();
|
|
private readonly List<double> _temps = new();
|
|
private readonly List<double> _duties = new();
|
|
private readonly List<double> _rpms = new();
|
|
private DateTime _chartAnchor = DateTime.UtcNow;
|
|
private bool _minimizeToTray = true;
|
|
private ScottPlot.IYAxis? _yRpmAxis;
|
|
|
|
public MainForm()
|
|
{
|
|
_miPause = new ToolStripMenuItem(Resources.TrayPause);
|
|
Text = Resources.AppTitle;
|
|
Size = new Size(960, 700);
|
|
StartPosition = FormStartPosition.CenterScreen;
|
|
MinimumSize = new Size(800, 500);
|
|
|
|
BuildUi();
|
|
WireTray();
|
|
|
|
Load += (_, _) =>
|
|
{
|
|
_config = AppConfig.LoadMerged();
|
|
_chartAnchor = DateTime.UtcNow;
|
|
ApplyConfigToUi();
|
|
_fan = new FanController(CloneConfigFromUi());
|
|
_fan.Sample += FanOnSample;
|
|
_fan.Start();
|
|
};
|
|
|
|
FormClosing += MainForm_FormClosing;
|
|
}
|
|
|
|
private void BuildUi()
|
|
{
|
|
var tabs = new TabControl { Dock = DockStyle.Fill };
|
|
|
|
var tabMonitor = new TabPage(Resources.TabMonitor);
|
|
var tabCurve = new TabPage(Resources.TabCurve);
|
|
var tabAdv = new TabPage(Resources.TabAdvanced);
|
|
|
|
tabs.TabPages.Add(tabMonitor);
|
|
tabs.TabPages.Add(tabCurve);
|
|
tabs.TabPages.Add(tabAdv);
|
|
|
|
_lblStatus.AutoSize = false;
|
|
_lblStatus.Height = 28;
|
|
_lblStatus.Dock = DockStyle.Bottom;
|
|
_lblStatus.Padding = new Padding(8, 4, 8, 4);
|
|
_lblStatus.AutoEllipsis = true;
|
|
_lblStatus.Text = Resources.StatusStarting;
|
|
|
|
var panelChart = new Panel { Dock = DockStyle.Fill };
|
|
panelChart.Controls.Add(_chart);
|
|
panelChart.Controls.Add(_lblStatus);
|
|
|
|
tabMonitor.Controls.Add(panelChart);
|
|
|
|
_gridCurve.Dock = DockStyle.Top;
|
|
_gridCurve.Height = 260;
|
|
_gridCurve.AutoGenerateColumns = false;
|
|
_gridCurve.AllowUserToAddRows = false;
|
|
_gridCurve.RowHeadersVisible = false;
|
|
_gridCurve.Columns.Add(new DataGridViewTextBoxColumn
|
|
{
|
|
Name = "colTemp",
|
|
HeaderText = Resources.GridColTemp,
|
|
ReadOnly = true,
|
|
Width = 80
|
|
});
|
|
_gridCurve.Columns.Add(new DataGridViewTextBoxColumn
|
|
{
|
|
Name = "colDuty",
|
|
HeaderText = Resources.GridColDuty,
|
|
Width = 100
|
|
});
|
|
for (var i = 0; i < 14; i++)
|
|
_gridCurve.Rows.Add(FanCurve.TempBreakpoints[i].ToString(), "0");
|
|
_gridCurve.CellValueChanged += (_, _) => RebuildCurveChart();
|
|
|
|
var panelCurveBtns = new FlowLayoutPanel
|
|
{
|
|
Dock = DockStyle.Bottom,
|
|
AutoSize = true,
|
|
FlowDirection = FlowDirection.LeftToRight,
|
|
Padding = new Padding(8)
|
|
};
|
|
var btnSave = new Button { Text = Resources.BtnSaveApply, AutoSize = true };
|
|
btnSave.Click += (_, _) => SaveFromUi();
|
|
var btnDefault = new Button { Text = Resources.BtnRestoreDefault, AutoSize = true };
|
|
btnDefault.Click += (_, _) =>
|
|
{
|
|
var def = new AppConfig();
|
|
for (var i = 0; i < 14; i++)
|
|
_gridCurve.Rows[i].Cells[1].Value = def.CurvePoints[i].ToString();
|
|
RebuildCurveChart();
|
|
};
|
|
panelCurveBtns.Controls.Add(btnSave);
|
|
panelCurveBtns.Controls.Add(btnDefault);
|
|
|
|
var panelCurveTop = new Panel { Dock = DockStyle.Top, Height = 360 };
|
|
panelCurveTop.Controls.Add(_gridCurve);
|
|
panelCurveTop.Controls.Add(panelCurveBtns);
|
|
|
|
var panelCurve = new Panel { Dock = DockStyle.Fill };
|
|
panelCurve.Controls.Add(_curveChart);
|
|
panelCurve.Controls.Add(panelCurveTop);
|
|
tabCurve.Controls.Add(panelCurve);
|
|
|
|
var flp = new TableLayoutPanel
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
ColumnCount = 2,
|
|
RowCount = 13,
|
|
Padding = new Padding(12)
|
|
};
|
|
flp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 35));
|
|
flp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 65));
|
|
|
|
var row = 0;
|
|
AddRow(flp, row++, Resources.LblLanguage, _cmbLanguage);
|
|
_cmbLanguage.DropDownStyle = ComboBoxStyle.DropDownList;
|
|
_cmbLanguage.Items.AddRange([Resources.LangAuto, Resources.LangEn, Resources.LangZhHans, Resources.LangZhHant]);
|
|
|
|
AddRow(flp, row++, Resources.LblEctoolPath, _txtEctool);
|
|
AddRow(flp, row++, Resources.LblPollInterval, _nudPoll);
|
|
_nudPoll.Minimum = 250;
|
|
_nudPoll.Maximum = 60_000;
|
|
_nudPoll.Increment = 250;
|
|
|
|
AddRow(flp, row++, Resources.LblRpmEvery, _nudRpmEvery);
|
|
_nudRpmEvery.Minimum = 1;
|
|
_nudRpmEvery.Maximum = 100;
|
|
|
|
AddRow(flp, row++, Resources.LblFanRpmArgs, _txtFanRpmArgs);
|
|
AddRow(flp, row++, Resources.LblFanDutyArgs, _txtFanDutyArgs);
|
|
|
|
AddRow(flp, row++, Resources.LblAutoFanArgs, _txtAutoFanArgs);
|
|
|
|
AddRow(flp, row++, Resources.LblTempSource, _cmbTempSource);
|
|
_cmbTempSource.DropDownStyle = ComboBoxStyle.DropDownList;
|
|
_cmbTempSource.Items.AddRange(["AverageCore", "MaxCore"]);
|
|
|
|
AddRow(flp, row++, Resources.LblFailCount, _nudFailCount);
|
|
_nudFailCount.Minimum = 1;
|
|
_nudFailCount.Maximum = 100;
|
|
|
|
AddRow(flp, row++, Resources.LblFailPct, _nudFailPct);
|
|
_nudFailPct.Minimum = 0;
|
|
_nudFailPct.Maximum = 100;
|
|
|
|
_chkFailAuto.Text = Resources.ChkFailAuto;
|
|
_chkFailAuto.AutoSize = true;
|
|
var failPanel = new FlowLayoutPanel
|
|
{
|
|
FlowDirection = FlowDirection.LeftToRight,
|
|
AutoSize = true,
|
|
Dock = DockStyle.Fill,
|
|
WrapContents = false
|
|
};
|
|
failPanel.Controls.Add(_chkFailAuto);
|
|
flp.Controls.Add(new System.Windows.Forms.Label { Text = Resources.LblFailStrategy, AutoSize = true, Anchor = AnchorStyles.Left }, 0, row);
|
|
flp.Controls.Add(failPanel, 1, row++);
|
|
|
|
AddRow(flp, row++, Resources.LblChartMin, _nudChartMin);
|
|
_nudChartMin.Minimum = 1;
|
|
_nudChartMin.Maximum = 120;
|
|
|
|
AddRow(flp, row++, Resources.LblChartPoints, _nudChartPoints);
|
|
_nudChartPoints.Minimum = 100;
|
|
_nudChartPoints.Maximum = 10_000;
|
|
_nudChartPoints.Increment = 100;
|
|
|
|
var btnSaveAdv = new Button { Text = Resources.BtnSaveAdvanced, AutoSize = true, Dock = DockStyle.Bottom };
|
|
btnSaveAdv.Click += (_, _) => SaveFromUi();
|
|
tabAdv.Controls.Add(btnSaveAdv);
|
|
tabAdv.Controls.Add(flp);
|
|
|
|
Controls.Add(tabs);
|
|
}
|
|
|
|
private static void AddRow(TableLayoutPanel t, int row, string label, Control editor)
|
|
{
|
|
t.Controls.Add(new System.Windows.Forms.Label { Text = label, AutoSize = true, Anchor = AnchorStyles.Left }, 0, row);
|
|
editor.Dock = DockStyle.Fill;
|
|
t.Controls.Add(editor, 1, row);
|
|
}
|
|
|
|
private void WireTray()
|
|
{
|
|
_tray.Text = Resources.AppTitle;
|
|
_tray.Visible = true;
|
|
_tray.Icon = SystemIcons.Application;
|
|
_tray.DoubleClick += (_, _) => ShowFromTray();
|
|
_miPause.Click += (_, _) =>
|
|
{
|
|
TogglePause();
|
|
};
|
|
_trayMenu.Items.Add(Resources.TrayOpen, null, (_, _) => ShowFromTray());
|
|
_trayMenu.Items.Add(_miPause);
|
|
_trayMenu.Items.Add(Resources.TrayAbout, null, (_, _) =>
|
|
MessageBox.Show(this, Resources.AboutMessage, Text, MessageBoxButtons.OK, MessageBoxIcon.Information));
|
|
_trayMenu.Items.Add(Resources.TrayExit, null, (_, _) => Close());
|
|
_tray.ContextMenuStrip = _trayMenu;
|
|
}
|
|
|
|
private void ShowFromTray()
|
|
{
|
|
Show();
|
|
WindowState = FormWindowState.Normal;
|
|
ShowInTaskbar = true;
|
|
Activate();
|
|
}
|
|
|
|
private void TogglePause()
|
|
{
|
|
if (_fan == null)
|
|
return;
|
|
_fan.Paused = !_fan.Paused;
|
|
_miPause.Text = _fan.Paused ? Resources.TrayResume : Resources.TrayPause;
|
|
}
|
|
|
|
private static readonly string[] LanguageValues = ["auto", "en", "zh-Hans", "zh-Hant"];
|
|
|
|
private void ApplyConfigToUi()
|
|
{
|
|
var langIdx = Array.IndexOf(LanguageValues, _config.Language);
|
|
_cmbLanguage.SelectedIndex = langIdx >= 0 ? langIdx : 0;
|
|
|
|
_txtEctool.Text = _config.EctoolPath;
|
|
_nudPoll.Value = _config.PollIntervalMs;
|
|
_nudRpmEvery.Value = _config.FanRpmPollEveryNCycles;
|
|
_txtFanRpmArgs.Text = string.Join(' ', _config.FanRpmArgs);
|
|
_txtFanDutyArgs.Text = _config.FanDutyArgs is { Length: > 0 } da ? string.Join(' ', da) : "";
|
|
_txtAutoFanArgs.Text = string.Join(' ', _config.AutoFanCtrlArgs);
|
|
_cmbTempSource.SelectedItem = _config.TempSource;
|
|
if (_cmbTempSource.SelectedIndex < 0)
|
|
_cmbTempSource.SelectedIndex = 0;
|
|
_nudFailCount.Value = _config.FailSafeAfterConsecutiveErrors;
|
|
_nudFailPct.Value = _config.FailSafeFanPercent;
|
|
_chkFailAuto.Checked = _config.FailSafeRestoreAutoFan;
|
|
_nudChartMin.Value = _config.ChartHistoryMinutes;
|
|
_nudChartPoints.Value = _config.ChartMaxPoints;
|
|
for (var i = 0; i < 14; i++)
|
|
_gridCurve.Rows[i].Cells[1].Value = _config.CurvePoints[i].ToString();
|
|
RebuildCurveChart();
|
|
}
|
|
|
|
private AppConfig CloneConfigFromUi()
|
|
{
|
|
var langIdx = _cmbLanguage.SelectedIndex;
|
|
var lang = langIdx >= 0 && langIdx < LanguageValues.Length ? LanguageValues[langIdx] : "auto";
|
|
|
|
var c = new AppConfig
|
|
{
|
|
Language = lang,
|
|
EctoolPath = _txtEctool.Text.Trim(),
|
|
PollIntervalMs = (int)_nudPoll.Value,
|
|
FanRpmPollEveryNCycles = (int)_nudRpmEvery.Value,
|
|
FanRpmArgs = SplitArgs(_txtFanRpmArgs.Text),
|
|
FanDutyArgs = SplitArgs(_txtFanDutyArgs.Text) is { Length: > 0 } fa ? fa : null,
|
|
AutoFanCtrlArgs = SplitArgs(_txtAutoFanArgs.Text),
|
|
TempSource = _cmbTempSource.SelectedItem?.ToString() ?? "AverageCore",
|
|
FailSafeAfterConsecutiveErrors = (int)_nudFailCount.Value,
|
|
FailSafeFanPercent = (int)_nudFailPct.Value,
|
|
FailSafeRestoreAutoFan = _chkFailAuto.Checked,
|
|
ChartHistoryMinutes = (int)_nudChartMin.Value,
|
|
ChartMaxPoints = (int)_nudChartPoints.Value,
|
|
CurvePoints = ReadCurveFromGrid()
|
|
};
|
|
return c;
|
|
}
|
|
|
|
private static string[] SplitArgs(string line) =>
|
|
string.IsNullOrWhiteSpace(line)
|
|
? []
|
|
: line.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
private int[] ReadCurveFromGrid()
|
|
{
|
|
var pts = new int[14];
|
|
for (var i = 0; i < 14; i++)
|
|
{
|
|
var cell = _gridCurve.Rows[i].Cells[1].Value?.ToString();
|
|
if (!int.TryParse(cell, out var v))
|
|
v = 0;
|
|
pts[i] = Math.Clamp(v, 0, 100);
|
|
}
|
|
|
|
return pts;
|
|
}
|
|
|
|
private void RebuildCurveChart()
|
|
{
|
|
var temps = FanCurve.TempBreakpoints.Select(t => (double)t).ToArray();
|
|
var duties = ReadCurveFromGrid().Select(d => (double)d).ToArray();
|
|
|
|
var plt = _curveChart.Plot;
|
|
plt.Clear();
|
|
plt.Axes.Left.Label.Text = Resources.CurveChartDuty;
|
|
plt.Axes.Bottom.Label.Text = Resources.CurveChartTemp;
|
|
plt.Axes.SetLimitsX(0, 100);
|
|
plt.Axes.SetLimitsY(0, 100);
|
|
|
|
if (temps.Length > 0 && duties.Length > 0)
|
|
{
|
|
var scatter = plt.Add.Scatter(temps, duties);
|
|
scatter.Color = Colors.SteelBlue;
|
|
scatter.LineWidth = 2;
|
|
scatter.MarkerSize = 6;
|
|
}
|
|
|
|
plt.Font.Automatic();
|
|
_curveChart.Refresh();
|
|
}
|
|
|
|
private void SaveFromUi()
|
|
{
|
|
try
|
|
{
|
|
var rpmArgs = SplitArgs(_txtFanRpmArgs.Text);
|
|
if (rpmArgs.Length == 0)
|
|
{
|
|
MessageBox.Show(this, Resources.ErrRpmArgsEmpty, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
var autoArgs = SplitArgs(_txtAutoFanArgs.Text);
|
|
if (autoArgs.Length == 0)
|
|
{
|
|
MessageBox.Show(this, Resources.ErrAutoFanArgsEmpty, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
return;
|
|
}
|
|
|
|
var langBefore = _config.Language;
|
|
_config = CloneConfigFromUi();
|
|
_config.SaveUser();
|
|
_fan?.UpdateConfig(_config);
|
|
var msg = Resources.MsgSaved;
|
|
if (langBefore != _config.Language)
|
|
msg = Resources.MsgRestartToApplyLanguage;
|
|
MessageBox.Show(this, msg, Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
}
|
|
|
|
private void FanOnSample(object? sender, FanSampleEventArgs e)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
if (InvokeRequired)
|
|
BeginInvoke(new MethodInvoker(() => OnSampleUi(e)));
|
|
else
|
|
OnSampleUi(e);
|
|
}
|
|
|
|
private void OnSampleUi(FanSampleEventArgs e)
|
|
{
|
|
var sec = (e.Time.ToUniversalTime() - _chartAnchor).TotalSeconds;
|
|
var temp = e.TempC ?? double.NaN;
|
|
var duty = (e.ActualDutyPercent ?? e.TargetDutyPercent) is { } d ? d : double.NaN;
|
|
var rpm = e.Rpm.HasValue ? e.Rpm.Value : double.NaN;
|
|
|
|
lock (_seriesLock)
|
|
{
|
|
_tSec.Add(sec);
|
|
_temps.Add(temp);
|
|
_duties.Add(duty);
|
|
_rpms.Add(rpm);
|
|
TrimSeries();
|
|
}
|
|
|
|
var tStr = e.TempC.HasValue ? $"{e.TempC:F1} °C" : "—";
|
|
var dVal = e.ActualDutyPercent ?? e.TargetDutyPercent;
|
|
var dStr = dVal.HasValue ? $"{dVal} %" : "—";
|
|
var rStr = e.Rpm.HasValue ? $"{e.Rpm} RPM" : "—";
|
|
var state = e.Paused ? Resources.StatusPaused : e.FailSafe ? Resources.StatusFailSafe : Resources.StatusRunning;
|
|
_lblStatus.Text =
|
|
$"{state} {Resources.LabelTemp} {tStr} {Resources.LabelTargetDuty} {dStr} {Resources.LabelRpm} {rStr} ectool: {e.LastEctoolMessage ?? "—"}";
|
|
|
|
_tray.Text = $"{Resources.AppTitle} {tStr} {dStr}";
|
|
|
|
RebuildChart();
|
|
}
|
|
|
|
private void TrimSeries()
|
|
{
|
|
var maxPts = _config.ChartMaxPoints;
|
|
var winSec = Math.Max(60, _config.ChartHistoryMinutes * 60.0);
|
|
while (_tSec.Count > 0 && _tSec.Count > maxPts)
|
|
{
|
|
_tSec.RemoveAt(0);
|
|
_temps.RemoveAt(0);
|
|
_duties.RemoveAt(0);
|
|
_rpms.RemoveAt(0);
|
|
}
|
|
|
|
if (_tSec.Count == 0)
|
|
return;
|
|
var last = _tSec[^1];
|
|
while (_tSec.Count > 0 && last - _tSec[0] > winSec)
|
|
{
|
|
_tSec.RemoveAt(0);
|
|
_temps.RemoveAt(0);
|
|
_duties.RemoveAt(0);
|
|
_rpms.RemoveAt(0);
|
|
}
|
|
}
|
|
|
|
private void RebuildChart()
|
|
{
|
|
double[] x, t, d, r;
|
|
lock (_seriesLock)
|
|
{
|
|
x = _tSec.ToArray();
|
|
t = _temps.ToArray();
|
|
d = _duties.ToArray();
|
|
r = _rpms.ToArray();
|
|
}
|
|
|
|
var plt = _chart.Plot;
|
|
plt.Clear();
|
|
|
|
plt.Axes.Left.Label.Text = Resources.ChartTemp;
|
|
plt.Axes.Bottom.Label.Text = Resources.ChartTime;
|
|
plt.Axes.Right.IsVisible = true;
|
|
plt.Axes.Right.Label.Text = Resources.ChartDuty;
|
|
|
|
if (_yRpmAxis == null)
|
|
_yRpmAxis = plt.Axes.AddRightAxis();
|
|
_yRpmAxis.Label.Text = Resources.ChartRpm;
|
|
|
|
if (x.Length > 0)
|
|
{
|
|
var st = plt.Add.Scatter(x, t);
|
|
st.Axes.YAxis = plt.Axes.Left;
|
|
st.LegendText = string.Empty;
|
|
st.Color = Colors.Blue;
|
|
st.LineWidth = 2;
|
|
st.MarkerSize = 0;
|
|
|
|
var sd = plt.Add.Scatter(x, d);
|
|
sd.Axes.YAxis = plt.Axes.Right;
|
|
sd.LegendText = string.Empty;
|
|
sd.Color = Colors.Orange;
|
|
sd.LineWidth = 2;
|
|
sd.MarkerSize = 0;
|
|
|
|
var sr = plt.Add.Scatter(x, r);
|
|
sr.Axes.YAxis = _yRpmAxis;
|
|
sr.LegendText = string.Empty;
|
|
sr.Color = Colors.Green;
|
|
sr.LineWidth = 2;
|
|
sr.MarkerSize = 0;
|
|
|
|
var tempValid = t.Where(v => !double.IsNaN(v)).ToArray();
|
|
if (tempValid.Length > 0)
|
|
{
|
|
var tMin = tempValid.Min();
|
|
var tMax = tempValid.Max();
|
|
var pad = Math.Max(5, (tMax - tMin) * 0.1);
|
|
plt.Axes.SetLimitsY(Math.Max(0, tMin - pad), tMax + pad, plt.Axes.Left);
|
|
}
|
|
else
|
|
{
|
|
plt.Axes.SetLimitsY(0, 100, plt.Axes.Left);
|
|
}
|
|
|
|
plt.Axes.SetLimitsY(0, 100, plt.Axes.Right);
|
|
|
|
var rpmValid = r.Where(v => !double.IsNaN(v) && v > 0).ToArray();
|
|
if (rpmValid.Length > 0)
|
|
{
|
|
var rMin = rpmValid.Min();
|
|
var rMax = rpmValid.Max();
|
|
var rpmRange = rMax - rMin;
|
|
var pad = Math.Max(500, Math.Max(rpmRange * 0.2, rMax * 0.15));
|
|
plt.Axes.SetLimitsY(Math.Max(0, rMin - pad * 0.2), rMax + pad, _yRpmAxis);
|
|
}
|
|
else
|
|
{
|
|
plt.Axes.SetLimitsY(0, 6000, _yRpmAxis);
|
|
}
|
|
|
|
var xMin = x[0];
|
|
var xMax = x[^1];
|
|
var range = xMax - xMin;
|
|
var xMargin = Math.Min(10, Math.Max(2, range * 0.03));
|
|
plt.Axes.SetLimitsX(Math.Max(0, xMin - xMargin * 0.5), xMax + xMargin);
|
|
}
|
|
else
|
|
{
|
|
plt.Axes.SetLimitsY(0, 6000, _yRpmAxis);
|
|
}
|
|
|
|
LegendItem[] leg =
|
|
[
|
|
new() { LineColor = Colors.Blue, LineWidth = 2, LabelText = Resources.LegendCelsius },
|
|
new() { LineColor = Colors.Orange, LineWidth = 2, LabelText = Resources.LegendDuty },
|
|
new() { LineColor = Colors.Green, LineWidth = 2, LabelText = Resources.ChartRpm }
|
|
];
|
|
plt.ShowLegend(leg, Alignment.UpperRight);
|
|
plt.Legend.Orientation = ScottPlot.Orientation.Horizontal;
|
|
plt.Font.Automatic();
|
|
_chart.Refresh();
|
|
}
|
|
|
|
private void MainForm_FormClosing(object? sender, FormClosingEventArgs e)
|
|
{
|
|
_tray.Visible = false;
|
|
_tray.Dispose();
|
|
|
|
try
|
|
{
|
|
_fan?.Stop();
|
|
var cfg = _config;
|
|
if (File.Exists(cfg.EctoolPath))
|
|
EctoolRunner.RunSync(cfg.EctoolPath, cfg.AutoFanCtrlArgs);
|
|
}
|
|
catch
|
|
{
|
|
/* best effort */
|
|
}
|
|
|
|
_fan?.Dispose();
|
|
}
|
|
|
|
protected override void OnResize(EventArgs e)
|
|
{
|
|
base.OnResize(e);
|
|
if (WindowState == FormWindowState.Minimized && _minimizeToTray)
|
|
{
|
|
Hide();
|
|
ShowInTaskbar = false;
|
|
}
|
|
}
|
|
}
|