更新2026
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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)} s;X轴转速 / Y轴扭矩";
|
||||
return;
|
||||
double elapsed = Math.Max(0, (DateTime.Now - _speedTorqueStartedAt.Value).TotalSeconds);
|
||||
if (elapsed < _parameterConfig.TorqueHoldTime)
|
||||
{
|
||||
TorqueCurveStatusText = $"实时曲线:采集中 {FormatConfigNumber(elapsed)} / {FormatConfigNumber(_parameterConfig.TorqueHoldTime)} s;X轴扭矩 / 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,
|
||||
|
||||
@@ -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; } = [];
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user