Files
NoBuilding/建材不燃性试验炉/MainWindow.xaml.cs
2026-01-31 09:14:24 +08:00

1604 lines
56 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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<double> _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<double>();
_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<float> updateAction)
{
try
{
// 安全检查
if (_modbusMaster == null || c == null)
{
Debug.WriteLine($"ModbusMaster或DataChange为空地址{address}跳过");
return;
}
// 读取寄存器
ushort[] registers = _modbusMaster.ReadHoldingRegisters(1, (ushort)address, (ushort)count);
// 空值检查
if (registers == null || registers.Length < count)
{
Debug.WriteLine($"读取地址{address}返回空值或长度不足");
return;
}
// 转换为浮点数
float value = c.UshortToFloat(registers[1], registers[0]);
updateAction?.Invoke(value);
}
catch (Exception ex)
{
Debug.WriteLine($"读取地址{address}失败:{ex.Message}");
}
}
//private void UpdateTemperatureDisplay(double furnaceWallTemp, double centerTemp, double surfaceTemp)
//{
// Dispatcher.Invoke(() =>
// {
// txtFurnaceWallTemp.Text = $"{furnaceWallTemp:F1}°C";
// txtSampleCenterTemp.Text = $"{centerTemp:F1}°C";
// txtSampleSurfaceTemp.Text = $"{surfaceTemp:F1}°C";
// // 检查温度波动
// CheckTemperatureFluctuation(furnaceWallTemp);
// });
//}
private void CheckAllTemperatureFluctuations(float furnaceTemp, float sampleInternalTemp, float sampleSurfaceTemp)
{
try
{
// 获取允许的波动范围
if (!double.TryParse(txtTempFluctuation.Text, out double allowedFluctuation))
{
allowedFluctuation = 5.0; // 默认值
}
// 获取目标温度
if (!double.TryParse(txtTestTemp.Text, out double targetTemp))
{
targetTemp = 0;
}
// 检查炉内温度波动
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<double> 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<T>(ref T windowInstance, Func<T> createFunc) where T : Window, new()
{
// 1. 停止当前窗口的定时器(不释放资源)
_statusTimer?.Stop();
// 2. 复用窗口实例:不存在则创建,存在则激活
if (windowInstance == null)
{
windowInstance = createFunc();
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
_statusTimer?.Start();
this.Show();
this.Activate();
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
return;
}
// 3. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show();
}
#endregion
}
}