diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..7ee88df
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..ee14c28
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,14 @@
+using System.Configuration;
+using System.Data;
+using System.Windows;
+
+namespace PetroleumViscosityTest
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+
+}
diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs
new file mode 100644
index 0000000..b0ec827
--- /dev/null
+++ b/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/Controls/TestRecordControl.xaml b/Controls/TestRecordControl.xaml
new file mode 100644
index 0000000..53b5ee7
--- /dev/null
+++ b/Controls/TestRecordControl.xaml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Controls/TestRecordControl.xaml.cs b/Controls/TestRecordControl.xaml.cs
new file mode 100644
index 0000000..9b6d704
--- /dev/null
+++ b/Controls/TestRecordControl.xaml.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Linq;
+using System.Windows.Controls;
+using PetroleumViscosityTest.Models;
+
+namespace PetroleumViscosityTest.Controls
+{
+ public partial class TestRecordControl : UserControl
+ {
+ public TestRecordControl()
+ {
+ InitializeComponent();
+ }
+
+ public void LoadData(ViscosityTestData data)
+ {
+ txtSampleID.Text = data.SampleID;
+ txtSampleName.Text = data.SampleName;
+ txtTestTemp.Text = data.TestTemp.ToString("F1");
+ txtThermostatTime.Text = Constants.GetThermostatTime(data.TestTemp).ToString();
+ txtConstant.Text = data.Constant.ToString("G5");
+ txtDensity.Text = data.Density.HasValue ? data.Density.Value.ToString("F3") : "—";
+ txtAvgTime.Text = data.AvgTime.ToString("F3");
+ txtViscosity.Text = data.Viscosity.ToString("G4");
+ txtDynamicViscosity.Text = data.DynamicViscosity > 0 ? data.DynamicViscosity.ToString("G4") : "—";
+ txtValidCount.Text = $"{data.ValidTimes.Count} / {data.RawTimes.Count}";
+ txtOperator.Text = data.Operator;
+ txtTestDate.Text = data.TestDate.ToString("yyyy-MM-dd");
+ txtRemark.Text = data.Remark;
+
+ // 显示所有流动时间
+ var timeItems = data.RawTimes.Select((t, i) => new { Display = $"第{i + 1}次: {t:F3}s" }).ToList();
+ lstTimes.ItemsSource = timeItems;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Controls/TestReportControl.xaml b/Controls/TestReportControl.xaml
new file mode 100644
index 0000000..4ac0ef5
--- /dev/null
+++ b/Controls/TestReportControl.xaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Controls/TestReportControl.xaml.cs b/Controls/TestReportControl.xaml.cs
new file mode 100644
index 0000000..d5fe49a
--- /dev/null
+++ b/Controls/TestReportControl.xaml.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Windows.Controls;
+using PetroleumViscosityTest.Models;
+
+namespace PetroleumViscosityTest.Controls
+{
+ public partial class TestReportControl : UserControl
+ {
+ public TestReportControl()
+ {
+ InitializeComponent();
+ txtReportNo.Text = "R" + DateTime.Now.ToString("yyyyMMddHHmmss");
+ }
+
+ public void LoadData(ViscosityTestData data)
+ {
+ txtTestDate.Text = data.TestDate.ToString("yyyy-MM-dd");
+ txtSampleID.Text = data.SampleID;
+ txtSampleName.Text = data.SampleName;
+ txtTestTemp.Text = data.TestTemp.ToString("F1");
+ txtConstant.Text = data.Constant.ToString("G5");
+ txtViscosity.Text = data.Viscosity.ToString("G4");
+ txtDynamicViscosity.Text = data.DynamicViscosity > 0 ? data.DynamicViscosity.ToString("G4") : "—";
+ txtPrecision.Text = data.IsRepeatabilityOK ? "符合重复性要求" : "重复性超差,数据仅供参考";
+ }
+ }
+}
\ No newline at end of file
diff --git a/MainWindow.xaml b/MainWindow.xaml
new file mode 100644
index 0000000..af6d933
--- /dev/null
+++ b/MainWindow.xaml
@@ -0,0 +1,203 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
new file mode 100644
index 0000000..6785a86
--- /dev/null
+++ b/MainWindow.xaml.cs
@@ -0,0 +1,314 @@
+using Microsoft.VisualBasic;
+using PetroleumViscosityTest.Controls;
+using PetroleumViscosityTest.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace PetroleumViscosityTest
+{
+ public partial class MainWindow : Window
+ {
+ private ViscosityTestData currentData;
+ private List rawTimes = new List();
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ currentData = new ViscosityTestData();
+ dpTestDate.SelectedDate = DateTime.Today;
+ UpdateThermostatTime();
+ }
+
+ // 温度变化时自动更新恒温时间(表2)
+ private void TxtTestTemp_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ UpdateThermostatTime();
+ }
+
+ private void UpdateThermostatTime()
+ {
+ if (double.TryParse(txtTestTemp.Text, out double temp))
+ {
+ int time = Models.Constants.GetThermostatTime(temp);
+ txtThermostatTime.Text = time.ToString();
+ }
+ else
+ {
+ txtThermostatTime.Text = "";
+ }
+ }
+
+ // 加载示例数据
+ private void BtnLoadExample_Click(object sender, RoutedEventArgs e)
+ {
+ txtSampleID.Text = "S2026-001";
+ txtSampleName.Text = "润滑油";
+ txtTestTemp.Text = "50";
+ txtConstant.Text = "0.4780";
+ txtDensity.Text = "0.85";
+ txtOperator.Text = "张三";
+ txtRemark.Text = "示例数据";
+
+ txtTime1.Text = "322.4";
+ txtTime2.Text = "322.6";
+ txtTime3.Text = "321.0";
+ txtTime4.Text = "";
+ txtTime5.Text = "";
+ txtTime6.Text = "";
+
+ UpdateThermostatTime();
+ txtStatus.Text = "示例数据已加载";
+ }
+
+ // 清空所有输入
+ private void BtnClearAll_Click(object sender, RoutedEventArgs e)
+ {
+ txtSampleID.Clear();
+ txtSampleName.Clear();
+ txtTestTemp.Clear();
+ txtConstant.Clear();
+ txtDensity.Clear();
+ txtOperator.Clear();
+ txtRemark.Clear();
+ txtTime1.Clear();
+ txtTime2.Clear();
+ txtTime3.Clear();
+ txtTime4.Clear();
+ txtTime5.Clear();
+ txtTime6.Clear();
+ txtAvgTime.Clear();
+ txtViscosity.Clear();
+ txtDynamicViscosity.Clear();
+ txtValidCount.Clear();
+ txtPrecisionJudge.Clear();
+ txtRepeatability.Clear();
+ txtReproducibility.Clear();
+ txtMeasuredRepeatability.Clear();
+ txtRepeatJudge.Clear();
+ lstRawTimes.ItemsSource = null;
+ rawTimes.Clear();
+ txtStatus.Text = "已清空";
+ }
+
+ // 清空流动时间
+ private void BtnClearTimes_Click(object sender, RoutedEventArgs e)
+ {
+ txtTime1.Clear();
+ txtTime2.Clear();
+ txtTime3.Clear();
+ txtTime4.Clear();
+ txtTime5.Clear();
+ txtTime6.Clear();
+ rawTimes.Clear();
+ lstRawTimes.ItemsSource = null;
+ }
+
+ // 增加一次测量(在界面中添加更多输入框,为简化,我们已预留6个)
+ private void BtnAddMeasurement_Click(object sender, RoutedEventArgs e)
+ {
+ // 本版本固定6个输入框,无需动态增加
+ MessageBox.Show("已支持最多6次测量,请直接在对应框中输入。", "提示");
+ }
+
+ // 核心计算:运动粘度、动力粘度、精密度
+ private void BtnCalcViscosity_Click(object sender, RoutedEventArgs e)
+ {
+ try
+ {
+ // 1. 读取流动时间
+ rawTimes = GetTimeValues();
+ if (rawTimes.Count < 4)
+ {
+ MessageBox.Show("至少需要输入4次流动时间!", "数据不足", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+
+ // 2. 读取常数、密度、温度
+ if (!double.TryParse(txtConstant.Text, out double constant))
+ {
+ MessageBox.Show("请输入有效的粘度计常数 C", "参数错误", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+ if (!double.TryParse(txtTestTemp.Text, out double temp))
+ {
+ MessageBox.Show("请输入试验温度", "参数错误", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+ double density = 0;
+ if (!string.IsNullOrWhiteSpace(txtDensity.Text))
+ {
+ if (!double.TryParse(txtDensity.Text, out density))
+ {
+ MessageBox.Show("密度格式错误,将不计算动力粘度", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ }
+
+ // 3. 根据温度获取允许偏差(%)
+ double allowedDeviationPercent = Models.Constants.GetAllowedDeviation(temp);
+ double allowedDeviation = 0;
+
+ // 4. 计算平均值、筛选有效数据
+ List validTimes = new List();
+ double avg = rawTimes.Average();
+ bool continueCalc = true;
+ int maxIterations = 10; // 防止死循环
+ while (continueCalc && maxIterations-- > 0)
+ {
+ validTimes = rawTimes.Where(t => Math.Abs(t - avg) <= avg * allowedDeviationPercent / 100.0).ToList();
+ if (validTimes.Count >= 3)
+ {
+ double newAvg = validTimes.Average();
+ if (Math.Abs(newAvg - avg) < 0.0001)
+ continueCalc = false;
+ else
+ avg = newAvg;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (validTimes.Count < 3)
+ {
+ MessageBox.Show("有效测量次数不足3次,无法计算!请检查数据是否离散过大。", "精密度超差", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ double finalAvg = validTimes.Average();
+
+ // 5. 计算运动粘度
+ double viscosity = constant * finalAvg;
+
+ // 6. 计算动力粘度(如果有密度)
+ double dynamicViscosity = 0;
+ if (density > 0)
+ {
+ dynamicViscosity = viscosity * density;
+ }
+
+ // 7. 计算本次测量的重复性(相对极差)
+ double measuredRepeatability = 0;
+ if (validTimes.Count > 1)
+ {
+ measuredRepeatability = (validTimes.Max() - validTimes.Min()) / finalAvg * 100;
+ }
+
+ // 8. 获取标准重复性限
+ double stdRepeatability = Models.Constants.GetRepeatabilityLimit(temp);
+
+ // 9. 更新UI
+ txtAvgTime.Text = finalAvg.ToString("F3");
+ txtViscosity.Text = viscosity.ToString("G4"); // 四位有效数字
+ if (density > 0)
+ txtDynamicViscosity.Text = dynamicViscosity.ToString("G4");
+ else
+ txtDynamicViscosity.Text = "未计算";
+ txtValidCount.Text = $"{validTimes.Count} / {rawTimes.Count}";
+ txtMeasuredRepeatability.Text = measuredRepeatability.ToString("F2") + "%";
+ txtRepeatability.Text = stdRepeatability.ToString("F1") + "%";
+ txtReproducibility.Text = Models.Constants.GetReproducibilityLimit(temp).ToString("F1") + "%";
+
+ bool repeatOK = measuredRepeatability <= stdRepeatability;
+ txtRepeatJudge.Text = repeatOK ? "合格" : $"超差 (允许 ≤ {stdRepeatability:F1}%)";
+ txtRepeatJudge.Foreground = repeatOK ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red;
+
+ txtPrecisionJudge.Text = repeatOK ? "精密度符合要求" : "精密度不符合要求,建议重新测试";
+
+ // 10. 显示原始数据
+ lstRawTimes.ItemsSource = rawTimes.Select((t, i) => new { Display = $"第{i + 1}次: {t:F3}s" }).ToList();
+
+ // 11. 保存当前数据到模型
+ currentData.SampleID = txtSampleID.Text;
+ currentData.SampleName = txtSampleName.Text;
+ currentData.TestTemp = temp;
+ currentData.Constant = constant;
+ currentData.Density = density > 0 ? density : (double?)null;
+ currentData.RawTimes = rawTimes;
+ currentData.ValidTimes = validTimes;
+ currentData.AvgTime = finalAvg;
+ currentData.Viscosity = viscosity;
+ currentData.DynamicViscosity = dynamicViscosity;
+ currentData.Operator = txtOperator.Text;
+ currentData.TestDate = dpTestDate.SelectedDate ?? DateTime.Now;
+ currentData.Remark = txtRemark.Text;
+ currentData.Repeatability = measuredRepeatability;
+ currentData.IsRepeatabilityOK = repeatOK;
+
+ txtStatus.Text = $"计算完成,有效次数 {validTimes.Count},运动粘度 {viscosity:G4} mm²/s";
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"计算错误: {ex.Message}", "异常", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ // 从文本框获取流动时间列表
+ private List GetTimeValues()
+ {
+ var list = new List();
+ AddIfValid(txtTime1, list);
+ AddIfValid(txtTime2, list);
+ AddIfValid(txtTime3, list);
+ AddIfValid(txtTime4, list);
+ AddIfValid(txtTime5, list);
+ AddIfValid(txtTime6, list);
+ return list;
+ }
+
+ private void AddIfValid(TextBox tb, List list)
+ {
+ if (!string.IsNullOrWhiteSpace(tb.Text) && double.TryParse(tb.Text, out double val))
+ {
+ list.Add(val);
+ }
+ }
+
+ // 生成原始记录
+ private void BtnGenRecord_Click(object sender, RoutedEventArgs e)
+ {
+ if (currentData.Viscosity <= 0)
+ {
+ MessageBox.Show("请先计算运动粘度!", "无数据", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+ var record = new TestRecordControl();
+ record.LoadData(currentData);
+ ReportContent.Content = record;
+ }
+
+ // 生成测试报告
+ private void BtnGenReport_Click(object sender, RoutedEventArgs e)
+ {
+ if (currentData.Viscosity <= 0)
+ {
+ MessageBox.Show("请先计算运动粘度!", "无数据", MessageBoxButton.OK, MessageBoxImage.Warning);
+ return;
+ }
+ var report = new TestReportControl();
+ report.LoadData(currentData);
+ ReportContent.Content = report;
+ }
+
+ // 打印预览
+ private void BtnPrintPreview_Click(object sender, RoutedEventArgs e)
+ {
+ if (ReportContent.Content == null)
+ {
+ MessageBox.Show("请先生成记录或报告", "无报表", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+ if (ReportContent.Content is FrameworkElement visual)
+ {
+ PrintDialog printDialog = new PrintDialog();
+ if (printDialog.ShowDialog() == true)
+ {
+ printDialog.PrintVisual(visual, "粘度测试报表");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Models/Constants.cs b/Models/Constants.cs
new file mode 100644
index 0000000..1dcaacd
--- /dev/null
+++ b/Models/Constants.cs
@@ -0,0 +1,43 @@
+using System.Collections.Generic;
+
+namespace PetroleumViscosityTest.Models
+{
+ public static class Constants
+ {
+ // 表2 粘度计在恒温水浴中的恒温时间
+ public static int GetThermostatTime(double temp)
+ {
+ if (temp == 80 || temp == 100) return 20;
+ if (temp == 40 || temp == 50) return 15;
+ if (temp >= 20 && temp <= 100) return 15; // 原文"100~50"? 表2写“100~50 15”? 实际根据逻辑
+ // 20℃及以下未明确,默认10分钟
+ return 10;
+ }
+
+ // 根据温度获取允许偏差(%),用于剔除异常值(5.4条)
+ public static double GetAllowedDeviation(double temp)
+ {
+ if (temp >= 15 && temp <= 100) return 0.5;
+ if (temp >= -30 && temp < 15) return 1.5;
+ if (temp < -30) return 2.5;
+ return 0.5; // 默认
+ }
+
+ // 7.1 重复性(%)
+ public static double GetRepeatabilityLimit(double temp)
+ {
+ if (temp >= 15 && temp <= 100) return 1.0;
+ if (temp >= -30 && temp < 15) return 3.0;
+ if (temp < -30) return 5.0;
+ return 1.0;
+ }
+
+ // 7.2 再现性(%)
+ public static double GetReproducibilityLimit(double temp)
+ {
+ if (temp >= 15 && temp <= 100) return 2.2;
+ // 其他温度未给出,按重复性的2倍左右估算
+ return 2.2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Models/ViscosityTestData.cs b/Models/ViscosityTestData.cs
new file mode 100644
index 0000000..709168e
--- /dev/null
+++ b/Models/ViscosityTestData.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+
+namespace PetroleumViscosityTest.Models
+{
+ public class ViscosityTestData
+ {
+ // 试样信息
+ public string SampleID { get; set; }
+ public string SampleName { get; set; }
+ public double TestTemp { get; set; }
+
+ // 粘度计常数
+ public double Constant { get; set; }
+
+ // 密度(可选)
+ public double? Density { get; set; }
+
+ // 流动时间
+ public List RawTimes { get; set; } = new List();
+ public List ValidTimes { get; set; } = new List();
+ public double AvgTime { get; set; }
+
+ // 结果
+ public double Viscosity { get; set; }
+ public double DynamicViscosity { get; set; }
+
+ // 人员日期
+ public string Operator { get; set; }
+ public DateTime TestDate { get; set; }
+ public string Remark { get; set; }
+
+ // 精密度
+ public double Repeatability { get; set; } // 实测重复性%
+ public bool IsRepeatabilityOK { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/PetroleumViscosityTest.csproj b/PetroleumViscosityTest.csproj
new file mode 100644
index 0000000..6e6420e
--- /dev/null
+++ b/PetroleumViscosityTest.csproj
@@ -0,0 +1,11 @@
+
+
+
+ WinExe
+ net10.0-windows
+ enable
+ enable
+ true
+
+
+
diff --git a/PetroleumViscosityTest.slnx b/PetroleumViscosityTest.slnx
new file mode 100644
index 0000000..e45763e
--- /dev/null
+++ b/PetroleumViscosityTest.slnx
@@ -0,0 +1,3 @@
+
+
+