Files
ASTM-D7896-19TransientHot-W…/ViewModels/D7896ViewModel.cs

620 lines
24 KiB
C#
Raw Normal View History

2026-05-15 10:59:24 +08:00
using ASTM_D7896_Tester.Helpers;
2026-04-18 19:00:34 +08:00
using ASTM_D7896_Tester.Models;
using ASTM_D7896_Tester.Services;
2026-05-15 10:59:24 +08:00
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;
2026-05-20 13:49:45 +08:00
using System.Threading;
2026-05-15 10:59:24 +08:00
using System.Threading.Tasks;
using System.Windows;
2026-04-18 19:00:34 +08:00
namespace ASTM_D7896_Tester.ViewModels;
public partial class D7896ViewModel : ObservableObject
{
2026-05-15 20:39:11 +08:00
private readonly IPlcService _plcService;
2026-04-18 19:00:34 +08:00
private AppConfig _config;
2026-05-15 20:39:11 +08:00
private readonly ReportService _reportService;
2026-05-20 19:46:52 +08:00
// 电压表服务
private Th1963LanService _th1963Ustd; // 6位半测量标准电阻电压 U_std
private FiveHalfDmmService _fiveHalfUpt; // 5位半测量铂丝电压 U_pt
2026-05-20 13:49:45 +08:00
2026-05-20 19:46:52 +08:00
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; // 铂丝平均电阻
2026-05-15 21:10:42 +08:00
2026-05-20 19:46:52 +08:00
// 温升曲线数据
[ObservableProperty] private string _curveTitle = "温升曲线";
[ObservableProperty] private PlotModel _temperatureCurveModel;
// UI 绑定属性 (与之前一致)
public ObservableCollection<string> ReferenceLiquids { get; } = new() { "蒸馏水", "甲苯", "乙二醇" };
2026-05-15 21:10:42 +08:00
[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 = "";
2026-05-20 19:46:52 +08:00
[ObservableProperty] private double _platinumResistance = 0.0;
[ObservableProperty] private double _chamberPressure = 0.0;
[ObservableProperty] private double _currentTestTemperature = 0.0;
2026-05-20 13:49:45 +08:00
2026-05-15 21:10:42 +08:00
[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;
2026-05-20 19:46:52 +08:00
// 实时电压显示(可选)
[ObservableProperty] private double _platinumVoltage;
[ObservableProperty] private double _standardResistorVoltage;
2026-05-15 10:59:24 +08:00
2026-05-20 13:49:45 +08:00
2026-05-20 19:46:52 +08:00
[ObservableProperty] private double _sampleDensity = 1000.0; // 新增密度默认值1000 kg/m³
2026-05-20 13:49:45 +08:00
2026-04-18 19:00:34 +08:00
public D7896ViewModel()
{
2026-05-15 21:10:42 +08:00
_config = App.PlcConfig ?? new AppConfig();
_plcService = App.PlcService;
2026-04-18 19:00:34 +08:00
_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;
2026-05-15 21:10:42 +08:00
2026-05-15 10:59:24 +08:00
IsCleanConfirmed = true;
BubbleRemoved = true;
PlatinumCompatible = true;
AmbientCalibrated = true;
2026-05-20 13:49:45 +08:00
2026-05-20 19:46:52 +08:00
// 初始化电压表服务
// TH1963 IP 地址需要根据实际配置修改,建议从配置文件读取
_th1963Ustd = new Th1963LanService();
// 5位半万用表串口名例如 "COM3"
2026-05-23 21:18:46 +08:00
_fiveHalfUpt = new FiveHalfDmmService("COM6", 115200);
2026-05-20 19:46:52 +08:00
2026-05-20 13:49:45 +08:00
StartBackgroundMonitoring();
2026-04-18 19:00:34 +08:00
}
2026-05-20 13:49:45 +08:00
private async void StartBackgroundMonitoring()
{
await Task.Delay(1000);
_monitorTimer = new Timer(async _ => await MonitorPlcValues(), null, 0, 1000);
}
private async Task MonitorPlcValues()
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
if (!await _plcService.IsConnectedAsync()) return;
try
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance);
double newResistance = rawResistance;
Application.Current.Dispatcher.Invoke(() => PlatinumResistance = newResistance);
2026-05-15 21:10:42 +08:00
2026-05-20 19:46:52 +08:00
float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure);
Application.Current.Dispatcher.Invoke(() => ChamberPressure = rawPressure);
2026-05-20 13:49:45 +08:00
2026-05-20 19:46:52 +08:00
float rawTemp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature);
Application.Current.Dispatcher.Invoke(() => CurrentTestTemperature = rawTemp);
2026-04-18 19:00:34 +08:00
}
2026-05-20 19:46:52 +08:00
catch { }
2026-05-20 13:49:45 +08:00
}
private async Task<double> GetInitialResistanceAsync()
{
2026-05-20 19:46:52 +08:00
if (!await _plcService.IsConnectedAsync()) return 0;
2026-05-20 13:49:45 +08:00
try
2026-04-18 19:00:34 +08:00
{
2026-05-20 13:49:45 +08:00
float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance);
return rawResistance / 1000.0;
}
2026-05-20 19:46:52 +08:00
catch { return 0; }
2026-05-15 21:10:42 +08:00
}
[RelayCommand]
private async Task StartTestAsync()
{
2026-05-20 19:46:52 +08:00
if (IsTesting)
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("测试正在进行中", "提示");
2026-04-18 19:00:34 +08:00
return;
}
2026-05-20 19:46:52 +08:00
// 前置检查
if (!IsCleanConfirmed || !BubbleRemoved || !PlatinumCompatible || !AmbientCalibrated)
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("请完成所有测试前确认项", "前置条件未满足");
2026-04-18 19:00:34 +08:00
return;
}
2026-05-20 19:46:52 +08:00
if (SampleVolume <= 0)
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("请输入有效的样品量", "参数错误");
2026-04-18 19:00:34 +08:00
return;
}
2026-05-20 19:46:52 +08:00
if (UsePressure && PressureValue <= 0)
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("请设置有效的加压值", "参数错误");
2026-04-18 19:00:34 +08:00
return;
}
2026-05-20 19:46:52 +08:00
// 连接PLC和电压表
2026-04-18 19:00:34 +08:00
if (!await _plcService.IsConnectedAsync())
{
2026-05-20 19:46:52 +08:00
if (!await _plcService.ConnectAsync())
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("无法连接到PLC", "错误");
2026-04-18 19:00:34 +08:00
return;
}
}
2026-05-20 19:46:52 +08:00
try
{
if (!_fiveHalfUpt.IsOpen) _fiveHalfUpt.Open();
await _th1963Ustd.ConnectAsync("192.168.1.12", 45454); // 改为实际IP
await _th1963Ustd.ConfigureForHighSpeedDcvAsync();
await _fiveHalfUpt.ConfigureHighSpeedDcvAsync();
}
catch (Exception ex)
{
MessageBox.Show($"电压表连接失败: {ex.Message}", "错误");
return;
}
2026-05-15 21:10:42 +08:00
if (UsePressure)
{
2026-05-20 13:49:45 +08:00
StatusMessage = "正在加压...";
2026-05-15 21:10:42 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, true);
2026-05-20 13:49:45 +08:00
await Task.Delay(3000);
2026-05-15 21:10:42 +08:00
await UpdateRealTimeParametersAsync();
if (ChamberPressure < PressureValue - 5)
2026-05-20 19:46:52 +08:00
MessageBox.Show($"压力未达到设定值 {PressureValue} kPa", "警告");
2026-05-15 21:10:42 +08:00
}
2026-05-20 13:49:45 +08:00
double initialResistance = await GetInitialResistanceAsync();
if (initialResistance > 0)
2026-05-20 19:46:52 +08:00
StatusMessage = $"初始电阻: {initialResistance:F4} Ω";
2026-05-20 13:49:45 +08:00
2026-04-18 19:00:34 +08:00
Measurements.Clear();
IsTesting = true;
2026-05-20 19:46:52 +08:00
_stopRequested = false;
_testCts = new CancellationTokenSource();
2026-04-18 19:00:34 +08:00
try
{
for (int i = 1; i <= _config.TestParameters.MeasurementCount; i++)
{
2026-05-20 19:46:52 +08:00
if (_stopRequested) break;
2026-04-18 19:00:34 +08:00
CurrentMeasurementIndex = i;
StatusMessage = $"正在执行第 {i} 次测量...";
2026-05-20 19:46:52 +08:00
// 准备批量采集参数每通道采样点数采样率1000点/秒加热时间1秒 -> 1000点
int samples = 1000; // 1秒 * 1000点/秒
// 预配置两台表:进入等待触发状态
await _th1963Ustd.PrepareBatchAsync(samples);
await _fiveHalfUpt.PrepareBatchAsync(samples); // 需要在FiveHalfDmmService中实现PrepareBatchAsync见补充
// 启动加热脉冲 (PLC)
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
2026-05-20 19:46:52 +08:00
// 同时发送触发信号给两台电压表
await Task.WhenAll(_th1963Ustd.TriggerAsync(), _fiveHalfUpt.TriggerAsync());
2026-04-18 19:00:34 +08:00
2026-05-20 19:46:52 +08:00
// 等待加热脉冲持续1秒
2026-05-24 10:36:57 +08:00
await Task.Delay(1);
2026-04-18 19:00:34 +08:00
2026-05-20 19:46:52 +08:00
// 停止加热
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-04-18 19:00:34 +08:00
2026-05-20 19:46:52 +08:00
// 等待采集完成(留出额外时间)
await Task.Delay(500);
// 获取采集数据
double[] ustd = await _th1963Ustd.FetchBatchAsync();
double[] upt = await _fiveHalfUpt.FetchBatchAsync();
2026-05-24 10:36:57 +08:00
StandardResistorVoltage = ustd.Average();
2026-05-20 19:46:52 +08:00
// 构建时间数组假设采样时间正好1秒采样点数 = ustd.Length
double[] timeArray = new double[ustd.Length];
for (int idx = 0; idx < timeArray.Length; idx++)
{
timeArray[idx] = idx / (double)samples; // 0 ~ 1 秒
}
// 计算本次测量的 λ 和 α
var (lambda, alpha, deltaT, coolingPoints) = ComputeThermalProperties(upt, ustd, timeArray, initialResistance, CurrentTestTemperature);
2026-05-15 21:10:42 +08:00
2026-05-20 19:46:52 +08:00
// 生成温升曲线图
GenerateTemperatureCurveFromData(timeArray, deltaT, coolingPoints);
2026-05-15 21:10:42 +08:00
2026-04-18 19:00:34 +08:00
var result = new MeasurementResult
{
Index = i,
ThermalConductivity = lambda,
ThermalDiffusivity = alpha
};
2026-05-20 19:46:52 +08:00
//result.CalculateVhc();
result.CalculateVhcAndCp(SampleDensity);
2026-05-15 21:10:42 +08:00
Application.Current.Dispatcher.Invoke(() => Measurements.Add(result));
2026-04-18 19:00:34 +08:00
StatusMessage = $"第 {i} 次测量完成,λ={lambda:F4} W/m·K";
2026-05-20 19:46:52 +08:00
if (i < _config.TestParameters.MeasurementCount && !_stopRequested)
2026-04-18 19:00:34 +08:00
await Task.Delay(_config.TestParameters.IntervalSeconds * 1000);
}
CalculateAverages();
2026-05-20 19:46:52 +08:00
StatusMessage = _stopRequested ? "测试已停止。" : "测试完成。";
2026-04-18 19:00:34 +08:00
}
catch (Exception ex)
{
StatusMessage = $"测试出错: {ex.Message}";
MessageBox.Show($"测试过程中发生错误: {ex.Message}", "错误");
}
finally
{
2026-05-20 19:46:52 +08:00
// 停止加热,泄压
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-05-15 21:10:42 +08:00
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);
}
2026-04-18 19:00:34 +08:00
IsTesting = false;
2026-05-20 19:46:52 +08:00
_fiveHalfUpt.Close();
_th1963Ustd.Dispose();
_testCts?.Dispose();
2026-04-18 19:00:34 +08:00
}
}
2026-05-20 19:46:52 +08:00
/// <summary>
/// 根据采集到的电压序列计算热导率 λ、热扩散率 α、温升数组以及冷却曲线数据点
/// </summary>
private (double lambda, double alpha, double[] deltaT, List<DataPoint> coolingPoints) ComputeThermalProperties(
double[] upt, double[] ustd, double[] time, double initialResistance, double bathTemp)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
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++)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
current[i] = ustd[i] / StandardResistor;
ptResistance[i] = upt[i] / current[i];
deltaT[i] = (ptResistance[i] - initialResistance) / (AlphaPt * initialResistance);
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
// 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++)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
heatingPoints.Add(new DataPoint(Math.Log(time[i]), deltaT[i]));
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
double slope = LeastSquaresSlope(heatingPoints);
if (slope <= 0) slope = 0.0001;
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);
// 3. ========== 冷却段拟合 → 热扩散率 α ==========
double coolingStart = 1.0;
double coolingEnd = 2.0;
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++)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
if (deltaT[i] > 0.001)
coolingPointsForFit.Add(new DataPoint(time[i], Math.Log(deltaT[i])));
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
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; // 默认值
// 准备冷却曲线数据点(用于绘图)
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);
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
/// <summary>
/// 查找时间数组中与目标时间最接近的索引
/// </summary>
private int FindIndex(double[] timeArray, double targetTime)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
for (int i = 0; i < timeArray.Length; i++)
{
if (timeArray[i] >= targetTime)
return i;
}
return timeArray.Length - 1;
}
2026-05-20 13:49:45 +08:00
2026-05-20 19:46:52 +08:00
/// <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)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
sumX += p.X;
sumY += p.Y;
sumXY += p.X * p.Y;
sumX2 += p.X * p.X;
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
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)
2026-05-20 13:49:45 +08:00
{
2026-05-20 19:46:52 +08:00
sumX += p.X;
sumY += p.Y;
sumXY += p.X * p.Y;
sumX2 += p.X * p.X;
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
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;
2026-05-20 13:49:45 +08:00
}
2026-05-20 19:46:52 +08:00
private void GenerateTemperatureCurveFromData(double[] time, double[] deltaT, List<DataPoint> coolingPoints)
2026-05-15 10:59:24 +08:00
{
if (TemperatureCurveModel == null)
{
2026-05-20 19:46:52 +08:00
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 = "温升 (℃)" });
2026-05-15 10:59:24 +08:00
}
2026-05-20 19:46:52 +08:00
// 加热段曲线(红色)
var heatingSeries = new LineSeries
2026-05-15 10:59:24 +08:00
{
2026-05-20 19:46:52 +08:00
Title = $"第{CurrentMeasurementIndex}次测量 - 加热段",
Color = OxyColors.Red,
2026-05-15 21:10:42 +08:00
StrokeThickness = 1.5
2026-05-15 10:59:24 +08:00
};
2026-05-20 19:46:52 +08:00
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);
2026-05-15 10:59:24 +08:00
2026-05-20 19:46:52 +08:00
// 冷却曲线(蓝色虚线)
if (coolingPoints != null && coolingPoints.Count > 0)
2026-05-15 10:59:24 +08:00
{
2026-05-20 19:46:52 +08:00
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);
2026-05-15 10:59:24 +08:00
}
2026-05-20 19:46:52 +08:00
2026-05-15 10:59:24 +08:00
TemperatureCurveModel.InvalidatePlot(true);
CurveTitle = $"已完成 {CurrentMeasurementIndex} 次测量";
}
2026-05-20 19:46:52 +08:00
private void GenerateTemperatureCurve(float lambda, float alpha) { /* 旧方法,不再使用 */ }
2026-05-15 10:59:24 +08:00
private OxyColor GetColorForIndex(int index)
{
2026-05-20 19:46:52 +08:00
var colors = new[] { OxyColors.Red, OxyColors.Blue, OxyColors.Green, OxyColors.Orange,
OxyColors.Purple, OxyColors.Brown, OxyColors.Pink, OxyColors.Cyan, OxyColors.Magenta, OxyColors.Olive };
2026-05-15 10:59:24 +08:00
return colors[(index - 1) % colors.Length];
}
2026-04-18 19:00:34 +08:00
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();
2026-05-15 21:10:42 +08:00
AverageThermalConductivity = AverageThermalDiffusivity = AverageVolumetricHeatCapacity = 0;
2026-04-18 19:00:34 +08:00
CurrentMeasurementIndex = 0;
StatusMessage = "已重置";
TestDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
2026-05-15 21:10:42 +08:00
TemperatureCurveModel = null;
2026-04-18 19:00:34 +08:00
}
[RelayCommand]
private async Task GenerateReportAsync()
{
if (Measurements.Count == 0)
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("没有测试数据", "提示");
2026-04-18 19:00:34 +08:00
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,
2026-05-20 13:49:45 +08:00
["LiquidReactivityNote"] = LiquidReactivityNote,
2026-05-20 19:46:52 +08:00
["InitialResistance"] = PlatinumResistance
2026-04-18 19:00:34 +08:00
};
2026-05-15 21:10:42 +08:00
string reportPath = await _reportService.GenerateReportAsync(SampleId, TestTemperature, Measurements.ToList(),
AverageThermalConductivity, AverageThermalDiffusivity, AverageVolumetricHeatCapacity,
_config.TestParameters, extraParams);
2026-05-20 19:46:52 +08:00
MessageBox.Show($"报告已生成: {reportPath}", "成功");
2026-04-18 19:00:34 +08:00
}
catch (Exception ex)
{
2026-05-20 19:46:52 +08:00
MessageBox.Show($"生成报告失败: {ex.Message}", "错误");
2026-04-18 19:00:34 +08:00
}
}
[RelayCommand]
2026-05-20 19:46:52 +08:00
private async Task StopTestCommandAsync()
2026-04-18 19:00:34 +08:00
{
2026-05-20 19:46:52 +08:00
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 = "测试已停止。";
2026-04-18 19:00:34 +08:00
}
2026-05-20 19:46:52 +08:00
[RelayCommand] private async Task PressureCalibrationAsync() => await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.PressureCalibrationCoil, true);
[RelayCommand] private async Task ResistanceZeroAsync() => await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.ResistanceZeroCoil, true);
2026-04-18 19:00:34 +08:00
[RelayCommand]
2026-05-15 21:10:42 +08:00
private async Task InletValveControlAsync()
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.InletValveCoil);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, !current);
2026-05-20 13:49:45 +08:00
StatusMessage = $"进气阀已{(current ? "" : "")}";
2026-04-18 19:00:34 +08:00
}
[RelayCommand]
2026-05-15 21:10:42 +08:00
private async Task OutletValveControlAsync()
{
bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, !current);
2026-05-20 13:49:45 +08:00
StatusMessage = $"排气阀已{(current ? "" : "")}";
2026-05-15 21:10:42 +08:00
}
[RelayCommand] private void ConfirmBubbleRemoved() => BubbleRemoved = true;
[RelayCommand]
private void ConfirmClean()
{
if (string.IsNullOrWhiteSpace(CleanerName))
{
2026-05-20 19:46:52 +08:00
MessageBox.Show("请输入清洁人员姓名", "提示");
2026-05-15 21:10:42 +08:00
return;
2026-04-18 19:00:34 +08:00
}
2026-05-15 21:10:42 +08:00
IsCleanConfirmed = true;
}
[RelayCommand] private void ConfirmPlatinumCompatible() => PlatinumCompatible = true;
[RelayCommand]
private async Task CalibrateAmbientAsync()
{
await EnsureConnected();
float temp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature);
2026-04-18 19:00:34 +08:00
AmbientTemperature = temp;
AmbientCalibrated = true;
StatusMessage = $"环境温度校准完成:{AmbientTemperature:F1} °C";
}
2026-05-20 19:46:52 +08:00
[RelayCommand] private async Task PerformSystemCalibrationAsync() { /* 系统校准逻辑待实现 */ }
2026-05-20 13:49:45 +08:00
private async Task EnsureConnected()
{
if (!await _plcService.IsConnectedAsync())
await _plcService.ConnectAsync();
2026-04-18 19:00:34 +08:00
}
2026-05-20 19:46:52 +08:00
private async Task UpdateRealTimeParametersAsync()
{
if (!await _plcService.IsConnectedAsync()) return;
try
{
float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure);
ChamberPressure = rawPressure / 10.0;
}
catch { }
}
2026-04-18 19:00:34 +08:00
}