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 Th1963LanService _th1963Ustd; // 6位半测量标准电阻电压 U_std private Th1963LanService _th1953Ustd; // 6位半测量标准电阻电压 U_std //private FiveHalfDmmService _fiveHalfUpt; // 5位半测量铂丝电压 U_pt private CancellationTokenSource _testCts; // 用于停止测试 private bool _stopRequested; // 后台监控定时器 private Timer? _monitorTimer; // 常量: 标准电阻值 1Ω private const double StandardResistor = 1.0; // 铂丝电阻温度系数 (纯铂) private const double AlphaPt = 0.00385; // /°C // 加热功率 Q 计算相关 private double _heatingCurrent; // 实际加热电流平均值 private double _wireResistanceAvg; // 铂丝平均电阻 // 温升曲线数据 [ObservableProperty] private string _curveTitle = "温升曲线"; [ObservableProperty] private PlotModel _temperatureCurveModel; // 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 double _platinumVoltage; [ObservableProperty] private double _standardResistorVoltage; [ObservableProperty] private double _sampleDensity = 1000.0; // 新增,密度默认值1000 kg/m³(水) double heatingDuration = 0.8; // 加热时间 0.8 秒(需与您的加热脉冲宽度一致) double totalDuration = 1.6; // 总采样时间(加热 + 冷却) 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; // 初始化电压表服务 // TH1963 IP 地址需要根据实际配置修改,建议从配置文件读取 _th1963Ustd = new Th1963LanService(); _th1953Ustd = new Th1963LanService(); StartBackgroundMonitoring(); } private async void StartBackgroundMonitoring() { await Task.Delay(1000); _monitorTimer = new Timer(async _ => await MonitorPlcValues(), null, 0, 1000); } private async Task MonitorPlcValues() { if (!await _plcService.IsConnectedAsync()) return; if (Application.Current == null || Application.Current.Dispatcher == null) return; try { float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance); double newResistance = rawResistance; Application.Current?.Dispatcher.Invoke(() => PlatinumResistance = newResistance); float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure); Application.Current?.Dispatcher.Invoke(() => ChamberPressure = rawPressure); float rawTemp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature); Application.Current?.Dispatcher.Invoke(() => CurrentTestTemperature = rawTemp); } catch { } } //private async Task GetInitialResistanceAsync() //{ // if (!await _plcService.IsConnectedAsync()) return 0; // try // { // float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance); // return rawResistance; // } // catch { return 0; } //} [RelayCommand] private async Task StartTestAsync() { if (IsTesting) { MessageBox.Show("测试正在进行中", "提示"); return; } // 前置检查 if (!IsCleanConfirmed || !BubbleRemoved || !PlatinumCompatible || !AmbientCalibrated) { MessageBox.Show("请完成所有测试前确认项", "前置条件未满足"); return; } if (SampleVolume <= 0) { MessageBox.Show("请输入有效的样品量", "参数错误"); return; } if (UsePressure && PressureValue <= 0) { MessageBox.Show("请设置有效的加压值", "参数错误"); return; } // 连接PLC和电压表 if (!await _plcService.IsConnectedAsync()) { if (!await _plcService.ConnectAsync()) { MessageBox.Show("无法连接到PLC", "错误"); return; } } try { await _th1963Ustd.ConnectAsync("192.168.1.12", 45454); // 改为实际IP await _th1963Ustd.ConfigureForHighSpeedDcvAsync(); await _th1953Ustd.ConnectAsync("192.168.1.13", 45454); // 改为实际IP await _th1953Ustd.ConfigureForHighSpeedDcvAsync(); } catch (Exception ex) { MessageBox.Show($"电压表连接失败: {ex.Message}", "错误"); return; } if (UsePressure) { StatusMessage = "正在加压..."; await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, true); const int pressureStableTimeoutMs = 10000; // 30秒超时 const double pressureTolerance = 5.0; // 允许误差 ±5 kPa var startTime = DateTime.Now; bool pressureReached = false; while ((DateTime.Now - startTime).TotalMilliseconds < pressureStableTimeoutMs) { await Task.Delay(500); // 每0.5秒检测一次 await UpdateRealTimeParametersAsync(); if (ChamberPressure >= PressureValue - pressureTolerance) { pressureReached = true; break; } } if (!pressureReached) { // 加压失败,关闭进气阀,中止测试 await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, false); MessageBox.Show($"加压超时,压力未能达到 {PressureValue} kPa(当前 {ChamberPressure:F1} kPa)", "错误"); return; } // 压力已达到,可关闭进气阀(或保持,看系统需求) await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, false); StatusMessage = $"压力已稳定在 {ChamberPressure:F1} kPa"; } //double initialResistance = await GetInitialResistanceAsync(); //if (initialResistance > 0) // StatusMessage = $"初始电阻: {initialResistance:F4} Ω"; Measurements.Clear(); IsTesting = true; _stopRequested = false; _testCts = new CancellationTokenSource(); try { // 预热:进行一次虚拟测量 await _th1963Ustd.ConfigureForHighSpeedDcvAsync(); await _th1963Ustd.PrepareBatchAsync(10); await _th1963Ustd.TriggerAsync(); await Task.Delay(100); await _th1963Ustd.FetchBatchAsync(); // 丢弃结果 // 预热:进行一次虚拟测量 await _th1953Ustd.ConfigureForHighSpeedDcvAsync(); await _th1953Ustd.PrepareBatchAsync(10); await _th1953Ustd.TriggerAsync(); await Task.Delay(100); await _th1953Ustd.FetchBatchAsync(); // 丢弃结果 for (int i = 1; i <= _config.TestParameters.MeasurementCount; i++) { if (_stopRequested) break; CurrentMeasurementIndex = i; StatusMessage = $"正在执行第 {i} 次测量..."; // 准备批量采集参数(每通道采样点数,采样率1000点/秒,加热时间1秒 -> 1000点) int samples = 200; // 1秒 * 1000点/秒 // 预配置两台表:进入等待触发状态 await _th1963Ustd.PrepareBatchAsync(samples); await _th1953Ustd.PrepareBatchAsync(samples); // 启动加热脉冲 (PLC) await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true); try { await Task.Delay(5, _testCts.Token); } catch (OperationCanceledException) { break; } // 同时发送触发信号给两台电压表 await Task.WhenAll(_th1963Ustd.TriggerAsync(), _th1953Ustd.TriggerAsync()); // 等待加热结束 try { await Task.Delay((int)(heatingDuration * 1000), _testCts.Token); } catch (OperationCanceledException) { break; } // 停止加热 await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); // 等待采集完成(剩余时间) int remainingMs = (int)((totalDuration - heatingDuration) * 1000) + 100; try { await Task.Delay(remainingMs, _testCts.Token); } catch (OperationCanceledException) { break; } // 获取采集数据 double[] ustd = await _th1963Ustd.FetchBatchAsync(); double[] upt = await _th1953Ustd.FetchBatchAsync(); for (int j = 0; j < 20; j++) { Logger.Log($"第{j}点: U_std={ustd[j]:F6} V, U_pt={upt[j]:F6} V"); } StandardResistorVoltage = ustd.Average(); PlatinumVoltage = upt.Average(); // 添加日志:原始电压统计 Logger.Log($"测量 {i}: U_std 点数={ustd.Length}, 平均值={ustd.Average():F6} V, 最大值={ustd.Max():F6} V"); Logger.Log($"测量 {i}: U_pt 点数={upt.Length}, 平均值={upt.Average():F6} V, 最大值={upt.Max():F6} V"); double[] timeArray = new double[ustd.Length]; for (int idx = 0; idx < timeArray.Length; idx++) { timeArray[idx] = idx * totalDuration / samples; } // 动态计算初始电阻 R0(取前 10 个点的平均值,这期间温升很小) int warmupPoints = Math.Min(10, ustd.Length); double sumR0 = 0; for (int j = 0; j < warmupPoints; j++) { double current = ustd[j] / StandardResistor; double resistance = upt[j] / current; sumR0 += resistance; } double dynamicR0 = sumR0 / warmupPoints; Logger.Log($"动态计算 R0 = {dynamicR0:F6} Ω"); // 计算本次测量的 λ 和 α var (lambda, alpha, deltaT, coolingPoints) = ComputeThermalProperties(upt, ustd, timeArray, dynamicR0, CurrentTestTemperature); // 添加结果日志 Logger.Log($"测量 {i} 结果: λ={lambda:F6} W/(m·K), α={alpha:E6} m²/s"); // 生成温升曲线图 GenerateTemperatureCurveFromData(timeArray, deltaT, coolingPoints); var result = new MeasurementResult { Index = i, ThermalConductivity = lambda, ThermalDiffusivity = alpha }; //result.CalculateVhc(); result.CalculateVhcAndCp(SampleDensity); Application.Current.Dispatcher.Invoke(() => Measurements.Add(result)); StatusMessage = $"第 {i} 次测量完成,λ={lambda:F4} W/m·K"; // 在 result.CalculateVhcAndCp(SampleDensity); 之后添加 Logger.Log($"========== 第 {i} 次测量详细数据 =========="); Logger.Log($"热导率 λ: {lambda:F6} W/(m·K)"); Logger.Log($"热扩散率 α: {alpha:E6} m²/s"); Logger.Log($"体积热容 VHC: {result.VolumetricHeatCapacity:F2} kJ/(m³·K)"); Logger.Log($"比热容 Cp: {result.SpecificHeatCapacity:F2} J/(kg·K) (密度 = {SampleDensity:F1} kg/m³)"); Logger.Log($"初始电阻 R0: {dynamicR0:F6} Ω"); Logger.Log($"测试温度: {CurrentTestTemperature:F2} °C"); Logger.Log($"铂丝平均电阻: {PlatinumResistance:F6} Ω"); Logger.Log($"样品池压力: {ChamberPressure:F2} kPa"); Logger.Log("==========================================="); if (i < _config.TestParameters.MeasurementCount && !_stopRequested) { try { await Task.Delay(_config.TestParameters.IntervalSeconds * 1000, _testCts.Token); } catch (OperationCanceledException) { break; } } } CalculateAverages(); StatusMessage = _stopRequested ? "测试已停止。" : "测试完成。"; } catch (Exception ex) { StatusMessage = $"测试出错: {ex.Message}"; MessageBox.Show($"测试过程中发生错误: {ex.Message}", "错误"); } finally { // 停止加热,泄压 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; //_fiveHalfUpt.Close(); _th1963Ustd.Dispose(); _th1953Ustd.Dispose(); _testCts?.Dispose(); } } ///// ///// 根据采集到的电压序列计算热导率 λ、热扩散率 α、温升数组以及冷却曲线数据点 ///// //private (double lambda, double alpha, double[] deltaT, List coolingPoints) ComputeThermalProperties( // double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp) //{ // int n = Math.Min(upt.Length, ustd.Length); // double[] current = new double[n]; // double[] ptResistance = new double[n]; // double[] deltaT = new double[n]; // // 1. 计算电流、铂丝电阻和温升 // for (int i = 0; i < n; i++) // { // current[i] = ustd[i] / StandardResistor; // ptResistance[i] = upt[i] / current[i]; // deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance); // } // // 添加日志:中间数据统计 // Logger.Log($"电流平均值: {current.Average():F6} A, 最大值: {current.Max():F6} A"); // Logger.Log($"铂丝电阻平均值: {ptResistance.Average():F6} Ω, 初始电阻: {initialResistance:F6} Ω"); // Logger.Log($"温升最大值: {deltaT.Max():F4} ℃, 平均值: {deltaT.Average():F4} ℃"); // // 2. ========== 加热段拟合 → 热导率 λ ========== // double tStart = 0.1; // double tEndHeating = 0.8; // int startIdx = FindIndex(time, tStart); // int endIdxHeating = FindIndex(time, tEndHeating); // if (startIdx < 0) startIdx = 0; // if (endIdxHeating >= n) endIdxHeating = n - 1; // var heatingPoints = new List(); // for (int i = startIdx; i <= endIdxHeating; i++) // { // heatingPoints.Add(new DataPoint(Math.Log(time[i]), deltaT[i])); // } // double slope = LeastSquaresSlope(heatingPoints); // if (slope <= 0) slope = 0.0001; // Logger.Log($"加热段拟合斜率 B = {slope:F6} (ΔT vs ln(t))"); // double avgCurrentSq = current.Average(c => c * c); // double avgResistance = ptResistance.Average(); // double powerPerLength = (avgCurrentSq * avgResistance) / _config.TestParameters.PlatinumWireLength; // double lambda = powerPerLength / (4 * Math.PI * slope); // Logger.Log($"单位长度加热功率 Q = {powerPerLength:F6} W/m"); // Logger.Log($"热导率 λ = {lambda:F6} W/(m·K)"); // // 3. ========== 冷却段拟合 → 热扩散率 α ========== // double coolingStart = heatingDuration; // 0.8 秒 // double coolingEnd = totalDuration; // 1.6 秒 // int coolingStartIdx = FindIndex(time, coolingStart); // int coolingEndIdx = FindIndex(time, coolingEnd); // if (coolingStartIdx < 0) coolingStartIdx = n / 2; // if (coolingEndIdx >= n) coolingEndIdx = n - 1; // var coolingPointsForFit = new List(); // for (int i = coolingStartIdx; i <= coolingEndIdx; i++) // { // if (deltaT[i] > 0.001) // coolingPointsForFit.Add(new DataPoint(time[i], Math.Log(deltaT[i]))); // } // double coolingSlope = LeastSquaresSlopeOnTime(coolingPointsForFit); // double tau = -1.0 / coolingSlope; // double wireRadius = 0.00003; // 半径 = 直径0.06mm /2 // double alpha = (wireRadius * wireRadius) / (4.0 * tau); // if (alpha <= 0 || double.IsNaN(alpha) || double.IsInfinity(alpha)) // alpha = 0.12e-6; // 默认值 // Logger.Log($"冷却段拟合斜率 D = {coolingSlope:F6} (lnΔT vs t)"); // Logger.Log($"时间常数 τ = {tau:F6} s"); // Logger.Log($"热扩散率 α = {alpha:E6} m²/s"); // // 准备冷却曲线数据点(用于绘图) // var coolingPoints = new List(); // for (int i = coolingStartIdx; i <= coolingEndIdx; i++) // { // if (deltaT[i] > 0.001) // coolingPoints.Add(new DataPoint(time[i], deltaT[i])); // } // return (lambda, alpha, deltaT, coolingPoints); //} /// /// 根据采集到的电压序列计算热导率 λ 和热扩散率 α(标准截距法) /// private (double lambda, double alpha, double[] deltaT, List coolingPoints) ComputeThermalProperties( double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp) { int n = Math.Min(upt.Length, ustd.Length); double[] current = new double[n]; double[] ptResistance = new double[n]; double[] deltaT = new double[n]; // 1. 计算电流、铂丝电阻和温升 for (int i = 0; i < n; i++) { current[i] = ustd[i] / StandardResistor; ptResistance[i] = upt[i] / current[i]; deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance); } Logger.Log($"电流平均值: {current.Average():F6} A, 最大值: {current.Max():F6} A"); Logger.Log($"铂丝电阻平均值: {ptResistance.Average():F6} Ω, 初始电阻: {initialResistance:F6} Ω"); Logger.Log($"温升最大值: {deltaT.Max():F4} ℃, 平均值: {deltaT.Average():F4} ℃"); // 2. 加热段拟合:选取有效时间窗口 (0.1s ~ 0.8s) double tStart = 0.1; double tEndHeating = 0.8; int startIdx = FindIndex(time, tStart); int endIdxHeating = FindIndex(time, tEndHeating); if (startIdx < 0) startIdx = 0; if (endIdxHeating >= n) endIdxHeating = n - 1; var points = new List(); for (int i = startIdx; i <= endIdxHeating; i++) { points.Add(new DataPoint(Math.Log(time[i]), deltaT[i])); } // 最小二乘法拟合直线 ΔT = slope * ln(t) + intercept (double slope, double intercept) = LinearRegression(points); if (slope <= 0) slope = 1e-6; Logger.Log($"加热段拟合斜率 S = {slope:F6}, 截距 B = {intercept:F6}"); // 3. 计算单位长度加热功率 q (W/m) double avgCurrentSq = current.Average(c => c * c); double avgResistance = ptResistance.Average(); double powerPerLength = (avgCurrentSq * avgResistance) / _config.TestParameters.PlatinumWireLength; // 4. 热导率 λ = q / (4π * slope) double lambda = powerPerLength / (4 * Math.PI * slope); Logger.Log($"热导率 λ = {lambda:F6} W/(m·K)"); // 5. 热扩散率 α(截距法) double wireRadius = 0.00003; // 铂丝半径 0.03 mm double eulerGamma = 0.5772156649; double alpha = (wireRadius * wireRadius / 4.0) * Math.Exp(intercept / slope + eulerGamma); if (alpha <= 0 || double.IsNaN(alpha) || double.IsInfinity(alpha)) alpha = 1e-7; // 合理默认值 Logger.Log($"热扩散率 α = {alpha:E6} m²/s"); // 冷却曲线数据点(仅用于绘图,不参与 α 计算) var coolingPoints = new List(); double coolingStart = heatingDuration; double coolingEnd = totalDuration; int coolingStartIdx = FindIndex(time, coolingStart); int coolingEndIdx = FindIndex(time, coolingEnd); if (coolingStartIdx < 0) coolingStartIdx = n / 2; if (coolingEndIdx >= n) coolingEndIdx = n - 1; for (int i = coolingStartIdx; i <= coolingEndIdx; i++) { if (deltaT[i] > 0.001) coolingPoints.Add(new DataPoint(time[i], deltaT[i])); } return (lambda, alpha, deltaT, coolingPoints); } /// /// 最小二乘法线性回归,返回 (斜率, 截距) /// private (double slope, double intercept) LinearRegression(List points) { if (points.Count < 2) return (0.001, 0); double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0; foreach (var p in points) { sumX += p.X; sumY += p.Y; sumXY += p.X * p.Y; sumX2 += p.X * p.X; } double n = points.Count; double denominator = n * sumX2 - sumX * sumX; if (Math.Abs(denominator) < 1e-10) return (0.001, 0); double slope = (n * sumXY - sumX * sumY) / denominator; double intercept = (sumY - slope * sumX) / n; return (slope, intercept); } /// /// 查找时间数组中与目标时间最接近的索引 /// private int FindIndex(double[] timeArray, double targetTime) { for (int i = 0; i < timeArray.Length; i++) { if (timeArray[i] >= targetTime) return i; } return timeArray.Length - 1; } ///// ///// 最小二乘法拟合斜率 (X轴为横坐标,Y轴为纵坐标) — 用于加热段 ln(t) vs ΔT ///// //private double LeastSquaresSlope(List points) //{ // if (points.Count < 2) return 0.001; // double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0; // foreach (var p in points) // { // sumX += p.X; // sumY += p.Y; // sumXY += p.X * p.Y; // sumX2 += p.X * p.X; // } // double n = points.Count; // double denominator = n * sumX2 - sumX * sumX; // if (Math.Abs(denominator) < 1e-10) return 0.001; // double slope = (n * sumXY - sumX * sumY) / denominator; // return slope; //} ///// ///// 最小二乘法拟合斜率 (X轴为时间t,Y轴为 ln(ΔT)) — 用于冷却段 ///// //private double LeastSquaresSlopeOnTime(List points) //{ // if (points.Count < 2) return -1.0; // double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0; // foreach (var p in points) // { // sumX += p.X; // sumY += p.Y; // sumXY += p.X * p.Y; // sumX2 += p.X * p.X; // } // double n = points.Count; // double denominator = n * sumX2 - sumX * sumX; // if (Math.Abs(denominator) < 1e-10) return -1.0; // double slope = (n * sumXY - sumX * sumY) / denominator; // return slope; //} private void GenerateTemperatureCurveFromData(double[] time, double[] deltaT, List coolingPoints) { if (TemperatureCurveModel == null) { TemperatureCurveModel = new PlotModel { Title = "温升与冷却曲线", Background = OxyColors.White }; TemperatureCurveModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "时间 (s)" }); TemperatureCurveModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "温升 (℃)" }); } // 加热段曲线(红色) var heatingSeries = new LineSeries { Title = $"第{CurrentMeasurementIndex}次测量 - 加热段", Color = OxyColors.Red, StrokeThickness = 1.5 }; for (int i = 0; i < time.Length && time[i] <= 1.0; i++) { heatingSeries.Points.Add(new DataPoint(time[i], deltaT[i])); } TemperatureCurveModel.Series.Add(heatingSeries); // 冷却曲线(蓝色虚线) if (coolingPoints != null && coolingPoints.Count > 0) { var coolingSeries = new LineSeries { Title = $"第{CurrentMeasurementIndex}次测量 - 冷却段", Color = OxyColors.Blue, StrokeThickness = 1.5, LineStyle = LineStyle.Dash }; foreach (var p in coolingPoints) { coolingSeries.Points.Add(p); } TemperatureCurveModel.Series.Add(coolingSeries); } TemperatureCurveModel.InvalidatePlot(true); CurveTitle = $"已完成 {CurrentMeasurementIndex} 次测量"; } 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 = 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 StopTest() { if (!IsTesting) return; _stopRequested = true; _testCts?.Cancel(); // 取消所有等待的 Delay StatusMessage = "正在停止测试..."; 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 = "测试已停止。"; } [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() { bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.InletValveCoil); await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, !current); StatusMessage = $"进气阀已{(current ? "关闭" : "开启")}"; } [RelayCommand] private async Task OutletValveControlAsync() { 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(); } private async Task UpdateRealTimeParametersAsync() { if (!await _plcService.IsConnectedAsync()) return; try { float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure); ChamberPressure = rawPressure / 10.0; } catch { } } }