更新2026

This commit is contained in:
GukSang.Jin
2026-06-13 10:20:29 +08:00
parent 424a2d8b2d
commit ca18602bca
4 changed files with 221 additions and 167 deletions

View File

@@ -692,7 +692,8 @@
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
@@ -713,11 +714,39 @@
<TextBlock Grid.Row="1"
x:Name="RealtimeSpeedText"
Text="{Binding RealtimeSpeedText}"
Style="{StaticResource MetricValue}"
FontSize="50"
Foreground="#0F766E"
VerticalAlignment="Center" />
<Border Grid.Row="2" Style="{StaticResource InfoStrip}">
Style="{StaticResource MetricValue}"
FontSize="50"
Foreground="#0F766E"
VerticalAlignment="Center"
Margin="0,4,0,10" />
<Grid Grid.Row="2" Margin="0,2,0,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="实时扭矩" Style="{StaticResource MetricTitle}" HorizontalAlignment="Left" />
<Button Grid.Column="1"
Content="扭矩归零"
Command="{Binding ZeroTorqueCommand}"
FontSize="13"
Padding="8,3"
MinHeight="28"
Background="#64748B"
BorderBrush="#475569" />
<TextBlock Grid.Row="1"
Grid.ColumnSpan="2"
x:Name="RealtimeTorqueText"
Text="{Binding RealtimeTorqueText}"
Style="{StaticResource MetricValue}"
FontSize="50"
Foreground="#1D4ED8"
Margin="0,4,0,0" />
</Grid>
<Border Grid.Row="3" Style="{StaticResource InfoStrip}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@@ -746,61 +775,20 @@
<Border Grid.Column="1" Style="{StaticResource PanelBorder}" Margin="10,0,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="实时扭矩" Style="{StaticResource MetricTitle}" HorizontalAlignment="Left" />
<Button Grid.Column="1"
Content="归零"
Command="{Binding ZeroTorqueCommand}"
FontSize="13"
Padding="8,3"
MinHeight="28"
Background="#64748B"
BorderBrush="#475569" />
</Grid>
<local:TorqueTrendControl Samples="{Binding TorqueSamples}"
MinHeight="210" />
<TextBlock Grid.Row="1"
x:Name="RealtimeTorqueText"
Text="{Binding RealtimeTorqueText}"
Style="{StaticResource MetricValue}"
FontSize="50"
Foreground="#1D4ED8"
Margin="0,2,0,4" />
<Grid Grid.Row="2" MinHeight="96" Margin="0,2,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="17*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:TorqueTrendControl Samples="{Binding TorqueSamples}"
MinHeight="86" Grid.ColumnSpan="2" />
<TextBlock Grid.Row="1"
Text="{Binding TorqueCurveStatusText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="#52616F"
HorizontalAlignment="Left"
Margin="115,4,0,0" Grid.Column="1" />
</Grid>
</Grid>
</Grid>
<TextBlock Grid.Row="3"
Text="{Binding TorqueCurveStatusText}"
FontSize="14"
FontWeight="SemiBold"
Foreground="#52616F"
HorizontalAlignment="Center"
Margin="0,6,0,0" />
<TextBlock Grid.Row="2"
x:Name="FinalSpeedTorqueText"
Text="{Binding FinalSpeedTorqueText}"
FontSize="17"

View File

@@ -101,6 +101,7 @@ public sealed class MainWindowViewModel : ObservableObject
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";
private static readonly TimeSpan RealtimeRefreshInterval = TimeSpan.FromMilliseconds(100);
private static readonly TimeSpan RealtimeDataFreshnessTimeout = TimeSpan.FromSeconds(3);
@@ -250,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 = "完成测试后可导出报表。";
@@ -779,7 +780,8 @@ public sealed class MainWindowViewModel : ObservableObject
_axialForce = axialForce;
_realtimeTorque = realtimeTorque;
_realtimeSpeed = realtimeSpeed;
AppendTorqueSample(GetScaledTorque(), _realtimeSpeed, DateTime.Now);
DateTime sampledAt = DateTime.Now;
AppendTorqueSample(GetScaledTorque(), _realtimeSpeed, sampledAt);
if (_isDisplacementRunning)
{
_maxDisplacement = Math.Max(_maxDisplacement, Math.Abs(_relativeDisplacement));
@@ -794,8 +796,8 @@ public sealed class MainWindowViewModel : ObservableObject
bool isVentValveOpen = ReadCoilValue(coilValues, VentValveCoil);
VentValveButtonText = isVentValveOpen ? "关闭气阀" : "通气阀";
_lastSuccessfulRealtimeReadAt = DateTime.Now;
CaptureRealtimeSample(dialIndicator, coilValues);
_lastSuccessfulRealtimeReadAt = sampledAt;
CaptureRealtimeSample(dialIndicator, coilValues, sampledAt);
FinalizeNoLoadSpeedRunIfDue();
QueueSnapshotIfDue();
@@ -1360,11 +1362,17 @@ public sealed class MainWindowViewModel : ObservableObject
CreateRecordPoint("转速/扭矩实时测试", "最终位移", finalDisplacement.HasValue ? $"{FormatDisplacement(finalDisplacement.Value)} mm" : "--", "mm", finalDisplacement.HasValue ? "记录" : "待停止"),
CreateRecordPoint("转速/扭矩实时测试", "最终转速", finalSpeed.HasValue ? $"{FormatSpeed(finalSpeed.Value)} r/min" : "--", "r/min", finalSpeed.HasValue ? "记录" : "待停止"),
CreateRecordPoint("转速/扭矩实时测试", "最终扭矩", finalTorque.HasValue ? $"{FormatTorque(finalTorque.Value)} {TorqueUnit}" : "--", TorqueUnit, finalTorque.HasValue ? "记录" : "待停止"),
CreateRecordPoint("转速/扭矩实时测试", "保持时间曲线判定", curve.Result, string.Empty, "记录"),
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 ? $"{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")
]
};
}
@@ -1783,13 +1791,13 @@ public sealed class MainWindowViewModel : ObservableObject
return;
}
var sheet = workbook.Worksheets.Add("扭矩曲线");
sheet.Cell(1, 1).Value = "扭矩保持时间曲线";
sheet.Range(1, 1, 1, 7).Merge().Style.Font.SetBold().Font.SetFontSize(16);
var sheet = workbook.Worksheets.Add("转速扭矩曲线");
sheet.Cell(1, 1).Value = "转速/扭矩保持时间关系曲线";
sheet.Range(1, 1, 1, 12).Merge().Style.Font.SetBold().Font.SetFontSize(16);
sheet.Cell(3, 1).Value = "保持时间(s)";
sheet.Cell(3, 2).Value = curve.HoldTimeSeconds;
sheet.Cell(4, 1).Value = $"判定阈值({TorqueUnit})";
sheet.Cell(4, 1).Value = $"扭矩变化阈值({TorqueUnit})";
sheet.Cell(4, 2).Value = curve.ChangeThresholdMilliNewtonMeters;
sheet.Cell(5, 1).Value = "曲线判定";
sheet.Cell(5, 2).Value = curve.Result;
@@ -1799,14 +1807,31 @@ public sealed class MainWindowViewModel : ObservableObject
sheet.Cell(7, 2).Value = curve.Samples.Count >= 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(9, 1).Value = $"波动值({TorqueUnit})";
sheet.Cell(9, 1).Value = $"扭矩波动值({TorqueUnit})";
sheet.Cell(9, 2).Value = curve.Samples.Count >= 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(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(7, 4).Value = "最大转速(r/min)";
sheet.Cell(7, 5).Value = curve.Samples.Count >= 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(9, 4).Value = "转速波动值(r/min)";
sheet.Cell(9, 5).Value = curve.Samples.Count >= 2 ? curve.SpeedFluctuationRpm : string.Empty;
sheet.Range(3, 4, 9, 4).Style.Font.SetBold();
sheet.Cell(11, 1).Value = "时间(s)";
sheet.Cell(11, 2).Value = $"扭矩({TorqueUnit})";
sheet.Range(11, 1, 11, 2).Style.Fill.SetBackgroundColor(XLColor.FromHtml("#D9EAF7"));
sheet.Range(11, 1, 11, 2).Style.Font.SetBold();
sheet.Cell(11, 3).Value = "转速(r/min)";
sheet.Range(11, 1, 11, 3).Style.Fill.SetBackgroundColor(XLColor.FromHtml("#D9EAF7"));
sheet.Range(11, 1, 11, 3).Style.Font.SetBold();
for (int i = 0; i < curve.Samples.Count; i++)
{
@@ -1814,6 +1839,7 @@ public sealed class MainWindowViewModel : ObservableObject
int row = 12 + i;
sheet.Cell(row, 1).Value = sample.ElapsedSeconds;
sheet.Cell(row, 2).Value = sample.TorqueMilliNewtonMeters;
sheet.Cell(row, 3).Value = sample.SpeedRpm;
}
if (curve.Samples.Count > 0)
@@ -1822,19 +1848,19 @@ public sealed class MainWindowViewModel : ObservableObject
{
byte[] imageBytes = CreateTorqueCurveImage(curve, 760, 360);
var imageStream = new MemoryStream(imageBytes);
var picture = sheet.AddPicture(imageStream, "TorqueCurve").MoveTo(sheet.Cell(3, 4));
var picture = sheet.AddPicture(imageStream, "SpeedTorqueCurve").MoveTo(sheet.Cell(3, 7));
picture.Width = 760;
picture.Height = 360;
}
catch (Exception ex)
{
sheet.Cell(3, 4).Value = "曲线图生成失败,原始曲线数据已完整保留";
Log.Warning(ex, "扭矩曲线图片生成失败,报表继续保留原始曲线数据");
sheet.Cell(3, 7).Value = "曲线图生成失败,原始曲线数据已完整保留";
Log.Warning(ex, "转速/扭矩关系曲线图片生成失败,报表继续保留原始曲线数据");
}
}
else
{
sheet.Cell(3, 4).Value = "无扭矩曲线数据";
sheet.Cell(3, 7).Value = "无转速/扭矩曲线数据";
}
sheet.Columns().AdjustToContents();
@@ -1853,7 +1879,7 @@ public sealed class MainWindowViewModel : ObservableObject
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, "转速/扭矩保持时间关系曲线", 18, textBrush, new System.Windows.Point(22, 16), true);
DrawChartText(context, $"判定:{curve.Result}", 14, markerBrush, new System.Windows.Point(22, 44), true);
Rect plot = new(62, 78, width - 92, height - 126);
@@ -1871,26 +1897,40 @@ public sealed class MainWindowViewModel : ObservableObject
if (curve.Samples.Count > 0)
{
double min = curve.Samples.Min(sample => sample.TorqueMilliNewtonMeters);
double max = curve.Samples.Max(sample => sample.TorqueMilliNewtonMeters);
double range = max - min;
if (range < 0.01)
double minTorque = curve.Samples.Min(sample => sample.TorqueMilliNewtonMeters);
double maxTorque = curve.Samples.Max(sample => sample.TorqueMilliNewtonMeters);
double torqueRange = maxTorque - minTorque;
if (torqueRange < 0.01)
{
min -= 0.5;
max += 0.5;
range = 1;
minTorque -= 0.5;
maxTorque += 0.5;
torqueRange = 1;
}
else
{
double padding = range * 0.12;
min -= padding;
max += padding;
range = max - min;
double padding = torqueRange * 0.12;
minTorque -= padding;
maxTorque += padding;
torqueRange = maxTorque - minTorque;
}
double firstElapsed = curve.Samples[0].ElapsedSeconds;
double lastElapsed = Math.Max(curve.Samples[^1].ElapsedSeconds, curve.HoldTimeSeconds);
double elapsedRange = Math.Max(lastElapsed - firstElapsed, 0.001);
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;
}
var geometry = new StreamGeometry();
using (StreamGeometryContext geometryContext = geometry.Open())
@@ -1898,8 +1938,8 @@ public sealed class MainWindowViewModel : ObservableObject
for (int i = 0; i < curve.Samples.Count; i++)
{
TorqueSamplePayload sample = curve.Samples[i];
double x = plot.Left + plot.Width * (sample.ElapsedSeconds - firstElapsed) / elapsedRange;
double y = plot.Bottom - (sample.TorqueMilliNewtonMeters - min) / range * plot.Height;
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)
{
@@ -1915,15 +1955,16 @@ public sealed class MainWindowViewModel : ObservableObject
geometry.Freeze();
context.DrawGeometry(null, linePen, geometry);
DrawChartText(context, max.ToString("0.##", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(16, plot.Top - 8), false);
DrawChartText(context, min.ToString("0.##", CultureInfo.InvariantCulture), 12, textBrush, new System.Windows.Point(16, plot.Bottom - 10), false);
DrawChartText(context, $"{firstElapsed:0.#}s", 12, textBrush, new System.Windows.Point(plot.Left, plot.Bottom + 10), false);
DrawChartText(context, $"{lastElapsed:0.#}s", 12, textBrush, new System.Windows.Point(plot.Right - 28, plot.Bottom + 10), false);
DrawChartText(context, TorqueUnit, 12, textBrush, new System.Windows.Point(18, plot.Top + 16), false);
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);
}
else
{
DrawChartText(context, "无扭矩曲线数据", 16, textBrush, new System.Windows.Point(plot.Left + 20, plot.Top + 20), false);
DrawChartText(context, "无转速/扭矩曲线数据", 16, textBrush, new System.Windows.Point(plot.Left + 20, plot.Top + 20), false);
}
}
@@ -2512,7 +2553,6 @@ public sealed class MainWindowViewModel : ObservableObject
_speedTorqueStartedAt = DateTime.Now;
_activeSpeedTorqueRun = CreateTestRun("转速/扭矩测试", _speedTorqueStartedAt.Value);
ClearTorqueSamples();
AppendTorqueSample(GetScaledTorque(), _realtimeSpeed, _speedTorqueStartedAt.Value);
_maxSpeedTorqueDisplacement = Math.Max(_maxSpeedTorqueDisplacement, Math.Abs(_speedTorqueDisplacement));
UpdateSpeedTorqueDisplay();
StatusText = "转速/扭矩测试已启动,通气阀由手动按钮独立控制。";
@@ -3342,6 +3382,12 @@ 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,
@@ -3366,15 +3412,20 @@ public sealed class MainWindowViewModel : ObservableObject
private void UpdateTorqueCurveStatus()
{
if (_isSpeedTorqueRunning && TorqueSamples.Count > 0)
if (_isSpeedTorqueRunning
&& _speedTorqueStartedAt.HasValue
&& _parameterConfig.TorqueHoldTime > 0)
{
double elapsed = TorqueSamples[^1].ElapsedSeconds;
TorqueCurveStatusText = $"实时曲线:采集中 {FormatConfigNumber(elapsed)} sX轴转速 / Y轴扭矩";
return;
double elapsed = Math.Max(0, (DateTime.Now - _speedTorqueStartedAt.Value).TotalSeconds);
if (elapsed < _parameterConfig.TorqueHoldTime)
{
TorqueCurveStatusText = $"实时曲线:采集中 {FormatConfigNumber(elapsed)} / {FormatConfigNumber(_parameterConfig.TorqueHoldTime)} sX轴扭矩 / Y轴转速";
return;
}
}
TorqueCurvePayload curve = CreateTorqueCurvePayload();
TorqueCurveStatusText = $"保持时间曲线:{curve.Result}";
TorqueCurveStatusText = $"转速/扭矩保持时间关系曲线:{curve.Result}";
}
private TorqueCurvePayload CreateTorqueCurvePayload()
@@ -3394,54 +3445,17 @@ public sealed class MainWindowViewModel : ObservableObject
})
.ToList();
double threshold = GetTorqueChangeThreshold();
if (_parameterConfig.TorqueHoldTime <= 0)
{
_cachedTorqueCurve = new TorqueCurvePayload
{
HoldTimeSeconds = _parameterConfig.TorqueHoldTime,
ChangeThresholdMilliNewtonMeters = threshold,
Result = "未设置保持时间,未判定",
Samples = samples
};
return _cachedTorqueCurve;
}
if (samples.Count < 2)
{
_cachedTorqueCurve = new TorqueCurvePayload
{
HoldTimeSeconds = _parameterConfig.TorqueHoldTime,
ChangeThresholdMilliNewtonMeters = threshold,
Result = "采样不足,未判定",
Samples = samples
};
return _cachedTorqueCurve;
}
double min = samples.Min(sample => sample.TorqueMilliNewtonMeters);
double max = samples.Max(sample => sample.TorqueMilliNewtonMeters);
double average = samples.Average(sample => sample.TorqueMilliNewtonMeters);
double fluctuation = max - min;
_cachedTorqueCurve = new TorqueCurvePayload
{
HoldTimeSeconds = _parameterConfig.TorqueHoldTime,
ChangeThresholdMilliNewtonMeters = threshold,
MinTorqueMilliNewtonMeters = min,
MaxTorqueMilliNewtonMeters = max,
AverageTorqueMilliNewtonMeters = average,
FluctuationMilliNewtonMeters = fluctuation,
Result = fluctuation > threshold ? "有变化" : "无明显变化",
Samples = samples
};
_cachedTorqueCurve = BuildTorqueCurvePayload(
samples,
_parameterConfig.TorqueHoldTime,
GetTorqueChangeThreshold(),
GetSpeedChangeThreshold());
return _cachedTorqueCurve;
}
private static TorqueCurvePayload CreateTorqueCurvePayload(TestRunPayload run)
{
double holdTime = run.ParameterSnapshot.TorqueHoldTime;
double threshold = Math.Max(MinimumTorqueChangeThreshold, Math.Abs(run.ParameterSnapshot.HoldTorque) * 0.01);
List<TorqueSamplePayload> samples = run.Samples
.Select(sample => new TorqueSamplePayload
{
@@ -3452,12 +3466,26 @@ public sealed class MainWindowViewModel : ObservableObject
.Where(sample => sample.ElapsedSeconds <= holdTime)
.ToList();
return BuildTorqueCurvePayload(
samples,
holdTime,
Math.Max(MinimumTorqueChangeThreshold, Math.Abs(run.ParameterSnapshot.HoldTorque) * 0.01),
Math.Max(MinimumSpeedChangeThreshold, Math.Abs(run.ParameterSnapshot.LoadSpeedSetting) * 0.01));
}
private static TorqueCurvePayload BuildTorqueCurvePayload(
List<TorqueSamplePayload> samples,
double holdTime,
double torqueThreshold,
double speedThreshold)
{
if (holdTime <= 0)
{
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
ChangeThresholdMilliNewtonMeters = threshold,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
Result = "未设置保持时间,未判定",
Samples = samples
};
@@ -3468,23 +3496,43 @@ public sealed class MainWindowViewModel : ObservableObject
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
ChangeThresholdMilliNewtonMeters = threshold,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
Result = "采样不足,未判定",
Samples = samples
};
}
double min = samples.Min(static sample => sample.TorqueMilliNewtonMeters);
double max = samples.Max(static sample => sample.TorqueMilliNewtonMeters);
double minTorque = samples.Min(static sample => sample.TorqueMilliNewtonMeters);
double maxTorque = samples.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 speedFluctuation = maxSpeed - minSpeed;
bool torqueChanged = torqueFluctuation > torqueThreshold;
bool speedChanged = speedFluctuation > speedThreshold;
string result = (torqueChanged, speedChanged) switch
{
(true, true) => "有变化(扭矩、转速)",
(true, false) => "有变化(扭矩)",
(false, true) => "有变化(转速)",
_ => "无明显变化"
};
return new TorqueCurvePayload
{
HoldTimeSeconds = holdTime,
ChangeThresholdMilliNewtonMeters = threshold,
MinTorqueMilliNewtonMeters = min,
MaxTorqueMilliNewtonMeters = max,
ChangeThresholdMilliNewtonMeters = torqueThreshold,
SpeedChangeThresholdRpm = speedThreshold,
MinTorqueMilliNewtonMeters = minTorque,
MaxTorqueMilliNewtonMeters = maxTorque,
AverageTorqueMilliNewtonMeters = samples.Average(static sample => sample.TorqueMilliNewtonMeters),
FluctuationMilliNewtonMeters = max - min,
Result = max - min > threshold ? "有变化" : "无明显变化",
FluctuationMilliNewtonMeters = torqueFluctuation,
MinSpeedRpm = minSpeed,
MaxSpeedRpm = maxSpeed,
AverageSpeedRpm = samples.Average(static sample => sample.SpeedRpm),
SpeedFluctuationRpm = speedFluctuation,
Result = result,
Samples = samples
};
}
@@ -3494,6 +3542,11 @@ public sealed class MainWindowViewModel : ObservableObject
return Math.Max(MinimumTorqueChangeThreshold, Math.Abs(_parameterConfig.HoldTorque) * 0.01);
}
private double GetSpeedChangeThreshold()
{
return Math.Max(MinimumSpeedChangeThreshold, Math.Abs(_parameterConfig.LoadSpeedSetting) * 0.01);
}
private int GetTorqueSampleLimit()
{
if (_parameterConfig.TorqueHoldTime <= 0)
@@ -3506,12 +3559,15 @@ public sealed class MainWindowViewModel : ObservableObject
return Math.Max(MaxTorqueSampleCount, requiredSamples);
}
private void CaptureRealtimeSample(double dialIndicator, IReadOnlyDictionary<ushort, bool> coilValues)
private void CaptureRealtimeSample(
double dialIndicator,
IReadOnlyDictionary<ushort, bool> coilValues,
DateTime sampledAt)
{
var sample = new RealtimeSamplePayload
{
Sequence = ++_realtimeSampleSequence,
SampledAt = DateTime.Now,
SampledAt = sampledAt,
DialIndicatorMm = dialIndicator,
RelativeDisplacementMm = _relativeDisplacement,
AxialAxisPositionMm = _axialAxisPosition,

View File

@@ -214,6 +214,8 @@ public sealed class TorqueCurvePayload
public double ChangeThresholdMilliNewtonMeters { get; init; }
public double SpeedChangeThresholdRpm { get; init; }
public double MinTorqueMilliNewtonMeters { get; init; }
public double MaxTorqueMilliNewtonMeters { get; init; }
@@ -222,6 +224,14 @@ public sealed class TorqueCurvePayload
public double FluctuationMilliNewtonMeters { get; init; }
public double MinSpeedRpm { get; init; }
public double MaxSpeedRpm { get; init; }
public double AverageSpeedRpm { get; init; }
public double SpeedFluctuationRpm { get; init; }
public string Result { get; init; } = string.Empty;
public List<TorqueSamplePayload> Samples { get; init; } = [];

View File

@@ -65,8 +65,8 @@ public sealed class TorqueTrendControl : FrameworkElement
if (samples.Count == 0)
{
DrawText(drawingContext, "等待转速/扭矩数据", 15, Color.FromRgb(82, 97, 111), new Point(plot.Left + 8, plot.Top + 8));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 12, Color.FromRgb(82, 97, 111), new Point(6, plot.Top));
DrawText(drawingContext, $"转速 ({SpeedUnit})", 12, Color.FromRgb(82, 97, 111), new Point(plot.Right - 82, plot.Bottom + 5));
DrawText(drawingContext, $"转速 ({SpeedUnit})", 12, Color.FromRgb(82, 97, 111), new Point(6, plot.Top));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 12, Color.FromRgb(82, 97, 111), new Point(plot.Right - 82, plot.Bottom + 5));
return;
}
@@ -107,20 +107,20 @@ public sealed class TorqueTrendControl : FrameworkElement
speedRange = maxSpeed - minSpeed;
}
DrawText(drawingContext, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(8, plot.Top - 2));
DrawText(drawingContext, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(8, plot.Bottom - 14));
DrawText(drawingContext, minSpeed.ToString("0", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(plot.Left, plot.Bottom + 5));
DrawText(drawingContext, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(plot.Right - 38, plot.Bottom + 5));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Left + 4, plot.Top + 3));
DrawText(drawingContext, $"转速 ({SpeedUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Right - 82, plot.Bottom - 18));
DrawText(drawingContext, maxSpeed.ToString("0", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(8, plot.Top - 2));
DrawText(drawingContext, minSpeed.ToString("0", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(8, plot.Bottom - 14));
DrawText(drawingContext, minTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(plot.Left, plot.Bottom + 5));
DrawText(drawingContext, maxTorque.ToString("0.##", CultureInfo.InvariantCulture), 12, Color.FromRgb(82, 97, 111), new Point(plot.Right - 38, plot.Bottom + 5));
DrawText(drawingContext, $"转速 ({SpeedUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Left + 4, plot.Top + 3));
DrawText(drawingContext, $"扭矩 ({TorqueUnit})", 11, Color.FromRgb(82, 97, 111), new Point(plot.Right - 82, plot.Bottom - 18));
var geometry = new StreamGeometry();
using (StreamGeometryContext context = geometry.Open())
{
for (int i = 0; i < samples.Count; i++)
{
double x = plot.Left + plot.Width * (samples[i].SpeedRpm - minSpeed) / speedRange;
double y = plot.Bottom - (samples[i].TorqueMilliNewtonMeters - minTorque) / torqueRange * plot.Height;
double x = plot.Left + plot.Width * (samples[i].TorqueMilliNewtonMeters - minTorque) / torqueRange;
double y = plot.Bottom - (samples[i].SpeedRpm - minSpeed) / speedRange * plot.Height;
Point point = new(x, y);
if (i == 0)
@@ -139,7 +139,7 @@ public sealed class TorqueTrendControl : FrameworkElement
drawingContext.DrawGeometry(null, linePen, geometry);
TorqueSamplePayload current = samples[^1];
double currentY = plot.Bottom - (current.TorqueMilliNewtonMeters - minTorque) / torqueRange * plot.Height;
double currentY = plot.Bottom - (current.SpeedRpm - minSpeed) / speedRange * plot.Height;
var currentPen = new Pen(new SolidColorBrush(Color.FromRgb(15, 118, 110)), 1.2);
drawingContext.DrawLine(currentPen, new Point(plot.Left, currentY), new Point(plot.Right, currentY));
DrawText(