更新12123

This commit is contained in:
GukSang.Jin
2026-06-13 20:01:50 +08:00
parent cfc6d2b6fa
commit 3eadf6c075
3 changed files with 283 additions and 298 deletions

View File

@@ -99,7 +99,6 @@ public sealed class MainWindowViewModel : ObservableObject
private const ushort PressureCoefficientRegister = 1378;
private const ushort PressureDisplayRegister = 1388;
private const double TenthsRegisterScale = 10.0;
private const int MaxTorqueSampleCount = 300;
private const double MinimumTorqueChangeThreshold = 0.05;
private const double MinimumSpeedChangeThreshold = 1.0;
private const string TorqueUnit = "mN.m";
@@ -252,7 +251,7 @@ public sealed class MainWindowViewModel : ObservableObject
private string _speedTorquePeakTorqueText = $"0.00 {TorqueUnit}";
private string _speedTorqueMaxDisplacementText = "0.000 mm";
private string _speedTorqueFinalDisplacementText = "--";
private string _torqueCurveStatusText = "转速/扭矩保持时间关系曲线:未启动";
private string _torqueCurveStatusText = "转速/扭矩随时间变化曲线:未启动";
private string _axialConfigSummaryText = "--";
private string _speedTorqueConfigSummaryText = "--";
private string _statusText = "完成测试后可导出报表。";
@@ -1372,14 +1371,14 @@ public sealed class MainWindowViewModel : ObservableObject
CreateRecordPoint("转速/扭矩实时测试", "转速/扭矩保持时间关系曲线判定", curve.Result, string.Empty, "记录"),
CreateRecordPoint("转速/扭矩实时测试", "扭矩变化阈值", $"{FormatTorque(curve.ChangeThresholdMilliNewtonMeters)} {TorqueUnit}", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "转速变化阈值", $"{FormatSpeed(curve.SpeedChangeThresholdRpm)} r/min", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最小扭矩", curve.Samples.Count >= 2 ? $"{FormatTorque(curve.MinTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最大扭矩", curve.Samples.Count >= 2 ? $"{FormatTorque(curve.MaxTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内平均扭矩", curve.Samples.Count >= 2 ? $"{FormatTorque(curve.AverageTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内扭矩波动值", curve.Samples.Count >= 2 ? $"{FormatTorque(curve.FluctuationMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最小转速", curve.Samples.Count >= 2 ? $"{FormatSpeed(curve.MinSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最大转速", curve.Samples.Count >= 2 ? $"{FormatSpeed(curve.MaxSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内平均转速", curve.Samples.Count >= 2 ? $"{FormatSpeed(curve.AverageSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内转速波动值", curve.Samples.Count >= 2 ? $"{FormatSpeed(curve.SpeedFluctuationRpm)} r/min" : "--", "r/min")
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最小扭矩", curve.EvaluationSampleCount >= 2 ? $"{FormatTorque(curve.MinTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最大扭矩", curve.EvaluationSampleCount >= 2 ? $"{FormatTorque(curve.MaxTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内平均扭矩", curve.EvaluationSampleCount >= 2 ? $"{FormatTorque(curve.AverageTorqueMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内扭矩波动值", curve.EvaluationSampleCount >= 2 ? $"{FormatTorque(curve.FluctuationMilliNewtonMeters)} {TorqueUnit}" : "--", TorqueUnit),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最小转速", curve.EvaluationSampleCount >= 2 ? $"{FormatSpeed(curve.MinSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内最大转速", curve.EvaluationSampleCount >= 2 ? $"{FormatSpeed(curve.MaxSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内平均转速", curve.EvaluationSampleCount >= 2 ? $"{FormatSpeed(curve.AverageSpeedRpm)} r/min" : "--", "r/min"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间内转速波动值", curve.EvaluationSampleCount >= 2 ? $"{FormatSpeed(curve.SpeedFluctuationRpm)} r/min" : "--", "r/min")
]
};
}
@@ -1799,7 +1798,7 @@ public sealed class MainWindowViewModel : ObservableObject
}
var sheet = workbook.Worksheets.Add("转速扭矩曲线");
sheet.Cell(1, 1).Value = "转速/扭矩保持时间关系曲线";
sheet.Cell(1, 1).Value = "转速/扭矩随时间变化曲线";
sheet.Range(1, 1, 1, 12).Merge().Style.Font.SetBold().Font.SetFontSize(16);
sheet.Cell(3, 1).Value = "保持时间(s)";
@@ -1809,29 +1808,29 @@ public sealed class MainWindowViewModel : ObservableObject
sheet.Cell(5, 1).Value = "曲线判定";
sheet.Cell(5, 2).Value = curve.Result;
sheet.Cell(6, 1).Value = $"最小扭矩({TorqueUnit})";
sheet.Cell(6, 2).Value = curve.Samples.Count >= 2 ? curve.MinTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(6, 2).Value = curve.EvaluationSampleCount >= 2 ? curve.MinTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(7, 1).Value = $"最大扭矩({TorqueUnit})";
sheet.Cell(7, 2).Value = curve.Samples.Count >= 2 ? curve.MaxTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(7, 2).Value = curve.EvaluationSampleCount >= 2 ? curve.MaxTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(8, 1).Value = $"平均扭矩({TorqueUnit})";
sheet.Cell(8, 2).Value = curve.Samples.Count >= 2 ? curve.AverageTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(8, 2).Value = curve.EvaluationSampleCount >= 2 ? curve.AverageTorqueMilliNewtonMeters : string.Empty;
sheet.Cell(9, 1).Value = $"扭矩波动值({TorqueUnit})";
sheet.Cell(9, 2).Value = curve.Samples.Count >= 2 ? curve.FluctuationMilliNewtonMeters : string.Empty;
sheet.Cell(9, 2).Value = curve.EvaluationSampleCount >= 2 ? curve.FluctuationMilliNewtonMeters : string.Empty;
sheet.Range(3, 1, 9, 1).Style.Font.SetBold();
sheet.Cell(3, 4).Value = "X轴";
sheet.Cell(3, 5).Value = $"扭矩({TorqueUnit})";
sheet.Cell(4, 4).Value = "Y轴";
sheet.Cell(4, 5).Value = "转速(r/min)";
sheet.Cell(3, 5).Value = "时间(s)";
sheet.Cell(4, 4).Value = "Y轴";
sheet.Cell(4, 5).Value = $"左:扭矩({TorqueUnit});右:转速(r/min)";
sheet.Cell(5, 4).Value = "转速变化阈值(r/min)";
sheet.Cell(5, 5).Value = curve.SpeedChangeThresholdRpm;
sheet.Cell(6, 4).Value = "最小转速(r/min)";
sheet.Cell(6, 5).Value = curve.Samples.Count >= 2 ? curve.MinSpeedRpm : string.Empty;
sheet.Cell(6, 5).Value = curve.EvaluationSampleCount >= 2 ? curve.MinSpeedRpm : string.Empty;
sheet.Cell(7, 4).Value = "最大转速(r/min)";
sheet.Cell(7, 5).Value = curve.Samples.Count >= 2 ? curve.MaxSpeedRpm : string.Empty;
sheet.Cell(7, 5).Value = curve.EvaluationSampleCount >= 2 ? curve.MaxSpeedRpm : string.Empty;
sheet.Cell(8, 4).Value = "平均转速(r/min)";
sheet.Cell(8, 5).Value = curve.Samples.Count >= 2 ? curve.AverageSpeedRpm : string.Empty;
sheet.Cell(8, 5).Value = curve.EvaluationSampleCount >= 2 ? curve.AverageSpeedRpm : string.Empty;
sheet.Cell(9, 4).Value = "转速波动值(r/min)";
sheet.Cell(9, 5).Value = curve.Samples.Count >= 2 ? curve.SpeedFluctuationRpm : string.Empty;
sheet.Cell(9, 5).Value = curve.EvaluationSampleCount >= 2 ? curve.SpeedFluctuationRpm : string.Empty;
sheet.Range(3, 4, 9, 4).Style.Font.SetBold();
sheet.Cell(11, 1).Value = "时间(s)";
@@ -1862,7 +1861,7 @@ public sealed class MainWindowViewModel : ObservableObject
catch (Exception ex)
{
sheet.Cell(3, 7).Value = "曲线图生成失败,原始曲线数据已完整保留";
Log.Warning(ex, "转速/扭矩关系曲线图片生成失败,报表继续保留原始曲线数据");
Log.Warning(ex, "转速/扭矩时间曲线图片生成失败,报表继续保留原始曲线数据");
}
}
else
@@ -1881,15 +1880,15 @@ public sealed class MainWindowViewModel : ObservableObject
var background = new SolidColorBrush(Color.FromRgb(248, 250, 252));
var borderPen = new Pen(new SolidColorBrush(Color.FromRgb(197, 210, 222)), 1);
var gridPen = new Pen(new SolidColorBrush(Color.FromRgb(219, 229, 238)), 1);
var linePen = new Pen(new SolidColorBrush(Color.FromRgb(29, 78, 216)), 2.6);
var markerBrush = new SolidColorBrush(Color.FromRgb(15, 118, 110));
var torqueBrush = new SolidColorBrush(Color.FromRgb(29, 78, 216));
var speedBrush = new SolidColorBrush(Color.FromRgb(15, 118, 110));
var textBrush = new SolidColorBrush(Color.FromRgb(51, 65, 85));
context.DrawRectangle(background, borderPen, new Rect(0, 0, width, height));
DrawChartText(context, "转速/扭矩保持时间关系曲线", 18, textBrush, new System.Windows.Point(22, 16), true);
DrawChartText(context, $"判定:{curve.Result}", 14, markerBrush, new System.Windows.Point(22, 44), true);
DrawChartText(context, "转速/扭矩随时间变化曲线", 18, textBrush, new System.Windows.Point(22, 16), true);
DrawChartText(context, $"保持时间判定:{curve.Result}", 14, speedBrush, new System.Windows.Point(22, 44), true);
Rect plot = new(62, 78, width - 92, height - 126);
Rect plot = new(70, 78, width - 145, height - 126);
for (int i = 0; i <= 4; i++)
{
double y = plot.Top + plot.Height * i / 4;
@@ -1904,70 +1903,33 @@ public sealed class MainWindowViewModel : ObservableObject
if (curve.Samples.Count > 0)
{
double minTorque = curve.Samples.Min(sample => sample.TorqueMilliNewtonMeters);
double maxTorque = curve.Samples.Max(sample => sample.TorqueMilliNewtonMeters);
double torqueRange = maxTorque - minTorque;
if (torqueRange < 0.01)
{
minTorque -= 0.5;
maxTorque += 0.5;
torqueRange = 1;
}
else
{
double padding = torqueRange * 0.12;
minTorque -= padding;
maxTorque += padding;
torqueRange = maxTorque - minTorque;
}
double minTime = 0;
double maxTime = Math.Max(0.5, curve.Samples.Max(sample => sample.ElapsedSeconds));
(double minTorque, double maxTorque) = GetChartRange(
curve.Samples.Min(sample => sample.TorqueMilliNewtonMeters),
curve.Samples.Max(sample => sample.TorqueMilliNewtonMeters),
0.02);
(double minSpeed, double maxSpeed) = GetChartRange(
curve.Samples.Min(sample => sample.SpeedRpm),
curve.Samples.Max(sample => sample.SpeedRpm),
1);
double minSpeed = curve.Samples.Min(sample => sample.SpeedRpm);
double maxSpeed = curve.Samples.Max(sample => sample.SpeedRpm);
double speedRange = maxSpeed - minSpeed;
if (speedRange < 1)
{
double padding = Math.Max(Math.Abs(minSpeed) * 0.05, 1);
minSpeed -= padding;
maxSpeed += padding;
speedRange = maxSpeed - minSpeed;
}
else
{
double padding = speedRange * 0.05;
minSpeed -= padding;
maxSpeed += padding;
speedRange = maxSpeed - minSpeed;
}
StreamGeometry torqueGeometry = CreateTimeSeriesGeometry(
curve.Samples, plot, minTime, maxTime, minTorque, maxTorque, static sample => sample.TorqueMilliNewtonMeters);
StreamGeometry speedGeometry = CreateTimeSeriesGeometry(
curve.Samples, plot, minTime, maxTime, minSpeed, maxSpeed, static sample => sample.SpeedRpm);
context.DrawGeometry(null, new Pen(torqueBrush, 2.4), torqueGeometry);
context.DrawGeometry(null, new Pen(speedBrush, 2.4), speedGeometry);
var geometry = new StreamGeometry();
using (StreamGeometryContext geometryContext = geometry.Open())
{
for (int i = 0; i < curve.Samples.Count; i++)
{
TorqueSamplePayload sample = curve.Samples[i];
double x = plot.Left + plot.Width * (sample.TorqueMilliNewtonMeters - minTorque) / torqueRange;
double y = plot.Bottom - (sample.SpeedRpm - minSpeed) / speedRange * plot.Height;
var point = new System.Windows.Point(x, y);
if (i == 0)
{
geometryContext.BeginFigure(point, false, false);
}
else
{
geometryContext.LineTo(point, true, false);
}
}
}
geometry.Freeze();
context.DrawGeometry(null, linePen, geometry);
DrawChartText(context, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(16, plot.Top - 8), false);
DrawChartText(context, minSpeed.ToString("0", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(16, plot.Bottom - 10), false);
DrawChartText(context, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(plot.Left, plot.Bottom + 10), false);
DrawChartText(context, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(plot.Right - 34, plot.Bottom + 10), false);
DrawChartText(context, "转速(r/min)", 12, textBrush, new System.Windows.Point(12, plot.Top + 16), false);
DrawChartText(context, $"扭矩({TorqueUnit})", 12, textBrush, new System.Windows.Point(plot.Right - 78, plot.Bottom - 18), false);
DrawChartText(context, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, torqueBrush, new System.Windows.Point(14, plot.Top - 8), false);
DrawChartText(context, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, torqueBrush, new System.Windows.Point(14, plot.Bottom - 10), false);
DrawChartText(context, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 12, speedBrush, new System.Windows.Point(plot.Right + 8, plot.Top - 8), false);
DrawChartText(context, minSpeed.ToString("0", CultureInfo.InvariantCulture), 12, speedBrush, new System.Windows.Point(plot.Right + 8, plot.Bottom - 10), false);
DrawChartText(context, minTime.ToString("0.0", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(plot.Left, plot.Bottom + 10), false);
DrawChartText(context, maxTime.ToString("0.0", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(plot.Right - 34, plot.Bottom + 10), false);
DrawChartText(context, $"扭矩({TorqueUnit})", 12, torqueBrush, new System.Windows.Point(plot.Left + 8, plot.Top + 16), false);
DrawChartText(context, "转速(r/min)", 12, speedBrush, new System.Windows.Point(plot.Right - 82, plot.Top + 16), false);
DrawChartText(context, "时间(s)", 12, textBrush, new System.Windows.Point(plot.Right - 54, plot.Bottom - 18), false);
}
else
{
@@ -1986,6 +1948,52 @@ public sealed class MainWindowViewModel : ObservableObject
return pngStream.ToArray();
}
private static (double Min, double Max) GetChartRange(double min, double max, double minimumRange)
{
double range = max - min;
if (range < minimumRange)
{
double center = (min + max) / 2;
return (center - minimumRange / 2, center + minimumRange / 2);
}
double padding = range * 0.12;
return (min - padding, max + padding);
}
private static StreamGeometry CreateTimeSeriesGeometry(
IReadOnlyList<TorqueSamplePayload> samples,
Rect plot,
double minTime,
double maxTime,
double minValue,
double maxValue,
Func<TorqueSamplePayload, double> valueSelector)
{
var geometry = new StreamGeometry();
using (StreamGeometryContext geometryContext = geometry.Open())
{
for (int i = 0; i < samples.Count; i++)
{
TorqueSamplePayload sample = samples[i];
double x = plot.Left + plot.Width * (sample.ElapsedSeconds - minTime) / (maxTime - minTime);
double y = plot.Bottom - (valueSelector(sample) - minValue) / (maxValue - minValue) * plot.Height;
var point = new System.Windows.Point(x, y);
if (i == 0)
{
geometryContext.BeginFigure(point, false, false);
}
else
{
geometryContext.LineTo(point, true, false);
}
}
}
geometry.Freeze();
return geometry;
}
private static void DrawChartText(DrawingContext context, string text, double fontSize, Brush brush, System.Windows.Point origin, bool bold)
{
var formattedText = new FormattedText(
@@ -3458,12 +3466,6 @@ public sealed class MainWindowViewModel : ObservableObject
}
double elapsedSeconds = Math.Max(0, (sampledAt - _speedTorqueStartedAt.Value).TotalSeconds);
if (_parameterConfig.TorqueHoldTime <= 0
|| elapsedSeconds > _parameterConfig.TorqueHoldTime)
{
return;
}
TorqueSamples.Add(new TorqueSamplePayload
{
ElapsedSeconds = elapsedSeconds,
@@ -3471,12 +3473,6 @@ public sealed class MainWindowViewModel : ObservableObject
TorqueMilliNewtonMeters = torque
});
_cachedTorqueCurve = null;
int sampleLimit = GetTorqueSampleLimit();
while (TorqueSamples.Count > sampleLimit)
{
TorqueSamples.RemoveAt(0);
}
}
private void ClearTorqueSamples()
@@ -3489,19 +3485,15 @@ public sealed class MainWindowViewModel : ObservableObject
private void UpdateTorqueCurveStatus()
{
if (_isSpeedTorqueRunning
&& _speedTorqueStartedAt.HasValue
&& _parameterConfig.TorqueHoldTime > 0)
&& _speedTorqueStartedAt.HasValue)
{
double elapsed = Math.Max(0, (DateTime.Now - _speedTorqueStartedAt.Value).TotalSeconds);
if (elapsed < _parameterConfig.TorqueHoldTime)
{
TorqueCurveStatusText = $"实时曲线:采集中 {FormatConfigNumber(elapsed)} / {FormatConfigNumber(_parameterConfig.TorqueHoldTime)} sX轴扭矩 / Y轴转速";
return;
}
TorqueCurveStatusText = $"实时曲线:已采集 {FormatConfigNumber(elapsed)} sX轴时间 / 左Y轴扭矩 / 右Y轴转速";
return;
}
TorqueCurvePayload curve = CreateTorqueCurvePayload();
TorqueCurveStatusText = $"转速/扭矩保持时间关系曲线:{curve.Result}";
TorqueCurveStatusText = $"转速/扭矩随时间变化曲线:保持时间判定 {curve.Result}";
}
private TorqueCurvePayload CreateTorqueCurvePayload()
@@ -3512,7 +3504,6 @@ public sealed class MainWindowViewModel : ObservableObject
}
List<TorqueSamplePayload> samples = TorqueSamples
.Where(sample => sample.ElapsedSeconds <= _parameterConfig.TorqueHoldTime)
.Select(sample => new TorqueSamplePayload
{
ElapsedSeconds = sample.ElapsedSeconds,
@@ -3539,7 +3530,6 @@ public sealed class MainWindowViewModel : ObservableObject
SpeedRpm = sample.RealtimeSpeedRpm,
TorqueMilliNewtonMeters = sample.RealtimeTorqueMilliNewtonMeters
})
.Where(sample => sample.ElapsedSeconds <= holdTime)
.ToList();
return BuildTorqueCurvePayload(
@@ -3555,11 +3545,16 @@ public sealed class MainWindowViewModel : ObservableObject
double torqueThreshold,
double speedThreshold)
{
List<TorqueSamplePayload> evaluationSamples = samples
.Where(sample => sample.ElapsedSeconds <= holdTime)
.ToList();
if (holdTime <= 0)
{
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
EvaluationSampleCount = evaluationSamples.Count,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
Result = "未设置保持时间,未判定",
@@ -3567,11 +3562,12 @@ public sealed class MainWindowViewModel : ObservableObject
};
}
if (samples.Count < 2)
if (evaluationSamples.Count < 2)
{
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
EvaluationSampleCount = evaluationSamples.Count,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
Result = "采样不足,未判定",
@@ -3579,11 +3575,11 @@ public sealed class MainWindowViewModel : ObservableObject
};
}
double minTorque = samples.Min(static sample => sample.TorqueMilliNewtonMeters);
double maxTorque = samples.Max(static sample => sample.TorqueMilliNewtonMeters);
double minTorque = evaluationSamples.Min(static sample => sample.TorqueMilliNewtonMeters);
double maxTorque = evaluationSamples.Max(static sample => sample.TorqueMilliNewtonMeters);
double torqueFluctuation = maxTorque - minTorque;
double minSpeed = samples.Min(static sample => sample.SpeedRpm);
double maxSpeed = samples.Max(static sample => sample.SpeedRpm);
double minSpeed = evaluationSamples.Min(static sample => sample.SpeedRpm);
double maxSpeed = evaluationSamples.Max(static sample => sample.SpeedRpm);
double speedFluctuation = maxSpeed - minSpeed;
bool torqueChanged = torqueFluctuation > torqueThreshold;
bool speedChanged = speedFluctuation > speedThreshold;
@@ -3598,15 +3594,16 @@ public sealed class MainWindowViewModel : ObservableObject
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
EvaluationSampleCount = evaluationSamples.Count,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
MinTorqueMilliNewtonMeters = minTorque,
MaxTorqueMilliNewtonMeters = maxTorque,
AverageTorqueMilliNewtonMeters = samples.Average(static sample => sample.TorqueMilliNewtonMeters),
AverageTorqueMilliNewtonMeters = evaluationSamples.Average(static sample => sample.TorqueMilliNewtonMeters),
FluctuationMilliNewtonMeters = torqueFluctuation,
MinSpeedRpm = minSpeed,
MaxSpeedRpm = maxSpeed,
AverageSpeedRpm = samples.Average(static sample => sample.SpeedRpm),
AverageSpeedRpm = evaluationSamples.Average(static sample => sample.SpeedRpm),
SpeedFluctuationRpm = speedFluctuation,
Result = result,
Samples = samples
@@ -3623,18 +3620,6 @@ public sealed class MainWindowViewModel : ObservableObject
return Math.Max(MinimumSpeedChangeThreshold, Math.Abs(_parameterConfig.LoadSpeedSetting) * 0.01);
}
private int GetTorqueSampleLimit()
{
if (_parameterConfig.TorqueHoldTime <= 0)
{
return MaxTorqueSampleCount;
}
int requiredSamples = (int)Math.Ceiling(
_parameterConfig.TorqueHoldTime / RealtimeRefreshInterval.TotalSeconds) + 2;
return Math.Max(MaxTorqueSampleCount, requiredSamples);
}
private void CaptureRealtimeSample(
double dialIndicator,
IReadOnlyDictionary<ushort, bool> coilValues,

View File

@@ -212,6 +212,8 @@ public sealed class TorqueCurvePayload
{
public double HoldTimeSeconds { get; init; }
public int EvaluationSampleCount { get; init; }
public double ChangeThresholdMilliNewtonMeters { get; init; }
public double SpeedChangeThresholdRpm { get; init; }

View File

@@ -9,6 +9,8 @@ namespace DentistryHandpieces;
public readonly record struct TorqueTrendViewState(
bool IsManual,
double MinTime,
double MaxTime,
double MinTorque,
double MaxTorque,
double MinSpeed,
@@ -20,16 +22,21 @@ public sealed class TorqueTrendControl : FrameworkElement
private const string SpeedUnit = "r/min";
private const double SensorMinTorque = 0;
private const double SensorMaxTorque = 100;
private const double MinimumTorqueRange = 0.1;
private const double MinimumTimeRange = 0.5;
private const double MinimumTorqueRange = 0.02;
private const double MinimumSpeedRange = 1;
private const double TorqueRangePaddingRatio = 0.12;
private const double TorqueTrendBinSize = 0.01;
private const double RangePaddingRatio = 0.12;
private static readonly Color TorqueColor = Color.FromRgb(29, 78, 216);
private static readonly Color SpeedColor = Color.FromRgb(15, 118, 110);
private bool _isManualView;
private double _viewMinTime;
private double _viewMaxTime = 1;
private double _viewMinTorque = SensorMinTorque;
private double _viewMaxTorque = SensorMaxTorque;
private double _viewMaxTorque = MinimumTorqueRange;
private double _viewMinSpeed;
private double _viewMaxSpeed = 1;
private double _viewMaxSpeed = MinimumSpeedRange;
private Point? _lastDragPoint;
public TorqueTrendControl()
@@ -56,6 +63,8 @@ public sealed class TorqueTrendControl : FrameworkElement
{
return new TorqueTrendViewState(
_isManualView,
_viewMinTime,
_viewMaxTime,
_viewMinTorque,
_viewMaxTorque,
_viewMinSpeed,
@@ -71,11 +80,13 @@ public sealed class TorqueTrendControl : FrameworkElement
}
_isManualView = true;
_viewMinTime = state.MinTime;
_viewMaxTime = state.MaxTime;
_viewMinTorque = state.MinTorque;
_viewMaxTorque = state.MaxTorque;
_viewMinSpeed = state.MinSpeed;
_viewMaxSpeed = state.MaxSpeed;
ClampView();
ClampView(ReadSamples());
InvalidateVisual();
}
@@ -92,8 +103,6 @@ public sealed class TorqueTrendControl : FrameworkElement
public void ResetView()
{
_isManualView = false;
_viewMinTorque = SensorMinTorque;
_viewMaxTorque = SensorMaxTorque;
InvalidateVisual();
}
@@ -115,7 +124,7 @@ public sealed class TorqueTrendControl : FrameworkElement
6,
6);
if (ActualWidth < 120 || ActualHeight < 80)
if (ActualWidth < 160 || ActualHeight < 80)
{
return;
}
@@ -131,41 +140,37 @@ public sealed class TorqueTrendControl : FrameworkElement
return;
}
(double minTorque, double maxTorque, double minSpeed, double maxSpeed) = GetVisibleRanges(samples);
DrawAxisLabels(drawingContext, plot, minTorque, maxTorque, minSpeed, maxSpeed);
(double minTime, double maxTime, double minTorque, double maxTorque, double minSpeed, double maxSpeed) = GetVisibleRanges(samples);
DrawAxisLabels(drawingContext, plot, minTime, maxTime, minTorque, maxTorque, minSpeed, maxSpeed);
var points = samples
.Select(sample => ToPlotPoint(sample, plot, minTorque, maxTorque, minSpeed, maxSpeed))
List<TorqueSamplePayload> visibleSamples = DownsampleForDrawing(
samples.Where(sample => sample.ElapsedSeconds >= minTime && sample.ElapsedSeconds <= maxTime).ToList(),
plot.Width);
List<Point> torquePoints = visibleSamples
.Select(sample => ToPlotPoint(sample.ElapsedSeconds, sample.TorqueMilliNewtonMeters, plot, minTime, maxTime, minTorque, maxTorque))
.ToList();
List<Point> speedPoints = visibleSamples
.Select(sample => ToPlotPoint(sample.ElapsedSeconds, sample.SpeedRpm, plot, minTime, maxTime, minSpeed, maxSpeed))
.ToList();
List<TorqueTrendPoint> trendPoints = BuildTrendPoints(samples);
drawingContext.PushClip(new RectangleGeometry(plot));
DrawSamplePoints(drawingContext, points);
DrawTrendLine(
drawingContext,
trendPoints
.Select(point => ToPlotPoint(point.Torque, point.Speed, plot, minTorque, maxTorque, minSpeed, maxSpeed))
.ToList());
DrawCurrentPoint(drawingContext, points[^1]);
DrawLine(drawingContext, torquePoints, TorqueColor);
DrawLine(drawingContext, speedPoints, SpeedColor);
if (torquePoints.Count > 0)
{
DrawCurrentPoint(drawingContext, torquePoints[^1], TorqueColor);
DrawCurrentPoint(drawingContext, speedPoints[^1], SpeedColor);
}
drawingContext.Pop();
TorqueSamplePayload current = samples[^1];
DrawText(
drawingContext,
$"当前 {current.SpeedRpm:0} {SpeedUnit} / {current.TorqueMilliNewtonMeters:0.##} {TorqueUnit}",
$"当前 {current.ElapsedSeconds:0.0} s / {current.TorqueMilliNewtonMeters:0.##} {TorqueUnit} / {current.SpeedRpm:0} {SpeedUnit}",
12,
Color.FromRgb(15, 118, 110),
SpeedColor,
new Point(plot.Left + 6, plot.Top + 5));
DrawLegend(drawingContext, plot, trendPoints.Count >= 2);
if (trendPoints.Count < 2)
{
DrawText(
drawingContext,
"扭矩变化小于传感器精度,暂无有效趋势线",
11,
Color.FromRgb(180, 83, 9),
new Point(plot.Left + 6, plot.Bottom - 38));
}
DrawLegend(drawingContext, plot);
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
@@ -261,7 +266,7 @@ public sealed class TorqueTrendControl : FrameworkElement
private Rect GetPlotRect()
{
return new Rect(52, 14, Math.Max(1, ActualWidth - 70), Math.Max(1, ActualHeight - 42));
return new Rect(58, 14, Math.Max(1, ActualWidth - 116), Math.Max(1, ActualHeight - 44));
}
private static void DrawGrid(DrawingContext drawingContext, Rect plot)
@@ -280,91 +285,96 @@ public sealed class TorqueTrendControl : FrameworkElement
}
}
private (double MinTorque, double MaxTorque, double MinSpeed, double MaxSpeed) GetVisibleRanges(List<TorqueSamplePayload> samples)
private (double MinTime, double MaxTime, double MinTorque, double MaxTorque, double MinSpeed, double MaxSpeed) GetVisibleRanges(List<TorqueSamplePayload> samples)
{
if (!_isManualView)
{
(_viewMinTime, _viewMaxTime) = GetAutoTimeRange(samples);
(_viewMinTorque, _viewMaxTorque) = GetAutoTorqueRange(samples);
(_viewMinSpeed, _viewMaxSpeed) = GetAutoSpeedRange(samples);
}
return (_viewMinTorque, _viewMaxTorque, _viewMinSpeed, _viewMaxSpeed);
return (_viewMinTime, _viewMaxTime, _viewMinTorque, _viewMaxTorque, _viewMinSpeed, _viewMaxSpeed);
}
private static (double MinTime, double MaxTime) GetAutoTimeRange(List<TorqueSamplePayload> samples)
{
double maxTime = Math.Max(MinimumTimeRange, samples.Max(sample => sample.ElapsedSeconds));
return (0, maxTime);
}
private static (double MinTorque, double MaxTorque) GetAutoTorqueRange(List<TorqueSamplePayload> samples)
{
double minTorque = Math.Clamp(samples.Min(sample => sample.TorqueMilliNewtonMeters), SensorMinTorque, SensorMaxTorque);
double maxTorque = Math.Clamp(samples.Max(sample => sample.TorqueMilliNewtonMeters), SensorMinTorque, SensorMaxTorque);
double range = maxTorque - minTorque;
if (range < MinimumTorqueRange)
{
double center = (minTorque + maxTorque) / 2;
minTorque = center - MinimumTorqueRange / 2;
maxTorque = center + MinimumTorqueRange / 2;
}
else
{
double padding = range * TorqueRangePaddingRatio;
minTorque -= padding;
maxTorque += padding;
}
return ClampTorqueRange(minTorque, maxTorque);
return ClampTorqueRangeWithPadding(minTorque, maxTorque);
}
private static (double MinSpeed, double MaxSpeed) GetAutoSpeedRange(List<TorqueSamplePayload> samples)
{
double minSpeed = samples.Min(sample => sample.SpeedRpm);
double maxSpeed = samples.Max(sample => sample.SpeedRpm);
double range = maxSpeed - minSpeed;
double padding = range < MinimumSpeedRange
? Math.Max(Math.Abs(minSpeed) * 0.05, MinimumSpeedRange)
: range * 0.08;
return (minSpeed - padding, maxSpeed + padding);
return GetPaddedRange(
samples.Min(sample => sample.SpeedRpm),
samples.Max(sample => sample.SpeedRpm),
MinimumSpeedRange);
}
private static (double Min, double Max) GetPaddedRange(double min, double max, double minimumRange)
{
double range = max - min;
if (range < minimumRange)
{
double center = (min + max) / 2;
return (center - minimumRange / 2, center + minimumRange / 2);
}
double padding = range * RangePaddingRatio;
return (min - padding, max + padding);
}
private static (double MinTorque, double MaxTorque) ClampTorqueRangeWithPadding(double minTorque, double maxTorque)
{
(minTorque, maxTorque) = GetPaddedRange(minTorque, maxTorque, MinimumTorqueRange);
return ClampTorqueRange(minTorque, maxTorque);
}
private static Point ToPlotPoint(
TorqueSamplePayload sample,
double time,
double value,
Rect plot,
double minTorque,
double maxTorque,
double minSpeed,
double maxSpeed)
double minTime,
double maxTime,
double minValue,
double maxValue)
{
double x = plot.Left + plot.Width * (sample.TorqueMilliNewtonMeters - minTorque) / (maxTorque - minTorque);
double y = plot.Bottom - (sample.SpeedRpm - minSpeed) / (maxSpeed - minSpeed) * plot.Height;
double x = plot.Left + plot.Width * (time - minTime) / (maxTime - minTime);
double y = plot.Bottom - (value - minValue) / (maxValue - minValue) * plot.Height;
return new Point(x, y);
}
private static Point ToPlotPoint(
double torque,
double speed,
Rect plot,
double minTorque,
double maxTorque,
double minSpeed,
double maxSpeed)
private static List<TorqueSamplePayload> DownsampleForDrawing(List<TorqueSamplePayload> samples, double plotWidth)
{
double x = plot.Left + plot.Width * (torque - minTorque) / (maxTorque - minTorque);
double y = plot.Bottom - (speed - minSpeed) / (maxSpeed - minSpeed) * plot.Height;
return new Point(x, y);
int maximumPoints = Math.Max(100, (int)Math.Ceiling(plotWidth * 2));
if (samples.Count <= maximumPoints)
{
return samples;
}
int step = (int)Math.Ceiling((double)samples.Count / maximumPoints);
var result = new List<TorqueSamplePayload>(maximumPoints + 1);
for (int i = 0; i < samples.Count; i += step)
{
result.Add(samples[i]);
}
if (!ReferenceEquals(result[^1], samples[^1]))
{
result.Add(samples[^1]);
}
return result;
}
private static List<TorqueTrendPoint> BuildTrendPoints(IReadOnlyList<TorqueSamplePayload> samples)
{
return samples
.Where(sample => sample.TorqueMilliNewtonMeters >= SensorMinTorque
&& sample.TorqueMilliNewtonMeters <= SensorMaxTorque)
.GroupBy(sample => Math.Floor(sample.TorqueMilliNewtonMeters / TorqueTrendBinSize))
.Select(group => new TorqueTrendPoint(
group.Average(sample => sample.TorqueMilliNewtonMeters),
group.Average(sample => sample.SpeedRpm)))
.OrderBy(point => point.Torque)
.ToList();
}
private static void DrawTrendLine(DrawingContext drawingContext, IReadOnlyList<Point> points)
private static void DrawLine(DrawingContext drawingContext, IReadOnlyList<Point> points, Color color)
{
if (points.Count < 2)
{
@@ -377,80 +387,55 @@ public sealed class TorqueTrendControl : FrameworkElement
context.BeginFigure(points[0], false, false);
for (int i = 1; i < points.Count; i++)
{
Point previous = points[i - 1];
Point current = points[i];
double deltaX = current.X - previous.X;
var control1 = new Point(previous.X + deltaX / 3, previous.Y);
var control2 = new Point(current.X - deltaX / 3, current.Y);
context.BezierTo(control1, control2, current, true, false);
context.LineTo(points[i], true, false);
}
}
geometry.Freeze();
var lineBrush = new SolidColorBrush(Color.FromRgb(15, 118, 110));
drawingContext.DrawGeometry(null, new Pen(lineBrush, 2.4), geometry);
drawingContext.DrawGeometry(null, new Pen(new SolidColorBrush(color), 2.3), geometry);
}
private static void DrawSamplePoints(DrawingContext drawingContext, IReadOnlyList<Point> points)
private static void DrawCurrentPoint(DrawingContext drawingContext, Point point, Color color)
{
var pointBrush = new SolidColorBrush(Color.FromArgb(175, 29, 78, 216));
Point? lastDrawn = null;
foreach (Point point in points)
{
if (lastDrawn is Point previous
&& Math.Abs(point.X - previous.X) < 1.5
&& Math.Abs(point.Y - previous.Y) < 1.5)
{
continue;
}
drawingContext.DrawEllipse(pointBrush, null, point, 2.2, 2.2);
lastDrawn = point;
}
var brush = new SolidColorBrush(color);
drawingContext.DrawEllipse(brush, new Pen(Brushes.White, 1.5), point, 4.5, 4.5);
}
private static void DrawCurrentPoint(DrawingContext drawingContext, Point point)
private void DrawLegend(DrawingContext drawingContext, Rect plot)
{
var brush = new SolidColorBrush(Color.FromRgb(15, 118, 110));
drawingContext.DrawEllipse(brush, new Pen(Brushes.White, 1.5), point, 5, 5);
}
private void DrawLegend(DrawingContext drawingContext, Rect plot, bool hasTrend)
{
double y = plot.Bottom + 5;
var sampleBrush = new SolidColorBrush(Color.FromArgb(175, 29, 78, 216));
drawingContext.DrawEllipse(sampleBrush, null, new Point(plot.Left + 116, y + 7), 2.5, 2.5);
DrawText(drawingContext, "原始采样点", 10, Color.FromRgb(82, 97, 111), new Point(plot.Left + 122, y));
if (!hasTrend)
{
return;
}
var trendPen = new Pen(new SolidColorBrush(Color.FromRgb(15, 118, 110)), 2.4);
drawingContext.DrawLine(trendPen, new Point(plot.Left + 194, y + 7), new Point(plot.Left + 214, y + 7));
DrawText(drawingContext, "显示趋势线", 10, Color.FromRgb(82, 97, 111), new Point(plot.Left + 220, y));
double y = plot.Bottom + 7;
var torquePen = new Pen(new SolidColorBrush(TorqueColor), 2.3);
var speedPen = new Pen(new SolidColorBrush(SpeedColor), 2.3);
drawingContext.DrawLine(torquePen, new Point(plot.Left + 82, y + 6), new Point(plot.Left + 104, y + 6));
DrawText(drawingContext, "扭矩", 10, TorqueColor, new Point(plot.Left + 110, y));
drawingContext.DrawLine(speedPen, new Point(plot.Left + 156, y + 6), new Point(plot.Left + 178, y + 6));
DrawText(drawingContext, "转速", 10, SpeedColor, new Point(plot.Left + 184, y));
}
private void DrawAxisLabels(
DrawingContext drawingContext,
Rect plot,
double minTime,
double maxTime,
double minTorque,
double maxTorque,
double minSpeed,
double maxSpeed)
{
DrawText(drawingContext, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 11, Color.FromRgb(82, 97, 111), new Point(5, plot.Top - 2));
DrawText(drawingContext, minSpeed.ToString("0", CultureInfo.InvariantCulture), 11, Color.FromRgb(82, 97, 111), new Point(5, plot.Bottom - 14));
DrawText(drawingContext, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 11, Color.FromRgb(82, 97, 111), new Point(plot.Left, plot.Bottom + 5));
DrawText(drawingContext, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 11, Color.FromRgb(82, 97, 111), new Point(plot.Right - 34, plot.Bottom + 5));
DrawText(drawingContext, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 11, TorqueColor, new Point(3, plot.Top - 2));
DrawText(drawingContext, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 11, TorqueColor, new Point(3, plot.Bottom - 14));
DrawText(drawingContext, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 11, SpeedColor, new Point(plot.Right + 7, plot.Top - 2));
DrawText(drawingContext, minSpeed.ToString("0", CultureInfo.InvariantCulture), 11, SpeedColor, new Point(plot.Right + 7, plot.Bottom - 14));
DrawText(drawingContext, $"{minTime:0.0}", 11, Color.FromRgb(82, 97, 111), new Point(plot.Left, plot.Bottom + 7));
DrawText(drawingContext, $"{maxTime:0.0}", 11, Color.FromRgb(82, 97, 111), new Point(plot.Right - 30, plot.Bottom + 7));
DrawAxisTitles(drawingContext, plot);
}
private void DrawAxisTitles(DrawingContext drawingContext, Rect plot)
{
DrawText(drawingContext, $"转速 ({SpeedUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Left + 4, plot.Top + 22));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Right - 82, plot.Bottom - 18));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 10, TorqueColor, new Point(plot.Left + 4, plot.Top + 22));
DrawText(drawingContext, $"转速 ({SpeedUnit})", 10, SpeedColor, new Point(plot.Right - 78, plot.Top + 22));
DrawText(drawingContext, "时间 (s)", 10, Color.FromRgb(82, 97, 111), new Point(plot.Right - 52, plot.Bottom - 18));
}
private void Zoom(double factor, Point origin)
@@ -461,19 +446,14 @@ public sealed class TorqueTrendControl : FrameworkElement
return;
}
EnsureManualView();
List<TorqueSamplePayload> samples = ReadSamples();
EnsureManualView(samples);
double originX = Math.Clamp((origin.X - plot.Left) / plot.Width, 0, 1);
double originY = Math.Clamp((plot.Bottom - origin.Y) / plot.Height, 0, 1);
double torqueAtOrigin = _viewMinTorque + (_viewMaxTorque - _viewMinTorque) * originX;
double speedAtOrigin = _viewMinSpeed + (_viewMaxSpeed - _viewMinSpeed) * originY;
double torqueRange = Math.Max(MinimumTorqueRange, (_viewMaxTorque - _viewMinTorque) * factor);
double speedRange = Math.Max(MinimumSpeedRange, (_viewMaxSpeed - _viewMinSpeed) * factor);
_viewMinTorque = torqueAtOrigin - torqueRange * originX;
_viewMaxTorque = _viewMinTorque + torqueRange;
_viewMinSpeed = speedAtOrigin - speedRange * originY;
_viewMaxSpeed = _viewMinSpeed + speedRange;
ClampView();
(_viewMinTime, _viewMaxTime) = ScaleRange(_viewMinTime, _viewMaxTime, factor, originX, MinimumTimeRange);
(_viewMinTorque, _viewMaxTorque) = ScaleRange(_viewMinTorque, _viewMaxTorque, factor, originY, MinimumTorqueRange);
(_viewMinSpeed, _viewMaxSpeed) = ScaleRange(_viewMinSpeed, _viewMaxSpeed, factor, originY, MinimumSpeedRange);
ClampView(samples);
InvalidateVisual();
}
@@ -485,34 +465,58 @@ public sealed class TorqueTrendControl : FrameworkElement
return;
}
EnsureManualView();
double torqueShift = -deltaX / plot.Width * (_viewMaxTorque - _viewMinTorque);
List<TorqueSamplePayload> samples = ReadSamples();
EnsureManualView(samples);
double timeShift = -deltaX / plot.Width * (_viewMaxTime - _viewMinTime);
double torqueShift = deltaY / plot.Height * (_viewMaxTorque - _viewMinTorque);
double speedShift = deltaY / plot.Height * (_viewMaxSpeed - _viewMinSpeed);
_viewMinTime += timeShift;
_viewMaxTime += timeShift;
_viewMinTorque += torqueShift;
_viewMaxTorque += torqueShift;
_viewMinSpeed += speedShift;
_viewMaxSpeed += speedShift;
ClampView();
ClampView(samples);
InvalidateVisual();
}
private void EnsureManualView()
private static (double Min, double Max) ScaleRange(double min, double max, double factor, double originRatio, double minimumRange)
{
double valueAtOrigin = min + (max - min) * originRatio;
double range = Math.Max(minimumRange, (max - min) * factor);
double newMin = valueAtOrigin - range * originRatio;
return (newMin, newMin + range);
}
private void EnsureManualView(List<TorqueSamplePayload> samples)
{
if (_isManualView)
{
return;
}
List<TorqueSamplePayload> samples = ReadSamples();
(_viewMinTorque, _viewMaxTorque) = samples.Count == 0
? (SensorMinTorque, SensorMaxTorque)
: GetAutoTorqueRange(samples);
(_viewMinSpeed, _viewMaxSpeed) = samples.Count == 0 ? (0, 1) : GetAutoSpeedRange(samples);
if (samples.Count == 0)
{
(_viewMinTime, _viewMaxTime) = (0, 1);
(_viewMinTorque, _viewMaxTorque) = (SensorMinTorque, MinimumTorqueRange);
(_viewMinSpeed, _viewMaxSpeed) = (0, MinimumSpeedRange);
}
else
{
(_viewMinTime, _viewMaxTime) = GetAutoTimeRange(samples);
(_viewMinTorque, _viewMaxTorque) = GetAutoTorqueRange(samples);
(_viewMinSpeed, _viewMaxSpeed) = GetAutoSpeedRange(samples);
}
_isManualView = true;
}
private void ClampView()
private void ClampView(List<TorqueSamplePayload> samples)
{
double dataMaxTime = samples.Count == 0 ? 1 : Math.Max(MinimumTimeRange, samples.Max(sample => sample.ElapsedSeconds));
double timeRange = Math.Clamp(_viewMaxTime - _viewMinTime, MinimumTimeRange, dataMaxTime);
_viewMinTime = Math.Clamp(_viewMinTime, 0, dataMaxTime - timeRange);
_viewMaxTime = _viewMinTime + timeRange;
(_viewMinTorque, _viewMaxTorque) = ClampTorqueRange(_viewMinTorque, _viewMaxTorque);
if (!double.IsFinite(_viewMinSpeed)
@@ -559,10 +563,6 @@ public sealed class TorqueTrendControl : FrameworkElement
{
values.Add(torqueSample);
}
else if (sample is double value && double.IsFinite(value))
{
values.Add(new TorqueSamplePayload { ElapsedSeconds = values.Count, SpeedRpm = values.Count, TorqueMilliNewtonMeters = value });
}
}
return values;
@@ -581,6 +581,4 @@ public sealed class TorqueTrendControl : FrameworkElement
drawingContext.DrawText(formattedText, origin);
}
private readonly record struct TorqueTrendPoint(double Torque, double Speed);
}