Files
ASTM-D7896-19TransientHot-W…/ViewModels/D7896ViewModel.cs

502 lines
17 KiB
C#
Raw Normal View History

2026-05-15 10:59:24 +08:00
using ASTM_D7896_Tester.Helpers;
2026-04-18 19:00:34 +08:00
using ASTM_D7896_Tester.Models;
using ASTM_D7896_Tester.Services;
2026-05-15 10:59:24 +08:00
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using System;
using System.Collections.ObjectModel;
2026-04-18 19:00:34 +08:00
using System.IO;
2026-05-15 10:59:24 +08:00
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
2026-04-18 19:00:34 +08:00
namespace ASTM_D7896_Tester.ViewModels;
public partial class D7896ViewModel : ObservableObject
{
2026-05-15 20:39:11 +08:00
private readonly IPlcService _plcService;
2026-04-18 19:00:34 +08:00
private AppConfig _config;
2026-05-15 20:39:11 +08:00
private readonly ReportService _reportService;
2026-04-18 19:00:34 +08:00
public ObservableCollection<string> ReferenceLiquids { get; } = new ObservableCollection<string> { "蒸馏水", "甲苯", "乙二醇" };
// ========== 原有属性 ==========
[ObservableProperty]
private string _sampleId = "未命名样品";
[ObservableProperty]
private double _testTemperature = 25.0;
[ObservableProperty]
private string _testDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
[ObservableProperty]
private bool _isTesting = false;
[ObservableProperty]
private string _statusMessage = "就绪";
[ObservableProperty]
private int _currentMeasurementIndex = 0;
[ObservableProperty]
private ObservableCollection<MeasurementResult> _measurements = new();
[ObservableProperty]
private double _averageThermalConductivity;
[ObservableProperty]
private double _averageThermalDiffusivity;
[ObservableProperty]
private double _averageVolumetricHeatCapacity;
// ========== 新增属性(补充缺失功能) ==========
// 章节7.5:样品量 (mL)
[ObservableProperty]
private double _sampleVolume = 40.0;
// 章节7.6:排气泡确认
[ObservableProperty]
private bool _bubbleRemoved = false;
// 附录A2加压测试
[ObservableProperty]
private bool _usePressure = false;
[ObservableProperty]
private double _pressureValue = 0.0; // kPa
// 章节7.1:清洁确认
[ObservableProperty]
private bool _isCleanConfirmed = false;
[ObservableProperty]
private string _cleanerName = "";
// 章节8.1:环境温度校准
[ObservableProperty]
private double _ambientTemperature = 25.0;
[ObservableProperty]
private bool _ambientCalibrated = false;
// 章节1.4:铂反应性确认
[ObservableProperty]
private bool _platinumCompatible = false;
[ObservableProperty]
private string _liquidReactivityNote = "";
// 系统校准模块章节A3相关
[ObservableProperty]
private bool _isCalibrating = false;
[ObservableProperty]
private string _calibrationStatus = "";
[ObservableProperty]
private string _selectedReferenceLiquid = "蒸馏水";
[ObservableProperty]
private double _referenceConductivity = 0.606;
[ObservableProperty]
private double _measuredConductivity = 0.0;
[ObservableProperty]
private double _calibrationErrorPercent = 0.0;
2026-05-15 10:59:24 +08:00
// 新增核心参数(实时显示)
[ObservableProperty]
private double _platinumVoltage; // 铂丝电压 U_pt (V)
[ObservableProperty]
private double _standardResistorVoltage; // 标准电阻电压 U_std (V)
[ObservableProperty]
private double _platinumResistance; // 铂丝电阻 R_pt (Ω)
[ObservableProperty]
private double _chamberPressure; // 样品池压力 (kPa)
// 当前曲线标题
[ObservableProperty]
private string _curveTitle = "温升曲线";
// 在 ViewModel 中
[ObservableProperty]
private PlotModel _temperatureCurveModel;
2026-04-18 19:00:34 +08:00
public D7896ViewModel()
{
2026-05-15 20:39:11 +08:00
// 获取应用全局配置并确保不为 null
_config = ASTM_D7896_Tester.App.PlcConfig ?? new Models.AppConfig();
_plcService = ASTM_D7896_Tester.App.PlcService;
2026-04-18 19:00:34 +08:00
_reportService = new ReportService(_config.TestParameters.ReportOutputPath);
// 加载配置中的默认值
SampleVolume = _config.TestParameters.DefaultSampleVolume;
UsePressure = _config.TestParameters.UsePressure;
PressureValue = _config.TestParameters.DefaultPressure;
SelectedReferenceLiquid = _config.TestParameters.ReferenceLiquid;
ReferenceConductivity = _config.TestParameters.ReferenceConductivity;
2026-05-15 10:59:24 +08:00
IsCleanConfirmed = true;
BubbleRemoved = true;
PlatinumCompatible = true;
AmbientCalibrated = true;
2026-04-18 19:00:34 +08:00
}
// ========== 原有命令 ==========
[RelayCommand]
private async Task StartTestAsync()
{
// 增加前置条件检查:清洁、气泡、铂兼容性、环境校准、样品量
if (!IsCleanConfirmed)
{
MessageBox.Show("请确认采样池已清洁干燥章节7.1)。", "前置条件未满足");
return;
}
if (!BubbleRemoved)
{
MessageBox.Show("请确认已清除铂丝表面气泡章节7.6)。", "前置条件未满足");
return;
}
if (!PlatinumCompatible)
{
MessageBox.Show("请确认所测液体不与铂发生反应章节1.4)。", "前置条件未满足");
return;
}
if (!AmbientCalibrated)
{
MessageBox.Show("请先进行环境温度校准章节8.1)。", "前置条件未满足");
return;
}
if (SampleVolume <= 0)
{
MessageBox.Show("请输入有效的样品量≥1 mL。", "参数错误");
return;
}
if (UsePressure && PressureValue <= 0)
{
MessageBox.Show("请设置有效的加压值(>0 kPa。", "参数错误");
return;
}
if (IsTesting)
{
MessageBox.Show("测试正在进行中,请稍后...", "提示");
return;
}
if (!await _plcService.IsConnectedAsync())
{
var connected = await _plcService.ConnectAsync();
if (!connected)
{
MessageBox.Show("无法连接到PLC请检查配置。", "错误");
return;
}
}
Measurements.Clear();
IsTesting = true;
StatusMessage = "开始测试...";
try
{
for (int i = 1; i <= _config.TestParameters.MeasurementCount; i++)
{
CurrentMeasurementIndex = i;
StatusMessage = $"正在执行第 {i} 次测量...";
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
2026-04-18 19:00:34 +08:00
await Task.Delay(500);
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-04-18 19:00:34 +08:00
await Task.Delay(2000);
float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity);
float alpha = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalDiffusivity);
float temperature = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.TestTemperature);
if (i == 1) TestTemperature = temperature;
2026-05-15 10:59:24 +08:00
GenerateTemperatureCurve(lambda, alpha); // 更新曲线图
2026-04-18 19:00:34 +08:00
var result = new MeasurementResult
{
Index = i,
ThermalConductivity = lambda,
ThermalDiffusivity = alpha
};
result.CalculateVhc();
Application.Current.Dispatcher.Invoke(() =>
{
Measurements.Add(result);
});
StatusMessage = $"第 {i} 次测量完成,λ={lambda:F4} W/m·K";
if (i < _config.TestParameters.MeasurementCount)
{
await Task.Delay(_config.TestParameters.IntervalSeconds * 1000);
}
}
2026-05-15 10:59:24 +08:00
2026-04-18 19:00:34 +08:00
CalculateAverages();
StatusMessage = "测试完成。";
}
catch (Exception ex)
{
StatusMessage = $"测试出错: {ex.Message}";
MessageBox.Show($"测试过程中发生错误: {ex.Message}", "错误");
}
finally
{
IsTesting = false;
await _plcService.DisconnectAsync();
}
}
2026-05-15 10:59:24 +08:00
// 模拟获取实时核心参数实际应从PLC读取
private async Task UpdateRealTimeParametersAsync()
{
if (await _plcService.IsConnectedAsync())
{
// 示例从PLC读取这些值地址需在配置文件中定义
PlatinumVoltage = await _plcService.ReadFloatAsync(40010); // 假设地址
StandardResistorVoltage = await _plcService.ReadFloatAsync(40012);
PlatinumResistance = await _plcService.ReadFloatAsync(40014);
ChamberPressure = await _plcService.ReadFloatAsync(40016);
}
}
private void GenerateTemperatureCurve(float lambda, float alpha)
{
// 第一次调用时初始化 PlotModel只创建一次
if (TemperatureCurveModel == null)
{
TemperatureCurveModel = new PlotModel
{
Title = "温升曲线对比 (10次测量)",
Background = OxyColors.White
};
// 添加坐标轴(只添加一次)
TemperatureCurveModel.Axes.Add(new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "时间 (s)",
Minimum = 0,
Maximum = 2
});
TemperatureCurveModel.Axes.Add(new LinearAxis
{
Position = AxisPosition.Left,
Title = "温升 (℃)",
Minimum = 0
});
}
// 创建本次测量的曲线系列(用不同颜色区分)
var series = new LineSeries
{
Title = $"第{CurrentMeasurementIndex}次测量",
Color = GetColorForIndex(CurrentMeasurementIndex),
StrokeThickness = 1.5,
MarkerType = MarkerType.None
};
// 添加数据点(这里仍用模拟数据,实际应替换为真实采集数据)
// 理论公式ΔT = (Q/(4πλL)) * ln(t/t0) + C
// 其中 Q 是加热功率L 是铂丝长度C 是常数
double Q = 0.01; // 假设 10mW实际应从电流和电阻计算
double L = 0.04; // 40mm
double constant = 0.2;
for (int i = 0; i <= 200; i++)
{
double t = i * 0.01;
double deltaT = (Q / (4 * Math.PI * lambda * L)) * Math.Log(t + 0.1) + constant;
series.Points.Add(new DataPoint(t, deltaT));
}
// 将本次曲线添加到同一个 Model 中(叠加)
TemperatureCurveModel.Series.Add(series);
TemperatureCurveModel.InvalidatePlot(true);
CurveTitle = $"已完成 {CurrentMeasurementIndex} 次测量";
}
// 辅助方法:根据测量序号返回不同的颜色
private OxyColor GetColorForIndex(int index)
{
var colors = new[]
{
OxyColors.Red, OxyColors.Blue, OxyColors.Green, OxyColors.Orange,
OxyColors.Purple, OxyColors.Brown, OxyColors.Pink, OxyColors.Cyan,
OxyColors.Magenta, OxyColors.Olive
};
return colors[(index - 1) % colors.Length];
}
2026-04-18 19:00:34 +08:00
private void CalculateAverages()
{
if (Measurements.Count == 0) return;
AverageThermalConductivity = Measurements.Average(m => m.ThermalConductivity);
AverageThermalDiffusivity = Measurements.Average(m => m.ThermalDiffusivity);
AverageVolumetricHeatCapacity = Measurements.Average(m => m.VolumetricHeatCapacity);
}
[RelayCommand]
private void Reset()
{
Measurements.Clear();
AverageThermalConductivity = 0;
AverageThermalDiffusivity = 0;
AverageVolumetricHeatCapacity = 0;
CurrentMeasurementIndex = 0;
StatusMessage = "已重置";
TestDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
2026-05-15 10:59:24 +08:00
TemperatureCurveModel = null; // 下次测量会重新初始化
2026-04-18 19:00:34 +08:00
// 重置确认标志(可根据需要决定是否重置)
2026-05-15 10:59:24 +08:00
2026-04-18 19:00:34 +08:00
}
[RelayCommand]
private async Task GenerateReportAsync()
{
if (Measurements.Count == 0)
{
MessageBox.Show("没有测试数据,请先执行测试。", "提示");
return;
}
try
{
// 收集所有额外参数用于报告
var extraParams = new Dictionary<string, object>
{
["SampleVolume"] = SampleVolume,
["BubbleRemoved"] = BubbleRemoved,
["UsePressure"] = UsePressure,
["PressureValue"] = PressureValue,
["IsCleanConfirmed"] = IsCleanConfirmed,
["CleanerName"] = CleanerName,
["AmbientTemperature"] = AmbientTemperature,
["AmbientCalibrated"] = AmbientCalibrated,
["PlatinumCompatible"] = PlatinumCompatible,
["LiquidReactivityNote"] = LiquidReactivityNote
};
string reportPath = await _reportService.GenerateReportAsync(
SampleId,
TestTemperature,
Measurements.ToList(),
AverageThermalConductivity,
AverageThermalDiffusivity,
AverageVolumetricHeatCapacity,
_config.TestParameters,
extraParams
);
MessageBox.Show($"报告已生成:{reportPath}", "成功");
}
catch (Exception ex)
{
MessageBox.Show($"生成报告失败:{ex.Message}", "错误");
}
}
// ========== 新增命令:排气泡确认 ==========
[RelayCommand]
private void ConfirmBubbleRemoved()
{
BubbleRemoved = true;
StatusMessage = "已确认清除气泡。";
}
// ========== 新增命令:清洁确认 ==========
[RelayCommand]
private void ConfirmClean()
{
if (string.IsNullOrWhiteSpace(CleanerName))
{
MessageBox.Show("请输入清洁人员姓名。", "提示");
return;
}
IsCleanConfirmed = true;
StatusMessage = "已确认采样池清洁干燥。";
}
// ========== 新增命令:铂兼容性确认 ==========
[RelayCommand]
private void ConfirmPlatinumCompatible()
{
PlatinumCompatible = true;
StatusMessage = "已确认液体与铂兼容。";
}
// ========== 新增命令:环境温度校准 ==========
[RelayCommand]
private async Task CalibrateAmbientAsync()
{
if (!await _plcService.IsConnectedAsync())
{
await _plcService.ConnectAsync();
}
// 读取PLC当前环境温度假设寄存器地址可配置
// 这里简化让用户手动输入或从PLC读取
// 为了演示从PLC读取温度
float temp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.TestTemperature);
AmbientTemperature = temp;
AmbientCalibrated = true;
StatusMessage = $"环境温度校准完成:{AmbientTemperature:F1} °C";
}
// ========== 新增命令系统校准章节A3 ==========
[RelayCommand]
private async Task PerformSystemCalibrationAsync()
{
if (IsCalibrating)
{
MessageBox.Show("校准正在进行中...", "提示");
return;
}
var result = MessageBox.Show($"将使用参考液 [{SelectedReferenceLiquid}] 进行系统校准。\n请确保传感器已浸入参考液中并已清除气泡。\n是否继续", "系统校准", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes) return;
IsCalibrating = true;
CalibrationStatus = "正在测量参考液...";
try
{
// 执行一次测量
if (!await _plcService.IsConnectedAsync())
await _plcService.ConnectAsync();
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
2026-04-18 19:00:34 +08:00
await Task.Delay(500);
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-04-18 19:00:34 +08:00
await Task.Delay(2000);
float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity);
MeasuredConductivity = lambda;
CalibrationErrorPercent = Math.Abs((lambda - ReferenceConductivity) / ReferenceConductivity * 100);
CalibrationStatus = $"测量值: {MeasuredConductivity:F4} W/m·K, 参考值: {ReferenceConductivity:F4} W/m·K, 误差: {CalibrationErrorPercent:F2}%";
if (CalibrationErrorPercent <= 2.0)
MessageBox.Show($"校准成功!误差 {CalibrationErrorPercent:F2}% 在允许范围内≤2%)。", "校准结果");
else
MessageBox.Show($"校准警告:误差 {CalibrationErrorPercent:F2}% 超出2%限值,请检查传感器或参考液。", "校准结果", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
CalibrationStatus = $"校准失败: {ex.Message}";
MessageBox.Show($"校准失败: {ex.Message}", "错误");
}
finally
{
IsCalibrating = false;
}
2026-05-15 10:59:24 +08:00
2026-04-18 19:00:34 +08:00
}
2026-05-15 10:59:24 +08:00
2026-04-18 19:00:34 +08:00
}