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.Linq; using System.Threading; 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; // ======================= 成员变量 ======================= private Timer? _monitorTimer; // 后台监控定时器 private readonly object _monitorLock = new(); // 监控更新锁 private double _lastRawPressure = 0; // 上次压力原始值 private double _lastRawTemperature = 0; // 上次温度原始值 // ======================= UI绑定属性 ======================= public ObservableCollection ReferenceLiquids { get; } = new() { "蒸馏水", "甲苯", "乙二醇" }; [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; // 测试条件(测试前设置) [ObservableProperty] private double _sampleVolume = 40.0; [ObservableProperty] private bool _bubbleRemoved = true; [ObservableProperty] private bool _usePressure = false; [ObservableProperty] private double _pressureValue = 0.0; [ObservableProperty] private bool _isCleanConfirmed = true; [ObservableProperty] private string _cleanerName = ""; [ObservableProperty] private double _ambientTemperature = 25.0; [ObservableProperty] private bool _ambientCalibrated = true; [ObservableProperty] private bool _platinumCompatible = true; [ObservableProperty] private string _liquidReactivityNote = ""; // 实时监控参数(测试前+测试中+测试后持续更新) [ObservableProperty] private double _platinumResistance = 0.0; // 铂丝电阻(实时) [ObservableProperty] private double _chamberPressure = 0.0; // 样品池压力(实时) [ObservableProperty] private double _currentTestTemperature = 0.0; // 当前测试温度(实时) // 系统校准 [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 string _curveTitle = "温升曲线"; [ObservableProperty] private PlotModel _temperatureCurveModel; private Th1963LanService _th1963Upt; private Th1963LanService _th1963Ustd; // ======================= 构造函数 ======================= public D7896ViewModel() { _config = App.PlcConfig ?? new AppConfig(); _plcService = 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; // 启动后台监控 StartBackgroundMonitoring(); } // ======================= 后台监控系统 ======================= private async void StartBackgroundMonitoring() { // 等待PLC连接 await Task.Delay(1000); _monitorTimer = new Timer(async _ => await MonitorPlcValues(), null, 0, 1000); } private async Task MonitorPlcValues() { if (!await _plcService.IsConnectedAsync()) return; lock (_monitorLock) { try { // 1. 读取铂丝电阻值(D1422,0.001Ω单位) float rawResistance = _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance).Result; double newResistance = rawResistance / 1000.0; if (Math.Abs(newResistance - _lastRawPressure) > 0.0001) { _lastRawPressure = newResistance; Application.Current.Dispatcher.Invoke(() => PlatinumResistance = newResistance); } // 2. 读取压力值(D1322,0.1kPa单位) float rawPressure = _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure).Result; _lastRawTemperature = rawPressure; Application.Current.Dispatcher.Invoke(() => ChamberPressure = rawPressure); // 3. 读取温度值(D1376,0.1℃单位) float rawTemp = _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature).Result; Application.Current.Dispatcher.Invoke(() => CurrentTestTemperature = rawTemp); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"监控读取失败: {ex.Message}"); } } } // ======================= 测试前:获取初始铂丝电阻 ======================= private async Task GetInitialResistanceAsync() { if (!await _plcService.IsConnectedAsync()) return 0; try { float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance); return rawResistance / 1000.0; } catch { return 0; } } // ======================= 核心测试流程 ======================= [RelayCommand] private async Task StartTestAsync() { // ========== 第1阶段:测试前置检查(标准7.1、7.6、1.4、8.1) ========== if (!IsCleanConfirmed || !BubbleRemoved || !PlatinumCompatible || !AmbientCalibrated) { MessageBox.Show("请完成所有测试前确认项(清洁、排泡、铂兼容性、环境校准)。", "前置条件未满足"); return; } if (SampleVolume <= 0) { MessageBox.Show("请输入有效的样品量(≥1 mL)。", "参数错误"); return; } // 加压条件检查(标准1.8、附录A2) if (UsePressure && PressureValue <= 0) { MessageBox.Show("请设置有效的加压值(>0 kPa)。", "参数错误"); return; } if (IsTesting) { MessageBox.Show("测试正在进行中,请稍后...", "提示"); return; } // 连接PLC if (!await _plcService.IsConnectedAsync()) { var connected = await _plcService.ConnectAsync(); if (!connected) { MessageBox.Show("无法连接到PLC,请检查配置。", "错误"); return; } } // ========== 第2阶段:加压控制(标准1.7-1.8、附录A2) ========== if (UsePressure) { StatusMessage = "正在加压..."; await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, true); await Task.Delay(3000); await UpdateRealTimeParametersAsync(); if (ChamberPressure < PressureValue - 5) MessageBox.Show($"压力未达到设定值 {PressureValue} kPa,当前 {ChamberPressure:F1} kPa", "警告"); } // ========== 第3阶段:获取初始铂丝电阻 R₀(用于校准) ========== double initialResistance = await GetInitialResistanceAsync(); if (initialResistance > 0) { StatusMessage = $"初始电阻已读取: {initialResistance:F4} Ω"; } // ========== 第4阶段:执行10次重复测量(标准4.1、5.4) ========== Measurements.Clear(); IsTesting = true; StatusMessage = "开始测试..."; try { for (int i = 1; i <= _config.TestParameters.MeasurementCount; i++) { CurrentMeasurementIndex = i; StatusMessage = $"正在执行第 {i} 次测量..."; // 4.1 发送加热脉冲(标准5.4:短时电流施加) await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true); //await Task.Delay(500); //await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); // 4.2 等待测量完成(标准5.3:0.8秒测试时间) await Task.Delay(800); // 4.3 读取计算结果(标准1.2:λ和α直接测量) float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity); float alpha = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalDiffusivity); // 记录测试温度 if (i == 1) TestTemperature = CurrentTestTemperature; // 生成温升曲线 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"; // 4.4 间隔30秒(标准5.4) 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 { // ========== 第5阶段:测试结束后泄压 ========== if (UsePressure) { await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, false); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, true); await Task.Delay(1000); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, false); } IsTesting = false; } } [RelayCommand] private async Task StopTestCommandAsync() { if (IsTesting) { IsTesting = false; StatusMessage = "已停止测试。"; } if (!IsTesting) { MessageBox.Show("没有正在进行的测试。", "提示"); return; } await Task.Delay(500); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); // 泄压 if (UsePressure) { await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, false); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, true); await Task.Delay(1000); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, false); } IsTesting = false; StatusMessage = "测试已停止。"; } // ======================= 辅助方法 ======================= private async Task UpdateRealTimeParametersAsync() { if (!await _plcService.IsConnectedAsync()) return; try { // 压力(D1322,0.1kPa单位) float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure); ChamberPressure = rawPressure / 10.0; } catch (Exception ex) { StatusMessage = $"实时参数读取失败: {ex.Message}"; } } private void GenerateTemperatureCurve(float lambda, float alpha) { if (TemperatureCurveModel == null) { TemperatureCurveModel = new PlotModel { Title = "温升曲线对比", 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 }; // 理论公式 ΔT = (Q/(4πλL)) * ln(t/t0) + C double Q = 0.01; double L = _config.TestParameters.PlatinumWireLength; double constant = 0.2; for (int i = 1; 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)); } 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); } // ======================= UI命令 ======================= [RelayCommand] private void Reset() { Measurements.Clear(); AverageThermalConductivity = AverageThermalDiffusivity = 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, ["InitialResistance"] = PlatinumResistance // 测试前的初始电阻 }; 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 async Task PressureCalibrationAsync() { await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.PressureCalibrationCoil, true); } [RelayCommand] private async Task ResistanceZeroAsync() { await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.ResistanceZeroCoil, true); } [RelayCommand] private async Task InletValveControlAsync() { await EnsureConnected(); bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.InletValveCoil); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, !current); StatusMessage = $"进气阀已{(current ? "关闭" : "开启")}"; } [RelayCommand] private async Task OutletValveControlAsync() { await EnsureConnected(); bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, !current); StatusMessage = $"排气阀已{(current ? "关闭" : "开启")}"; } [RelayCommand] private void ConfirmBubbleRemoved() => BubbleRemoved = true; [RelayCommand] private void ConfirmClean() { if (string.IsNullOrWhiteSpace(CleanerName)) { MessageBox.Show("请输入清洁人员姓名。", "提示"); return; } IsCleanConfirmed = true; } [RelayCommand] private void ConfirmPlatinumCompatible() => PlatinumCompatible = true; [RelayCommand] private async Task CalibrateAmbientAsync() { await EnsureConnected(); float temp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature); AmbientTemperature = temp; AmbientCalibrated = true; StatusMessage = $"环境温度校准完成:{AmbientTemperature:F1} °C"; } [RelayCommand] private async Task PerformSystemCalibrationAsync() { /* 系统校准逻辑 */ } private async Task EnsureConnected() { if (!await _plcService.IsConnectedAsync()) await _plcService.ConnectAsync(); } }