diff --git a/CSI-H238M/CSI-H238M/App.xaml.cs b/CSI-H238M/CSI-H238M/App.xaml.cs
index 12b5256..9bd3fe8 100644
--- a/CSI-H238M/CSI-H238M/App.xaml.cs
+++ b/CSI-H238M/CSI-H238M/App.xaml.cs
@@ -107,7 +107,9 @@ namespace CSI_H238M
// 保存配置(如果需要)
try
{
- _config?.Save(AppConfig.GetDefaultConfigPath());
+ string configPath = AppConfig.GetDefaultConfigPath();
+ _config = AppConfig.Load(configPath);
+ _config?.Save(configPath);
}
catch
{
diff --git a/CSI-H238M/CSI-H238M/Models/AppConfig.cs b/CSI-H238M/CSI-H238M/Models/AppConfig.cs
index d8e126d..09e86ec 100644
--- a/CSI-H238M/CSI-H238M/Models/AppConfig.cs
+++ b/CSI-H238M/CSI-H238M/Models/AppConfig.cs
@@ -42,6 +42,8 @@ namespace COFTester.Models
///
public string Language { get; set; } = "zh-CN";
+ public string ForceDisplayUnit { get; set; } = "N";
+
///
/// 自動保存測試結果
///
diff --git a/CSI-H238M/CSI-H238M/Models/Model.cs b/CSI-H238M/CSI-H238M/Models/Model.cs
index 93dc880..6fdf704 100644
--- a/CSI-H238M/CSI-H238M/Models/Model.cs
+++ b/CSI-H238M/CSI-H238M/Models/Model.cs
@@ -240,8 +240,10 @@ namespace COFTester.Models
///
public class TestResult : INotifyPropertyChanged
{
+ private const double StandardGravity = 9.80665;
private bool _isVisible = true;
private string _sampleNumber = string.Empty;
+ private string _forceDisplayUnit = "N";
public Guid TestId { get; set; } = Guid.NewGuid(); // 測試唯一ID
public DateTime TestDateTime { get; set; } = DateTime.Now; // 測試時間
@@ -267,6 +269,31 @@ namespace COFTester.Models
///
/// 试样编号
///
+ 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
{
get => _sampleNumber;
@@ -282,6 +309,13 @@ namespace COFTester.Models
set { _isVisible = value; OnPropertyChanged(); }
}
+ private double ConvertForceForDisplay(double forceInNewton)
+ {
+ return ForceDisplayUnit == "g"
+ ? forceInNewton * 1000.0 / StandardGravity
+ : forceInNewton;
+ }
+
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
diff --git a/CSI-H238M/CSI-H238M/Services/PdfReportService.cs b/CSI-H238M/CSI-H238M/Services/PdfReportService.cs
index 9904c21..e529f30 100644
--- a/CSI-H238M/CSI-H238M/Services/PdfReportService.cs
+++ b/CSI-H238M/CSI-H238M/Services/PdfReportService.cs
@@ -166,6 +166,7 @@ namespace COFTester.Services
///
public class PdfReportService
{
+ private const double StandardGravity = 9.80665;
private const double PageWidth = 595; // A4宽度(点)
private const double PageHeight = 842; // A4高度(点)
private const double MarginLeft = 50;
@@ -195,10 +196,11 @@ namespace COFTester.Services
/// 测试记录
/// 测试参数
/// 是否使用中文(默认根据当前语言)
- public void GenerateReport(string filePath, IEnumerable testRecords, TestParameters parameters, bool? isChinese = null)
+ public void GenerateReport(string filePath, IEnumerable testRecords, TestParameters parameters, string forceDisplayUnit = "N", bool? isChinese = null)
{
// 如果未指定语言,根据当前语言资源判断
bool useChinese = isChinese ?? (Resources.LanguageResources.Instance.CurrentLanguage == "zh-CN");
+ string normalizedForceUnit = NormalizeForceDisplayUnit(forceDisplayUnit);
if (testRecords == null || !testRecords.Any())
throw new ArgumentException(useChinese ? "测试记录不能为空" : "Test records cannot be empty");
@@ -232,13 +234,13 @@ namespace COFTester.Services
yPosition = DrawHeader(gfx, yPosition, useChinese);
System.Diagnostics.Debug.WriteLine("标题已绘制");
- yPosition = DrawTestInfo(gfx, yPosition, parameters, useChinese);
+ yPosition = DrawTestInfo(gfx, yPosition, parameters, normalizedForceUnit, useChinese);
System.Diagnostics.Debug.WriteLine("测试信息已绘制");
yPosition = DrawStatistics(gfx, yPosition, testRecords, useChinese);
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("详细结果已绘制");
// 保存文档
@@ -283,7 +285,7 @@ namespace COFTester.Services
///
/// 绘制测试信息
///
- 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);
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
@@ -299,6 +301,7 @@ namespace COFTester.Services
{
$"测试标准: {parameters.Standard}",
$"滑块质量: {parameters.SledMass:F1} g",
+ $"力值单位: {forceDisplayUnit}",
$"测试速度: {parameters.TestSpeed:F1} mm/min",
$"测试行程: {parameters.TestStroke:F1} mm",
$"操作员: {parameters.Operator}"
@@ -306,6 +309,7 @@ namespace COFTester.Services
{
$"Test Standard: {parameters.Standard}",
$"Sled Mass: {parameters.SledMass:F1} g",
+ $"Force Unit: {forceDisplayUnit}",
$"Test Speed: {parameters.TestSpeed:F1} mm/min",
$"Test Stroke: {parameters.TestStroke:F1} mm",
$"Operator: {parameters.Operator}"
@@ -403,7 +407,7 @@ namespace COFTester.Services
/// 绘制详细测试结果
///
private double DrawDetailedResults(XGraphics gfx, double yPosition, IEnumerable testRecords,
- PdfDocument document, XGraphics currentGfx, PdfPage currentPage, bool isChinese)
+ string forceDisplayUnit, PdfDocument document, XGraphics currentGfx, PdfPage currentPage, bool isChinese)
{
string fontFamily = GetFontFamily(isChinese);
XFont sectionFont = new XFont(fontFamily, 14, XFontStyleEx.Bold);
@@ -434,8 +438,8 @@ namespace COFTester.Services
// 表头
string[] headers = isChinese ?
- new[] { "编号", "测试时间", "静摩擦", "动摩擦", "最大力", "平均力" } :
- new[] { "No.", "Test Time", "Static", "Kinetic", "Max F", "Avg F" };
+ new[] { "编号", "测试时间", "静摩擦", "动摩擦", $"最大力({forceDisplayUnit})", $"平均力({forceDisplayUnit})" } :
+ new[] { "No.", "Test Time", "Static", "Kinetic", $"Max F ({forceDisplayUnit})", $"Avg F ({forceDisplayUnit})" };
double currentX = tableX;
for (int i = 0; i < headers.Length; i++)
@@ -474,8 +478,8 @@ namespace COFTester.Services
record.TestDateTime.ToString("MM-dd HH:mm"),
record.StaticCOF.ToString("F4"),
record.KineticCOF.ToString("F4"),
- record.MaxForce.ToString("F2") + " N",
- record.AvgKineticForce.ToString("F2") + " N"
+ ConvertForceForDisplay(record.MaxForce, forceDisplayUnit).ToString("F2") + " " + forceDisplayUnit,
+ ConvertForceForDisplay(record.AvgKineticForce, forceDisplayUnit).ToString("F2") + " " + forceDisplayUnit
};
for (int i = 0; i < rowData.Length; i++)
@@ -555,6 +559,18 @@ namespace COFTester.Services
new XRect(0, footerY, PageWidth, 20),
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;
+ }
///
/// 计算标准差
diff --git a/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs b/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
index 66fcdbf..1256cf0 100644
--- a/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
+++ b/CSI-H238M/CSI-H238M/ViewModels/ViewModel.cs
@@ -9,6 +9,7 @@ using System.Windows.Threading;
using System.Windows;
using System.Threading.Tasks;
using System.Threading;
+using System.Globalization;
using COFTester.Models;
using COFTester.Services;
using COFTester.Resources;
@@ -33,6 +34,7 @@ namespace COFTester.ViewModels
private bool _isUiDataUpdateQueued;
private long _lastPlotRefreshTick;
private const int PlotRefreshIntervalMs = 100;
+ private const double StandardGravity = 9.80665;
private double _currentForce;
private double _currentDisp;
@@ -52,6 +54,8 @@ namespace COFTester.ViewModels
private bool _stopRequestedByUser = false;
private bool _acceptIncomingTestData = false;
private int _testSessionId = 0;
+ private string _selectedSledMassPreset = "200";
+ private string _forceDisplayUnit = "N";
private string _selectedDirection = ""; // 选中的方向:Up/Down/Right/Left
private string _resetButtonText; // 复位按钮文本
private string _testButtonText; // 测试按钮文本
@@ -100,6 +104,9 @@ namespace COFTester.ViewModels
UpdateCustomParametersCommand = new AsyncRelayCommand(UpdateCustomParametersAsync, () => IsConnected && SelectedStandard == "Custom");
Parameters = _config.DefaultTestParameters ?? new TestParameters();
+ Parameters.PropertyChanged += OnParametersPropertyChanged;
+ _forceDisplayUnit = NormalizeForceDisplayUnit(_config.ForceDisplayUnit);
+ SyncSledMassPresetFromValue();
TestRecords = new ObservableCollection();
TestRecords.CollectionChanged += (s, e) =>
{
@@ -289,6 +296,69 @@ namespace COFTester.ViewModels
/// 语言资源实例
///
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";
///
/// 初始化 ScottPlot 图表
@@ -310,7 +380,7 @@ namespace COFTester.ViewModels
// 设置坐标轴标签(使用已设置的中文字体)
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
- _wpfPlot.Plot.YLabel(Lang.ForceAxis);
+ _wpfPlot.Plot.YLabel(ForceAxisLabel);
// 设置Y轴最小值为0
_wpfPlot.Plot.Axes.SetLimitsY(0, 10);
@@ -389,7 +459,7 @@ namespace COFTester.ViewModels
else
{
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);
@@ -509,9 +579,16 @@ namespace COFTester.ViewModels
public double CurrentForce
{
get => _currentForce;
- set { _currentForce = value; OnPropertyChanged(); }
+ set
+ {
+ _currentForce = value;
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(DisplayedCurrentForce));
+ }
}
+ public double DisplayedCurrentForce => ConvertForceForDisplay(CurrentForce);
+
public double CurrentDisp
{
get => _currentDisp;
@@ -545,9 +622,21 @@ namespace COFTester.ViewModels
public TestResult? 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
{
get => _statusMessage;
@@ -688,6 +777,7 @@ namespace COFTester.ViewModels
};
// 添加到测试记录集合
+ result.ForceDisplayUnit = ForceDisplayUnit;
TestRecords.Add(result);
LatestResult = result;
@@ -1552,7 +1642,7 @@ namespace COFTester.ViewModels
return;
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);
scatter.LineWidth = 2;
@@ -1656,7 +1746,7 @@ namespace COFTester.ViewModels
try
{
var pdfService = new Services.PdfReportService();
- pdfService.GenerateReport(filePath, TestRecords, Parameters);
+ pdfService.GenerateReport(filePath, TestRecords, Parameters, ForceDisplayUnit);
}
catch (Exception ex)
{
@@ -1689,9 +1779,16 @@ namespace COFTester.ViewModels
if (_wpfPlot != null)
{
_wpfPlot.Plot.XLabel(Lang.DisplacementAxis);
- _wpfPlot.Plot.YLabel(Lang.ForceAxis);
+ _wpfPlot.Plot.YLabel(ForceAxisLabel);
_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")
@@ -1711,6 +1808,101 @@ namespace COFTester.ViewModels
TestButtonText = Lang.StartTest;
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()
@@ -1865,6 +2057,7 @@ namespace COFTester.ViewModels
_daqService.DataReceived -= OnDataReceived;
_daqService.TestFinished -= OnTestFinished;
_daqService.ErrorOccurred -= OnErrorOccurred;
+ Parameters.PropertyChanged -= OnParametersPropertyChanged;
// 停止測試
if (_isTesting)
diff --git a/CSI-H238M/CSI-H238M/Views/HistoryPage.xaml b/CSI-H238M/CSI-H238M/Views/HistoryPage.xaml
index ebe66cb..ebb5527 100644
--- a/CSI-H238M/CSI-H238M/Views/HistoryPage.xaml
+++ b/CSI-H238M/CSI-H238M/Views/HistoryPage.xaml
@@ -69,17 +69,17 @@
-
+
-
+
-
+
-
+
diff --git a/CSI-H238M/CSI-H238M/Views/SettingsPage.xaml b/CSI-H238M/CSI-H238M/Views/SettingsPage.xaml
index 1c5e7b7..75d8f5d 100644
--- a/CSI-H238M/CSI-H238M/Views/SettingsPage.xaml
+++ b/CSI-H238M/CSI-H238M/Views/SettingsPage.xaml
@@ -53,17 +53,49 @@
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CSI-H238M/CSI-H238M/Views/TestPage.xaml b/CSI-H238M/CSI-H238M/Views/TestPage.xaml
index 8c2b28f..7e65436 100644
--- a/CSI-H238M/CSI-H238M/Views/TestPage.xaml
+++ b/CSI-H238M/CSI-H238M/Views/TestPage.xaml
@@ -88,13 +88,13 @@
-
+
-
+
-
+
@@ -151,13 +151,13 @@
-
-
+
+
-
-
+
+