Files
ASTM-D7896-19TransientHot-W…/ViewModels/D7896ViewModel.cs
2026-05-26 20:58:48 +08:00

771 lines
31 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³
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.12", 45454); // 改为实际IP
await _th1953Ustd.ConfigureForHighSpeedDcvAsync();
}
catch (Exception ex)
{
MessageBox.Show($"电压表连接失败: {ex.Message}", "错误");
return;
}
if (UsePressure)
{
StatusMessage = "正在加压...";
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, true);
await Task.Delay(3000);
await UpdateRealTimeParametersAsync();
if (ChamberPressure < PressureValue - 5)
MessageBox.Show($"压力未达到设定值 {PressureValue} 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 = 800; // 1秒 * 1000点/秒
// 预配置两台表:进入等待触发状态
await _th1963Ustd.PrepareBatchAsync(samples);
await _th1953Ustd.PrepareBatchAsync(samples);
// 启动加热脉冲 (PLC)
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
// 等待极短时间确保电流稳定(如 5ms
await Task.Delay(5);
// 同时发送触发信号给两台电压表
await Task.WhenAll(_th1963Ustd.TriggerAsync(), _th1953Ustd.TriggerAsync());
// 等待加热结束
await Task.Delay((int)(heatingDuration * 1000));
// 停止加热
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
// 等待采集完成(剩余时间)
int remainingMs = (int)((totalDuration - heatingDuration) * 1000) + 100;
await Task.Delay(remainingMs);
// 获取采集数据
double[] ustd = await _th1963Ustd.FetchBatchAsync();
double[] upt = await _th1953Ustd.FetchBatchAsync();
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;
}
// 计算本次测量的 λ 和 α
var (lambda, alpha, deltaT, coolingPoints) = ComputeThermalProperties(upt, ustd, timeArray, initialResistance, 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: {initialResistance:F6} Ω");
Logger.Log($"测试温度: {CurrentTestTemperature:F2} °C");
Logger.Log($"铂丝平均电阻: {PlatinumResistance:F6} Ω");
Logger.Log($"样品池压力: {ChamberPressure:F2} kPa");
Logger.Log("===========================================");
if (i < _config.TestParameters.MeasurementCount && !_stopRequested)
await Task.Delay(_config.TestParameters.IntervalSeconds * 1000);
}
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();
}
}
///// <summary>
///// 根据采集到的电压序列计算热导率 λ、热扩散率 α、温升数组以及冷却曲线数据点
///// </summary>
//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);
// 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<DataPoint>();
// 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<DataPoint>();
// 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<DataPoint>();
// 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);
//}
/// <summary>
/// 根据采集到的电压序列计算热导率 λ 和热扩散率 α(标准截距法)
/// </summary>
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);
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<DataPoint>();
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<DataPoint>();
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);
}
/// <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 StopTestCommandAsync()
{
if (!IsTesting)
{
MessageBox.Show("没有正在进行的测试", "提示");
return;
}
_stopRequested = true;
_testCts?.Cancel();
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 { }
}
}