20260313 客户需求整改
This commit is contained in:
@@ -107,7 +107,9 @@ namespace CSI_H238M
|
|||||||
// 保存配置(如果需要)
|
// 保存配置(如果需要)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_config?.Save(AppConfig.GetDefaultConfigPath());
|
string configPath = AppConfig.GetDefaultConfigPath();
|
||||||
|
_config = AppConfig.Load(configPath);
|
||||||
|
_config?.Save(configPath);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ namespace COFTester.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; } = "zh-CN";
|
public string Language { get; set; } = "zh-CN";
|
||||||
|
|
||||||
|
public string ForceDisplayUnit { get; set; } = "N";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 自動保存測試結果
|
/// 自動保存測試結果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -240,8 +240,10 @@ namespace COFTester.Models
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TestResult : INotifyPropertyChanged
|
public class TestResult : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
|
private const double StandardGravity = 9.80665;
|
||||||
private bool _isVisible = true;
|
private bool _isVisible = true;
|
||||||
private string _sampleNumber = string.Empty;
|
private string _sampleNumber = string.Empty;
|
||||||
|
private string _forceDisplayUnit = "N";
|
||||||
|
|
||||||
public Guid TestId { get; set; } = Guid.NewGuid(); // 測試唯一ID
|
public Guid TestId { get; set; } = Guid.NewGuid(); // 測試唯一ID
|
||||||
public DateTime TestDateTime { get; set; } = DateTime.Now; // 測試時間
|
public DateTime TestDateTime { get; set; } = DateTime.Now; // 測試時間
|
||||||
@@ -267,6 +269,31 @@ namespace COFTester.Models
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 试样编号
|
/// 试样编号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public string ForceDisplayUnit
|
||||||
|
{
|
||||||
|
get => _forceDisplayUnit;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string normalizedUnit = string.Equals(value, "g", StringComparison.OrdinalIgnoreCase) ? "g" : "N";
|
||||||
|
if (_forceDisplayUnit == normalizedUnit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_forceDisplayUnit = normalizedUnit;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(DisplayedMaxForce));
|
||||||
|
OnPropertyChanged(nameof(DisplayedAvgKineticForce));
|
||||||
|
OnPropertyChanged(nameof(DisplayedPeelStrength25mm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double DisplayedMaxForce => ConvertForceForDisplay(MaxForce);
|
||||||
|
|
||||||
|
public double DisplayedAvgKineticForce => ConvertForceForDisplay(AvgKineticForce);
|
||||||
|
|
||||||
|
public double DisplayedPeelStrength25mm => ConvertForceForDisplay(PeelStrength_N_25mm);
|
||||||
|
|
||||||
public string SampleNumber
|
public string SampleNumber
|
||||||
{
|
{
|
||||||
get => _sampleNumber;
|
get => _sampleNumber;
|
||||||
@@ -282,6 +309,13 @@ namespace COFTester.Models
|
|||||||
set { _isVisible = value; OnPropertyChanged(); }
|
set { _isVisible = value; OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double ConvertForceForDisplay(double forceInNewton)
|
||||||
|
{
|
||||||
|
return ForceDisplayUnit == "g"
|
||||||
|
? forceInNewton * 1000.0 / StandardGravity
|
||||||
|
: forceInNewton;
|
||||||
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
protected void OnPropertyChanged([CallerMemberName] string? name = null)
|
protected void OnPropertyChanged([CallerMemberName] string? name = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ namespace COFTester.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class PdfReportService
|
public class PdfReportService
|
||||||
{
|
{
|
||||||
|
private const double StandardGravity = 9.80665;
|
||||||
private const double PageWidth = 595; // A4宽度(点)
|
private const double PageWidth = 595; // A4宽度(点)
|
||||||
private const double PageHeight = 842; // A4高度(点)
|
private const double PageHeight = 842; // A4高度(点)
|
||||||
private const double MarginLeft = 50;
|
private const double MarginLeft = 50;
|
||||||
@@ -195,10 +196,11 @@ namespace COFTester.Services
|
|||||||
/// <param name="testRecords">测试记录</param>
|
/// <param name="testRecords">测试记录</param>
|
||||||
/// <param name="parameters">测试参数</param>
|
/// <param name="parameters">测试参数</param>
|
||||||
/// <param name="isChinese">是否使用中文(默认根据当前语言)</param>
|
/// <param name="isChinese">是否使用中文(默认根据当前语言)</param>
|
||||||
public void GenerateReport(string filePath, IEnumerable<TestResult> testRecords, TestParameters parameters, bool? isChinese = null)
|
public void GenerateReport(string filePath, IEnumerable<TestResult> testRecords, TestParameters parameters, string forceDisplayUnit = "N", bool? isChinese = null)
|
||||||
{
|
{
|
||||||
// 如果未指定语言,根据当前语言资源判断
|
// 如果未指定语言,根据当前语言资源判断
|
||||||
bool useChinese = isChinese ?? (Resources.LanguageResources.Instance.CurrentLanguage == "zh-CN");
|
bool useChinese = isChinese ?? (Resources.LanguageResources.Instance.CurrentLanguage == "zh-CN");
|
||||||
|
string normalizedForceUnit = NormalizeForceDisplayUnit(forceDisplayUnit);
|
||||||
|
|
||||||
if (testRecords == null || !testRecords.Any())
|
if (testRecords == null || !testRecords.Any())
|
||||||
throw new ArgumentException(useChinese ? "测试记录不能为空" : "Test records cannot be empty");
|
throw new ArgumentException(useChinese ? "测试记录不能为空" : "Test records cannot be empty");
|
||||||
@@ -232,13 +234,13 @@ namespace COFTester.Services
|
|||||||
yPosition = DrawHeader(gfx, yPosition, useChinese);
|
yPosition = DrawHeader(gfx, yPosition, useChinese);
|
||||||
System.Diagnostics.Debug.WriteLine("标题已绘制");
|
System.Diagnostics.Debug.WriteLine("标题已绘制");
|
||||||
|
|
||||||
yPosition = DrawTestInfo(gfx, yPosition, parameters, useChinese);
|
yPosition = DrawTestInfo(gfx, yPosition, parameters, normalizedForceUnit, useChinese);
|
||||||
System.Diagnostics.Debug.WriteLine("测试信息已绘制");
|
System.Diagnostics.Debug.WriteLine("测试信息已绘制");
|
||||||
|
|
||||||
yPosition = DrawStatistics(gfx, yPosition, testRecords, useChinese);
|
yPosition = DrawStatistics(gfx, yPosition, testRecords, useChinese);
|
||||||
System.Diagnostics.Debug.WriteLine("统计信息已绘制");
|
System.Diagnostics.Debug.WriteLine("统计信息已绘制");
|
||||||
|
|
||||||
yPosition = DrawDetailedResults(gfx, yPosition, testRecords, document, gfx, page, useChinese);
|
yPosition = DrawDetailedResults(gfx, yPosition, testRecords, normalizedForceUnit, document, gfx, page, useChinese);
|
||||||
System.Diagnostics.Debug.WriteLine("详细结果已绘制");
|
System.Diagnostics.Debug.WriteLine("详细结果已绘制");
|
||||||
|
|
||||||
// 保存文档
|
// 保存文档
|
||||||
@@ -283,7 +285,7 @@ namespace COFTester.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 绘制测试信息
|
/// 绘制测试信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private double DrawTestInfo(XGraphics gfx, double yPosition, TestParameters parameters, bool isChinese)
|
private double DrawTestInfo(XGraphics gfx, double yPosition, TestParameters parameters, string forceDisplayUnit, bool isChinese)
|
||||||
{
|
{
|
||||||
string fontFamily = GetFontFamily(isChinese);
|
string fontFamily = GetFontFamily(isChinese);
|
||||||
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
|
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
|
||||||
@@ -299,6 +301,7 @@ namespace COFTester.Services
|
|||||||
{
|
{
|
||||||
$"测试标准: {parameters.Standard}",
|
$"测试标准: {parameters.Standard}",
|
||||||
$"滑块质量: {parameters.SledMass:F1} g",
|
$"滑块质量: {parameters.SledMass:F1} g",
|
||||||
|
$"力值单位: {forceDisplayUnit}",
|
||||||
$"测试速度: {parameters.TestSpeed:F1} mm/min",
|
$"测试速度: {parameters.TestSpeed:F1} mm/min",
|
||||||
$"测试行程: {parameters.TestStroke:F1} mm",
|
$"测试行程: {parameters.TestStroke:F1} mm",
|
||||||
$"操作员: {parameters.Operator}"
|
$"操作员: {parameters.Operator}"
|
||||||
@@ -306,6 +309,7 @@ namespace COFTester.Services
|
|||||||
{
|
{
|
||||||
$"Test Standard: {parameters.Standard}",
|
$"Test Standard: {parameters.Standard}",
|
||||||
$"Sled Mass: {parameters.SledMass:F1} g",
|
$"Sled Mass: {parameters.SledMass:F1} g",
|
||||||
|
$"Force Unit: {forceDisplayUnit}",
|
||||||
$"Test Speed: {parameters.TestSpeed:F1} mm/min",
|
$"Test Speed: {parameters.TestSpeed:F1} mm/min",
|
||||||
$"Test Stroke: {parameters.TestStroke:F1} mm",
|
$"Test Stroke: {parameters.TestStroke:F1} mm",
|
||||||
$"Operator: {parameters.Operator}"
|
$"Operator: {parameters.Operator}"
|
||||||
@@ -403,7 +407,7 @@ namespace COFTester.Services
|
|||||||
/// 绘制详细测试结果
|
/// 绘制详细测试结果
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private double DrawDetailedResults(XGraphics gfx, double yPosition, IEnumerable<TestResult> testRecords,
|
private double DrawDetailedResults(XGraphics gfx, double yPosition, IEnumerable<TestResult> testRecords,
|
||||||
PdfDocument document, XGraphics currentGfx, PdfPage currentPage, bool isChinese)
|
string forceDisplayUnit, PdfDocument document, XGraphics currentGfx, PdfPage currentPage, bool isChinese)
|
||||||
{
|
{
|
||||||
string fontFamily = GetFontFamily(isChinese);
|
string fontFamily = GetFontFamily(isChinese);
|
||||||
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
|
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
|
||||||
@@ -434,8 +438,8 @@ namespace COFTester.Services
|
|||||||
|
|
||||||
// 表头
|
// 表头
|
||||||
string[] headers = isChinese ?
|
string[] headers = isChinese ?
|
||||||
new[] { "编号", "测试时间", "静摩擦", "动摩擦", "最大力", "平均力" } :
|
new[] { "编号", "测试时间", "静摩擦", "动摩擦", $"最大力({forceDisplayUnit})", $"平均力({forceDisplayUnit})" } :
|
||||||
new[] { "No.", "Test Time", "Static", "Kinetic", "Max F", "Avg F" };
|
new[] { "No.", "Test Time", "Static", "Kinetic", $"Max F ({forceDisplayUnit})", $"Avg F ({forceDisplayUnit})" };
|
||||||
|
|
||||||
double currentX = tableX;
|
double currentX = tableX;
|
||||||
for (int i = 0; i < headers.Length; i++)
|
for (int i = 0; i < headers.Length; i++)
|
||||||
@@ -474,8 +478,8 @@ namespace COFTester.Services
|
|||||||
record.TestDateTime.ToString("MM-dd HH:mm"),
|
record.TestDateTime.ToString("MM-dd HH:mm"),
|
||||||
record.StaticCOF.ToString("F4"),
|
record.StaticCOF.ToString("F4"),
|
||||||
record.KineticCOF.ToString("F4"),
|
record.KineticCOF.ToString("F4"),
|
||||||
record.MaxForce.ToString("F2") + " N",
|
ConvertForceForDisplay(record.MaxForce, forceDisplayUnit).ToString("F2") + " " + forceDisplayUnit,
|
||||||
record.AvgKineticForce.ToString("F2") + " N"
|
ConvertForceForDisplay(record.AvgKineticForce, forceDisplayUnit).ToString("F2") + " " + forceDisplayUnit
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < rowData.Length; i++)
|
for (int i = 0; i < rowData.Length; i++)
|
||||||
@@ -556,6 +560,18 @@ namespace COFTester.Services
|
|||||||
XStringFormats.TopCenter);
|
XStringFormats.TopCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeForceDisplayUnit(string? unit)
|
||||||
|
{
|
||||||
|
return string.Equals(unit, "g", StringComparison.OrdinalIgnoreCase) ? "g" : "N";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double ConvertForceForDisplay(double forceInNewton, string forceDisplayUnit)
|
||||||
|
{
|
||||||
|
return NormalizeForceDisplayUnit(forceDisplayUnit) == "g"
|
||||||
|
? forceInNewton * 1000.0 / StandardGravity
|
||||||
|
: forceInNewton;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 计算标准差
|
/// 计算标准差
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Windows.Threading;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Globalization;
|
||||||
using COFTester.Models;
|
using COFTester.Models;
|
||||||
using COFTester.Services;
|
using COFTester.Services;
|
||||||
using COFTester.Resources;
|
using COFTester.Resources;
|
||||||
@@ -33,6 +34,7 @@ namespace COFTester.ViewModels
|
|||||||
private bool _isUiDataUpdateQueued;
|
private bool _isUiDataUpdateQueued;
|
||||||
private long _lastPlotRefreshTick;
|
private long _lastPlotRefreshTick;
|
||||||
private const int PlotRefreshIntervalMs = 100;
|
private const int PlotRefreshIntervalMs = 100;
|
||||||
|
private const double StandardGravity = 9.80665;
|
||||||
|
|
||||||
private double _currentForce;
|
private double _currentForce;
|
||||||
private double _currentDisp;
|
private double _currentDisp;
|
||||||
@@ -52,6 +54,8 @@ namespace COFTester.ViewModels
|
|||||||
private bool _stopRequestedByUser = false;
|
private bool _stopRequestedByUser = false;
|
||||||
private bool _acceptIncomingTestData = false;
|
private bool _acceptIncomingTestData = false;
|
||||||
private int _testSessionId = 0;
|
private int _testSessionId = 0;
|
||||||
|
private string _selectedSledMassPreset = "200";
|
||||||
|
private string _forceDisplayUnit = "N";
|
||||||
private string _selectedDirection = ""; // 选中的方向:Up/Down/Right/Left
|
private string _selectedDirection = ""; // 选中的方向:Up/Down/Right/Left
|
||||||
private string _resetButtonText; // 复位按钮文本
|
private string _resetButtonText; // 复位按钮文本
|
||||||
private string _testButtonText; // 测试按钮文本
|
private string _testButtonText; // 测试按钮文本
|
||||||
@@ -100,6 +104,9 @@ namespace COFTester.ViewModels
|
|||||||
UpdateCustomParametersCommand = new AsyncRelayCommand(UpdateCustomParametersAsync, () => IsConnected && SelectedStandard == "Custom");
|
UpdateCustomParametersCommand = new AsyncRelayCommand(UpdateCustomParametersAsync, () => IsConnected && SelectedStandard == "Custom");
|
||||||
|
|
||||||
Parameters = _config.DefaultTestParameters ?? new TestParameters();
|
Parameters = _config.DefaultTestParameters ?? new TestParameters();
|
||||||
|
Parameters.PropertyChanged += OnParametersPropertyChanged;
|
||||||
|
_forceDisplayUnit = NormalizeForceDisplayUnit(_config.ForceDisplayUnit);
|
||||||
|
SyncSledMassPresetFromValue();
|
||||||
TestRecords = new ObservableCollection<TestResult>();
|
TestRecords = new ObservableCollection<TestResult>();
|
||||||
TestRecords.CollectionChanged += (s, e) =>
|
TestRecords.CollectionChanged += (s, e) =>
|
||||||
{
|
{
|
||||||
@@ -290,6 +297,69 @@ namespace COFTester.ViewModels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public LanguageResources Lang => LanguageResources.Instance;
|
public LanguageResources Lang => LanguageResources.Instance;
|
||||||
|
|
||||||
|
public string ForceDisplayUnit
|
||||||
|
{
|
||||||
|
get => _forceDisplayUnit;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var normalizedUnit = NormalizeForceDisplayUnit(value);
|
||||||
|
if (_forceDisplayUnit == normalizedUnit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_forceDisplayUnit = normalizedUnit;
|
||||||
|
_config.ForceDisplayUnit = normalizedUnit;
|
||||||
|
OnPropertyChanged();
|
||||||
|
NotifyForceDisplayChanged();
|
||||||
|
PersistSettings();
|
||||||
|
RefreshPlotForDisplaySettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ForceDisplayLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"力值 ({ForceDisplayUnit})" : $"Force ({ForceDisplayUnit})";
|
||||||
|
|
||||||
|
public string ForceAxisLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"力值 ({ForceDisplayUnit})" : $"Force ({ForceDisplayUnit})";
|
||||||
|
|
||||||
|
public string AvgPeelForceLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"平均剥离力 ({ForceDisplayUnit})" : $"Avg Peel Force ({ForceDisplayUnit})";
|
||||||
|
|
||||||
|
public string PeelStrengthLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"剥离强度 ({ForceDisplayUnit}/25mm)" : $"Peel Strength ({ForceDisplayUnit}/25mm)";
|
||||||
|
|
||||||
|
public string MaxForceLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"最大力值 ({ForceDisplayUnit})" : $"Max Force ({ForceDisplayUnit})";
|
||||||
|
|
||||||
|
public string AvgForceDisplayLabel =>
|
||||||
|
Lang.CurrentLanguage == "zh-CN" ? $"平均力值 ({ForceDisplayUnit})" : $"Avg Force ({ForceDisplayUnit})";
|
||||||
|
|
||||||
|
public string SelectedSledMassPreset
|
||||||
|
{
|
||||||
|
get => _selectedSledMassPreset;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var normalizedPreset = string.IsNullOrWhiteSpace(value) ? "Custom" : value;
|
||||||
|
if (_selectedSledMassPreset == normalizedPreset)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_selectedSledMassPreset = normalizedPreset;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(IsCustomSledMass));
|
||||||
|
|
||||||
|
if (double.TryParse(normalizedPreset, NumberStyles.Float, CultureInfo.InvariantCulture, out double presetMass))
|
||||||
|
{
|
||||||
|
Parameters.SledMass = presetMass;
|
||||||
|
PersistSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCustomSledMass => SelectedSledMassPreset == "Custom";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化 ScottPlot 图表
|
/// 初始化 ScottPlot 图表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -310,7 +380,7 @@ namespace COFTester.ViewModels
|
|||||||
|
|
||||||
// 设置坐标轴标签(使用已设置的中文字体)
|
// 设置坐标轴标签(使用已设置的中文字体)
|
||||||
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
|
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
|
||||||
_wpfPlot.Plot.YLabel(Lang.ForceAxis);
|
_wpfPlot.Plot.YLabel(ForceAxisLabel);
|
||||||
|
|
||||||
// 设置Y轴最小值为0
|
// 设置Y轴最小值为0
|
||||||
_wpfPlot.Plot.Axes.SetLimitsY(0, 10);
|
_wpfPlot.Plot.Axes.SetLimitsY(0, 10);
|
||||||
@@ -389,7 +459,7 @@ namespace COFTester.ViewModels
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var xData = _realTimePoints.Select(p => p.Displacement).ToArray();
|
var xData = _realTimePoints.Select(p => p.Displacement).ToArray();
|
||||||
var yData = _realTimePoints.Select(p => p.Force).ToArray();
|
var yData = _realTimePoints.Select(p => ConvertForceForDisplay(p.Force)).ToArray();
|
||||||
|
|
||||||
// 创建新的散点图
|
// 创建新的散点图
|
||||||
_scatterPlot = _wpfPlot.Plot.Add.Scatter(xData, yData);
|
_scatterPlot = _wpfPlot.Plot.Add.Scatter(xData, yData);
|
||||||
@@ -509,9 +579,16 @@ namespace COFTester.ViewModels
|
|||||||
public double CurrentForce
|
public double CurrentForce
|
||||||
{
|
{
|
||||||
get => _currentForce;
|
get => _currentForce;
|
||||||
set { _currentForce = value; OnPropertyChanged(); }
|
set
|
||||||
|
{
|
||||||
|
_currentForce = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(DisplayedCurrentForce));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double DisplayedCurrentForce => ConvertForceForDisplay(CurrentForce);
|
||||||
|
|
||||||
public double CurrentDisp
|
public double CurrentDisp
|
||||||
{
|
{
|
||||||
get => _currentDisp;
|
get => _currentDisp;
|
||||||
@@ -545,9 +622,21 @@ namespace COFTester.ViewModels
|
|||||||
public TestResult? LatestResult
|
public TestResult? LatestResult
|
||||||
{
|
{
|
||||||
get => _latestResult;
|
get => _latestResult;
|
||||||
set { _latestResult = value; OnPropertyChanged(); }
|
set
|
||||||
|
{
|
||||||
|
_latestResult = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(DisplayedLatestAvgKineticForce));
|
||||||
|
OnPropertyChanged(nameof(DisplayedLatestPeelStrength));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double? DisplayedLatestAvgKineticForce =>
|
||||||
|
LatestResult == null ? null : ConvertForceForDisplay(LatestResult.AvgKineticForce);
|
||||||
|
|
||||||
|
public double? DisplayedLatestPeelStrength =>
|
||||||
|
LatestResult == null ? null : ConvertForceForDisplay(LatestResult.PeelStrength_N_25mm);
|
||||||
|
|
||||||
public string StatusMessage
|
public string StatusMessage
|
||||||
{
|
{
|
||||||
get => _statusMessage;
|
get => _statusMessage;
|
||||||
@@ -688,6 +777,7 @@ namespace COFTester.ViewModels
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 添加到测试记录集合
|
// 添加到测试记录集合
|
||||||
|
result.ForceDisplayUnit = ForceDisplayUnit;
|
||||||
TestRecords.Add(result);
|
TestRecords.Add(result);
|
||||||
LatestResult = result;
|
LatestResult = result;
|
||||||
|
|
||||||
@@ -1552,7 +1642,7 @@ namespace COFTester.ViewModels
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var xData = result.RawData.Select(p => p.Displacement).ToArray();
|
var xData = result.RawData.Select(p => p.Displacement).ToArray();
|
||||||
var yData = result.RawData.Select(p => p.Force).ToArray();
|
var yData = result.RawData.Select(p => ConvertForceForDisplay(p.Force)).ToArray();
|
||||||
|
|
||||||
var scatter = _wpfPlot.Plot.Add.Scatter(xData, yData);
|
var scatter = _wpfPlot.Plot.Add.Scatter(xData, yData);
|
||||||
scatter.LineWidth = 2;
|
scatter.LineWidth = 2;
|
||||||
@@ -1656,7 +1746,7 @@ namespace COFTester.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pdfService = new Services.PdfReportService();
|
var pdfService = new Services.PdfReportService();
|
||||||
pdfService.GenerateReport(filePath, TestRecords, Parameters);
|
pdfService.GenerateReport(filePath, TestRecords, Parameters, ForceDisplayUnit);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -1689,10 +1779,17 @@ namespace COFTester.ViewModels
|
|||||||
if (_wpfPlot != null)
|
if (_wpfPlot != null)
|
||||||
{
|
{
|
||||||
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
|
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
|
||||||
_wpfPlot.Plot.YLabel(Lang.ForceAxis);
|
_wpfPlot.Plot.YLabel(ForceAxisLabel);
|
||||||
_wpfPlot.Refresh();
|
_wpfPlot.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnPropertyChanged(nameof(ForceDisplayLabel));
|
||||||
|
OnPropertyChanged(nameof(ForceAxisLabel));
|
||||||
|
OnPropertyChanged(nameof(AvgPeelForceLabel));
|
||||||
|
OnPropertyChanged(nameof(PeelStrengthLabel));
|
||||||
|
OnPropertyChanged(nameof(MaxForceLabel));
|
||||||
|
OnPropertyChanged(nameof(AvgForceDisplayLabel));
|
||||||
|
|
||||||
// 更新当前状态消息
|
// 更新当前状态消息
|
||||||
if (_statusMessage == "系统就绪" || _statusMessage == "System Ready")
|
if (_statusMessage == "系统就绪" || _statusMessage == "System Ready")
|
||||||
StatusMessage = Lang.SystemReady;
|
StatusMessage = Lang.SystemReady;
|
||||||
@@ -1711,6 +1808,101 @@ namespace COFTester.ViewModels
|
|||||||
TestButtonText = Lang.StartTest;
|
TestButtonText = Lang.StartTest;
|
||||||
ResetButtonText = Lang.ResetSystem;
|
ResetButtonText = Lang.ResetSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PersistSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnParametersPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TestParameters.SledMass))
|
||||||
|
{
|
||||||
|
SyncSledMassPresetFromValue();
|
||||||
|
PersistSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SyncSledMassPresetFromValue()
|
||||||
|
{
|
||||||
|
string preset = Math.Abs(Parameters.SledMass - 200.0) < 0.0001 ? "200"
|
||||||
|
: Math.Abs(Parameters.SledMass - 300.0) < 0.0001 ? "300"
|
||||||
|
: Math.Abs(Parameters.SledMass - 500.0) < 0.0001 ? "500"
|
||||||
|
: "Custom";
|
||||||
|
|
||||||
|
if (_selectedSledMassPreset != preset)
|
||||||
|
{
|
||||||
|
_selectedSledMassPreset = preset;
|
||||||
|
OnPropertyChanged(nameof(SelectedSledMassPreset));
|
||||||
|
OnPropertyChanged(nameof(IsCustomSledMass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double ConvertForceForDisplay(double forceInNewton)
|
||||||
|
{
|
||||||
|
return ForceDisplayUnit == "g"
|
||||||
|
? forceInNewton * 1000.0 / StandardGravity
|
||||||
|
: forceInNewton;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string NormalizeForceDisplayUnit(string? unit)
|
||||||
|
{
|
||||||
|
return string.Equals(unit, "g", StringComparison.OrdinalIgnoreCase) ? "g" : "N";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyForceDisplayChanged()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(DisplayedCurrentForce));
|
||||||
|
OnPropertyChanged(nameof(DisplayedLatestAvgKineticForce));
|
||||||
|
OnPropertyChanged(nameof(DisplayedLatestPeelStrength));
|
||||||
|
OnPropertyChanged(nameof(ForceDisplayLabel));
|
||||||
|
OnPropertyChanged(nameof(ForceAxisLabel));
|
||||||
|
OnPropertyChanged(nameof(AvgPeelForceLabel));
|
||||||
|
OnPropertyChanged(nameof(PeelStrengthLabel));
|
||||||
|
OnPropertyChanged(nameof(MaxForceLabel));
|
||||||
|
OnPropertyChanged(nameof(AvgForceDisplayLabel));
|
||||||
|
|
||||||
|
foreach (var record in TestRecords)
|
||||||
|
{
|
||||||
|
record.ForceDisplayUnit = ForceDisplayUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshPlotForDisplaySettings()
|
||||||
|
{
|
||||||
|
if (_wpfPlot == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_scatterPlot = null;
|
||||||
|
_testCurves.Clear();
|
||||||
|
InitializeScottPlot();
|
||||||
|
|
||||||
|
foreach (var record in TestRecords)
|
||||||
|
{
|
||||||
|
AddCurveToPlot(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_realTimePoints.Count > 0)
|
||||||
|
{
|
||||||
|
UpdateScottPlot();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAllCurvesVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PersistSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_config.DefaultTestParameters = Parameters;
|
||||||
|
_config.Language = Lang.CurrentLanguage;
|
||||||
|
_config.ForceDisplayUnit = ForceDisplayUnit;
|
||||||
|
_config.Save(AppConfig.GetDefaultConfigPath());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[ViewModel] 保存配置失败: {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenConfig()
|
private void OpenConfig()
|
||||||
@@ -1865,6 +2057,7 @@ namespace COFTester.ViewModels
|
|||||||
_daqService.DataReceived -= OnDataReceived;
|
_daqService.DataReceived -= OnDataReceived;
|
||||||
_daqService.TestFinished -= OnTestFinished;
|
_daqService.TestFinished -= OnTestFinished;
|
||||||
_daqService.ErrorOccurred -= OnErrorOccurred;
|
_daqService.ErrorOccurred -= OnErrorOccurred;
|
||||||
|
Parameters.PropertyChanged -= OnParametersPropertyChanged;
|
||||||
|
|
||||||
// 停止測試
|
// 停止測試
|
||||||
if (_isTesting)
|
if (_isTesting)
|
||||||
|
|||||||
@@ -69,17 +69,17 @@
|
|||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTextColumn.HeaderTemplate>
|
</DataGridTextColumn.HeaderTemplate>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Binding="{Binding MaxForce, StringFormat={}{0:F3}}" Width="120">
|
<DataGridTextColumn Binding="{Binding DisplayedMaxForce, StringFormat={}{0:F3}}" Width="120">
|
||||||
<DataGridTextColumn.HeaderTemplate>
|
<DataGridTextColumn.HeaderTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding DataContext.Lang.MaxForce, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
|
<TextBlock Text="{Binding DataContext.MaxForceLabel, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTextColumn.HeaderTemplate>
|
</DataGridTextColumn.HeaderTemplate>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn Binding="{Binding AvgKineticForce, StringFormat={}{0:F3}}" Width="120">
|
<DataGridTextColumn Binding="{Binding DisplayedAvgKineticForce, StringFormat={}{0:F3}}" Width="120">
|
||||||
<DataGridTextColumn.HeaderTemplate>
|
<DataGridTextColumn.HeaderTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<TextBlock Text="{Binding DataContext.Lang.AvgForce, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
|
<TextBlock Text="{Binding DataContext.AvgForceDisplayLabel, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTextColumn.HeaderTemplate>
|
</DataGridTextColumn.HeaderTemplate>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
|
|||||||
@@ -53,17 +53,49 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- 质量选择 -->
|
<!-- 质量选择 -->
|
||||||
<TextBlock Text="滑块质量" Margin="0,10,0,5" FontSize="13" Foreground="{StaticResource GrayBrush}"/>
|
<TextBlock Text="滑块质量预设" Margin="0,10,0,5" FontSize="13" Foreground="{StaticResource GrayBrush}"/>
|
||||||
<ComboBox SelectedValue="{Binding Parameters.SledMass, Mode=TwoWay}"
|
<ComboBox SelectedValue="{Binding SelectedSledMassPreset, Mode=TwoWay}"
|
||||||
SelectedValuePath="Tag"
|
SelectedValuePath="Tag"
|
||||||
Margin="0,0,0,15"
|
Margin="0,0,0,15"
|
||||||
Height="40"
|
Height="40"
|
||||||
FontSize="13"
|
FontSize="13"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
BorderBrush="#E0E0E0">
|
BorderBrush="#E0E0E0">
|
||||||
<ComboBoxItem Content="200 g" Tag="200" IsSelected="True"/>
|
<ComboBoxItem Content="200 g" Tag="200"/>
|
||||||
<ComboBoxItem Content="300 g" Tag="300"/>
|
<ComboBoxItem Content="300 g" Tag="300"/>
|
||||||
<ComboBoxItem Content="500 g" Tag="500"/>
|
<ComboBoxItem Content="500 g" Tag="500"/>
|
||||||
|
<ComboBoxItem Content="自定义" Tag="Custom"/>
|
||||||
|
</ComboBox>
|
||||||
|
|
||||||
|
<StackPanel Margin="0,0,0,15">
|
||||||
|
<StackPanel.Style>
|
||||||
|
<Style TargetType="StackPanel">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsCustomSledMass}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Style>
|
||||||
|
<TextBlock Text="自定义滑块质量 (g)" Margin="0,0,0,5" FontSize="13" Foreground="{StaticResource GrayBrush}"/>
|
||||||
|
<TextBox Text="{Binding Parameters.SledMass, StringFormat={}{0:F1}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
|
||||||
|
Height="40"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
FontSize="13"
|
||||||
|
BorderBrush="#E0E0E0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Text="力值显示单位" Margin="0,10,0,5" FontSize="13" Foreground="{StaticResource GrayBrush}"/>
|
||||||
|
<ComboBox SelectedValue="{Binding ForceDisplayUnit, Mode=TwoWay}"
|
||||||
|
SelectedValuePath="Tag"
|
||||||
|
Margin="0,0,0,15"
|
||||||
|
Height="40"
|
||||||
|
FontSize="13"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
BorderBrush="#E0E0E0">
|
||||||
|
<ComboBoxItem Content="N" Tag="N"/>
|
||||||
|
<ComboBoxItem Content="g" Tag="g"/>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
<!-- 标准选择 -->
|
<!-- 标准选择 -->
|
||||||
|
|||||||
@@ -88,13 +88,13 @@
|
|||||||
<!-- 实时力值监控 - 工业仪表盘风格 -->
|
<!-- 实时力值监控 - 工业仪表盘风格 -->
|
||||||
<Border Style="{StaticResource CardStyle}" Background="#0A192F">
|
<Border Style="{StaticResource CardStyle}" Background="#0A192F">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{Binding Lang.Force}" FontSize="14" Foreground="#BDC3C7" HorizontalAlignment="Center" Margin="0,0,0,5"/>
|
<TextBlock Text="{Binding ForceDisplayLabel}" FontSize="14" Foreground="#BDC3C7" HorizontalAlignment="Center" Margin="0,0,0,5"/>
|
||||||
<Border Background="#000000" CornerRadius="4" Padding="10" Margin="0,5">
|
<Border Background="#000000" CornerRadius="4" Padding="10" Margin="0,5">
|
||||||
<Viewbox Height="60">
|
<Viewbox Height="60">
|
||||||
<TextBlock Text="{Binding CurrentForce, StringFormat={}{0:F4}}" Style="{StaticResource DigitalDisplayStyle}"/>
|
<TextBlock Text="{Binding DisplayedCurrentForce, StringFormat={}{0:F4}}" Style="{StaticResource DigitalDisplayStyle}"/>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
</Border>
|
</Border>
|
||||||
<TextBlock Text="N" FontSize="12" Foreground="#7F8C8D" HorizontalAlignment="Center"/>
|
<TextBlock Text="{Binding ForceDisplayUnit}" FontSize="12" Foreground="#7F8C8D" HorizontalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -151,13 +151,13 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{Binding Lang.TestResults}" FontWeight="Bold" FontSize="14" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
<TextBlock Text="{Binding Lang.TestResults}" FontWeight="Bold" FontSize="14" Foreground="#2C3E50" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
<TextBlock Text="{Binding Lang.PeelStrength}" FontSize="11" Foreground="#7F8C8D"/>
|
<TextBlock Text="{Binding PeelStrengthLabel}" FontSize="11" Foreground="#7F8C8D"/>
|
||||||
<TextBlock Text="{Binding LatestResult.PeelStrength_N_25mm, StringFormat={}{0:F3}, FallbackValue=--}" FontSize="32" FontWeight="Bold" Foreground="#27AE60" HorizontalAlignment="Center"/>
|
<TextBlock Text="{Binding DisplayedLatestPeelStrength, StringFormat={}{0:F3}, FallbackValue=--}" FontSize="32" FontWeight="Bold" Foreground="#27AE60" HorizontalAlignment="Center"/>
|
||||||
|
|
||||||
<Separator Margin="0,10"/>
|
<Separator Margin="0,10"/>
|
||||||
|
|
||||||
<TextBlock Text="{Binding Lang.AvgPeelForce}" FontSize="11" Foreground="#7F8C8D"/>
|
<TextBlock Text="{Binding AvgPeelForceLabel}" FontSize="11" Foreground="#7F8C8D"/>
|
||||||
<TextBlock Text="{Binding LatestResult.AvgKineticForce, StringFormat={}{0:F3}, FallbackValue=--}" FontSize="28" FontWeight="Bold" Foreground="#3498DB" HorizontalAlignment="Center"/>
|
<TextBlock Text="{Binding DisplayedLatestAvgKineticForce, StringFormat={}{0:F3}, FallbackValue=--}" FontSize="28" FontWeight="Bold" Foreground="#3498DB" HorizontalAlignment="Center"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user