3307 lines
123 KiB
C#
3307 lines
123 KiB
C#
using jiancaiburanxing;
|
||
using jiancaiburanxing.data;
|
||
using Microsoft.Win32;
|
||
using NModbus;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Timers;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Media;
|
||
using System.Windows.Threading;
|
||
|
||
namespace 建材不燃性试验炉
|
||
{
|
||
|
||
public partial class MainWindow : Window
|
||
{
|
||
#region 私有字段
|
||
private DispatcherTimer _statusTimer;
|
||
private DispatcherTimer _testTimer;
|
||
|
||
private bool _isTestRunning = false;
|
||
private bool _isCalibrating = false;
|
||
private BackgroundWorker _calibrationWorker;
|
||
|
||
//温度平衡相关
|
||
private DispatcherTimer _balanceTimer; // 平衡判断定时器(1秒触发一次)
|
||
private int _balanceElapsedSeconds; // 已计时秒数
|
||
private int _balanceTotalSeconds; // 总计时秒数(默认600秒)
|
||
private double _maxTempDiffThreshold; // 最大温差阈值(默认10°C)
|
||
private double _tempRangeThreshold; // 温度范围阈值(默认±2°C)
|
||
private List<double> _tempDataList; // 存储采集的温度数据
|
||
private double _maxTemp; // 采集周期内最高温度
|
||
private double _minTemp; // 采集周期内最低温度
|
||
private double _avgTemp; // 采集周期内平均温度
|
||
private bool _isBalanceChecking; // 是否正在进行平衡判断
|
||
|
||
|
||
|
||
//#region 试验结束温度平衡相关
|
||
//private DispatcherTimer _finalBalanceTimer;
|
||
//private List<double> _finalTempData;
|
||
//private bool _isFinalBalanceChecking;
|
||
//private double _finalInitialTemp;
|
||
//private double _finalMaxTemp;
|
||
//private double _finalFinalTemp;
|
||
//private TimeSpan _finalBalanceStartTime;
|
||
//#endregion
|
||
|
||
|
||
|
||
// Modbus相关
|
||
private TcpClient _tcpClient => Data.ModbusResourceManager.Instance?.TcpClient;
|
||
private IModbusMaster _modbusMaster => Data.ModbusResourceManager.Instance?.ModbusMaster;
|
||
|
||
Function ma;
|
||
DataChange c;
|
||
|
||
#endregion
|
||
|
||
#region 构造函数和初始化
|
||
public MainWindow()
|
||
{
|
||
InitializeComponent();
|
||
InitializeTimers();
|
||
InitializeControls();
|
||
|
||
InitializeBalanceVariables();//温度平衡相关变量
|
||
|
||
InitializeExperimentSystem();
|
||
//InitializeFinalBalance(); // 新增
|
||
//ResetFinalBalanceUI();
|
||
}
|
||
|
||
//private void InitializeBalanceVariables()
|
||
//{
|
||
// _tempDataList = new List<double>();
|
||
// _balanceTotalSeconds = 600;
|
||
// _maxTempDiffThreshold = 10;
|
||
// _tempRangeThreshold = 2;
|
||
// _isBalanceChecking = false;
|
||
//}
|
||
|
||
private void InitializeTimers()
|
||
{
|
||
// 状态更新定时器
|
||
_statusTimer = new DispatcherTimer();
|
||
_statusTimer.Interval = TimeSpan.FromSeconds(1);
|
||
_statusTimer.Tick += StatusTimer_Tick;
|
||
|
||
}
|
||
|
||
private void InitializeControls()
|
||
{
|
||
// 初始化重量计算
|
||
UpdateWeightCalculation();
|
||
|
||
|
||
//初始化温度平衡显示
|
||
InitializeBalanceDisplays();
|
||
|
||
}
|
||
|
||
private void InitializeBalanceDisplays()
|
||
{
|
||
txtBalanceTimeElapsed.Text = "0秒";
|
||
txtBalanceCondition.Text = "平衡条件: 炉内温度波动:±5℃10分钟 且 最大温差<10°C 且 温度漂移<2°C";
|
||
txtBalanceStatus.Text = "温度平衡状态: 未开始判断";
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Gray);
|
||
txtBalanceResult.Text = "未判断";
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 窗口加载事件
|
||
private void ThemedWindow_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
DisableFinalWeightInput();
|
||
|
||
UpdateStatusBar("系统加载完成,准备就绪");
|
||
|
||
// 连接Modbus设备(已注释,根据实际情况启用)
|
||
|
||
string plcIp = "192.168.1.10";
|
||
bool initSuccess = Data.ModbusResourceManager.Instance.Init(plcIp, 502);
|
||
if (!initSuccess)
|
||
{
|
||
MessageBox.Show("连接Modbus服务器失败!", "错误");
|
||
this.Close();
|
||
return;
|
||
}
|
||
|
||
// 检查连接状态
|
||
if (_tcpClient == null || !_tcpClient.Connected)
|
||
{
|
||
MessageBox.Show("Modbus连接异常!", "错误");
|
||
this.Close();
|
||
return;
|
||
}
|
||
|
||
c = new DataChange();
|
||
ma = new Function(_modbusMaster);
|
||
// 启动状态定时器
|
||
_statusTimer.Start();
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"初始化失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
this.Close();
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 定时器事件
|
||
private void StatusTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
// 更新当前时间
|
||
string currentTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||
|
||
// 温度更新(从Modbus读取)
|
||
UpdateSimulatedTemperatures();
|
||
|
||
// 更新系统状态
|
||
UpdateSystemStatus();
|
||
|
||
// 更新设备状态
|
||
UpdateEquipmentStatus();
|
||
|
||
// 更新按钮状态
|
||
UpdateTestFanStatus();
|
||
UpdateMoveUpStatus();
|
||
UpdateMoveDownStatus();
|
||
|
||
}
|
||
|
||
|
||
// 获取当前炉壁温度
|
||
private double GetFurnaceTemperature()
|
||
{
|
||
try
|
||
{
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1230, 2);//炉内温度显示
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
float modbusTemperature = c.UshortToFloat(registers[1], registers[0]);
|
||
return modbusTemperature;
|
||
}
|
||
return 0.0;
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取炉内温度失败: {ex.Message}");
|
||
return 0.0;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
private double GetSampleInnerTemperature()
|
||
{
|
||
try
|
||
{
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1280, 2);//样品内温度显示
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
float SampInTemp = c.UshortToFloat(registers[1], registers[0]);
|
||
return SampInTemp;
|
||
}
|
||
return 0.0;
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取样品内温度失败: {ex.Message}");
|
||
return 0.0;
|
||
}
|
||
}
|
||
|
||
private double GetSampleOuterTemperature()
|
||
{
|
||
try
|
||
{
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1330, 2);//样品外温度显示
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
float SampOutTemp = c.UshortToFloat(registers[1], registers[0]);
|
||
return SampOutTemp;
|
||
}
|
||
return 0.0;
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取样品内温度失败: {ex.Message}");
|
||
return 0.0;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 试验控制相关
|
||
//开始试验
|
||
private void btnStartTest_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
StartTest();
|
||
chkWeightConfirm.IsChecked = false;
|
||
|
||
_calculator.StartExperiment();
|
||
|
||
//更新系统状态
|
||
txtSystemStatus.Text = "试验中";
|
||
statusIndicator2.Fill = new SolidColorBrush(Colors.Red);
|
||
|
||
|
||
}
|
||
|
||
private void btnStop_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
EndTest();
|
||
|
||
var result = MessageBox.Show("确定要停止实验吗?",
|
||
"停止实验", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
// 手动停止实验并保存数据
|
||
_calculator.StopExperiment(false, "手动停止");
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//private void StopTest()
|
||
//{
|
||
// _isTestRunning = false;
|
||
|
||
// // 更新系统状态
|
||
// txtSystemStatus.Text = "试验结束";
|
||
// statusIndicator2.Fill = new SolidColorBrush(Colors.Green);
|
||
|
||
// // 提醒进行后称重
|
||
// Dispatcher.Invoke(() =>
|
||
// {
|
||
// var result = MessageBox.Show("试验已结束。\n\n请进行后称重,并将结果输入到'试验后重量'文本框中。",
|
||
// "后称重提醒",
|
||
// MessageBoxButton.OK, MessageBoxImage.Information);
|
||
// });
|
||
|
||
// UpdateStatusBar("试验结束");
|
||
//}
|
||
|
||
private void btnStartDevice_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ma?.BtnClickFunctionForNew(Function.ButtonType.切换型, 70);
|
||
}
|
||
|
||
private void chkWeightConfirm_Checked(object sender, RoutedEventArgs e)
|
||
{
|
||
SaveTestDataToReport();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 称重相关
|
||
private bool _isTestStopped = false;
|
||
|
||
// 开始测试
|
||
private void StartTest()
|
||
{
|
||
_isTestStopped = false;
|
||
// 禁用后称重输入
|
||
DisableFinalWeightInput();
|
||
}
|
||
|
||
// 停止测试
|
||
private void EndTest()
|
||
{
|
||
_isTestStopped = true;
|
||
// 启用后称重输入
|
||
EnableFinalWeightInput();
|
||
}
|
||
|
||
// 禁用后称重输入
|
||
private void DisableFinalWeightInput()
|
||
{
|
||
if (Dispatcher.CheckAccess())
|
||
{
|
||
txtFinalWeight.IsEnabled = false;
|
||
txtFinalWeight.Background = Brushes.LightGray;
|
||
txtFinalWeight.Text = "请输入试验后重量(g)";
|
||
}
|
||
else
|
||
{
|
||
Dispatcher.Invoke(() => DisableFinalWeightInput());
|
||
}
|
||
}
|
||
|
||
// 启用后称重输入
|
||
private void EnableFinalWeightInput()
|
||
{
|
||
if (Dispatcher.CheckAccess())
|
||
{
|
||
txtFinalWeight.IsEnabled = true;
|
||
txtFinalWeight.Background = Brushes.White;
|
||
txtFinalWeight.Text = "";
|
||
|
||
}
|
||
else
|
||
{
|
||
Dispatcher.Invoke(() => EnableFinalWeightInput());
|
||
}
|
||
}
|
||
|
||
private void txtFinalWeight_TextChanged(object sender, TextChangedEventArgs e)
|
||
{
|
||
// 如果测试未停止,阻止输入
|
||
if (!_isTestStopped)
|
||
{
|
||
if (Dispatcher.CheckAccess())
|
||
{
|
||
txtFinalWeight.Text = "请输入试验后重量(g)";
|
||
}
|
||
else
|
||
{
|
||
Dispatcher.Invoke(() => txtFinalWeight.Text = "请输入试验后重量(g)");
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 实时更新重量计算
|
||
UpdateWeightCalculation();
|
||
}
|
||
private void txtInitialWeight_TextChanged(object sender, TextChangedEventArgs e)
|
||
{
|
||
// 实时更新重量计算
|
||
UpdateWeightCalculation();
|
||
|
||
}
|
||
|
||
private void UpdateWeightCalculation()
|
||
{
|
||
try
|
||
{
|
||
double initialWeight = 0;
|
||
double finalWeight = 0;
|
||
bool hasInitialWeight = false;
|
||
bool hasFinalWeight = false;
|
||
|
||
// 解析前称重
|
||
string initialText = txtInitialWeight.Text;
|
||
if (!string.IsNullOrEmpty(initialText) && initialText != "请输入试验前重量(g)")
|
||
{
|
||
if (double.TryParse(initialText, out initialWeight) && initialWeight >= 0.1)
|
||
{
|
||
hasInitialWeight = true;
|
||
}
|
||
}
|
||
|
||
// 解析后称重
|
||
string finalText = txtFinalWeight.Text;
|
||
if (!string.IsNullOrEmpty(finalText) && finalText != "请输入试验后重量(g)")
|
||
{
|
||
if (double.TryParse(finalText, out finalWeight) && finalWeight >= 0.1)
|
||
{
|
||
hasFinalWeight = true;
|
||
}
|
||
}
|
||
|
||
// 计算重量损失
|
||
double weightLoss = 0;
|
||
double weightLossPercentage = 0;
|
||
string weightLossText = "";
|
||
string weightStatus = "";
|
||
Brush weightStatusColor = Brushes.Gray;
|
||
|
||
if (hasInitialWeight)
|
||
{
|
||
if (hasFinalWeight)
|
||
{
|
||
weightLoss = initialWeight - finalWeight;
|
||
weightLossPercentage = (weightLoss / initialWeight) * 100;
|
||
weightLossText = $"损失:{weightLossPercentage:F1}%";
|
||
weightStatus = "已称重";
|
||
|
||
// 根据损失百分比设置颜色
|
||
if (weightLossPercentage > 50)
|
||
{
|
||
weightStatusColor = Brushes.Red;
|
||
}
|
||
else if (weightLossPercentage > 20)
|
||
{
|
||
weightStatusColor = Brushes.Orange;
|
||
}
|
||
else if (weightLossPercentage >= 0)
|
||
{
|
||
weightStatusColor = Brushes.Green;
|
||
}
|
||
else
|
||
{
|
||
// 重量增加的情况
|
||
weightStatusColor = Brushes.Purple;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
weightLossText = "等待后称重";
|
||
weightStatus = "前称重完成";
|
||
weightStatusColor = Brushes.Orange;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
weightLossText = "未称重";
|
||
weightStatus = "未称重";
|
||
weightStatusColor = Brushes.Gray;
|
||
}
|
||
|
||
// 更新显示
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
string initialDisplay = hasInitialWeight ? $"{initialWeight:F1}g" : "未输入";
|
||
string finalDisplay = hasFinalWeight ? $"{finalWeight:F1}g" : "未输入";
|
||
txtCurrentWeight.Text = $"前:{initialDisplay} | 后:{finalDisplay}";
|
||
txtWeightLossDisplay.Text = weightLossText;
|
||
txtWeightState.Text = weightStatus;
|
||
weightStatusBorder.Background = weightStatusColor;
|
||
|
||
// 更新详细文本
|
||
if (hasInitialWeight && hasFinalWeight)
|
||
{
|
||
string lossDescription = weightLoss > 0 ? "减少" :
|
||
weightLoss < 0 ? "增加" : "无变化";
|
||
txtWeightLoss.Text = $"重量{lossDescription}: {Math.Abs(weightLoss):F1}g ({Math.Abs(weightLossPercentage):F1}%)";
|
||
txtWeightStatus.Text = weightLoss > 0 ? "试样质量减少" :
|
||
weightLoss < 0 ? "试样质量增加" : "试样质量无变化";
|
||
}
|
||
else if (hasInitialWeight)
|
||
{
|
||
txtWeightLoss.Text = "等待后称重数据";
|
||
txtWeightStatus.Text = "";
|
||
}
|
||
else
|
||
{
|
||
txtWeightLoss.Text = "";
|
||
txtWeightStatus.Text = "";
|
||
}
|
||
});
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"重量计算错误: {ex.Message}");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 燃烧状态相关
|
||
private void chkHasFlame_Checked(object sender, RoutedEventArgs e)
|
||
{
|
||
UpdateCombustionStatus();
|
||
}
|
||
|
||
private void chkHasFlame_Unchecked(object sender, RoutedEventArgs e)
|
||
{
|
||
UpdateCombustionStatus();
|
||
}
|
||
|
||
private void chkHasSmoke_Checked(object sender, RoutedEventArgs e)
|
||
{
|
||
UpdateCombustionStatus();
|
||
}
|
||
|
||
private void chkHasSmoke_Unchecked(object sender, RoutedEventArgs e)
|
||
{
|
||
UpdateCombustionStatus();
|
||
}
|
||
|
||
private void txtCombustionNote_TextChanged(object sender, TextChangedEventArgs e)
|
||
{
|
||
SyncCombustionNoteToFireTime();
|
||
}
|
||
|
||
private void UpdateCombustionStatus()
|
||
{
|
||
string flameStatus = chkHasFlame.IsChecked == true ? "有火焰" : "无火焰";
|
||
string smokeStatus = chkHasSmoke.IsChecked == true ? "有冒烟" : "无冒烟";
|
||
|
||
texfire.Text = $"{flameStatus}";
|
||
SyncCombustionNoteToFireTime();
|
||
|
||
texsmoking.Text = $"{smokeStatus}";
|
||
}
|
||
|
||
private void SyncCombustionNoteToFireTime()
|
||
{
|
||
if (txtbfiretime != null && txtCombustionNote != null)
|
||
{
|
||
|
||
// 或者只在有内容时显示:
|
||
if (!string.IsNullOrWhiteSpace(txtCombustionNote.Text))
|
||
{
|
||
txtbfiretime.Text = $"火焰持续时间(s): {txtCombustionNote.Text}";
|
||
}
|
||
else
|
||
{
|
||
txtbfiretime.Text = "";
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 温度监控相关
|
||
|
||
private void UpdateSimulatedTemperatures()
|
||
{
|
||
try
|
||
{
|
||
// 检查Modbus连接状态
|
||
if (_modbusMaster == null)
|
||
{
|
||
Debug.WriteLine("ModbusMaster为空,跳过更新");
|
||
return;
|
||
}
|
||
|
||
// 检查TCP连接状态
|
||
if (_tcpClient == null || !_tcpClient.Connected)
|
||
{
|
||
Debug.WriteLine("TCP连接断开,跳过更新");
|
||
return;
|
||
}
|
||
|
||
// 读取三个温度值
|
||
float TempSet = 0, furnaceTemp = 0, sampleInternalTemp = 0, sampleSurfaceTemp = 0;
|
||
//炉内温度设定
|
||
ReadAndUpdateRegister(350, 2, value =>
|
||
{
|
||
TempSet = value;
|
||
Dispatcher.Invoke(() => txtTestTemp.Text = $"{value:F0}°C");
|
||
});
|
||
|
||
// 炉内温度相关 (D1226, D1230)
|
||
ReadAndUpdateRegister(1230, 2, value =>
|
||
{
|
||
furnaceTemp = value;
|
||
Dispatcher.Invoke(() => txtFurnaceWallTemp.Text = $"{value:F1}°C");
|
||
});
|
||
|
||
// 样品内温度相关 (D1280)
|
||
ReadAndUpdateRegister(1280, 2, value =>
|
||
{
|
||
sampleInternalTemp = value;
|
||
Dispatcher.Invoke(() => txtSampleCenterTemp.Text = $"{value:F1}°C");
|
||
});
|
||
|
||
// 样品外温度相关 (D1330)
|
||
ReadAndUpdateRegister(1330, 2, value =>
|
||
{
|
||
sampleSurfaceTemp = value;
|
||
Dispatcher.Invoke(() => txtSampleSurfaceTemp.Text = $"{value:F1}°C");
|
||
});
|
||
|
||
// 检查温度波动
|
||
CheckAllTemperatureFluctuations(furnaceTemp, sampleInternalTemp, sampleSurfaceTemp);
|
||
}
|
||
catch (InvalidOperationException ioex)
|
||
{
|
||
Debug.WriteLine($"Modbus通信错误:{ioex.Message}");
|
||
}
|
||
catch (IOException ioex)
|
||
{
|
||
Debug.WriteLine($"IO错误:{ioex.Message}");
|
||
HandleConnectionLost();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新数据时发生未知错误:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void HandleConnectionLost()
|
||
{
|
||
try
|
||
{
|
||
// 停止定时器
|
||
_statusTimer?.Stop();
|
||
|
||
// 显示连接错误
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
MessageBox.Show("Modbus连接丢失,请检查网络连接!", "连接错误",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"处理连接丢失时出错:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void ReadAndUpdateRegister(int address, int count, Action<float> updateAction)
|
||
{
|
||
try
|
||
{
|
||
// 安全检查
|
||
if (_modbusMaster == null || c == null)
|
||
{
|
||
Debug.WriteLine($"ModbusMaster或DataChange为空,地址{address}跳过");
|
||
return;
|
||
}
|
||
|
||
// 读取寄存器
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, (ushort)address, (ushort)count);
|
||
|
||
// 空值检查
|
||
if (registers == null || registers.Length < count)
|
||
{
|
||
Debug.WriteLine($"读取地址{address}返回空值或长度不足");
|
||
return;
|
||
}
|
||
|
||
// 转换为浮点数
|
||
float value = c.UshortToFloat(registers[1], registers[0]);
|
||
updateAction?.Invoke(value);
|
||
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
//private void UpdateTemperatureDisplay(double furnaceWallTemp, double centerTemp, double surfaceTemp)
|
||
//{
|
||
// Dispatcher.Invoke(() =>
|
||
// {
|
||
// txtFurnaceWallTemp.Text = $"{furnaceWallTemp:F1}°C";
|
||
// txtSampleCenterTemp.Text = $"{centerTemp:F1}°C";
|
||
// txtSampleSurfaceTemp.Text = $"{surfaceTemp:F1}°C";
|
||
|
||
// // 检查温度波动
|
||
// CheckTemperatureFluctuation(furnaceWallTemp);
|
||
// });
|
||
//}
|
||
|
||
private void CheckAllTemperatureFluctuations(float furnaceTemp, float sampleInternalTemp, float sampleSurfaceTemp)
|
||
{
|
||
try
|
||
{
|
||
// 获取允许的波动范围
|
||
if (!double.TryParse(txtTempFluctuation.Text, out double allowedFluctuation))
|
||
{
|
||
allowedFluctuation = 5.0; // 默认值
|
||
}
|
||
|
||
// 获取目标温度
|
||
//if (!double.TryParse(txtTestTemp.Text, out double targetTemp))
|
||
//{
|
||
// targetTemp = 0;
|
||
//}
|
||
|
||
// 获取目标温度
|
||
if (!double.TryParse(txtTestTemp.Text.Replace("°C", ""), out double targetTemp))
|
||
{
|
||
targetTemp = 0;
|
||
}
|
||
|
||
// 检查炉内温度波动
|
||
CheckSingleTemperature("炉内", furnaceTemp, targetTemp, allowedFluctuation,
|
||
txtFurnaceTempStatus);
|
||
|
||
//// 检查样品内部温度波动
|
||
//CheckSingleTemperature("样品中心", sampleInternalTemp, targetTemp, allowedFluctuation,
|
||
// txtSampleInternalTempStatus);
|
||
|
||
//// 检查样品表面温度波动
|
||
//CheckSingleTemperature("样品表面", sampleSurfaceTemp, targetTemp, allowedFluctuation,
|
||
// txtSampleSurfaceTempStatus);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"检查温度波动时出错:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void CheckSingleTemperature(string tempName, double currentTemp, double targetTemp,
|
||
double allowedFluctuation, TextBlock statusTextBlock)
|
||
{
|
||
try
|
||
{
|
||
// 计算实际波动
|
||
double actualFluctuation = Math.Abs(currentTemp - targetTemp);
|
||
|
||
// 确定状态和颜色
|
||
string statusText;
|
||
string backgroundColor;
|
||
|
||
statusText = $"{tempName}温度波动 (±{actualFluctuation:F1}°C)";
|
||
backgroundColor = "#27AE60"; // 绿色 - 正常
|
||
|
||
|
||
// 更新UI
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
statusTextBlock.Text = statusText;
|
||
|
||
Border border = statusTextBlock.Parent as Border;
|
||
if (border != null)
|
||
{
|
||
border.Background = new SolidColorBrush(
|
||
(Color)ColorConverter.ConvertFromString(backgroundColor));
|
||
}
|
||
});
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"检查{tempName}温度波动时出错:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 系统状态更新
|
||
private void UpdateSystemStatus()
|
||
{
|
||
|
||
if (_tcpClient == null || _tcpClient.Connected)
|
||
{
|
||
txtStatusBar.Text = $"系统就绪 | {DateTime.Now:HH:mm:ss}";
|
||
}
|
||
else
|
||
{
|
||
txtStatusBar.Text = $"连接断开 | {DateTime.Now:HH:mm:ss}";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 更新设备状态
|
||
|
||
private void UpdateEquipmentStatus()
|
||
{
|
||
try
|
||
{
|
||
// 快速失败检查
|
||
if (_modbusMaster == null || _tcpClient == null || !_tcpClient.Connected)
|
||
{
|
||
SetDeviceStatusUI("设备未连接", "启动设备", Brushes.Gray);
|
||
return;
|
||
}
|
||
|
||
// 读取设备状态
|
||
bool[] isStart;
|
||
try
|
||
{
|
||
isStart = _modbusMaster.ReadCoils(1, 70, 1);
|
||
}
|
||
catch
|
||
{
|
||
SetDeviceStatusUI("读取失败", "启动设备", Brushes.Orange);
|
||
return;
|
||
}
|
||
|
||
// 验证数据
|
||
if (isStart == null || isStart.Length == 0)
|
||
{
|
||
SetDeviceStatusUI("状态未知", "启动设备", Brushes.Orange);
|
||
return;
|
||
}
|
||
|
||
// 更新UI
|
||
bool deviceStatus = isStart[0];
|
||
if (deviceStatus)
|
||
{
|
||
SetDeviceStatusUI("设备运行中", "运行中", Brushes.LimeGreen);
|
||
}
|
||
else
|
||
{
|
||
SetDeviceStatusUI("设备未启动", "启动设备", Brushes.Gray);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新设备状态时发生错误:{ex.GetType().Name}: {ex.Message}");
|
||
SetDeviceStatusUI("状态异常", "启动设备", ex is IOException ? Brushes.Red : Brushes.Orange);
|
||
}
|
||
}
|
||
|
||
private void SetDeviceStatusUI(string statusText, string buttonText, Brush statusColor)
|
||
{
|
||
try
|
||
{
|
||
// 检查UI控件是否为空
|
||
if (txtDeviceStatus == null || btnStartDevice == null || statusIndicator == null)
|
||
{
|
||
Debug.WriteLine("UI控件为空,无法更新状态");
|
||
return;
|
||
}
|
||
|
||
// 确保在UI线程上执行
|
||
if (Dispatcher.CheckAccess())
|
||
{
|
||
// 当前线程是UI线程
|
||
txtDeviceStatus.Text = statusText;
|
||
btnStartDevice.Content = buttonText;
|
||
statusIndicator.Background = statusColor;
|
||
|
||
}
|
||
else
|
||
{
|
||
// 从非UI线程调用
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
txtDeviceStatus.Text = statusText;
|
||
btnStartDevice.Content = buttonText;
|
||
statusIndicator.Background = statusColor;
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新UI状态时发生错误:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region 更新按钮状态
|
||
private void UpdateButtonStatus(byte slaveId, ushort coilAddress, Button button, string activeText, string inactiveText)
|
||
{
|
||
try
|
||
{
|
||
// 快速失败检查
|
||
if (_modbusMaster == null || _tcpClient == null || !_tcpClient.Connected) return;
|
||
|
||
// 读取线圈状态
|
||
bool[] coilStatus;
|
||
try
|
||
{
|
||
// 假设参数是:slaveId, startAddress, numberOfPoints
|
||
coilStatus = _modbusMaster.ReadCoils(slaveId, coilAddress, 1);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取线圈地址 {coilAddress} (slaveId: {slaveId}) 时失败: {ex.Message}");
|
||
return;
|
||
}
|
||
|
||
// 验证数据
|
||
if (coilStatus == null || coilStatus.Length == 0)
|
||
{
|
||
Debug.WriteLine($"线圈地址 {coilAddress} 返回数据为空");
|
||
return;
|
||
}
|
||
|
||
// 更新UI
|
||
bool isActive = coilStatus[0];
|
||
string buttonText = isActive ? activeText : inactiveText;
|
||
|
||
Debug.WriteLine($"线圈地址 {coilAddress} 状态: {isActive}, 更新文本: {buttonText}");
|
||
UpdateButtonText(button, buttonText);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新线圈状态时发生错误:{ex.GetType().Name}: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void UpdateTestFanStatus()
|
||
{
|
||
UpdateButtonStatus(1, 0, btnTestFan, "腔体风机工作中", "测试腔体风机");
|
||
}
|
||
|
||
private void UpdateMoveUpStatus()
|
||
{
|
||
UpdateButtonStatus(1, 1, btnMoveUp, "上升中", "上升");
|
||
}
|
||
|
||
private void UpdateMoveDownStatus()
|
||
{
|
||
UpdateButtonStatus(1, 2, btnMoveDown, "下降中", "下降");
|
||
}
|
||
|
||
private void UpdateButtonText(Button button, string buttonText)
|
||
{
|
||
try
|
||
{
|
||
// 检查UI控件是否为空
|
||
if (button == null)
|
||
{
|
||
Debug.WriteLine("按钮控件为空,无法更新状态");
|
||
return;
|
||
}
|
||
|
||
// 确保在UI线程上执行
|
||
if (button.Dispatcher.CheckAccess())
|
||
{
|
||
Debug.WriteLine($"直接更新按钮文本: {buttonText}");
|
||
button.Content = buttonText;
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"通过Dispatcher更新按钮文本: {buttonText}");
|
||
button.Dispatcher.Invoke(() =>
|
||
{
|
||
button.Content = buttonText;
|
||
});
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新按钮状态时发生错误:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 其他按钮事件
|
||
|
||
private void btnClearData_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var result = MessageBox.Show("确定要清空所有数据吗?", "确认",
|
||
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
ClearAllData();
|
||
UpdateStatusBar("数据已清空");
|
||
}
|
||
}
|
||
|
||
private void ClearAllData()
|
||
{
|
||
// 清空文本框
|
||
txtSampleName.Text = "";
|
||
txtSampleSpec.Text = "";
|
||
txtTestNo.Text = "";
|
||
txtInitialWeight.Text = "";
|
||
txtFinalWeight.Text = "";
|
||
txtTestTemp.Text = "750";
|
||
txtTempFluctuation.Text = "5";
|
||
txtCombustionNote.Text = "";
|
||
|
||
// 重置复选框
|
||
chkHasFlame.IsChecked = false;
|
||
chkHasSmoke.IsChecked = false;
|
||
|
||
|
||
// 重置状态显示
|
||
txtCurrentWeight.Text = "前:0.0g | 后:0.0g";
|
||
txtWeightLossDisplay.Text = "损失:0.0%";
|
||
txtWeightState.Text = "未称重";
|
||
weightStatusBorder.Background = Brushes.Gray;
|
||
|
||
|
||
}
|
||
|
||
//测试风机
|
||
private void btnTestFan_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ma?.BtnClickFunctionForNew(Function.ButtonType.切换型, 0);
|
||
}
|
||
//上升
|
||
private void btnMoveUp_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ma?.BtnClickFunctionForNew(Function.ButtonType.切换型, 1);
|
||
}
|
||
|
||
//下降
|
||
private void btnMoveDown_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ma?.BtnClickFunctionForNew(Function.ButtonType.切换型, 2);
|
||
}
|
||
#endregion
|
||
|
||
#region 辅助方法
|
||
private void UpdateStatusBar(string message)
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
txtStatusBar.Text = $"{DateTime.Now:HH:mm:ss} - {message}";
|
||
});
|
||
}
|
||
|
||
// 辅助方法 - 安全获取值(保留,用于类型转换)
|
||
private decimal GetDecimalValue(string text)
|
||
{
|
||
if (decimal.TryParse(text, out decimal result))
|
||
{
|
||
return result;
|
||
}
|
||
return 0m;
|
||
}
|
||
|
||
private double GetDoubleValue(string text)
|
||
{
|
||
if (double.TryParse(text, out double result))
|
||
{
|
||
return result;
|
||
}
|
||
return 0;
|
||
}
|
||
#endregion
|
||
|
||
#region 窗口关闭事件
|
||
private void Window_Closing(object sender, CancelEventArgs e)
|
||
{
|
||
base.OnClosing(e);
|
||
|
||
// 停止所有定时器
|
||
_statusTimer.Stop();
|
||
_testTimer.Stop();
|
||
_balanceTimer.Stop();
|
||
|
||
|
||
// 如果有试验在进行,询问是否保存
|
||
if (_isTestRunning)
|
||
{
|
||
var result = MessageBox.Show("试验正在进行中,确定要退出吗?", "确认退出",
|
||
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
|
||
if (result == MessageBoxResult.No)
|
||
{
|
||
e.Cancel = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 系数报表按钮相关
|
||
|
||
private CofficientSetting _cofficientSetting;
|
||
private DispatcherTimer _longPressTimer;
|
||
|
||
private void Button_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
// 检查是否勾选了重量确认
|
||
if (!chkWeightConfirm.IsChecked ?? false)
|
||
{
|
||
// 显示提示信息
|
||
MessageBox.Show("请先进行重量确认", "提示",
|
||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 使用异步避免阻塞
|
||
Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
// 创建窗口但不立即初始化
|
||
var TestReportWindow = new TestReportWindow();
|
||
|
||
TestReportWindow.Owner = this;
|
||
TestReportWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||
|
||
// 延迟显示
|
||
TestReportWindow.Show();
|
||
|
||
UpdateStatusBar("打开数据报表");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"无法打开数据报表: {ex.Message}", "错误");
|
||
}
|
||
}), DispatcherPriority.Background);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"打开报表窗口失败: {ex.Message}", "错误");
|
||
}
|
||
}
|
||
|
||
private void btnCoefficient_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||
{
|
||
// 启动长按计时器(1秒)
|
||
_longPressTimer = new DispatcherTimer();
|
||
_longPressTimer.Interval = TimeSpan.FromSeconds(1);
|
||
_longPressTimer.Tick += (s, args) =>
|
||
{
|
||
_longPressTimer.Stop();
|
||
SwitchWindow(ref _cofficientSetting, () => new CofficientSetting());
|
||
};
|
||
_longPressTimer.Start();
|
||
}
|
||
|
||
private void btnCoefficient_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||
{
|
||
// 释放鼠标时停止计时器
|
||
_longPressTimer?.Stop();
|
||
}
|
||
|
||
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
|
||
{
|
||
// 1. 停止当前窗口的定时器(不释放资源)
|
||
_statusTimer?.Stop();
|
||
|
||
// 2. 复用窗口实例:不存在则创建,存在则激活
|
||
if (windowInstance == null)
|
||
{
|
||
windowInstance = createFunc();
|
||
|
||
// 添加窗口关闭事件处理
|
||
windowInstance.Closed += (s, args) =>
|
||
{
|
||
// 窗口关闭时重新启动定时器并显示当前窗口
|
||
_statusTimer?.Start();
|
||
this.Show();
|
||
this.Activate();
|
||
};
|
||
}
|
||
else
|
||
{
|
||
// 激活已存在的窗口(前置显示)
|
||
windowInstance.Activate();
|
||
return;
|
||
}
|
||
|
||
// 3. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
|
||
this.Hide();
|
||
windowInstance.Show();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 温度平衡相关
|
||
|
||
// 添加全局变量来存储温度平衡结果
|
||
private double _balanceAverageTemp = 0.0; // 存储平衡平均温度
|
||
private double _startavgTemp = 0.0; // 实验开始时的平衡温度
|
||
|
||
// 开始平衡判断按钮点击事件
|
||
private void btnStartBalanceCheck_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (_isBalanceChecking)
|
||
{
|
||
MessageBox.Show("温度平衡判断正在进行中,请等待完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 初始化平衡判断变量
|
||
InitializeBalanceVariables();
|
||
|
||
// 2. 重置UI状态
|
||
ResetBalanceUI();
|
||
|
||
// 3. 开始平衡判断
|
||
StartBalanceCheck();
|
||
|
||
// 4. 打开平衡曲线图
|
||
OpenBalanceChartWindow();
|
||
|
||
UpdateStatusBar("开始温度平衡判断,严格按照GB/T 5464-2010标准执行");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"启动平衡判断失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
ResetBalanceUI();
|
||
}
|
||
}
|
||
|
||
#region 温度平衡核心方法
|
||
private void InitializeBalanceVariables()
|
||
{
|
||
// 标准7.2.4:平衡时间为10分钟
|
||
_balanceTotalSeconds = 600; // 600秒 = 10分钟
|
||
_balanceElapsedSeconds = 0;
|
||
|
||
// 标准参数:温度平均值750±5℃,最大偏差10℃,漂移2℃
|
||
_maxTempDiffThreshold = 10; // 10℃(标准7.2.4:相对平均温度的最大偏差不超过10℃)
|
||
_tempRangeThreshold = 2; // 2℃(标准7.2.4:温度漂移不超过2℃)
|
||
_targetTemperature = 750; // 标准目标温度
|
||
|
||
_tempDataList = new List<double>();
|
||
// 清空数据列表
|
||
_tempDataList?.Clear();
|
||
|
||
// 平衡统计变量
|
||
_isBalanceChecking = false;
|
||
_balanceStatistics = new BalanceStatistics();
|
||
}
|
||
|
||
private void ResetBalanceUI()
|
||
{
|
||
txtBalanceTimeElapsed.Text = "0秒";
|
||
txtBalanceCondition.Text = "平衡条件: 炉温(750±5)℃ 10分钟, 最大温差<10℃, 漂移<2℃";
|
||
txtBalanceStatus.Text = "温度平衡状态: 判断中...";
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Gray);
|
||
txtBalanceResult.Text = "判断中";
|
||
|
||
}
|
||
|
||
private void StartBalanceCheck()
|
||
{
|
||
|
||
// 创建新的平衡判断定时器(每秒检查一次)
|
||
_balanceTimer = new DispatcherTimer();
|
||
_balanceTimer.Interval = TimeSpan.FromSeconds(1);
|
||
_balanceTimer.Tick += BalanceTimer_Tick_Standard;
|
||
|
||
// 启动定时器
|
||
_balanceTimer.Start();
|
||
_isBalanceChecking = true;
|
||
}
|
||
|
||
private void BalanceTimer_Tick_Standard(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 1. 计时递增
|
||
_balanceElapsedSeconds++;
|
||
UpdateBalanceTimeDisplay();
|
||
|
||
// 2. 采集当前炉内温度(标准7.4.6要求记录炉内温度)
|
||
double currentTemp = GetCurrentFurnaceTemperature();
|
||
|
||
// 3. 记录温度数据
|
||
RecordTemperatureData(currentTemp);
|
||
|
||
// 4. 每10秒检查一次平衡状态
|
||
if (_balanceElapsedSeconds % 10 == 0)
|
||
{
|
||
CheckBalanceStatus();
|
||
}
|
||
|
||
// 5. 达到10分钟时进行最终判断
|
||
if (_balanceElapsedSeconds >= _balanceTotalSeconds)
|
||
{
|
||
CompleteBalanceCheck();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"温度平衡判断出错:{ex.Message}");
|
||
HandleBalanceError(ex);
|
||
}
|
||
}
|
||
|
||
private double GetCurrentFurnaceTemperature()
|
||
{
|
||
try
|
||
{
|
||
// 标准4.4.3:炉内热电偶位于加热炉管高度的中点,距管壁(10±0.5)mm
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1230, 2);
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
return c.UshortToFloat(registers[1], registers[0]);
|
||
}
|
||
return 0.0;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"读取炉内温度失败: {ex.Message}");
|
||
return 0.0;
|
||
}
|
||
}
|
||
|
||
private void RecordTemperatureData(double temperature)
|
||
{
|
||
// 记录当前温度
|
||
_tempDataList?.Add(temperature);
|
||
|
||
// 保持最近600秒的数据(10分钟)
|
||
if (_tempDataList?.Count > _balanceTotalSeconds)
|
||
{
|
||
_tempDataList?.RemoveAt(0);
|
||
}
|
||
}
|
||
|
||
private void CheckBalanceStatus()
|
||
{
|
||
if (_tempDataList?.Count < 60) // 至少需要1分钟数据
|
||
return;
|
||
|
||
// 计算统计指标
|
||
CalculateBalanceStatistics();
|
||
|
||
// 更新UI显示当前状态
|
||
UpdateBalanceDisplay();
|
||
|
||
// 检查是否符合标准条件
|
||
CheckStandardConditions();
|
||
}
|
||
|
||
private void CalculateBalanceStatistics()
|
||
{
|
||
if (_tempDataList?.Count == 0) return;
|
||
|
||
double sum = 0;
|
||
double min = double.MaxValue;
|
||
double max = double.MinValue;
|
||
|
||
foreach (var temp in _tempDataList)
|
||
{
|
||
sum += temp;
|
||
if (temp < min) min = temp;
|
||
if (temp > max) max = temp;
|
||
}
|
||
|
||
_balanceStatistics.AverageTemp = sum / _tempDataList.Count;
|
||
_balanceStatistics.MinTemp = min;
|
||
_balanceStatistics.MaxTemp = max;
|
||
_balanceStatistics.TempRange = max - min;
|
||
|
||
// 计算线性回归漂移(标准要求)
|
||
CalculateLinearRegression();
|
||
}
|
||
|
||
private void CalculateLinearRegression()
|
||
{
|
||
if (_tempDataList.Count < 10) return;
|
||
|
||
// 取最近10分钟的数据进行线性回归分析
|
||
int n = Math.Min(_tempDataList.Count, 600);
|
||
List<double> recentTemps = _tempDataList.Skip(_tempDataList.Count - n).Take(n).ToList();
|
||
|
||
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
|
||
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
double x = i;
|
||
double y = recentTemps[i];
|
||
sumX += x;
|
||
sumY += y;
|
||
sumXY += x * y;
|
||
sumX2 += x * x;
|
||
}
|
||
|
||
double denominator = n * sumX2 - sumX * sumX;
|
||
if (Math.Abs(denominator) > 0.0001)
|
||
{
|
||
_balanceStatistics.LinearSlope = (n * sumXY - sumX * sumY) / denominator;
|
||
|
||
_balanceStatistics.TenMinuteDrift = Math.Abs(_balanceStatistics.LinearSlope * 600);
|
||
}
|
||
}
|
||
|
||
private void CheckStandardConditions()
|
||
{
|
||
// 标准条件1:平均温度在750±5℃范围内
|
||
bool condition1 = (_balanceStatistics.AverageTemp >= 745 &&
|
||
_balanceStatistics.AverageTemp <= 755);
|
||
|
||
// 标准条件2:最大温差不超过10℃(最高与平均的偏差)
|
||
double maxDeviation = Math.Max(
|
||
Math.Abs(_balanceStatistics.MaxTemp - _balanceStatistics.AverageTemp),
|
||
Math.Abs(_balanceStatistics.MinTemp - _balanceStatistics.AverageTemp)
|
||
);
|
||
bool condition2 = maxDeviation <= 10;
|
||
|
||
// 标准条件3:10分钟内温度漂移不超过2℃
|
||
bool condition3 = _balanceStatistics.TenMinuteDrift <= 2;
|
||
|
||
// 更新平衡状态
|
||
_balanceStatistics.IsBalanced = condition1 && condition2 && condition3;
|
||
|
||
// 保存条件结果
|
||
_balanceStatistics.Condition1 = condition1;
|
||
_balanceStatistics.Condition2 = condition2;
|
||
_balanceStatistics.Condition3 = condition3;
|
||
}
|
||
|
||
private void UpdateBalanceDisplay()
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
// 更新计时显示
|
||
int minutes = _balanceElapsedSeconds / 60;
|
||
int seconds = _balanceElapsedSeconds % 60;
|
||
txtBalanceTimeElapsed.Text = $"{minutes:D2}:{seconds:D2}";
|
||
|
||
// 更新条件显示
|
||
string conditionText = $"平衡条件: 炉温({_balanceStatistics.AverageTemp:F1}℃) " +
|
||
$"最大温差:{Math.Max(Math.Abs(_balanceStatistics.MaxTemp - _balanceStatistics.AverageTemp), Math.Abs(_balanceStatistics.MinTemp - _balanceStatistics.AverageTemp)):F1}℃ " +
|
||
$"漂移:{_balanceStatistics.TenMinuteDrift:F2}℃";
|
||
txtBalanceCondition.Text = conditionText;
|
||
|
||
// 更新状态文本
|
||
txtBalanceStatus.Text = $"温度平衡状态: 采集{_tempDataList.Count}个数据点";
|
||
|
||
// 更新结果指示
|
||
if (_balanceElapsedSeconds < _balanceTotalSeconds)
|
||
{
|
||
txtBalanceResult.Text = "采集中...";
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Orange);
|
||
}
|
||
});
|
||
}
|
||
|
||
private void CompleteBalanceCheck()
|
||
{
|
||
// 停止定时器
|
||
_balanceTimer.Stop();
|
||
_isBalanceChecking = false;
|
||
|
||
// 进行最终计算
|
||
CalculateBalanceStatistics();
|
||
CheckStandardConditions();
|
||
|
||
// 只有当达到平衡标准时才计算并保存平均值
|
||
if (_balanceStatistics.IsBalanced)
|
||
{
|
||
// 保存平衡平均温度到全局变量
|
||
_balanceAverageTemp = _balanceStatistics.AverageTemp;
|
||
_modbusMaster.WriteSingleCoil(1, 71, true);
|
||
|
||
}
|
||
else
|
||
{
|
||
// 如果未达到平衡标准,不保存温度值
|
||
_balanceAverageTemp = 0.0;
|
||
|
||
}
|
||
|
||
// 显示最终结果
|
||
DisplayFinalResult();
|
||
|
||
// 记录日志
|
||
LogBalanceResult();
|
||
|
||
UpdateStatusBar($"温度平衡判断完成: {(_balanceStatistics.IsBalanced ? "达到标准" : "未达到标准")}");
|
||
}
|
||
|
||
private void DisplayFinalResult()
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
if (_balanceStatistics.IsBalanced)
|
||
{
|
||
// 达到平衡标准
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Green);
|
||
txtBalanceResult.Text = "已平衡";
|
||
txtBalanceStatus.Text = $"温度平衡状态: 达到GB/T 5464-2010标准\n" +
|
||
$"平均温度: {_balanceStatistics.AverageTemp:F1}℃\n" +
|
||
$"最大温差: {Math.Max(Math.Abs(_balanceStatistics.MaxTemp - _balanceStatistics.AverageTemp), Math.Abs(_balanceStatistics.MinTemp - _balanceStatistics.AverageTemp)):F1}℃\n" +
|
||
$"10分钟漂移: {_balanceStatistics.TenMinuteDrift:F2}℃";
|
||
}
|
||
else
|
||
{
|
||
// 未达到平衡标准
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Red);
|
||
txtBalanceResult.Text = "未平衡";
|
||
|
||
string failureReasons = "未满足条件:";
|
||
if (!_balanceStatistics.Condition1)
|
||
failureReasons += " 平均温度不在745-755℃范围";
|
||
if (!_balanceStatistics.Condition2)
|
||
failureReasons += " 最大温差>10℃";
|
||
if (!_balanceStatistics.Condition3)
|
||
failureReasons += " 10分钟漂移<2℃";
|
||
|
||
txtBalanceStatus.Text = $"温度平衡状态: 未达到标准\n{failureReasons}";
|
||
}
|
||
});
|
||
}
|
||
|
||
private void LogBalanceResult()
|
||
{
|
||
try
|
||
{
|
||
string logContent = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 温度平衡判断结果\n" +
|
||
$"执行标准: GB/T 5464-2010\n" +
|
||
$"判断时长: {_balanceElapsedSeconds}秒\n" +
|
||
$"平均温度: {_balanceStatistics.AverageTemp:F1}℃\n" +
|
||
$"温度范围: {_balanceStatistics.MinTemp:F1}℃ ~ {_balanceStatistics.MaxTemp:F1}℃\n" +
|
||
$"最大温差: {Math.Max(Math.Abs(_balanceStatistics.MaxTemp - _balanceStatistics.AverageTemp), Math.Abs(_balanceStatistics.MinTemp - _balanceStatistics.AverageTemp)):F1}℃\n" +
|
||
$"10分钟漂移: {_balanceStatistics.TenMinuteDrift:F2}℃\n" +
|
||
$"平衡状态: {(_balanceStatistics.IsBalanced ? "达到标准" : "未达到标准")}\n" +
|
||
$"符合条件: 平均温度{(_balanceStatistics.Condition1 ? "√" : "×")} " +
|
||
$"最大温差{(_balanceStatistics.Condition2 ? "√" : "×")} " +
|
||
$"温度漂移{(_balanceStatistics.Condition3 ? "√" : "×")}\n";
|
||
|
||
// 保存到日志文件
|
||
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs", "BalanceLog.txt");
|
||
Directory.CreateDirectory(Path.GetDirectoryName(logPath));
|
||
File.AppendAllText(logPath, logContent + Environment.NewLine);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"记录平衡日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void HandleBalanceError(Exception ex)
|
||
{
|
||
_balanceTimer.Stop();
|
||
_isBalanceChecking = false;
|
||
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Red);
|
||
txtBalanceResult.Text = "错误";
|
||
txtBalanceStatus.Text = $"温度平衡判断出错: {ex.Message}";
|
||
});
|
||
|
||
UpdateStatusBar("温度平衡判断出错");
|
||
}
|
||
|
||
private void UpdateBalanceTimeDisplay()
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
int minutes = _balanceElapsedSeconds / 60;
|
||
int seconds = _balanceElapsedSeconds % 60;
|
||
txtBalanceTimeElapsed.Text = $"{minutes:D2}:{seconds:D2}";
|
||
});
|
||
}
|
||
#endregion
|
||
|
||
#region 平衡统计类
|
||
private BalanceStatistics _balanceStatistics;
|
||
private double _targetTemperature;
|
||
|
||
private class BalanceStatistics
|
||
{
|
||
public double AverageTemp { get; set; }
|
||
public double MinTemp { get; set; }
|
||
public double MaxTemp { get; set; }
|
||
public double TempRange { get; set; }
|
||
public double LinearSlope { get; set; }
|
||
public double TenMinuteDrift { get; set; }
|
||
public bool IsBalanced { get; set; }
|
||
public bool Condition1 { get; set; }
|
||
public bool Condition2 { get; set; }
|
||
public bool Condition3 { get; set; }
|
||
}
|
||
#endregion
|
||
private void OpenBalanceChartWindow()
|
||
{
|
||
try
|
||
{
|
||
// 使用异步避免阻塞
|
||
Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
// 创建窗口但不立即初始化
|
||
var chartWindow = new BalanceChartWindow(() =>
|
||
{
|
||
try
|
||
{
|
||
if (_modbusMaster != null && _tcpClient != null && _tcpClient.Connected)
|
||
{
|
||
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1230, 2);
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
return c.UshortToFloat(registers[1], registers[0]);
|
||
}
|
||
}
|
||
return 0.0;
|
||
}
|
||
catch { return 0.0; }
|
||
});
|
||
|
||
chartWindow.Owner = this;
|
||
chartWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||
|
||
// 延迟显示
|
||
chartWindow.Show();
|
||
|
||
UpdateStatusBar("打开温度平衡曲线图");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"无法打开温度曲线图: {ex.Message}", "错误");
|
||
}
|
||
}), DispatcherPriority.Background);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"打开图表窗口失败: {ex.Message}", "错误");
|
||
}
|
||
}
|
||
|
||
private void btnStopBalanceCheck_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
if (!_isBalanceChecking)
|
||
{
|
||
MessageBox.Show("当前未进行温度平衡判断", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
|
||
// 停止定时器
|
||
_balanceTimer.Stop();
|
||
_isBalanceChecking = false;
|
||
|
||
// 如果已经采集了足够数据,进行最终判断
|
||
if (_tempDataList.Count >= 60) // 至少1分钟数据
|
||
{
|
||
CalculateBalanceStatistics();
|
||
CheckStandardConditions();
|
||
DisplayFinalResult();
|
||
}
|
||
else
|
||
{
|
||
// 数据不足,显示提示
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
txtBalanceStatus.Text = $"温度平衡状态: 已手动停止(数据不足,仅采集{_tempDataList.Count}秒)";
|
||
txtBalanceResult.Text = "数据不足";
|
||
balanceStatusBorder.Background = new SolidColorBrush(Colors.Orange);
|
||
});
|
||
}
|
||
|
||
UpdateStatusBar($"温度平衡判断已停止(已采集{_tempDataList.Count}秒数据)");
|
||
}
|
||
#endregion
|
||
|
||
//#region 最终平衡代码
|
||
|
||
|
||
//// 初始化试验结束平衡
|
||
//private void InitializeFinalBalance()
|
||
//{
|
||
// _finalBalanceTimer = new DispatcherTimer();
|
||
// _finalBalanceTimer.Interval = TimeSpan.FromSeconds(1);
|
||
// _finalBalanceTimer.Tick += FinalBalanceTimer_Tick;
|
||
// _finalTempData = new List<double>();
|
||
//}
|
||
|
||
//// 试验结束温度平衡定时器
|
||
//private void FinalBalanceTimer_Tick(object sender, EventArgs e)
|
||
//{
|
||
// //if (!_isTestRunning || !_isFinalBalanceChecking) return;
|
||
|
||
// try
|
||
// {
|
||
// // 获取当前温度
|
||
// double currentTemp = GetCurrentFurnaceTemp();
|
||
// _finalTempData.Add(currentTemp);
|
||
|
||
// // 更新最高温度
|
||
// if (currentTemp > _finalMaxTemp)
|
||
// _finalMaxTemp = currentTemp;
|
||
|
||
// // 检查是否达到30分钟
|
||
// TimeSpan elapsed = DateTime.Now.TimeOfDay - _finalBalanceStartTime;
|
||
// double currentDrift = 0;
|
||
// if (_finalTempData.Count >= 600) // 有足够数据计算漂移
|
||
// {
|
||
// var recentData = _finalTempData.Skip(Math.Max(0, _finalTempData.Count - 600)).ToList();
|
||
// double slope = CalculateLinearRegressionSlope(recentData); // 使用你现有的方法
|
||
// currentDrift = Math.Abs(slope * 600); // 10分钟的漂移量
|
||
// }
|
||
// // 更新UI信息(每秒更新)
|
||
// UpdateFinalBalanceInfo(elapsed, _finalTempData.Count, currentTemp,
|
||
// _finalInitialTemp, _finalMaxTemp, currentDrift);
|
||
// if (elapsed.TotalMinutes >= 30)
|
||
// {
|
||
// // 检查最终温度平衡条件
|
||
// CheckFinalTemperatureBalance();
|
||
|
||
// // 如果达到60分钟,强制结束
|
||
// if (elapsed.TotalMinutes >= 60)
|
||
// {
|
||
// CompleteFinalBalance(true);
|
||
// }
|
||
// }
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.WriteLine($"试验结束平衡检查出错: {ex.Message}");
|
||
// }
|
||
//}
|
||
|
||
//// 检查最终温度平衡(标准7.4.7)
|
||
//private void CheckFinalTemperatureBalance()
|
||
//{
|
||
// if (_finalTempData.Count < 600) return; // 至少10分钟数据
|
||
|
||
// // 取最近10分钟的数据
|
||
// var recentData = _finalTempData.Skip(Math.Max(0, _finalTempData.Count - 600)).ToList();
|
||
|
||
// // 计算线性回归漂移
|
||
// double slope = CalculateLinearRegressionSlope(recentData);
|
||
// double drift = Math.Abs(slope * 600); // 10分钟的漂移量
|
||
|
||
// // 更新漂移值显示
|
||
// UpdateFinalBalanceInfo(DateTime.Now.TimeOfDay - _finalBalanceStartTime,
|
||
// _finalTempData.Count, _finalTempData.Last(),
|
||
// _finalInitialTemp, _finalMaxTemp, drift);
|
||
|
||
// // 检查条件:10分钟内漂移不超过2°C
|
||
// if (drift <= 2.0)
|
||
// {
|
||
// // 达到温度平衡
|
||
// CompleteFinalBalance(false);
|
||
// }
|
||
//}
|
||
|
||
//// 计算线性回归斜率
|
||
//private double CalculateLinearRegressionSlope(List<double> data)
|
||
//{
|
||
// if (data.Count < 2) return 0;
|
||
|
||
// double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
|
||
// int n = data.Count;
|
||
|
||
// for (int i = 0; i < n; i++)
|
||
// {
|
||
// double x = i;
|
||
// double y = data[i];
|
||
// sumX += x;
|
||
// sumY += y;
|
||
// sumXY += x * y;
|
||
// sumX2 += x * x;
|
||
// }
|
||
|
||
// double denominator = n * sumX2 - sumX * sumX;
|
||
// if (Math.Abs(denominator) < 0.0001) return 0;
|
||
|
||
// return (n * sumXY - sumX * sumY) / denominator;
|
||
//}
|
||
|
||
//private void CompleteFinalBalance(bool isForced)
|
||
//{
|
||
// _isFinalBalanceChecking = false;
|
||
// _finalBalanceTimer.Stop();
|
||
|
||
// // === 新增:同时停止试验 ===
|
||
// _isTestRunning = false;
|
||
// _testTimer.Stop();
|
||
// // ===========================
|
||
|
||
// // 计算最终温度(最后1分钟平均值)
|
||
// int count = Math.Min(_finalTempData.Count, 60);
|
||
// var lastMinuteData = _finalTempData.Skip(_finalTempData.Count - count).ToList();
|
||
// _finalFinalTemp = lastMinuteData.Average();
|
||
|
||
// // 记录试验结束时间
|
||
// TimeSpan testDuration = DateTime.Now.TimeOfDay - _finalBalanceStartTime;
|
||
// // 更新最终UI状态
|
||
// string resultText = isForced ? "强制结束" : "达到平衡";
|
||
// Brush resultColor = isForced ? Brushes.Red : Brushes.Green;
|
||
// UpdateFinalBalanceUI("已完成", resultText, resultColor);
|
||
|
||
// // 更新最终漂移值显示
|
||
// double finalDrift = 0;
|
||
// if (_finalTempData.Count >= 600)
|
||
// {
|
||
// var recentData = _finalTempData.Skip(Math.Max(0, _finalTempData.Count - 600)).ToList();
|
||
// double slope = CalculateLinearRegressionSlope(recentData);
|
||
// finalDrift = Math.Abs(slope * 600);
|
||
// UpdateFinalBalanceInfo(testDuration, _finalTempData.Count, _finalFinalTemp,
|
||
// _finalInitialTemp, _finalMaxTemp, finalDrift);
|
||
// }
|
||
// // 更新状态显示
|
||
// Dispatcher.Invoke(() =>
|
||
// {
|
||
|
||
// // 显示平衡结果
|
||
// string result = isForced ? "60分钟强制结束" : "达到温度平衡";
|
||
// UpdateStatusBar($"试验结束: {result}");
|
||
|
||
// // === 新增:更新系统状态 ===
|
||
// txtSystemStatus.Text = "试验结束";
|
||
// statusIndicator2.Fill = new SolidColorBrush(Colors.Green);
|
||
// btnStartTest.IsEnabled = true;
|
||
// btnStop.IsEnabled = false;
|
||
// // =========================
|
||
|
||
// // 自动填入试验结束重量(如果未填写)
|
||
// if (string.IsNullOrEmpty(txtFinalWeight.Text) || txtFinalWeight.Text == "请输入试验后重量(g)")
|
||
// {
|
||
// EnableFinalWeightInput();
|
||
// MessageBox.Show("试验已结束,请进行后称重并输入试验后重量。",
|
||
// "后称重提醒", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
// }
|
||
|
||
// // 自动保存试验数据到报表
|
||
// SaveTestDataToReport();
|
||
// });
|
||
//}
|
||
|
||
//// 开始试验结束平衡判断
|
||
//private void StartFinalBalanceCheck()
|
||
//{
|
||
// _finalTempData.Clear();
|
||
// _finalInitialTemp = GetCurrentFurnaceTemp();
|
||
// _finalMaxTemp = _finalInitialTemp;
|
||
// _finalBalanceStartTime = DateTime.Now.TimeOfDay;
|
||
// _isFinalBalanceChecking = true;
|
||
// _finalBalanceTimer.Start();
|
||
|
||
// // 更新UI状态
|
||
// UpdateFinalBalanceUI("判断中", "判断中...", Brushes.Orange);
|
||
// UpdateFinalBalanceInfo(TimeSpan.Zero, 0, _finalInitialTemp,
|
||
// _finalInitialTemp, _finalMaxTemp);
|
||
|
||
// UpdateStatusBar("开始试验结束温度平衡判断");
|
||
//}
|
||
//// 重置最终平衡UI状态
|
||
//private void ResetFinalBalanceUI()
|
||
//{
|
||
// UpdateFinalBalanceUI("未开始", "等待开始", Brushes.Gray);
|
||
// UpdateFinalBalanceInfo(TimeSpan.Zero, 0, 0, 0, 0, 0);
|
||
//}
|
||
//// 停止试验结束平衡判断
|
||
//private void StopFinalBalanceCheck()
|
||
//{
|
||
// _isFinalBalanceChecking = false;
|
||
// _finalBalanceTimer.Stop();
|
||
// // 更新UI状态
|
||
// UpdateFinalBalanceUI("已停止", "手动停止", Brushes.Gray);
|
||
//}
|
||
|
||
|
||
//// 更新最终平衡UI状态
|
||
//private void UpdateFinalBalanceUI(string status, string statusText, Brush statusColor)
|
||
//{
|
||
// if (Dispatcher.CheckAccess())
|
||
// {
|
||
// txtFinalBalanceState.Text = status;
|
||
// txtFinalBalanceStatusText.Text = statusText;
|
||
// finalBalanceStatusBorder.Background = statusColor;
|
||
// }
|
||
// else
|
||
// {
|
||
// Dispatcher.Invoke(() => UpdateFinalBalanceUI(status, statusText, statusColor));
|
||
// }
|
||
//}
|
||
|
||
//// 更新最终平衡计时和温度显示
|
||
//private void UpdateFinalBalanceInfo(TimeSpan elapsed, int dataCount, double currentTemp,
|
||
// double initialTemp, double maxTemp, double drift = 0)
|
||
//{
|
||
// if (Dispatcher.CheckAccess())
|
||
// {
|
||
// // 更新时间显示(格式:mm:ss)
|
||
// txtFinalBalanceTimer.Text = $"{(int)elapsed.TotalMinutes:D2}:{elapsed.Seconds:D2}";
|
||
|
||
// // 更新温度数据
|
||
// txtFinalBalanceInitialTemp.Text = $"{initialTemp:F1}°C";
|
||
// txtFinalBalanceMaxTemp.Text = $"{maxTemp:F1}°C";
|
||
// txtFinalBalanceCurrentTemp.Text = $"{currentTemp:F1}°C";
|
||
|
||
// // 更新数据统计
|
||
// txtFinalBalanceDataCount.Text = dataCount.ToString();
|
||
// txtFinalBalanceDrift.Text = $"{drift:F2}°C";
|
||
// }
|
||
// else
|
||
// {
|
||
// Dispatcher.Invoke(() => UpdateFinalBalanceInfo(elapsed, dataCount, currentTemp,
|
||
// initialTemp, maxTemp, drift));
|
||
// }
|
||
//}
|
||
|
||
// 保存试验数据到报表
|
||
//private void SaveTestDataToReport()
|
||
//{
|
||
// try
|
||
// {
|
||
// // 获取报表窗口实例
|
||
// TestReportWindow reportWindow = Application.Current.Windows
|
||
// .OfType<TestReportWindow>()
|
||
// .FirstOrDefault();
|
||
|
||
// //if (reportWindow == null) return;
|
||
|
||
// // 解析重量数据
|
||
// double initialWeight = GetDoubleValue(txtInitialWeight.Text);
|
||
// double finalWeight = GetDoubleValue(txtFinalWeight.Text);
|
||
// double weightLossPercent = 0;
|
||
|
||
// if (initialWeight > 0 && finalWeight > 0)
|
||
// {
|
||
// weightLossPercent = ((initialWeight - finalWeight) / initialWeight) * 100;
|
||
// }
|
||
|
||
// // 解析火焰持续时间
|
||
// int flameDuration = 0;
|
||
// if (!string.IsNullOrEmpty(txtCombustionNote.Text))
|
||
// {
|
||
// int.TryParse(txtCombustionNote.Text, out flameDuration);
|
||
// }
|
||
|
||
// // 创建报表数据对象
|
||
// var reportData = new TestReportData
|
||
// {
|
||
// SampleCode = txtTestNo.Text,
|
||
// SampleName = txtSampleName.Text,
|
||
// SampleSpec = txtSampleSpec.Text,
|
||
// InitialTemp = _finalInitialTemp,
|
||
// MaxTemp = _finalMaxTemp,
|
||
// FinalTemp = _finalFinalTemp,
|
||
// TempRise = _finalMaxTemp - _finalInitialTemp,
|
||
// InitialWeight = initialWeight,
|
||
// FinalWeight = finalWeight,
|
||
// LossPercent = weightLossPercent,
|
||
// FlameDuration = flameDuration,
|
||
// TestDate = DateTime.Now.ToString("yyyy-MM-dd"),
|
||
// TestDuration = txtFinalBalanceTimer.Text,
|
||
// BalanceStatus = "达到平衡",
|
||
// Remarks = $"火焰:{(chkHasFlame.IsChecked == true ? "有" : "无")} 冒烟:{(chkHasSmoke.IsChecked == true ? "有" : "无")}"
|
||
// };
|
||
|
||
|
||
|
||
|
||
// SaveDataToTempFile(reportData);
|
||
|
||
|
||
// UpdateStatusBar("试验数据已保存到报表");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.WriteLine($"保存数据到报表失败: {ex.Message}");
|
||
// }
|
||
//}
|
||
|
||
//// 临时保存数据到文件(在主窗口类中添加这个方法)
|
||
//private void SaveDataToTempFile(TestReportData reportData)
|
||
//{
|
||
// try
|
||
// {
|
||
// // 使用简单的文本格式保存
|
||
// string dataLine = $"{reportData.SampleCode}|{reportData.SampleName}|" +
|
||
// $"{reportData.SampleSpec}|{reportData.InitialTemp:F1}|" +
|
||
// $"{reportData.MaxTemp:F1}|{reportData.FinalTemp:F1}|" +
|
||
// $"{reportData.TempRise:F1}|{reportData.InitialWeight:F2}|" +
|
||
// $"{reportData.FinalWeight:F2}|{reportData.LossPercent:F2}|" +
|
||
// $"{reportData.FlameDuration}|{reportData.TestDate}|" +
|
||
// $"{reportData.TestDuration}|{reportData.BalanceStatus}|" +
|
||
// $"{reportData.Remarks}";
|
||
|
||
// string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempReportData.txt");
|
||
|
||
// // 追加数据到文件
|
||
// File.AppendAllText(filePath, dataLine + Environment.NewLine, Encoding.UTF8);
|
||
|
||
// Debug.WriteLine($"数据已临时保存到文件: {filePath}");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.WriteLine($"临时保存数据失败: {ex.Message}");
|
||
// }
|
||
//}
|
||
|
||
|
||
//#endregion
|
||
|
||
#region 最终温度平衡判断
|
||
private TemperatureBalanceCalculator _calculator;
|
||
private DispatcherTimer _uiUpdateTimer;
|
||
private int _uiUpdateCounter = 0;
|
||
|
||
private void InitializeExperimentSystem()
|
||
{
|
||
_calculator = new TemperatureBalanceCalculator();
|
||
|
||
// 设置温度读取方法(这里连接你的实际温度读取代码)
|
||
_calculator.ReadTemperatureData = () =>
|
||
{
|
||
try
|
||
{
|
||
// 调用你的温度读取函数
|
||
double furnaceTemp = GetFurnaceTemperature();
|
||
double sampleInnerTemp = GetSampleInnerTemperature();
|
||
double sampleOuterTemp = GetSampleOuterTemperature();
|
||
|
||
return new TemperatureDataPoint
|
||
{
|
||
Timestamp = DateTime.Now,
|
||
ScaleTemp = furnaceTemp,
|
||
SampleTemp = sampleInnerTemp,
|
||
SlideTemp = sampleOuterTemp
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"温度读取失败: {ex.Message}");
|
||
return new TemperatureDataPoint();
|
||
}
|
||
};
|
||
|
||
// 订阅事件
|
||
_calculator.ExperimentProgressChanged += OnExperimentProgressChanged;
|
||
_calculator.TemperatureDataReceived += OnTemperatureDataReceived;
|
||
_calculator.WindowChecked += OnWindowChecked;
|
||
_calculator.ExperimentCompleted += OnExperimentCompleted;
|
||
_calculator.LogMessage += OnLogMessage;
|
||
|
||
// 初始化UI更新计时器(100ms更新一次UI,更流畅)
|
||
_uiUpdateTimer = new DispatcherTimer();
|
||
_uiUpdateTimer.Interval = TimeSpan.FromMilliseconds(100);
|
||
_uiUpdateTimer.Tick += UiUpdateTimer_Tick;
|
||
_uiUpdateTimer.Start();
|
||
|
||
InitializeUI();
|
||
}
|
||
|
||
// UI更新计时器(100ms更新)
|
||
private void UiUpdateTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
_uiUpdateCounter++;
|
||
|
||
// 每10次更新(1秒)更新一次详细统计
|
||
if (_uiUpdateCounter % 10 == 0)
|
||
{
|
||
UpdateDetailedStatistics();
|
||
}
|
||
|
||
// 总是更新计时显示
|
||
UpdateTimerDisplay();
|
||
}
|
||
|
||
// 更新计时显示
|
||
private void UpdateTimerDisplay()
|
||
{
|
||
var stats = _calculator.GetStatistics();
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
txtFinalBalanceTimer.Text = $"{stats.CurrentMinute:00}:{stats.CurrentSecond % 60:00} / 60:00";
|
||
});
|
||
}
|
||
|
||
// 事件处理
|
||
private void OnExperimentProgressChanged(object sender, ExperimentProgressEventArgs e)
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
// 更新温度显示(1秒一次)
|
||
if (e.CurrentTemperature != null && e.CurrentTemperature.IsValid)
|
||
{
|
||
txtFurnaceTemp.Text = $"{e.CurrentTemperature.ScaleTemp:F1}℃";
|
||
txtSampleInnerTemp.Text = $"{e.CurrentTemperature.SampleTemp:F1}℃";
|
||
txtSampleOuterTemp.Text = $"{e.CurrentTemperature.SlideTemp:F1}℃";
|
||
}
|
||
|
||
// 更新窗口信息
|
||
if (e.CurrentWindow != null)
|
||
{
|
||
txtCurrentWindow.Text = $"{e.CurrentWindow.WindowStart}-{e.CurrentWindow.WindowEnd}min";
|
||
txtCalculationCount.Text = $"第{e.CurrentWindow.CalculationCount}次";
|
||
}
|
||
|
||
// 更新进度
|
||
progressExperiment.Value = e.CurrentMinute;
|
||
txtExperimentProgress.Text = $"实验进度: {e.CurrentMinute}/60分钟 ({e.ProgressPercentage:F1}%)";
|
||
|
||
// 更新数据点信息
|
||
txtDataPointCount.Text = $"{e.TotalDataPoints} ({e.DataPointsPerSecond:F1}/秒)";
|
||
|
||
// 更新实验阶段
|
||
txtExperimentPhase.Text = e.CurrentMinute < 30 ? "前30分钟" : "后30分钟";
|
||
|
||
// 更新状态
|
||
UpdateExperimentStatus(e);
|
||
|
||
// 更新三通道斜率显示(实时)
|
||
if (e.RecentSlopes != null)
|
||
{
|
||
UpdateSlopesDisplay(e.RecentSlopes);
|
||
}
|
||
});
|
||
}
|
||
|
||
private void UpdateExperimentStatus(ExperimentProgressEventArgs e)
|
||
{
|
||
if (e.IsBalanced && e.FinalBalanceTime.HasValue)
|
||
{
|
||
txtFinalBalanceState.Text = $"已平衡 ({e.FinalBalanceTime}min)";
|
||
txtFinalBalanceState.Foreground = Brushes.Green;
|
||
txtFinalBalanceStatusText.Text = "平衡达成";
|
||
finalBalanceStatusBorder.Background = new SolidColorBrush(Color.FromRgb(39, 174, 96));
|
||
txtBalanceStatus.Text = "已平衡";
|
||
txtBalanceStatus.Foreground = Brushes.Green;
|
||
txtFinalBalanceTime.Text = $"{e.FinalBalanceTime}min";
|
||
txtFinalBalanceTime.Foreground = Brushes.Green;
|
||
|
||
|
||
}
|
||
else if (e.IsExperimentRunning)
|
||
{
|
||
txtFinalBalanceState.Text = $"实验中... {e.CurrentSecond % 60:00}秒";
|
||
txtFinalBalanceState.Foreground = Brushes.Blue;
|
||
txtFinalBalanceStatusText.Text = "进行中";
|
||
finalBalanceStatusBorder.Background = new SolidColorBrush(Color.FromRgb(52, 152, 219));
|
||
txtBalanceStatus.Text = "未平衡";
|
||
txtBalanceStatus.Foreground = Brushes.Orange;
|
||
txtFinalBalanceTime.Text = "未达到";
|
||
txtFinalBalanceTime.Foreground = Brushes.Gray;
|
||
}
|
||
else
|
||
{
|
||
txtFinalBalanceState.Text = "实验停止";
|
||
txtFinalBalanceState.Foreground = Brushes.Gray;
|
||
txtFinalBalanceStatusText.Text = "已停止";
|
||
finalBalanceStatusBorder.Background = new SolidColorBrush(Color.FromRgb(149, 165, 166));
|
||
txtBalanceStatus.Text = "未平衡";
|
||
txtBalanceStatus.Foreground = Brushes.Gray;
|
||
txtFinalBalanceTime.Text = "未达到";
|
||
txtFinalBalanceTime.Foreground = Brushes.Gray;
|
||
}
|
||
}
|
||
|
||
// 更新详细统计信息
|
||
private void UpdateDetailedStatistics()
|
||
{
|
||
var stats = _calculator.GetStatistics();
|
||
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
// 更新平衡窗口计数
|
||
txtBalancedWindowsCount.Text = $"{stats.BalancedWindowsCount}/{stats.CheckedWindowsCount}";
|
||
|
||
// 更新温度范围显示
|
||
UpdateTemperatureRangeDisplay(stats);
|
||
});
|
||
}
|
||
|
||
private void UpdateTemperatureRangeDisplay(ExperimentStatistics stats)
|
||
{
|
||
// 可以在界面上添加这些显示控件
|
||
// txtInitialTemp.Text = $"{stats.InitialScaleTemp:F1}℃";
|
||
// txtMaxTemp.Text = $"{stats.MaxScaleTemp:F1}℃";
|
||
// txtCurrentTemp.Text = $"{stats.CurrentScaleTemp:F1}℃";
|
||
|
||
// 计算漂移
|
||
double drift = stats.MaxScaleTemp - stats.MinScaleTemp;
|
||
// txtDrift.Text = $"{drift:F1}℃";
|
||
}
|
||
|
||
// 更新三通道斜率显示
|
||
private void UpdateSlopesDisplay(TemperatureSlopes slopes)
|
||
{
|
||
if (slopes == null) return;
|
||
|
||
txtFurnaceSlope.Text = $"炉内: {slopes.ScaleRate:F2}℃/10min";
|
||
txtSampleInnerSlope.Text = $"样品内: {slopes.SampleRate:F2}℃/10min";
|
||
txtSampleOuterSlope.Text = $"样品表面: {slopes.SlideRate:F2}℃/10min";
|
||
|
||
// 设置颜色
|
||
SetSlopeColor(txtFurnaceSlope, slopes.ScaleRate < 2.0);
|
||
SetSlopeColor(txtSampleInnerSlope, slopes.SampleRate < 2.0);
|
||
SetSlopeColor(txtSampleOuterSlope, slopes.SlideRate < 2.0);
|
||
|
||
// 设置边框背景
|
||
SetSlopeBorderColor(txtFurnaceSlope.Parent as Border, slopes.ScaleRate < 2.0, Color.FromRgb(231, 76, 60));
|
||
SetSlopeBorderColor(txtSampleInnerSlope.Parent as Border, slopes.SampleRate < 2.0, Color.FromRgb(52, 152, 219));
|
||
SetSlopeBorderColor(txtSampleOuterSlope.Parent as Border, slopes.SlideRate < 2.0, Color.FromRgb(155, 89, 182));
|
||
}
|
||
|
||
private void SetSlopeColor(TextBlock textBlock, bool isOk)
|
||
{
|
||
textBlock.Foreground = isOk ? Brushes.Green : Brushes.Red;
|
||
}
|
||
|
||
private void SetSlopeBorderColor(Border border, bool isOk, Color baseColor)
|
||
{
|
||
if (border == null) return;
|
||
|
||
byte alpha = isOk ? (byte)50 : (byte)30;
|
||
border.Background = new SolidColorBrush(Color.FromArgb(alpha, baseColor.R, baseColor.G, baseColor.B));
|
||
}
|
||
|
||
// 其他事件处理
|
||
private void OnTemperatureDataReceived(object sender, TemperatureDataPoint e)
|
||
{
|
||
// 温度数据已实时更新,这里可以添加额外处理
|
||
}
|
||
|
||
private void OnWindowChecked(object sender, ExperimentWindow e)
|
||
{
|
||
Debug.WriteLine($"窗口检查完成: {e}");
|
||
}
|
||
|
||
private void OnExperimentCompleted(object sender, bool isBalanced)
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
string message = isBalanced ?
|
||
"实验完成!已达到温度平衡条件,请输入测试后样品重量。" :
|
||
"实验结束!未达到温度平衡条件,请输入测试后样品重量。";
|
||
|
||
txtSystemStatus.Text = "试验结束";
|
||
statusIndicator2.Fill = new SolidColorBrush(Colors.Green);
|
||
|
||
EndTest();
|
||
|
||
MessageBox.Show(message, "实验完成", MessageBoxButton.OK,
|
||
isBalanced ? MessageBoxImage.Information : MessageBoxImage.Warning);
|
||
|
||
|
||
});
|
||
|
||
|
||
}
|
||
|
||
private void OnLogMessage(object sender, string message)
|
||
{
|
||
Debug.WriteLine($"日志: {message}");
|
||
}
|
||
|
||
|
||
private void btnResetExperiment_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
var result = MessageBox.Show("确定要重置实验吗?所有数据将被清除。",
|
||
"重置实验", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
_calculator.ResetExperiment();
|
||
InitializeUI();
|
||
}
|
||
}
|
||
|
||
private void InitializeUI()
|
||
{
|
||
// 重置所有UI状态
|
||
txtFinalBalanceTimer.Text = "00:00 / 60:00";
|
||
txtFinalBalanceState.Text = "实验未开始";
|
||
txtFinalBalanceState.Foreground = Brushes.Black;
|
||
txtFinalBalanceStatusText.Text = "等待开始";
|
||
finalBalanceStatusBorder.Background = new SolidColorBrush(Color.FromRgb(189, 195, 199));
|
||
|
||
progressExperiment.Value = 0;
|
||
txtExperimentProgress.Text = "实验进度: 0/60分钟 (0%)";
|
||
txtDataPointCount.Text = "0 (0.0/秒)";
|
||
txtBalancedWindowsCount.Text = "0/0";
|
||
|
||
txtFurnaceTemp.Text = "0.0°C";
|
||
txtSampleInnerTemp.Text = "0.0°C";
|
||
txtSampleOuterTemp.Text = "0.0°C";
|
||
|
||
txtFurnaceSlope.Text = "炉内: 0.00℃/10min";
|
||
txtSampleInnerSlope.Text = "样品内: 0.00℃/10min";
|
||
txtSampleOuterSlope.Text = "样品表面: 0.00℃/10min";
|
||
|
||
txtCurrentWindow.Text = "0-10min";
|
||
txtCalculationCount.Text = "第1次";
|
||
txtExperimentPhase.Text = "前30分钟";
|
||
txtBalanceStatus.Text = "未平衡";
|
||
txtBalanceCondition2.Text = "漂移<2℃/10min";
|
||
txtFinalBalanceTime.Text = "未达到";
|
||
|
||
}
|
||
|
||
protected override void OnClosed(EventArgs e)
|
||
{
|
||
base.OnClosed(e);
|
||
_calculator?.Dispose();
|
||
_uiUpdateTimer?.Stop();
|
||
}
|
||
|
||
|
||
//private void SaveTestDataToReport()
|
||
//{
|
||
// try
|
||
// {
|
||
// // 获取报表窗口实例
|
||
// TestReportWindow reportWindow = Application.Current.Windows
|
||
// .OfType<TestReportWindow>()
|
||
// .FirstOrDefault();
|
||
|
||
// //if (reportWindow == null) return;
|
||
|
||
// // 解析重量数据
|
||
// double initialWeight = GetDoubleValue(txtInitialWeight.Text);
|
||
// double finalWeight = GetDoubleValue(txtFinalWeight.Text);
|
||
// double weightLossPercent = 0;
|
||
|
||
// if (initialWeight > 0 && finalWeight > 0)
|
||
// {
|
||
// weightLossPercent = ((initialWeight - finalWeight) / initialWeight) * 100;
|
||
// }
|
||
|
||
// // 解析火焰持续时间
|
||
// int flameDuration = 0;
|
||
// if (!string.IsNullOrEmpty(txtCombustionNote.Text))
|
||
// {
|
||
// int.TryParse(txtCombustionNote.Text, out flameDuration);
|
||
// }
|
||
|
||
// // 创建报表数据对象
|
||
// var reportData = new TestReportData
|
||
// {
|
||
// SampleCode = txtTestNo.Text,
|
||
// SampleName = txtSampleName.Text,
|
||
// SampleSpec = txtSampleSpec.Text,
|
||
// InitialTemp = 0,//炉内初始温度平衡平均值
|
||
// MaxTemp = 0,//试验期间炉内最高温度
|
||
// FinalTemp = 0,//实验过程达到平衡最后一分钟炉内温度平均值
|
||
// TempRise = 0 - 0,//温升:MaxTemp-FinalTemp
|
||
// CenterMaxTemp = 0,//试验期间,试样中心温度最高温度
|
||
// CenterFinalTemp = 0,//实验过程达到平衡最后一分钟试样中心温度平均值
|
||
// SurfaceMaxTemp = 0,//试验期间,试样表面温度最高温度
|
||
// SurfaceFinalTemp = 0,//实验过程达到平衡最后一分钟试样表面温度平均值
|
||
// InitialWeight = initialWeight,
|
||
// FinalWeight = finalWeight,
|
||
// LossPercent = weightLossPercent,
|
||
// FlameDuration = flameDuration,
|
||
// TestDate = DateTime.Now.ToString("yyyy-MM-dd"),
|
||
// TestDuration = txtFinalBalanceTimer.Text,
|
||
// BalanceStatus = "达到平衡",
|
||
// Remarks = $"火焰:{(chkHasFlame.IsChecked == true ? "有" : "无")} 冒烟:{(chkHasSmoke.IsChecked == true ? "有" : "无")}"
|
||
// };
|
||
|
||
|
||
|
||
|
||
// SaveDataToTempFile(reportData);
|
||
|
||
|
||
// UpdateStatusBar("试验数据已保存到报表");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.WriteLine($"保存数据到报表失败: {ex.Message}");
|
||
// }
|
||
//}
|
||
|
||
// 临时保存数据到文件(在主窗口类中添加这个方法)
|
||
|
||
private void SaveTestDataToReport()
|
||
{
|
||
try
|
||
{
|
||
// 获取报表窗口实例
|
||
TestReportWindow reportWindow = Application.Current.Windows
|
||
.OfType<TestReportWindow>()
|
||
.FirstOrDefault();
|
||
|
||
// 从实验数据中提取温度信息
|
||
var stats = _calculator?.GetStatistics();
|
||
var allTempData = _calculator?.GetAllTemperatureData();
|
||
var allWindows = _calculator?.GetAllWindows();
|
||
|
||
// 1. 炉内初始温度平衡平均值 - 获取初始温度
|
||
double initialTemp = _startavgTemp;
|
||
|
||
// 2. 试验期间炉内最高温度
|
||
double maxTemp = stats?.MaxScaleTemp ?? 0;
|
||
|
||
// 3. 实验过程达到平衡最后一分钟炉内温度平均值
|
||
double finalTemp = CalculateFinalBalanceTemperature(allTempData, allWindows);
|
||
|
||
// 4. 温升: MaxTemp - InitialTemp
|
||
double tempRise = maxTemp - initialTemp;
|
||
|
||
// 5. 试验期间,试样中心温度最高温度
|
||
double centerMaxTemp = stats?.MaxSampleTemp ?? 0;
|
||
|
||
// 6. 实验过程达到平衡最后一分钟试样中心温度平均值
|
||
double centerFinalTemp = CalculateCenterFinalTemperature(allTempData, allWindows);
|
||
|
||
// 7. 试验期间,试样表面温度最高温度
|
||
double surfaceMaxTemp = stats?.MaxSlideTemp ?? 0;
|
||
|
||
// 8. 实验过程达到平衡最后一分钟试样表面温度平均值
|
||
double surfaceFinalTemp = CalculateSurfaceFinalTemperature(allTempData, allWindows);
|
||
|
||
// 解析重量数据
|
||
double initialWeight = GetDoubleValue2(txtInitialWeight.Text);
|
||
double finalWeight = GetDoubleValue2(txtFinalWeight.Text);
|
||
double weightLossPercent = 0;
|
||
|
||
if (initialWeight > 0 && finalWeight > 0)
|
||
{
|
||
weightLossPercent = ((initialWeight - finalWeight) / initialWeight) * 100;
|
||
}
|
||
|
||
// 解析火焰持续时间
|
||
int flameDuration = 0;
|
||
if (!string.IsNullOrEmpty(txtCombustionNote.Text))
|
||
{
|
||
int.TryParse(txtCombustionNote.Text, out flameDuration);
|
||
}
|
||
|
||
// 创建报表数据对象
|
||
var reportData = new TestReportData
|
||
{
|
||
SampleCode = txtTestNo.Text,
|
||
SampleName = txtSampleName.Text,
|
||
SampleSpec = txtSampleSpec.Text,
|
||
InitialTemp = initialTemp,
|
||
MaxTemp = maxTemp,
|
||
FinalTemp = finalTemp,
|
||
TempRise = tempRise,
|
||
CenterMaxTemp = centerMaxTemp,
|
||
CenterFinalTemp = centerFinalTemp,
|
||
SurfaceMaxTemp = surfaceMaxTemp,
|
||
SurfaceFinalTemp = surfaceFinalTemp,
|
||
InitialWeight = initialWeight,
|
||
FinalWeight = finalWeight,
|
||
LossPercent = weightLossPercent,
|
||
FlameDuration = flameDuration,
|
||
TestDate = DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"),
|
||
TestDuration = txtFinalBalanceTimer.Text,
|
||
BalanceStatus = GetBalanceStatus(),
|
||
Remarks = $"火焰:{(chkHasFlame.IsChecked == true ? "有" : "无")} 冒烟:{(chkHasSmoke.IsChecked == true ? "有" : "无")}"
|
||
};
|
||
|
||
SaveDataToTempFile(reportData);
|
||
UpdateStatusBar("试验数据已保存到报表");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"保存数据到报表失败: {ex.Message}");
|
||
}
|
||
}
|
||
//private void SaveDataToTempFile(TestReportData reportData)
|
||
//{
|
||
// try
|
||
// {
|
||
// // 使用简单的文本格式保存
|
||
// string dataLine = $"{reportData.SampleCode}|{reportData.SampleName}|" +
|
||
// $"{reportData.SampleSpec}|{reportData.InitialTemp:F1}|" +
|
||
// $"{reportData.MaxTemp:F1}|{reportData.FinalTemp:F1}|" +
|
||
// $"{reportData.TempRise:F1}|{reportData.InitialWeight:F2}|" +
|
||
// $"{reportData.FinalWeight:F2}|{reportData.LossPercent:F2}|" +
|
||
// $"{reportData.FlameDuration}|{reportData.TestDate}|" +
|
||
// $"{reportData.TestDuration}|{reportData.BalanceStatus}|" +
|
||
// $"{reportData.Remarks}";
|
||
|
||
// string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempReportData.txt");
|
||
|
||
// // 追加数据到文件
|
||
// File.AppendAllText(filePath, dataLine + Environment.NewLine, Encoding.UTF8);
|
||
|
||
// Debug.WriteLine($"数据已临时保存到文件: {filePath}");
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Debug.WriteLine($"临时保存数据失败: {ex.Message}");
|
||
// }
|
||
//}
|
||
|
||
// 获取平衡状态
|
||
|
||
private void SaveDataToTempFile(TestReportData reportData)
|
||
{
|
||
try
|
||
{
|
||
// 使用简单的文本格式保存 - 添加所有温度字段
|
||
string dataLine = $"{reportData.SampleCode}|{reportData.SampleName}|" +
|
||
$"{reportData.SampleSpec}|{reportData.InitialTemp:F1}|" +
|
||
$"{reportData.MaxTemp:F1}|{reportData.FinalTemp:F1}|" +
|
||
$"{reportData.TempRise:F1}|" +
|
||
$"{reportData.CenterMaxTemp:F1}|" + // 试样中心最高温度
|
||
$"{reportData.CenterFinalTemp:F1}|" + // 试样中心最终温度
|
||
$"{reportData.SurfaceMaxTemp:F1}|" + // 试样表面最高温度
|
||
$"{reportData.SurfaceFinalTemp:F1}|" + // 试样表面最终温度
|
||
$"{reportData.InitialWeight:F2}|" +
|
||
$"{reportData.FinalWeight:F2}|{reportData.LossPercent:F2}|" +
|
||
$"{reportData.FlameDuration}|{reportData.TestDate}|" +
|
||
$"{reportData.TestDuration}|{reportData.BalanceStatus}|" +
|
||
$"{reportData.Remarks}";
|
||
|
||
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TempReportData.txt");
|
||
|
||
// 追加数据到文件
|
||
File.AppendAllText(filePath, dataLine + Environment.NewLine, Encoding.UTF8);
|
||
|
||
Debug.WriteLine($"数据已临时保存到文件: {filePath}");
|
||
|
||
// 可选:在界面上显示保存成功消息
|
||
UpdateStatusBar($"数据已保存到临时文件: {Path.GetFileName(filePath)}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"临时保存数据失败: {ex.Message}");
|
||
}
|
||
}
|
||
private string GetBalanceStatus()
|
||
{
|
||
var stats = _calculator?.GetStatistics();
|
||
if (stats == null) return "未开始";
|
||
|
||
if (stats.IsExperimentBalanced && stats.FinalBalanceTime.HasValue)
|
||
{
|
||
return $"达到平衡 ({stats.FinalBalanceTime}min)";
|
||
}
|
||
else if (stats.IsExperimentRunning)
|
||
{
|
||
return "实验中";
|
||
}
|
||
else
|
||
{
|
||
return "未达到平衡";
|
||
}
|
||
}
|
||
|
||
// 计算最终平衡时的炉内温度平均值(最后一分钟)
|
||
private double CalculateFinalBalanceTemperature(List<TemperatureDataPoint> allTempData, List<ExperimentWindow> allWindows)
|
||
{
|
||
if (allTempData == null || allTempData.Count == 0) return 0;
|
||
|
||
// 找到达到平衡的窗口
|
||
var balancedWindow = allWindows?.LastOrDefault(w => w.IsBalanced);
|
||
if (balancedWindow == null) return 0;
|
||
|
||
// 获取平衡窗口最后一分钟的数据
|
||
DateTime endTime = balancedWindow.CheckTime ?? DateTime.Now;
|
||
DateTime startTime = endTime.AddMinutes(-1);
|
||
|
||
var finalMinuteData = allTempData
|
||
.Where(d => d.Timestamp >= startTime && d.Timestamp <= endTime)
|
||
.ToList();
|
||
|
||
if (finalMinuteData.Count == 0) return 0;
|
||
|
||
// 计算平均值
|
||
return finalMinuteData.Average(d => d.ScaleTemp);
|
||
}
|
||
|
||
// 计算最终平衡时的试样中心温度平均值(最后一分钟)
|
||
private double CalculateCenterFinalTemperature(List<TemperatureDataPoint> allTempData, List<ExperimentWindow> allWindows)
|
||
{
|
||
if (allTempData == null || allTempData.Count == 0) return 0;
|
||
|
||
var balancedWindow = allWindows?.LastOrDefault(w => w.IsBalanced);
|
||
if (balancedWindow == null) return 0;
|
||
|
||
DateTime endTime = balancedWindow.CheckTime ?? DateTime.Now;
|
||
DateTime startTime = endTime.AddMinutes(-1);
|
||
|
||
var finalMinuteData = allTempData
|
||
.Where(d => d.Timestamp >= startTime && d.Timestamp <= endTime)
|
||
.ToList();
|
||
|
||
if (finalMinuteData.Count == 0) return 0;
|
||
|
||
return finalMinuteData.Average(d => d.SampleTemp);
|
||
}
|
||
|
||
// 计算最终平衡时的试样表面温度平均值(最后一分钟)
|
||
private double CalculateSurfaceFinalTemperature(List<TemperatureDataPoint> allTempData, List<ExperimentWindow> allWindows)
|
||
{
|
||
if (allTempData == null || allTempData.Count == 0) return 0;
|
||
|
||
var balancedWindow = allWindows?.LastOrDefault(w => w.IsBalanced);
|
||
if (balancedWindow == null) return 0;
|
||
|
||
DateTime endTime = balancedWindow.CheckTime ?? DateTime.Now;
|
||
DateTime startTime = endTime.AddMinutes(-1);
|
||
|
||
var finalMinuteData = allTempData
|
||
.Where(d => d.Timestamp >= startTime && d.Timestamp <= endTime)
|
||
.ToList();
|
||
|
||
if (finalMinuteData.Count == 0) return 0;
|
||
|
||
return finalMinuteData.Average(d => d.SlideTemp);
|
||
}
|
||
|
||
// 辅助方法:从文本解析double值
|
||
private double GetDoubleValue2(string text)
|
||
{
|
||
if (string.IsNullOrEmpty(text)) return 0;
|
||
|
||
// 移除单位字符
|
||
text = text.Replace("℃", "").Replace("°C", "").Replace("g", "").Trim();
|
||
|
||
if (double.TryParse(text, out double result))
|
||
{
|
||
return result;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
|
||
#region 最终平衡温度数据结构
|
||
|
||
// 温度数据结构
|
||
public class TemperatureDataPoint
|
||
{
|
||
public DateTime Timestamp { get; set; }
|
||
public double ScaleTemp { get; set; } // 炉内温度(标尺)
|
||
public double SampleTemp { get; set; } // 样品内温度
|
||
public double SlideTemp { get; set; } // 样品表面温度
|
||
|
||
public bool IsValid => !double.IsNaN(ScaleTemp) &&
|
||
!double.IsNaN(SampleTemp) &&
|
||
!double.IsNaN(SlideTemp);
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"{Timestamp:HH:mm:ss.fff} - 炉内:{ScaleTemp:F2}℃, 样品内:{SampleTemp:F2}℃, 样品表面:{SlideTemp:F2}℃";
|
||
}
|
||
}
|
||
|
||
// 温度斜率结构
|
||
public class TemperatureSlopes
|
||
{
|
||
public double ScaleSlope { get; set; } // 炉内温度斜率(℃/min)
|
||
public double SampleSlope { get; set; } // 样品内温度斜率(℃/min)
|
||
public double SlideSlope { get; set; } // 样品表面温度斜率(℃/min)
|
||
|
||
// 转换为10分钟变化率
|
||
public double ScaleRate => Math.Abs(ScaleSlope * 10); // 标尺温度变化率(℃/10min)
|
||
public double SampleRate => Math.Abs(SampleSlope * 10); // 样品温度变化率
|
||
public double SlideRate => Math.Abs(SlideSlope * 10); // 样品表面温度变化率
|
||
|
||
// 判断是否平衡
|
||
public bool IsBalanced(double threshold) => ScaleRate < threshold && SampleRate < threshold && SlideRate < threshold;
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"炉内:{ScaleRate:F2}℃/10min, 样品内:{SampleRate:F2}℃/10min, 样品表面:{SlideRate:F2}℃/10min";
|
||
}
|
||
}
|
||
|
||
// 实验窗口结构
|
||
public class ExperimentWindow
|
||
{
|
||
public int WindowStart { get; set; }
|
||
public int WindowEnd { get; set; }
|
||
public int CalculationCount { get; set; }
|
||
public bool IsBalanced { get; set; }
|
||
public int? FinalBalanceTime { get; set; }
|
||
public TemperatureSlopes Slopes { get; set; } = new TemperatureSlopes();
|
||
public bool IsCurrentWindow { get; set; }
|
||
public bool IsChecked { get; set; }
|
||
public DateTime? CheckTime { get; set; }
|
||
public int DataPointsCount { get; set; }
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"{WindowStart}-{WindowEnd}min: 第{CalculationCount}次计算 {Slopes} {(IsBalanced ? $"✓ 平衡于{FinalBalanceTime}min" : "✗ 未平衡")}";
|
||
}
|
||
}
|
||
|
||
// 实验进度事件参数
|
||
public class ExperimentProgressEventArgs : EventArgs
|
||
{
|
||
public int CurrentMinute { get; set; }
|
||
public int CurrentSecond { get; set; }
|
||
public TemperatureDataPoint CurrentTemperature { get; set; }
|
||
public ExperimentWindow CurrentWindow { get; set; }
|
||
public TemperatureSlopes RecentSlopes { get; set; }
|
||
public double ProgressPercentage { get; set; }
|
||
public bool IsExperimentRunning { get; set; }
|
||
public bool IsBalanced { get; set; }
|
||
public int? FinalBalanceTime { get; set; }
|
||
public double DataPointsPerSecond { get; set; }
|
||
public int TotalDataPoints { get; set; }
|
||
}
|
||
|
||
// 完整的温度平衡计算器(1秒采集间隔)
|
||
public class TemperatureBalanceCalculator : IDisposable
|
||
{
|
||
private List<TemperatureDataPoint> _temperatureData = new List<TemperatureDataPoint>();
|
||
private readonly object _dataLock = new object();
|
||
private System.Timers.Timer _dataCollectionTimer;
|
||
private System.Timers.Timer _windowCheckTimer;
|
||
private DateTime _experimentStartTime;
|
||
private bool _isExperimentRunning = false;
|
||
private int _currentExperimentMinute = 0;
|
||
private int _currentExperimentSecond = 0;
|
||
private TemperatureDataPoint _initialTemperature;
|
||
private DateTime _lastCollectionTime;
|
||
private int _dataCollectionCount = 0;
|
||
|
||
// 实验参数
|
||
private const int TOTAL_EXPERIMENT_TIME = 60; // 总实验时间(分钟)
|
||
private const double BALANCE_THRESHOLD = 2.0; // 平衡阈值(℃/10min)
|
||
private const int DATA_COLLECTION_INTERVAL = 1000; // 1秒采集一次
|
||
private const int WINDOW_CHECK_INTERVAL = 60000; // 1分钟检查一次窗口
|
||
private const int MAX_DATA_POINTS = 36000; // 最大数据点(1小时,每秒1个)
|
||
|
||
// 实验窗口定义
|
||
private readonly List<ExperimentWindow> _experimentWindows = new List<ExperimentWindow>
|
||
{
|
||
// 前30分钟窗口 - 平衡时间30min
|
||
new ExperimentWindow { WindowStart = 0, WindowEnd = 10, CalculationCount = 1, FinalBalanceTime = 30 },
|
||
new ExperimentWindow { WindowStart = 5, WindowEnd = 15, CalculationCount = 2, FinalBalanceTime = 30 },
|
||
new ExperimentWindow { WindowStart = 10, WindowEnd = 20, CalculationCount = 3, FinalBalanceTime = 30 },
|
||
new ExperimentWindow { WindowStart = 15, WindowEnd = 25, CalculationCount = 4, FinalBalanceTime = 30 },
|
||
new ExperimentWindow { WindowStart = 20, WindowEnd = 30, CalculationCount = 5, FinalBalanceTime = 30 },
|
||
|
||
// 后30分钟窗口 - 分别对应35-60min平衡时间
|
||
new ExperimentWindow { WindowStart = 25, WindowEnd = 35, CalculationCount = 1, FinalBalanceTime = 35 },
|
||
new ExperimentWindow { WindowStart = 30, WindowEnd = 40, CalculationCount = 2, FinalBalanceTime = 40 },
|
||
new ExperimentWindow { WindowStart = 35, WindowEnd = 45, CalculationCount = 3, FinalBalanceTime = 45 },
|
||
new ExperimentWindow { WindowStart = 40, WindowEnd = 50, CalculationCount = 4, FinalBalanceTime = 50 },
|
||
new ExperimentWindow { WindowStart = 45, WindowEnd = 55, CalculationCount = 5, FinalBalanceTime = 55 },
|
||
new ExperimentWindow { WindowStart = 50, WindowEnd = 60, CalculationCount = 6, FinalBalanceTime = 60 }
|
||
};
|
||
|
||
// 温度读取委托
|
||
public Func<TemperatureDataPoint> ReadTemperatureData { get; set; }
|
||
|
||
// 事件
|
||
public event EventHandler<ExperimentProgressEventArgs> ExperimentProgressChanged;
|
||
public event EventHandler<TemperatureDataPoint> TemperatureDataReceived;
|
||
public event EventHandler<ExperimentWindow> WindowChecked;
|
||
public event EventHandler<bool> ExperimentCompleted;
|
||
public event EventHandler<string> LogMessage;
|
||
|
||
public TemperatureBalanceCalculator()
|
||
{
|
||
InitializeTimers();
|
||
}
|
||
|
||
private void InitializeTimers()
|
||
{
|
||
// 1秒采集计时器
|
||
_dataCollectionTimer = new System.Timers.Timer(DATA_COLLECTION_INTERVAL);
|
||
_dataCollectionTimer.Elapsed += DataCollectionTimer_Elapsed;
|
||
_dataCollectionTimer.AutoReset = true;
|
||
|
||
// 1分钟窗口检查计时器
|
||
_windowCheckTimer = new System.Timers.Timer(WINDOW_CHECK_INTERVAL);
|
||
_windowCheckTimer.Elapsed += WindowCheckTimer_Elapsed;
|
||
_windowCheckTimer.AutoReset = true;
|
||
}
|
||
|
||
// 核心:1秒温度采集
|
||
private void DataCollectionTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
if (!_isExperimentRunning || ReadTemperatureData == null)
|
||
return;
|
||
|
||
try
|
||
{
|
||
var tempData = ReadTemperatureData();
|
||
if (tempData.IsValid)
|
||
{
|
||
AddTemperatureData(tempData);
|
||
TemperatureDataReceived?.Invoke(this, tempData);
|
||
|
||
_dataCollectionCount++;
|
||
_lastCollectionTime = DateTime.Now;
|
||
|
||
// 每10秒更新一次进度(避免过于频繁的UI更新)
|
||
if (_dataCollectionCount % 10 == 0)
|
||
{
|
||
UpdateExperimentProgress();
|
||
}
|
||
|
||
// 记录日志
|
||
LogMessage?.Invoke(this, $"温度采集: {tempData}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogMessage?.Invoke(this, $"温度采集失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
// 核心:每分钟窗口检查
|
||
private void WindowCheckTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
if (!_isExperimentRunning)
|
||
return;
|
||
|
||
_currentExperimentMinute++;
|
||
|
||
// 检查是否超时
|
||
if (_currentExperimentMinute >= TOTAL_EXPERIMENT_TIME)
|
||
{
|
||
StopExperiment(false, "实验时间结束(60分钟)");
|
||
return;
|
||
}
|
||
|
||
// 检查当前窗口
|
||
CheckCurrentWindows();
|
||
|
||
// 更新进度
|
||
UpdateExperimentProgress();
|
||
|
||
LogMessage?.Invoke(this, $"进入第 {_currentExperimentMinute} 分钟");
|
||
}
|
||
|
||
// 核心:添加温度数据
|
||
public void AddTemperatureData(TemperatureDataPoint dataPoint)
|
||
{
|
||
lock (_dataLock)
|
||
{
|
||
_temperatureData.Add(dataPoint);
|
||
|
||
// 记录初始温度
|
||
if (_temperatureData.Count == 1)
|
||
{
|
||
_initialTemperature = dataPoint;
|
||
LogMessage?.Invoke(this, "记录初始温度");
|
||
}
|
||
|
||
// 限制数据量
|
||
if (_temperatureData.Count > MAX_DATA_POINTS)
|
||
{
|
||
_temperatureData.RemoveAt(0);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 核心:检查当前所有应检查的窗口
|
||
private void CheckCurrentWindows()
|
||
{
|
||
var windowsToCheck = _experimentWindows
|
||
.Where(w => !w.IsChecked && _currentExperimentMinute >= w.WindowEnd)
|
||
.ToList();
|
||
|
||
foreach (var window in windowsToCheck)
|
||
{
|
||
CheckWindowBalance(window);
|
||
}
|
||
|
||
// 设置当前窗口
|
||
foreach (var window in _experimentWindows)
|
||
{
|
||
window.IsCurrentWindow = (_currentExperimentMinute >= window.WindowStart &&
|
||
_currentExperimentMinute <= window.WindowEnd);
|
||
}
|
||
}
|
||
|
||
// 核心:计算窗口平衡状态
|
||
private void CheckWindowBalance(ExperimentWindow window)
|
||
{
|
||
var windowData = GetTemperatureDataInRange(window.WindowStart, window.WindowEnd);
|
||
window.DataPointsCount = windowData.Count;
|
||
|
||
if (windowData.Count < 10) // 至少需要10个数据点(10秒)
|
||
{
|
||
LogMessage?.Invoke(this, $"窗口{window.WindowStart}-{window.WindowEnd}min: 数据不足({windowData.Count}个点)");
|
||
window.IsChecked = true;
|
||
window.CheckTime = DateTime.Now;
|
||
return;
|
||
}
|
||
|
||
// 计算温度斜率
|
||
var slopes = CalculateTemperatureSlopes(windowData);
|
||
window.Slopes = slopes;
|
||
|
||
// 判断平衡
|
||
window.IsBalanced = slopes.IsBalanced(BALANCE_THRESHOLD);
|
||
window.IsChecked = true;
|
||
window.CheckTime = DateTime.Now;
|
||
|
||
LogMessage?.Invoke(this, $"窗口检查: {window}");
|
||
|
||
// 触发事件
|
||
WindowChecked?.Invoke(this, window);
|
||
|
||
// 如果平衡,停止实验
|
||
if (window.IsBalanced)
|
||
{
|
||
StopExperiment(true, $"在窗口{window.WindowStart}-{window.WindowEnd}min达到平衡");
|
||
}
|
||
}
|
||
|
||
// 核心:计算温度斜率(线性回归)
|
||
public TemperatureSlopes CalculateTemperatureSlopes(List<TemperatureDataPoint> data)
|
||
{
|
||
if (data.Count < 2)
|
||
return new TemperatureSlopes();
|
||
|
||
int sampleCount = Math.Min(data.Count, 600); // 最多使用600个点(10分钟数据,每秒1个)
|
||
int step = data.Count / sampleCount;
|
||
step = Math.Max(step, 1);
|
||
|
||
List<double> timePoints = new List<double>();
|
||
List<double> scaleTemps = new List<double>();
|
||
List<double> sampleTemps = new List<double>();
|
||
List<double> slideTemps = new List<double>();
|
||
|
||
var firstTime = data[0].Timestamp;
|
||
for (int i = 0; i < data.Count; i += step)
|
||
{
|
||
if (i >= data.Count) break;
|
||
|
||
timePoints.Add((data[i].Timestamp - firstTime).TotalMinutes);
|
||
scaleTemps.Add(data[i].ScaleTemp);
|
||
sampleTemps.Add(data[i].SampleTemp);
|
||
slideTemps.Add(data[i].SlideTemp);
|
||
}
|
||
|
||
return new TemperatureSlopes
|
||
{
|
||
ScaleSlope = CalculateLinearRegressionSlope(timePoints.ToArray(), scaleTemps.ToArray()),
|
||
SampleSlope = CalculateLinearRegressionSlope(timePoints.ToArray(), sampleTemps.ToArray()),
|
||
SlideSlope = CalculateLinearRegressionSlope(timePoints.ToArray(), slideTemps.ToArray())
|
||
};
|
||
}
|
||
|
||
// 核心:线性回归计算斜率(优化版)
|
||
private double CalculateLinearRegressionSlope(double[] x, double[] y)
|
||
{
|
||
int n = x.Length;
|
||
if (n < 2) return 0;
|
||
|
||
double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
|
||
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
sumX += x[i];
|
||
sumY += y[i];
|
||
sumXY += x[i] * y[i];
|
||
sumX2 += x[i] * x[i];
|
||
}
|
||
|
||
double denominator = n * sumX2 - sumX * sumX;
|
||
if (Math.Abs(denominator) < 0.0001) return 0;
|
||
|
||
return (n * sumXY - sumX * sumY) / denominator;
|
||
}
|
||
|
||
// 获取最近10分钟的斜率(用于实时显示)
|
||
public TemperatureSlopes GetRecentTemperatureSlopes()
|
||
{
|
||
int recentStart = Math.Max(0, _currentExperimentMinute - 10);
|
||
var recentData = GetTemperatureDataInRange(recentStart, _currentExperimentMinute);
|
||
return CalculateTemperatureSlopes(recentData);
|
||
}
|
||
|
||
// 获取最近N秒的实时温度变化(用于快速响应)
|
||
public TemperatureSlopes GetRecentTemperatureSlopes(int lastSeconds)
|
||
{
|
||
if (_temperatureData.Count == 0)
|
||
return new TemperatureSlopes();
|
||
|
||
var cutoffTime = DateTime.Now.AddSeconds(-lastSeconds);
|
||
var recentData = _temperatureData
|
||
.Where(d => d.Timestamp >= cutoffTime)
|
||
.ToList();
|
||
|
||
return CalculateTemperatureSlopes(recentData);
|
||
}
|
||
|
||
// 获取指定时间范围内的数据
|
||
private List<TemperatureDataPoint> GetTemperatureDataInRange(int startMinute, int endMinute)
|
||
{
|
||
lock (_dataLock)
|
||
{
|
||
if (_temperatureData.Count == 0 || _experimentStartTime == DateTime.MinValue)
|
||
return new List<TemperatureDataPoint>();
|
||
|
||
var startTime = _experimentStartTime.AddMinutes(startMinute);
|
||
var endTime = _experimentStartTime.AddMinutes(endMinute);
|
||
|
||
return _temperatureData
|
||
.Where(d => d.Timestamp >= startTime && d.Timestamp <= endTime)
|
||
.ToList();
|
||
}
|
||
}
|
||
|
||
// 更新实验进度
|
||
private void UpdateExperimentProgress()
|
||
{
|
||
_currentExperimentSecond = (int)(DateTime.Now - _experimentStartTime).TotalSeconds;
|
||
|
||
var currentTemp = GetCurrentTemperatureData();
|
||
var recentSlopes = GetRecentTemperatureSlopes(30); // 最近30秒的斜率
|
||
var currentWindow = _experimentWindows.FirstOrDefault(w => w.IsCurrentWindow);
|
||
|
||
// 计算数据采集频率
|
||
double dataPointsPerSecond = 0;
|
||
if (_experimentStartTime != DateTime.MinValue && _dataCollectionCount > 0)
|
||
{
|
||
var elapsedSeconds = (DateTime.Now - _experimentStartTime).TotalSeconds;
|
||
dataPointsPerSecond = elapsedSeconds > 0 ? _dataCollectionCount / elapsedSeconds : 0;
|
||
}
|
||
|
||
// 获取平衡状态
|
||
bool isBalanced = false;
|
||
int? finalBalanceTime = null;
|
||
var balancedWindow = _experimentWindows.LastOrDefault(w => w.IsBalanced);
|
||
if (balancedWindow != null)
|
||
{
|
||
isBalanced = true;
|
||
finalBalanceTime = balancedWindow.FinalBalanceTime;
|
||
}
|
||
|
||
var args = new ExperimentProgressEventArgs
|
||
{
|
||
CurrentMinute = _currentExperimentMinute,
|
||
CurrentSecond = _currentExperimentSecond,
|
||
CurrentTemperature = currentTemp,
|
||
CurrentWindow = currentWindow,
|
||
RecentSlopes = recentSlopes,
|
||
ProgressPercentage = (_currentExperimentMinute * 100.0) / TOTAL_EXPERIMENT_TIME,
|
||
IsExperimentRunning = _isExperimentRunning,
|
||
IsBalanced = isBalanced,
|
||
FinalBalanceTime = finalBalanceTime,
|
||
DataPointsPerSecond = dataPointsPerSecond,
|
||
TotalDataPoints = _temperatureData.Count
|
||
};
|
||
|
||
ExperimentProgressChanged?.Invoke(this, args);
|
||
}
|
||
|
||
// 实验控制方法
|
||
public void StartExperiment()
|
||
{
|
||
if (_isExperimentRunning)
|
||
return;
|
||
|
||
ResetExperiment();
|
||
|
||
_experimentStartTime = DateTime.Now;
|
||
_currentExperimentMinute = 0;
|
||
_currentExperimentSecond = 0;
|
||
_dataCollectionCount = 0;
|
||
_isExperimentRunning = true;
|
||
|
||
_dataCollectionTimer.Start();
|
||
_windowCheckTimer.Start();
|
||
|
||
LogMessage?.Invoke(this, $"实验开始于: {_experimentStartTime:HH:mm:ss.fff}");
|
||
UpdateExperimentProgress();
|
||
}
|
||
|
||
public void StopExperiment(bool isBalanced, string reason)
|
||
{
|
||
if (!_isExperimentRunning)
|
||
return;
|
||
|
||
_isExperimentRunning = false;
|
||
_dataCollectionTimer.Stop();
|
||
_windowCheckTimer.Stop();
|
||
|
||
LogMessage?.Invoke(this, $"实验停止: {reason}, 是否平衡: {isBalanced}");
|
||
|
||
ExperimentCompleted?.Invoke(this, isBalanced);
|
||
UpdateExperimentProgress();
|
||
}
|
||
|
||
|
||
|
||
public void ResumeExperiment()
|
||
{
|
||
if (!_isExperimentRunning && _currentExperimentMinute < TOTAL_EXPERIMENT_TIME)
|
||
{
|
||
_isExperimentRunning = true;
|
||
_dataCollectionTimer.Start();
|
||
_windowCheckTimer.Start();
|
||
LogMessage?.Invoke(this, "实验已继续");
|
||
UpdateExperimentProgress();
|
||
}
|
||
}
|
||
|
||
public void ResetExperiment()
|
||
{
|
||
lock (_dataLock)
|
||
{
|
||
_temperatureData.Clear();
|
||
_initialTemperature = null;
|
||
|
||
foreach (var window in _experimentWindows)
|
||
{
|
||
window.IsBalanced = false;
|
||
window.IsChecked = false;
|
||
window.IsCurrentWindow = false;
|
||
window.Slopes = new TemperatureSlopes();
|
||
window.CheckTime = null;
|
||
window.DataPointsCount = 0;
|
||
}
|
||
}
|
||
|
||
_currentExperimentMinute = 0;
|
||
_currentExperimentSecond = 0;
|
||
_dataCollectionCount = 0;
|
||
_isExperimentRunning = false;
|
||
|
||
LogMessage?.Invoke(this, "实验已重置");
|
||
UpdateExperimentProgress();
|
||
}
|
||
|
||
// 统计方法
|
||
public ExperimentStatistics GetStatistics()
|
||
{
|
||
var stats = new ExperimentStatistics();
|
||
|
||
lock (_dataLock)
|
||
{
|
||
// 基础统计
|
||
stats.TotalDataPoints = _temperatureData.Count;
|
||
stats.DataCollectionCount = _dataCollectionCount;
|
||
if (_temperatureData.Count > 0)
|
||
{
|
||
stats.StartTime = _temperatureData.First().Timestamp;
|
||
stats.EndTime = _temperatureData.Last().Timestamp;
|
||
|
||
// 计算数据采集频率
|
||
if ((stats.EndTime - stats.StartTime).TotalSeconds > 0)
|
||
{
|
||
stats.DataPointsPerSecond = stats.TotalDataPoints / (stats.EndTime - stats.StartTime).TotalSeconds;
|
||
}
|
||
|
||
// 温度统计
|
||
stats.InitialScaleTemp = _initialTemperature?.ScaleTemp ?? 0;
|
||
stats.CurrentScaleTemp = _temperatureData.Last().ScaleTemp;
|
||
stats.MaxScaleTemp = _temperatureData.Max(d => d.ScaleTemp);
|
||
stats.MinScaleTemp = _temperatureData.Min(d => d.ScaleTemp);
|
||
|
||
stats.CurrentSampleTemp = _temperatureData.Last().SampleTemp;
|
||
stats.MaxSampleTemp = _temperatureData.Max(d => d.SampleTemp);
|
||
stats.MinSampleTemp = _temperatureData.Min(d => d.SampleTemp);
|
||
|
||
stats.CurrentSlideTemp = _temperatureData.Last().SlideTemp;
|
||
stats.MaxSlideTemp = _temperatureData.Max(d => d.SlideTemp);
|
||
stats.MinSlideTemp = _temperatureData.Min(d => d.SlideTemp);
|
||
|
||
// 计算温度稳定性
|
||
stats.TemperatureStability = CalculateTemperatureStability();
|
||
}
|
||
|
||
// 窗口统计
|
||
stats.TotalWindowsCount = _experimentWindows.Count;
|
||
stats.BalancedWindowsCount = _experimentWindows.Count(w => w.IsBalanced);
|
||
stats.CheckedWindowsCount = _experimentWindows.Count(w => w.IsChecked);
|
||
|
||
// 进度统计
|
||
stats.CurrentMinute = _currentExperimentMinute;
|
||
stats.CurrentSecond = _currentExperimentSecond;
|
||
stats.TotalMinutes = TOTAL_EXPERIMENT_TIME;
|
||
stats.ProgressPercentage = (_currentExperimentMinute * 100.0) / TOTAL_EXPERIMENT_TIME;
|
||
stats.IsExperimentRunning = _isExperimentRunning;
|
||
|
||
// 平衡状态
|
||
var balancedWindow = _experimentWindows.LastOrDefault(w => w.IsBalanced);
|
||
if (balancedWindow != null)
|
||
{
|
||
stats.IsExperimentBalanced = true;
|
||
stats.FinalBalanceTime = balancedWindow.FinalBalanceTime;
|
||
stats.FinalBalanceWindow = $"{balancedWindow.WindowStart}-{balancedWindow.WindowEnd}min";
|
||
}
|
||
|
||
// 当前窗口
|
||
stats.CurrentWindow = _experimentWindows.FirstOrDefault(w => w.IsCurrentWindow);
|
||
|
||
// 计算成功率
|
||
if (stats.CheckedWindowsCount > 0)
|
||
{
|
||
stats.SuccessRate = (stats.BalancedWindowsCount * 100.0) / stats.CheckedWindowsCount;
|
||
}
|
||
}
|
||
|
||
return stats;
|
||
}
|
||
|
||
private double CalculateTemperatureStability()
|
||
{
|
||
if (_temperatureData.Count < 10) return 0;
|
||
|
||
// 计算最近1分钟的温度波动
|
||
var recentData = _temperatureData
|
||
.Where(d => d.Timestamp >= DateTime.Now.AddMinutes(-1))
|
||
.ToList();
|
||
|
||
if (recentData.Count < 2) return 0;
|
||
|
||
double sum = 0;
|
||
double mean = recentData.Average(d => d.ScaleTemp);
|
||
|
||
foreach (var data in recentData)
|
||
{
|
||
sum += Math.Pow(data.ScaleTemp - mean, 2);
|
||
}
|
||
|
||
return Math.Sqrt(sum / recentData.Count); // 标准差
|
||
}
|
||
|
||
public TemperatureDataPoint GetCurrentTemperatureData()
|
||
{
|
||
lock (_dataLock)
|
||
{
|
||
return _temperatureData.LastOrDefault() ?? new TemperatureDataPoint();
|
||
}
|
||
}
|
||
|
||
public List<TemperatureDataPoint> GetAllTemperatureData()
|
||
{
|
||
lock (_dataLock)
|
||
{
|
||
return new List<TemperatureDataPoint>(_temperatureData);
|
||
}
|
||
}
|
||
|
||
public List<ExperimentWindow> GetAllWindows()
|
||
{
|
||
return new List<ExperimentWindow>(_experimentWindows);
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_dataCollectionTimer?.Stop();
|
||
_windowCheckTimer?.Stop();
|
||
_dataCollectionTimer?.Dispose();
|
||
_windowCheckTimer?.Dispose();
|
||
}
|
||
}
|
||
|
||
// 完整的实验统计类
|
||
public class ExperimentStatistics
|
||
{
|
||
// 基础信息
|
||
public int TotalDataPoints { get; set; }
|
||
public int DataCollectionCount { get; set; }
|
||
public double DataPointsPerSecond { get; set; }
|
||
public DateTime StartTime { get; set; }
|
||
public DateTime EndTime { get; set; }
|
||
|
||
// 温度统计
|
||
public double InitialScaleTemp { get; set; }
|
||
public double CurrentScaleTemp { get; set; }
|
||
public double MaxScaleTemp { get; set; }
|
||
public double MinScaleTemp { get; set; }
|
||
|
||
public double CurrentSampleTemp { get; set; }
|
||
public double MaxSampleTemp { get; set; }
|
||
public double MinSampleTemp { get; set; }
|
||
|
||
public double CurrentSlideTemp { get; set; }
|
||
public double MaxSlideTemp { get; set; }
|
||
public double MinSlideTemp { get; set; }
|
||
|
||
public double TemperatureStability { get; set; }
|
||
|
||
// 窗口统计
|
||
public int TotalWindowsCount { get; set; }
|
||
public int BalancedWindowsCount { get; set; }
|
||
public int CheckedWindowsCount { get; set; }
|
||
public double SuccessRate { get; set; }
|
||
public ExperimentWindow CurrentWindow { get; set; }
|
||
|
||
// 进度信息
|
||
public int CurrentMinute { get; set; }
|
||
public int CurrentSecond { get; set; }
|
||
public int TotalMinutes { get; set; }
|
||
public double ProgressPercentage { get; set; }
|
||
public bool IsExperimentRunning { get; set; }
|
||
|
||
// 平衡结果
|
||
public bool IsExperimentBalanced { get; set; }
|
||
public int? FinalBalanceTime { get; set; }
|
||
public string FinalBalanceWindow { get; set; }
|
||
|
||
public override string ToString()
|
||
{
|
||
return $"实验进度: {CurrentMinute}:{CurrentSecond:00} / 60:00 ({ProgressPercentage:F1}%)\n" +
|
||
$"数据点: {TotalDataPoints} ({DataPointsPerSecond:F1}/秒)\n" +
|
||
$"温度: 炉内{CurrentScaleTemp:F1}℃, 样品内{CurrentSampleTemp:F1}℃, 样品表面{CurrentSlideTemp:F1}℃\n" +
|
||
$"窗口: {BalancedWindowsCount}/{CheckedWindowsCount}平衡 (成功率{SuccessRate:F1}%)\n" +
|
||
$"平衡: {(IsExperimentBalanced ? $"是 ({FinalBalanceWindow}, {FinalBalanceTime}min)" : "否")}";
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
|