Files
NoBuilding/建材不燃性试验炉/MainWindow.xaml.cs
2026-02-03 19:59:21 +08:00

3307 lines
123 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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 = "请输入试验后重量";
}
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 = "请输入试验后重量";
}
else
{
Dispatcher.Invoke(() => txtFinalWeight.Text = "请输入试验后重量");
}
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 != "请输入试验后重量")
{
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;
// 标准条件310分钟内温度漂移不超过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
}