Files
ASTM-D7896-19TransientHot-W…/ViewModels/D7896ViewModel - 副本.cs
2026-05-29 15:32:13 +08:00

747 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string> 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<MeasurementResult> _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³
int samples = 400; // 1秒 * 1000点/秒
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<double> 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} 次测量...";
// === 新增:在加热前,单独测量冷态初始电阻 R0 ===
StatusMessage = $"第 {i} 次测量:正在获取冷态电阻...";
await _th1963Ustd.PrepareBatchAsync(20);
await _th1953Ustd.PrepareBatchAsync(20);
await Task.WhenAll(_th1963Ustd.TriggerAsync(), _th1953Ustd.TriggerAsync());
await Task.Delay(250); // 等待采集完成
double[] ustd_r0 = await _th1963Ustd.FetchBatchAsync();
double[] upt_r0 = await _th1953Ustd.FetchBatchAsync();
double sumR0 = 0;
int validR0Count = 0;
for (int j = 2; j < ustd_r0.Length; j++) // 跳过前2个不稳定点
{
if (ustd_r0[j] > 0.01)
{
sumR0 += upt_r0[j] / ustd_r0[j]; // R = Upt / I = Upt / (Ustd / 1Ω)
validR0Count++;
}
}
double dynamicR0 = validR0Count > 0 ? sumR0 / validR0Count : 2.34; // 给个默认值防错
Logger.Log($"冷态测量 R0 = {dynamicR0:F6} Ω");
// === 正式加热与采集 ===
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 < ustd.Length; 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.Average():F6} V, U_pt 平均值={upt.Average():F6} V");
double[] timeArray = new double[ustd.Length];
for (int idx = 0; idx < timeArray.Length; idx++)
{
timeArray[idx] = idx * totalDuration / samples;
}
// 计算本次测量的 λ 和 α (传入刚才测得的冷态 dynamicR0)
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.CalculateVhcAndCp(SampleDensity);
Application.Current.Dispatcher.Invoke(() => Measurements.Add(result));
StatusMessage = $"第 {i} 次测量完成,λ={lambda:F4} W/m·K";
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)");
Logger.Log($"初始电阻 R0: {dynamicR0:F6} Ω");
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<DataPoint> coolingPoints) ComputeThermalProperties(
double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp)
{
int n = Math.Min(upt.Length, ustd.Length);
// 【核心优化:滑动平均滤波,抹平万用表的高频噪声】
// 窗口大小设为 15如果是400Hz采样相当于约37毫秒的平滑窗口
int windowSize = 15;
double[] smoothedUpt = new double[n];
for (int i = 0; i < n; i++)
{
int start = Math.Max(0, i - windowSize / 2);
int end = Math.Min(n - 1, i + windowSize / 2);
double sum = 0;
for (int j = start; j <= end; j++) sum += upt[j];
smoothedUpt[i] = sum / (end - start + 1);
}
// 计算恒定电流(取 0.1s~0.7s 的 U_std 平均值)
int avgStart = FindIndex(time, 0.1);
int avgEnd = FindIndex(time, 0.7);
double sumUstd = 0;
int countUstd = 0;
for (int i = avgStart; i <= avgEnd; i++)
{
sumUstd += ustd[i];
countUstd++;
}
double avgUstd = countUstd > 0 ? sumUstd / countUstd : ustd.Average();
double constantCurrent = avgUstd / StandardResistor;
double[] ptResistance = new double[n];
double[] deltaT = new double[n];
for (int i = 0; i < n; i++)
{
// 使用滤波后的 smoothedUpt 计算电阻,彻底消除毛刺!
ptResistance[i] = smoothedUpt[i] / constantCurrent;
deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance);
}
// 时间零点补偿 (t0_shift)
double t0_shift = 0.030;
// 选取 0.15s ~ 0.70s 进行拟合
double tStart = 0.15;
double tEndHeating = 0.70;
int startIdx = FindIndex(time, tStart);
int endIdxHeating = FindIndex(time, tEndHeating);
var points = new List<DataPoint>();
for (int i = startIdx; i <= endIdxHeating; i++)
{
double realTime = time[i] - t0_shift;
if (realTime > 0.001)
{
points.Add(new DataPoint(Math.Log(realTime), deltaT[i]));
}
}
(double slope, double intercept) = LinearRegression(points);
// 保护:如果滤波后斜率依然小于 0.01,说明数据彻底废了,给个合理兜底值
if (slope <= 0.01)
{
Logger.Log("警告: 滤波后斜率依然异常,启用兜底值 0.05");
slope = 0.05;
}
// 计算热导率 λ
double avgResistance = ptResistance.Skip(startIdx).Take(endIdxHeating - startIdx + 1).Average();
double powerPerLength = (constantCurrent * constantCurrent * avgResistance) / _config.TestParameters.PlatinumWireLength;
double lambda = powerPerLength / (4 * Math.PI * slope);
// 计算热扩散率 α
double eulerGamma = 0.5772156649;
double wireRadius = 0.00003; // 30 微米 (0.03mm)
double alpha = (wireRadius * wireRadius / 4.0) * Math.Exp(eulerGamma) * Math.Exp(intercept / slope);
if (alpha <= 0 || double.IsNaN(alpha) || double.IsInfinity(alpha) || alpha > 1e-5)
alpha = 1.4e-7;
// 提取冷却曲线数据点
var coolingPoints = new List<DataPoint>();
int coolingStartIdx = FindIndex(time, heatingDuration);
int coolingEndIdx = FindIndex(time, totalDuration);
for (int i = coolingStartIdx; i <= coolingEndIdx; i++)
{
if (deltaT[i] > 0.001) coolingPoints.Add(new DataPoint(time[i], deltaT[i]));
}
Logger.Log($"[调试] 滤波后拟合参数: Slope={slope:F4}, Intercept={intercept:F4}");
return (lambda, alpha, deltaT, coolingPoints);
}
/// <summary>
/// 最小二乘法线性回归,返回 (斜率, 截距)
/// </summary>
private (double slope, double intercept) LinearRegression(List<DataPoint> 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);
}
/// <summary>
/// 查找时间数组中与目标时间最接近的索引
/// </summary>
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;
}
///// <summary>
///// 最小二乘法拟合斜率 (X轴为横坐标Y轴为纵坐标) — 用于加热段 ln(t) vs ΔT
///// </summary>
//private double LeastSquaresSlope(List<DataPoint> 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;
//}
///// <summary>
///// 最小二乘法拟合斜率 (X轴为时间tY轴为 ln(ΔT)) — 用于冷却段
///// </summary>
//private double LeastSquaresSlopeOnTime(List<DataPoint> 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<DataPoint> 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<string, object>
{
["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 { }
}
}