using DevExpress.Xpf.Charts; using jiancaiburanxing; using Microsoft.Win32; using NModbus; using NModbus.Device; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using 建材不燃性试验炉.Data; // 这是关键 namespace 建材不燃性试验炉 { public partial class MainWindow : Window { #region 私有字段 private DispatcherTimer _statusTimer; private DispatcherTimer _testTimer; private TimeSpan _testElapsedTime = TimeSpan.Zero; private bool _isTestRunning = false; private bool _isCalibrating = false; private BackgroundWorker _calibrationWorker; private Random _random = new Random(); //温度平衡相关 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; // 是否正在进行平衡判断 // 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();//温度平衡相关变量 } 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; // 试验计时器 _testTimer = new DispatcherTimer(); _testTimer.Interval = TimeSpan.FromSeconds(1); _testTimer.Tick += TestTimer_Tick; //初始化平衡判断定时器 _balanceTimer = new DispatcherTimer(); _balanceTimer.Interval = TimeSpan.FromSeconds(1); _balanceTimer.Tick += BalanceTimer_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 void TestTimer_Tick(object sender, EventArgs e) { if (_isTestRunning) { _testElapsedTime = _testElapsedTime.Add(TimeSpan.FromSeconds(1)); txtTestTime.Text = _testElapsedTime.ToString(@"hh\:mm\:ss"); // 更新进度 UpdateTestProgress(); // 检查计时节点 CheckTimeReminders(); } } private void BalanceTimer_Tick(object sender, EventArgs e) { try { // 1. 计时递增 _balanceElapsedSeconds++; txtBalanceTimeElapsed.Text = $"{_balanceElapsedSeconds}秒"; // 2. 采集当前炉壁温度 double currentTemp = GetCurrentFurnaceTemp(); _tempDataList.Add(currentTemp); // 4. 计算最大值、最小值、平均值 _maxTemp = double.MinValue; _minTemp = double.MaxValue; double sumTemp = 0; foreach (var temp in _tempDataList) { if (temp > _maxTemp) _maxTemp = temp; if (temp < _minTemp) _minTemp = temp; sumTemp += temp; } _avgTemp = sumTemp / _tempDataList.Count; double currentMaxDiff = _maxTemp - _avgTemp; // 最大值与平均值的温差 double tempRange = _maxTemp - _minTemp; // 温度波动范围 txtBalanceCondition.Text = $"平衡条件: 最大温差≤{_maxTempDiffThreshold}°C 且 温度波动≤±{_tempRangeThreshold}°C (当前: {tempRange:F1}°C)"; // 6. 判断是否达到平衡条件(计时结束后) if (_balanceElapsedSeconds >= _balanceTotalSeconds) { _balanceTimer.Stop(); // 停止定时器 _isBalanceChecking = false; bool isBalanced = currentMaxDiff <= _maxTempDiffThreshold && tempRange <= _tempRangeThreshold * 2; // 更新平衡状态显示 if (isBalanced) { balanceStatusBorder.Background = new SolidColorBrush(Colors.Green); txtBalanceResult.Text = "已平衡"; txtBalanceStatus.Text = $"温度平衡状态: 达到平衡(最大温差{currentMaxDiff:F1}°C,温度波动{tempRange:F1}°C)"; UpdateStatusBar("温度平衡判断完成:炉温已稳定"); } } } catch (Exception ex) { MessageBox.Show($"温度平衡判断出错:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); _balanceTimer.Stop(); _isBalanceChecking = false; } } // 获取当前炉壁温度 private double GetCurrentFurnaceTemp() { 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($"读取Modbus温度失败: {ex.Message}"); return 0.0; } } #endregion #region 试验控制相关 //开始试验 private void btnStartTest_Click(object sender, RoutedEventArgs e) { StartTest(); try { _isTestRunning = true; _testElapsedTime = TimeSpan.Zero; // 更新按钮状态 btnStartTest.IsEnabled = false; btnStop.IsEnabled = true; // 更新系统状态 txtSystemStatus.Text = "试验中"; statusIndicator2.Fill = new SolidColorBrush(Colors.Red); // 启动试验计时器 _testTimer.Start(); // 重置进度条 progressBar.Width = 0; txtProgress.Text = "0%"; UpdateStatusBar("试验开始"); } catch (Exception ex) { MessageBox.Show($"启动试验失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); _isTestRunning = false; btnStartTest.IsEnabled = true; btnStop.IsEnabled = false; } } private void btnStop_Click(object sender, RoutedEventArgs e) { EndTest(); StopTest(); //停止平衡判断 if (_isBalanceChecking) { _balanceTimer.Stop(); _isBalanceChecking = false; btnStartBalanceCheck.IsEnabled = true; txtBalanceStatus.Text = "温度平衡状态: 已停止判断"; } } private void StopTest() { _isTestRunning = false; _testTimer.Stop(); // 更新按钮状态 btnStartTest.IsEnabled = true; btnStop.IsEnabled = false; // 更新系统状态 txtSystemStatus.Text = "试验结束"; statusIndicator2.Fill = new SolidColorBrush(Colors.Green); // 检查是否已进行后称重 string finalText = txtFinalWeight.Text; bool hasAfterWeight = false; if (!string.IsNullOrEmpty(finalText) && finalText != "请输入试验后重量(g)") { if (double.TryParse(finalText, out double afterWeight) && afterWeight >= 0.1) { hasAfterWeight = true; } } if (!hasAfterWeight) { // 提醒进行后称重 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 UpdateTestProgress() { // 假设试验总时长为60分钟 TimeSpan totalDuration = TimeSpan.FromMinutes(60); double progress = (_testElapsedTime.TotalSeconds / totalDuration.TotalSeconds) * 100; if (progress > 100) progress = 100; Dispatcher.Invoke(() => { // 更新进度条 double maxWidth = 400; // 进度条容器宽度 progressBar.Width = maxWidth * (progress / 100); txtProgress.Text = $"{progress:F1}%"; }); } private void CheckTimeReminders() { if (!_isTestRunning) return; TimeSpan reminderTime = TimeSpan.Zero; string reminderText = ""; if (_testElapsedTime.TotalMinutes >= 10 && chkTime10min.IsChecked == true) { reminderTime = TimeSpan.FromMinutes(10); reminderText = "10分钟节点"; } else if (_testElapsedTime.TotalMinutes >= 30 && chkTime30min.IsChecked == true) { reminderTime = TimeSpan.FromMinutes(30); reminderText = "30分钟节点"; } else if (_testElapsedTime.TotalMinutes >= 60 && chkTime60min.IsChecked == true) { reminderTime = TimeSpan.FromMinutes(60); reminderText = "60分钟节点"; } if (reminderTime != TimeSpan.Zero) { txtTimeReminder.Text = $"已完成: {reminderText}"; // 显示消息 if (_testElapsedTime.TotalMinutes >= reminderTime.TotalMinutes && _testElapsedTime.TotalMinutes < reminderTime.TotalMinutes + 1) { UpdateStatusBar($"达到{reminderText}"); } } // 更新下一个节点 UpdateNextReminder(); } private void UpdateNextReminder() { if (!_isTestRunning) return; if (_testElapsedTime.TotalMinutes < 10 && chkTime10min.IsChecked == true) { txtTimeReminder.Text = $"下一个节点: 10分钟 (剩余:{10 - _testElapsedTime.TotalMinutes:F1}分钟)"; } else if (_testElapsedTime.TotalMinutes < 30 && chkTime30min.IsChecked == true) { txtTimeReminder.Text = $"下一个节点: 30分钟 (剩余:{30 - _testElapsedTime.TotalMinutes:F1}分钟)"; } else if (_testElapsedTime.TotalMinutes < 60 && chkTime60min.IsChecked == true) { txtTimeReminder.Text = $"下一个节点: 60分钟 (剩余:{60 - _testElapsedTime.TotalMinutes:F1}分钟)"; } else { txtTimeReminder.Text = "所有计时节点已完成"; } } #endregion #region 称重相关 private bool _isTestStopped = false; // 开始测试 private void StartTest() { _isTestStopped = false; // 禁用后称重输入 DisableFinalWeightInput(); } // 停止测试 private void EndTest() { _isTestStopped = true; // 启用后称重输入 EnableFinalWeightInput(); } // 禁用后称重输入 private void DisableFinalWeightInput() { if (Dispatcher.CheckAccess()) { txtFinalWeight.IsEnabled = false; txtFinalWeight.Background = Brushes.LightGray; txtFinalWeight.Text = "请输入试验后重量(g)"; } else { Dispatcher.Invoke(() => DisableFinalWeightInput()); } } // 启用后称重输入 private void EnableFinalWeightInput() { if (Dispatcher.CheckAccess()) { txtFinalWeight.IsEnabled = true; txtFinalWeight.Background = Brushes.White; txtFinalWeight.Text = ""; } else { Dispatcher.Invoke(() => EnableFinalWeightInput()); } } private void txtFinalWeight_TextChanged(object sender, TextChangedEventArgs e) { // 如果测试未停止,阻止输入 if (!_isTestStopped) { if (Dispatcher.CheckAccess()) { txtFinalWeight.Text = "请输入试验后重量(g)"; } else { Dispatcher.Invoke(() => txtFinalWeight.Text = "请输入试验后重量(g)"); } return; } // 实时更新重量计算 UpdateWeightCalculation(); } private void txtInitialWeight_TextChanged(object sender, TextChangedEventArgs e) { // 实时更新重量计算 UpdateWeightCalculation(); } private void UpdateWeightCalculation() { try { double initialWeight = 0; double finalWeight = 0; bool hasInitialWeight = false; bool hasFinalWeight = false; // 解析前称重 string initialText = txtInitialWeight.Text; if (!string.IsNullOrEmpty(initialText) && initialText != "请输入试验前重量(g)") { if (double.TryParse(initialText, out initialWeight) && initialWeight >= 0.1) { hasInitialWeight = true; } } // 解析后称重 string finalText = txtFinalWeight.Text; if (!string.IsNullOrEmpty(finalText) && finalText != "请输入试验后重量(g)") { if (double.TryParse(finalText, out finalWeight) && finalWeight >= 0.1) { hasFinalWeight = true; } } // 计算重量损失 double weightLoss = 0; double weightLossPercentage = 0; string weightLossText = ""; string weightStatus = ""; Brush weightStatusColor = Brushes.Gray; if (hasInitialWeight) { if (hasFinalWeight) { weightLoss = initialWeight - finalWeight; weightLossPercentage = (weightLoss / initialWeight) * 100; weightLossText = $"损失:{weightLossPercentage:F1}%"; weightStatus = "已称重"; // 根据损失百分比设置颜色 if (weightLossPercentage > 50) { weightStatusColor = Brushes.Red; } else if (weightLossPercentage > 20) { weightStatusColor = Brushes.Orange; } else if (weightLossPercentage >= 0) { weightStatusColor = Brushes.Green; } else { // 重量增加的情况 weightStatusColor = Brushes.Purple; } } else { weightLossText = "等待后称重"; weightStatus = "前称重完成"; weightStatusColor = Brushes.Orange; } } else { weightLossText = "未称重"; weightStatus = "未称重"; weightStatusColor = Brushes.Gray; } // 更新显示 Dispatcher.Invoke(() => { string initialDisplay = hasInitialWeight ? $"{initialWeight:F1}g" : "未输入"; string finalDisplay = hasFinalWeight ? $"{finalWeight:F1}g" : "未输入"; txtCurrentWeight.Text = $"前:{initialDisplay} | 后:{finalDisplay}"; txtWeightLossDisplay.Text = weightLossText; txtWeightState.Text = weightStatus; weightStatusBorder.Background = weightStatusColor; // 更新详细文本 if (hasInitialWeight && hasFinalWeight) { string lossDescription = weightLoss > 0 ? "减少" : weightLoss < 0 ? "增加" : "无变化"; txtWeightLoss.Text = $"重量{lossDescription}: {Math.Abs(weightLoss):F1}g ({Math.Abs(weightLossPercentage):F1}%)"; txtWeightStatus.Text = weightLoss > 0 ? "试样质量减少" : weightLoss < 0 ? "试样质量增加" : "试样质量无变化"; } else if (hasInitialWeight) { txtWeightLoss.Text = "等待后称重数据"; txtWeightStatus.Text = ""; } else { txtWeightLoss.Text = ""; txtWeightStatus.Text = ""; } }); } catch (Exception ex) { Debug.WriteLine($"重量计算错误: {ex.Message}"); } } #endregion #region 燃烧状态相关 private void chkHasFlame_Checked(object sender, RoutedEventArgs e) { UpdateCombustionStatus(); } private void chkHasFlame_Unchecked(object sender, RoutedEventArgs e) { UpdateCombustionStatus(); } private void chkHasSmoke_Checked(object sender, RoutedEventArgs e) { UpdateCombustionStatus(); } private void chkHasSmoke_Unchecked(object sender, RoutedEventArgs e) { UpdateCombustionStatus(); } private void txtCombustionNote_TextChanged(object sender, TextChangedEventArgs e) { SyncCombustionNoteToFireTime(); } private void UpdateCombustionStatus() { string flameStatus = chkHasFlame.IsChecked == true ? "有火焰" : "无火焰"; string smokeStatus = chkHasSmoke.IsChecked == true ? "有冒烟" : "无冒烟"; texfire.Text = $"{flameStatus}"; SyncCombustionNoteToFireTime(); texsmoking.Text = $"{smokeStatus}"; } private void SyncCombustionNoteToFireTime() { if (txtbfiretime != null && txtCombustionNote != null) { // 或者只在有内容时显示: if (!string.IsNullOrWhiteSpace(txtCombustionNote.Text)) { txtbfiretime.Text = $"火焰持续时间(s): {txtCombustionNote.Text}"; } else { txtbfiretime.Text = ""; } } } #endregion #region 温度监控相关 //private void UpdateSimulatedTemperatures() //{ // // 试验中的温度模拟 // double furnaceWallTemp = 750 + (_random.NextDouble() * 10 - 5); // 750±5°C // double sampleCenterTemp = 500 + (_random.NextDouble() * 50 - 25); // 500±25°C // double sampleSurfaceTemp = 600 + (_random.NextDouble() * 40 - 20); // 600±20°C // UpdateTemperatureDisplay(furnaceWallTemp, sampleCenterTemp, sampleSurfaceTemp); //} private void UpdateSimulatedTemperatures() { try { // 检查Modbus连接状态 if (_modbusMaster == null) { Debug.WriteLine("ModbusMaster为空,跳过更新"); return; } // 检查TCP连接状态 if (_tcpClient == null || !_tcpClient.Connected) { Debug.WriteLine("TCP连接断开,跳过更新"); return; } // 读取三个温度值 float furnaceTemp = 0, sampleInternalTemp = 0, sampleSurfaceTemp = 0; // 炉内温度相关 (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; } // 检查炉内温度波动 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; if (Math.Abs(targetTemp) < 0.001) // 目标温度为0,表示未设置 { statusText = $"{tempName}温度: {currentTemp:F1}°C"; backgroundColor = "#95A5A6"; // 灰色 - 未设置目标 } else if (actualFluctuation <= allowedFluctuation) { statusText = $"{tempName}温度波动正常 (±{actualFluctuation:F1}°C)"; backgroundColor = "#27AE60"; // 绿色 - 正常 } else if (actualFluctuation <= allowedFluctuation * 2) // 在2倍范围内 { statusText = $"{tempName}温度波动警告 (±{actualFluctuation:F1}°C)"; backgroundColor = "#F39C12"; // 橙色 - 警告 } else { statusText = $"{tempName}温度波动异常 (±{actualFluctuation:F1}°C)"; backgroundColor = "#E74C3C"; // 红色 - 异常 } // 更新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 btnShowChart_Click(object sender, RoutedEventArgs e) { try { // 创建并显示温度曲线图窗口 TemperatureChartWindow chartWindow = new TemperatureChartWindow(); chartWindow.Owner = this; chartWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; chartWindow.Show(); UpdateStatusBar("打开温度曲线图"); } catch (Exception ex) { MessageBox.Show($"无法打开温度曲线图: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } //private void btnBalanceChart_Click(object sender, RoutedEventArgs e) //{ // try // { // // 创建并显示温度曲线图窗口 // BalanceChartWindow chartWindow2 = new BalanceChartWindow(); // chartWindow2.Owner = this; // chartWindow2.WindowStartupLocation = WindowStartupLocation.CenterOwner; // chartWindow2.Show(); // UpdateStatusBar("打开温度曲线图"); // } // catch (Exception ex) // { // MessageBox.Show($"无法打开温度曲线图: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); // } //} 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 = ""; // 重置复选框 chkTime10min.IsChecked = false; chkTime30min.IsChecked = false; chkTime60min.IsChecked = false; chkHasFlame.IsChecked = false; chkHasSmoke.IsChecked = false; // 重置状态显示 txtCurrentWeight.Text = "前:0.0g | 后:0.0g"; txtWeightLossDisplay.Text = "损失:0.0%"; txtWeightState.Text = "未称重"; weightStatusBorder.Background = Brushes.Gray; // 停止试验 if (_isTestRunning) { StopTest(); } // 重置试验时间 _testElapsedTime = TimeSpan.Zero; txtTestTime.Text = "00:00:00"; // 重置进度条 progressBar.Width = 0; txtProgress.Text = "0%"; } private void btnSaveData_Click(object sender, RoutedEventArgs e) { try { SaveTestData(); UpdateStatusBar("数据已保存"); } catch (Exception ex) { MessageBox.Show($"保存数据失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void btnExportData_Click(object sender, RoutedEventArgs e) { try { ExportTestReport(); UpdateStatusBar("报告已导出"); } catch (Exception ex) { MessageBox.Show($"导出报告失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void SaveTestData() { // 创建数据保存对话框 SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"; saveDialog.FileName = $"试验数据_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; if (saveDialog.ShowDialog() == true) { StringBuilder data = new StringBuilder(); data.AppendLine("建材不燃性试验数据记录"); data.AppendLine($"生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); data.AppendLine("=".PadRight(50, '=')); data.AppendLine(); // 试样信息 data.AppendLine("一、试样信息"); data.AppendLine($" 名称/型号: {txtSampleName.Text}"); data.AppendLine($" 规格: {txtSampleSpec.Text}"); data.AppendLine($" 试验编号: {txtTestNo.Text}"); data.AppendLine(); // 重量数据 data.AppendLine("二、重量数据 (g)"); data.AppendLine($" 试验前重量: {txtInitialWeight.Text}"); data.AppendLine($" 试验后重量: {txtFinalWeight.Text}"); if (!string.IsNullOrEmpty(txtWeightLoss.Text)) { data.AppendLine($" 重量损失: {txtWeightLoss.Text}"); } data.AppendLine(); // 温度数据 data.AppendLine("三、温度数据 (°C)"); data.AppendLine($" 试验温度: {txtTestTemp.Text}"); data.AppendLine($" 允许波动: ±{txtTempFluctuation.Text}°C"); data.AppendLine(); // 试验过程 data.AppendLine("四、试验过程"); data.AppendLine($" 试验时间: {txtTestTime.Text}"); data.AppendLine($" 火焰: {(chkHasFlame.IsChecked == true ? "有" : "无")}"); data.AppendLine($" 冒烟: {(chkHasSmoke.IsChecked == true ? "有" : "无")}"); data.AppendLine($" 备注: {txtCombustionNote.Text}"); data.AppendLine(); data.AppendLine("=".PadRight(50, '=')); data.AppendLine("记录人: 系统自动生成"); data.AppendLine($"符合标准: GB/T 5464-2010"); // 写入文件 File.WriteAllText(saveDialog.FileName, data.ToString(), Encoding.UTF8); MessageBox.Show($"数据已保存到:\n{saveDialog.FileName}", "保存成功", MessageBoxButton.OK, MessageBoxImage.Information); } } private void ExportTestReport() { // 创建报告导出对话框 SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*"; saveDialog.FileName = $"试验报告_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; if (saveDialog.ShowDialog() == true) { StringBuilder csv = new StringBuilder(); // CSV标题行 csv.AppendLine("项目,数值,单位,备注"); // 添加数据行 csv.AppendLine($"试样名称,{txtSampleName.Text},,"); csv.AppendLine($"试样规格,{txtSampleSpec.Text},mm,"); csv.AppendLine($"试验编号,{txtTestNo.Text},,"); csv.AppendLine($"试验前重量,{txtInitialWeight.Text},g,"); csv.AppendLine($"试验后重量,{txtFinalWeight.Text},g,"); csv.AppendLine($"重量损失百分比,{txtWeightLossDisplay.Text.Replace("损失:", "")},%,"); csv.AppendLine($"试验温度,{txtTestTemp.Text},°C,"); csv.AppendLine($"温度波动允许值,{txtTempFluctuation.Text},°C,"); csv.AppendLine($"试验时间,{txtTestTime.Text},,"); csv.AppendLine($"是否有火焰,{(chkHasFlame.IsChecked == true ? "是" : "否")},,"); csv.AppendLine($"是否有冒烟,{(chkHasSmoke.IsChecked == true ? "是" : "否")},,"); csv.AppendLine($"燃烧状态备注,{txtCombustionNote.Text},,"); csv.AppendLine($"生成时间,{DateTime.Now:yyyy-MM-dd HH:mm:ss},,"); // 写入文件 File.WriteAllText(saveDialog.FileName, csv.ToString(), Encoding.UTF8); MessageBox.Show($"报告已导出到:\n{saveDialog.FileName}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information); } } //测试风机 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 btnStartBalanceCheck_Click(object sender, RoutedEventArgs e) { if (_isBalanceChecking) { MessageBox.Show("温度平衡判断正在进行中,请等待完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information); return; } try { // 1. 获取用户设置的参数 if (!int.TryParse(txtBalanceTime.Text, out _balanceTotalSeconds) || _balanceTotalSeconds <= 0) { MessageBox.Show("请输入有效的平衡计时秒数(正数)", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (!double.TryParse(txtMaxTempDiff.Text, out _maxTempDiffThreshold) || _maxTempDiffThreshold < 0) { MessageBox.Show("请输入有效的最大温差阈值(非负数)", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (!double.TryParse(txtTempRange.Text, out _tempRangeThreshold) || _tempRangeThreshold < 0) { MessageBox.Show("请输入有效的温度范围阈值(非负数)", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 2. 重置变量 _balanceElapsedSeconds = 0; _tempDataList.Clear(); _maxTemp = double.MinValue; _minTemp = double.MaxValue; _avgTemp = 0; _isBalanceChecking = true; // 3. 更新界面 txtBalanceTimeElapsed.Text = $"0秒"; txtBalanceStatus.Text = $"温度平衡状态: 正在判断"; balanceStatusBorder.Background = new SolidColorBrush(Colors.Gray); txtBalanceResult.Text = "判断中"; UpdateStatusBar($"开始温度平衡判断,计时..."); // 4. 启动定时器 _balanceTimer.Start(); // 5. 打开温度曲线图窗口 OpenBalanceChartWindow(); } catch (Exception ex) { MessageBox.Show($"启动平衡判断失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void OpenBalanceChartWindow() { try { // 创建温度获取函数 Func getFurnaceTemperature = () => { try { // 从Modbus读取炉内温度 if (_modbusMaster != null && _tcpClient != null && _tcpClient.Connected) { ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, 1230, 2); if (registers != null && registers.Length >= 2) { float temperature = c.UshortToFloat(registers[1], registers[0]); return temperature; } } return 0.0; } catch (Exception ex) { Debug.WriteLine($"获取炉内温度失败: {ex.Message}"); return 0.0; } }; // 创建并显示温度曲线图窗口 BalanceChartWindow chartWindow = new BalanceChartWindow(getFurnaceTemperature); chartWindow.Owner = this; chartWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner; chartWindow.Show(); UpdateStatusBar("打开温度平衡曲线图"); } catch (Exception ex) { MessageBox.Show($"无法打开温度曲线图: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void btnStopBalanceCheck_Click(object sender, RoutedEventArgs e) { if (!_isBalanceChecking) { MessageBox.Show("当前未进行温度平衡判断", "提示", MessageBoxButton.OK, MessageBoxImage.Information); return; } // 1. 停止定时器 _balanceTimer.Stop(); // 2. 重置状态 _isBalanceChecking = false; // 3. 更新界面提示 txtBalanceStatus.Text = $"温度平衡状态: 已手动停止(已计时{_balanceElapsedSeconds}秒)"; txtBalanceResult.Text = "已停止"; balanceStatusBorder.Background = new SolidColorBrush(Colors.Orange); UpdateStatusBar($"温度平衡判断已手动停止(已计时{_balanceElapsedSeconds}秒)"); } #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 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 } }