1604 lines
56 KiB
C#
1604 lines
56 KiB
C#
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
|
||
|
||
|
||
}
|
||
}
|