using ASTM_D7896_Tester.Helpers; using ASTM_D7896_Tester.Models; using ASTM_D7896_Tester.Services; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using OxyPlot; using OxyPlot.Axes; using OxyPlot.Series; using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; namespace ASTM_D7896_Tester.ViewModels; public partial class D7896ViewModel : ObservableObject { private readonly IPlcService _plcService; private AppConfig _config; private readonly ReportService _reportService; public ObservableCollection ReferenceLiquids { get; } = new ObservableCollection { "蒸馏水", "甲苯", "乙二醇" }; // ========== 原有属性 ========== [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 _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; // 新增核心参数(实时显示) [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; public D7896ViewModel() { // 获取应用全局配置并确保不为 null _config = ASTM_D7896_Tester.App.PlcConfig ?? new Models.AppConfig(); _plcService = ASTM_D7896_Tester.App.PlcService; _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; IsCleanConfirmed = true; BubbleRemoved = true; PlatinumCompatible = true; AmbientCalibrated = true; } // ========== 原有命令 ========== [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} 次测量..."; await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true); await Task.Delay(500); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); 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; GenerateTemperatureCurve(lambda, alpha); // 更新曲线图 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); } } CalculateAverages(); StatusMessage = "测试完成。"; } catch (Exception ex) { StatusMessage = $"测试出错: {ex.Message}"; MessageBox.Show($"测试过程中发生错误: {ex.Message}", "错误"); } finally { IsTesting = false; await _plcService.DisconnectAsync(); } } // 模拟获取实时核心参数(实际应从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]; } 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"); TemperatureCurveModel = null; // 下次测量会重新初始化 // 重置确认标志(可根据需要决定是否重置) } [RelayCommand] private async Task GenerateReportAsync() { if (Measurements.Count == 0) { MessageBox.Show("没有测试数据,请先执行测试。", "提示"); return; } try { // 收集所有额外参数用于报告 var extraParams = new Dictionary { ["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(); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true); await Task.Delay(500); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); 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; } } }