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

442 lines
18 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;
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-15 21:10:42 +08:00
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 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;
// 实时核心参数直接从PLC读取
[ObservableProperty] private double _platinumVoltage; // 铂丝电压 U_pt (V)
[ObservableProperty] private double _standardResistorVoltage; // 标准电阻电压 (V)
[ObservableProperty] private double _platinumResistance; // 铂丝电阻 (Ω)
[ObservableProperty] private double _chamberPressure; // 样品池压力 (kPa)
// 温升曲线
[ObservableProperty] private string _curveTitle = "温升曲线";
[ObservableProperty] private PlotModel _temperatureCurveModel;
2026-05-15 10:59:24 +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);
2026-05-15 21:10:42 +08:00
// 加载默认值
2026-04-18 19:00:34 +08:00
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
// 默认确认项为 true避免每次手动勾选
2026-05-15 10:59:24 +08:00
IsCleanConfirmed = true;
BubbleRemoved = true;
PlatinumCompatible = true;
AmbientCalibrated = true;
2026-04-18 19:00:34 +08:00
}
2026-05-15 21:10:42 +08:00
// ======================= 实时数据更新 =======================
private async Task UpdateRealTimeParametersAsync()
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
if (!await _plcService.IsConnectedAsync())
2026-04-18 19:00:34 +08:00
return;
2026-05-15 21:10:42 +08:00
try
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
// 读取温度D1376假设为0.1℃单位,转换为℃)
float rawTemp = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Temperature);
TestTemperature = rawTemp / 10.0;
// 读取压力D1322假设为0.1kPa单位)
float rawPressure = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Pressure);
ChamberPressure = rawPressure / 10.0;
// 读取铂丝电阻D1422假设为0.001Ω单位)
float rawResistance = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.Resistance);
PlatinumResistance = rawResistance / 1000.0;
// 注意:铂丝电压和标准电阻电压可能需要从其他寄存器读取,如果没有则不更新
// 如有对应寄存器请添加
2026-04-18 19:00:34 +08:00
}
2026-05-15 21:10:42 +08:00
catch (Exception ex)
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
StatusMessage = $"实时参数读取失败: {ex.Message}";
2026-04-18 19:00:34 +08:00
}
2026-05-15 21:10:42 +08:00
}
// ======================= 核心测试流程 =======================
[RelayCommand]
private async Task StartTestAsync()
{
// 前置检查
if (!IsCleanConfirmed || !BubbleRemoved || !PlatinumCompatible || !AmbientCalibrated)
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
MessageBox.Show("请完成所有测试前确认项(清洁、排泡、铂兼容性、环境校准)。", "前置条件未满足");
2026-04-18 19:00:34 +08:00
return;
}
if (SampleVolume <= 0)
{
MessageBox.Show("请输入有效的样品量≥1 mL。", "参数错误");
return;
}
if (UsePressure && PressureValue <= 0)
{
MessageBox.Show("请设置有效的加压值(>0 kPa。", "参数错误");
return;
}
if (IsTesting)
{
MessageBox.Show("测试正在进行中,请稍后...", "提示");
return;
}
if (!await _plcService.IsConnectedAsync())
{
var connected = await _plcService.ConnectAsync();
if (!connected)
{
MessageBox.Show("无法连接到PLC请检查配置。", "错误");
return;
}
}
2026-05-15 21:10:42 +08:00
// 如果需要加压,开启进气阀并等待压力稳定
if (UsePressure)
{
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, true);
await Task.Delay(2000); // 等待加压
// 实时读取压力并显示
await UpdateRealTimeParametersAsync();
if (ChamberPressure < PressureValue - 5)
MessageBox.Show($"压力未达到设定值 {PressureValue} kPa当前 {ChamberPressure:F1} kPa", "警告");
}
2026-04-18 19:00:34 +08:00
Measurements.Clear();
IsTesting = true;
StatusMessage = "开始测试...";
try
{
for (int i = 1; i <= _config.TestParameters.MeasurementCount; i++)
{
CurrentMeasurementIndex = i;
StatusMessage = $"正在执行第 {i} 次测量...";
2026-05-15 21:10:42 +08:00
// 启动单次测量触发PLC内的加热脉冲
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
2026-04-18 19:00:34 +08:00
await Task.Delay(500);
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-04-18 19:00:34 +08:00
2026-05-15 21:10:42 +08:00
// 等待测量完成PLC计算需要时间
2026-04-18 19:00:34 +08:00
await Task.Delay(2000);
2026-05-15 21:10:42 +08:00
// 读取计算结果(热导率λ、热扩散率α)
2026-04-18 19:00:34 +08:00
float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity);
float alpha = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalDiffusivity);
2026-05-15 21:10:42 +08:00
// 实时更新温度、压力、电阻
await UpdateRealTimeParametersAsync();
// 生成温升曲线根据实际λ和α如果用真实U_pt数据需额外采集
GenerateTemperatureCurve(lambda, alpha);
2026-04-18 19:00:34 +08:00
var result = new MeasurementResult
{
Index = i,
ThermalConductivity = lambda,
ThermalDiffusivity = alpha
};
result.CalculateVhc();
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";
if (i < _config.TestParameters.MeasurementCount)
await Task.Delay(_config.TestParameters.IntervalSeconds * 1000);
}
CalculateAverages();
StatusMessage = "测试完成。";
}
catch (Exception ex)
{
StatusMessage = $"测试出错: {ex.Message}";
MessageBox.Show($"测试过程中发生错误: {ex.Message}", "错误");
}
finally
{
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;
await _plcService.DisconnectAsync();
}
}
2026-05-15 21:10:42 +08:00
// ======================= 温升曲线真实数据需要U_pt时间序列此处暂用理论公式 =======================
2026-05-15 10:59:24 +08:00
private void GenerateTemperatureCurve(float lambda, float alpha)
{
if (TemperatureCurveModel == null)
{
2026-05-15 21:10:42 +08:00
TemperatureCurveModel = new PlotModel { Title = "温升曲线对比", Background = OxyColors.White };
TemperatureCurveModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "时间 (s)", Minimum = 0, Maximum = 2 });
TemperatureCurveModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "温升 (℃)", Minimum = 0 });
2026-05-15 10:59:24 +08:00
}
var series = new LineSeries
{
Title = $"第{CurrentMeasurementIndex}次测量",
Color = GetColorForIndex(CurrentMeasurementIndex),
2026-05-15 21:10:42 +08:00
StrokeThickness = 1.5
2026-05-15 10:59:24 +08:00
};
2026-05-15 21:10:42 +08:00
// 理论公式 ΔT = (Q/(4πλL)) * ln(t/t0) + CQ = I²R
// 实际应用时应根据采集的铂丝电压 U_pt(t) 计算温升
double Q = 0.01; // 示例值,应从实际电流和电阻计算
double L = _config.TestParameters.PlatinumWireLength;
2026-05-15 10:59:24 +08:00
double constant = 0.2;
2026-05-15 21:10:42 +08:00
for (int i = 1; i <= 200; i++)
2026-05-15 10:59:24 +08:00
{
double t = i * 0.01;
double deltaT = (Q / (4 * Math.PI * lambda * L)) * Math.Log(t + 0.1) + constant;
series.Points.Add(new DataPoint(t, deltaT));
}
TemperatureCurveModel.Series.Add(series);
TemperatureCurveModel.InvalidatePlot(true);
CurveTitle = $"已完成 {CurrentMeasurementIndex} 次测量";
}
private OxyColor GetColorForIndex(int index)
{
2026-05-15 21:10:42 +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)
{
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
};
2026-05-15 21:10:42 +08:00
string reportPath = await _reportService.GenerateReportAsync(SampleId, TestTemperature, Measurements.ToList(),
AverageThermalConductivity, AverageThermalDiffusivity, AverageVolumetricHeatCapacity,
_config.TestParameters, extraParams);
2026-04-18 19:00:34 +08:00
MessageBox.Show($"报告已生成:{reportPath}", "成功");
}
catch (Exception ex)
{
MessageBox.Show($"生成报告失败:{ex.Message}", "错误");
}
}
2026-05-15 21:10:42 +08:00
// ======================= 新增控制命令 =======================
2026-04-18 19:00:34 +08:00
[RelayCommand]
2026-05-15 21:10:42 +08:00
private async Task PressureCalibrationAsync()
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
await EnsureConnected();
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.PressureCalibrationCoil, true);
await Task.Delay(500);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.PressureCalibrationCoil, false);
StatusMessage = "压力校准指令已发送";
2026-04-18 19:00:34 +08:00
}
[RelayCommand]
2026-05-15 21:10:42 +08:00
private async Task ResistanceZeroAsync()
2026-04-18 19:00:34 +08:00
{
2026-05-15 21:10:42 +08:00
await EnsureConnected();
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.ResistanceZeroCoil, true);
await Task.Delay(500);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.ResistanceZeroCoil, false);
StatusMessage = "电阻归零指令已发送";
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
await EnsureConnected();
bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.InletValveCoil);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.InletValveCoil, !current);
StatusMessage = $"进气阀 {(current ? "" : "")}";
2026-04-18 19:00:34 +08:00
}
[RelayCommand]
2026-05-15 21:10:42 +08:00
private async Task OutletValveControlAsync()
{
await EnsureConnected();
bool current = await _plcService.ReadCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil);
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.OutletValveCoil, !current);
StatusMessage = $"排气阀 {(current ? "" : "")}";
}
private async Task EnsureConnected()
2026-04-18 19:00:34 +08:00
{
if (!await _plcService.IsConnectedAsync())
await _plcService.ConnectAsync();
2026-05-15 21:10:42 +08:00
}
// 以下原有的 Confirm 命令保持不变,但可根据需要简化(因默认已勾选)
[RelayCommand] private void ConfirmBubbleRemoved() => BubbleRemoved = true;
[RelayCommand]
private void ConfirmClean()
{
if (string.IsNullOrWhiteSpace(CleanerName))
{
MessageBox.Show("请输入清洁人员姓名。", "提示");
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";
}
// ========== 新增命令系统校准章节A3 ==========
[RelayCommand]
private async Task PerformSystemCalibrationAsync()
{
if (IsCalibrating)
{
MessageBox.Show("校准正在进行中...", "提示");
return;
}
var result = MessageBox.Show($"将使用参考液 [{SelectedReferenceLiquid}] 进行系统校准。\n请确保传感器已浸入参考液中并已清除气泡。\n是否继续", "系统校准", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes) return;
IsCalibrating = true;
CalibrationStatus = "正在测量参考液...";
try
{
// 执行一次测量
if (!await _plcService.IsConnectedAsync())
await _plcService.ConnectAsync();
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, true);
2026-04-18 19:00:34 +08:00
await Task.Delay(500);
2026-05-15 20:39:11 +08:00
await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false);
2026-04-18 19:00:34 +08:00
await Task.Delay(2000);
float lambda = await _plcService.ReadFloatAsync(_config.PlcRegisterAddresses.ThermalConductivity);
MeasuredConductivity = lambda;
CalibrationErrorPercent = Math.Abs((lambda - ReferenceConductivity) / ReferenceConductivity * 100);
CalibrationStatus = $"测量值: {MeasuredConductivity:F4} W/m·K, 参考值: {ReferenceConductivity:F4} W/m·K, 误差: {CalibrationErrorPercent:F2}%";
if (CalibrationErrorPercent <= 2.0)
MessageBox.Show($"校准成功!误差 {CalibrationErrorPercent:F2}% 在允许范围内≤2%)。", "校准结果");
else
MessageBox.Show($"校准警告:误差 {CalibrationErrorPercent:F2}% 超出2%限值,请检查传感器或参考液。", "校准结果", MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
CalibrationStatus = $"校准失败: {ex.Message}";
MessageBox.Show($"校准失败: {ex.Message}", "错误");
}
finally
{
IsCalibrating = false;
}
2026-05-15 10:59:24 +08:00
2026-04-18 19:00:34 +08:00
}
}