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 _tempDataList; // 存储采集的温度数据 private double _maxTemp; // 采集周期内最高温度 private double _minTemp; // 采集周期内最低温度 private double _avgTemp; // 采集周期内平均温度 private bool _isBalanceChecking; // 是否正在进行平衡判断 //#region 试验结束温度平衡相关 //private DispatcherTimer _finalBalanceTimer; //private List _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(); // _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 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(ref T windowInstance, Func 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(); // 清空数据列表 _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 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(); //} //// 试验结束温度平衡定时器 //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 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() // .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() // .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() .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 allTempData, List 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 allTempData, List 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 allTempData, List 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 _temperatureData = new List(); 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 _experimentWindows = new List { // 前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 ReadTemperatureData { get; set; } // 事件 public event EventHandler ExperimentProgressChanged; public event EventHandler TemperatureDataReceived; public event EventHandler WindowChecked; public event EventHandler ExperimentCompleted; public event EventHandler 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 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 timePoints = new List(); List scaleTemps = new List(); List sampleTemps = new List(); List slideTemps = new List(); 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 GetTemperatureDataInRange(int startMinute, int endMinute) { lock (_dataLock) { if (_temperatureData.Count == 0 || _experimentStartTime == DateTime.MinValue) return new List(); 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 GetAllTemperatureData() { lock (_dataLock) { return new List(_temperatureData); } } public List GetAllWindows() { return new List(_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 }