using Microsoft.Win32; using Modbus.Device; using Modbus; using OfficeOpenXml; using System; using System.Configuration; using System.Data.SQLite; using System.IO; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using 口罩泄露定制款; using static ShanghaiEnvironmentalTechnology.Window5; namespace ShanghaiEnvironmentalTechnology { /// /// 防窒息压力测试窗口(Modbus通信+压力数据处理) /// public partial class Window3 : Window, IDisposable { DataChange c = new DataChange(); #region 寄存器/线圈地址定义(按功能分组) // 压力设置相关 private readonly ushort _pressureSettingRegisterAddress = 350; // 开阀压差判别(D350) private readonly ushort _pressureCloseSettingRegisterAddress = 352; // 关阀压差判别(D352) private readonly ushort _openRegisterAddress = 202; // 开阀压力记录地址 private readonly ushort _closeRegisterAddress = 206; // 关阀压力记录地址 private readonly ushort _pressureRegisterAddress = 1372; // 实时压力读取地址 // 测试控制线圈 private readonly ushort _testStartAddress = 0x0014; // 防窒息压力测试启动(M20) private readonly ushort _testStopAddress = 0x001E; // 防窒息压力测试停止(M30) private readonly ushort _calibrationCoilAddress = 0x0048; // 校准线圈(M72) #endregion #region 私有字段 private TcpClient _tcpClient; private IModbusMaster _modbusMaster; private System.Timers.Timer _pressureReadTimer; // 实时压力读取定时器 private System.Timers.Timer _settingReadTimer; // 开阀压力设置定时器 private System.Timers.Timer _settingReadTimer2; // 关阀压力设置定时器 private System.Timers.Timer startTimer; // 启动状态实时定时器 Function fc; #endregion public Window3() { InitializeComponent(); InitializeModbusTcp(); Loaded += Window_Loaded; } #region 初始化与资源释放 /// /// 初始化Modbus连接和定时器 /// private void InitializeModbusTcp() { try { string plcIp = ConfigurationManager.AppSettings["PLC1_IP"]; int plcPort = int.Parse(ConfigurationManager.AppSettings["PLC1_Port"]); _tcpClient = new TcpClient(plcIp, plcPort); _modbusMaster = ModbusIpMaster.CreateIp(_tcpClient); _modbusMaster.Transport.ReadTimeout = 3000; _modbusMaster.Transport.WriteTimeout = 3000; // 初始化定时器 _pressureReadTimer = CreateTimer(1000, OnPressureTimerElapsed); _settingReadTimer = CreateTimer(1000, OnSettingReadTimerTimerElapsed); _settingReadTimer2 = CreateTimer(1000, OnSettingReadTimerTimerElapsed2); startTimer = CreateTimer(1000, OnStartTimerElapsed); fc = new Function(_modbusMaster); // 初始化数据库 InitializeDatabase(); } catch (Exception ex) { ShowError($"Modbus初始化失败: {ex.Message}"); } } /// /// 通用定时器创建方法 /// private System.Timers.Timer CreateTimer(int intervalMs, ElapsedEventHandler elapsedAction) { var timer = new System.Timers.Timer(intervalMs) { AutoReset = true, Enabled = true }; timer.Elapsed += elapsedAction; return timer; } /// /// 实时流量读取定时器(修正跨线程访问UI问题) /// private void OnStartTimerElapsed(object sender, ElapsedEventArgs e) { try { bool[] result = _modbusMaster?.ReadCoils(0x01, 21, 1); bool isTestRunning = result != null && result.Length > 0 && result[0]; string testStartButtonText = ""; string ButtonStatus = ""; string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (currentLanguage == "en-US") { testStartButtonText = "Test Start Success"; ButtonStatus = "Test initiation"; } else if (currentLanguage == "zh-CN") { testStartButtonText = "测试启动成功"; ButtonStatus = "测试启动"; } TestStartButton.Dispatcher.Invoke(() => { if (isTestRunning) { TestStartButton.Content = testStartButtonText; TestStartButton.Foreground = Brushes.LightGreen; } else { TestStartButton.Content = ButtonStatus; TestStartButton.Foreground = Brushes.White; } }); } catch (Exception ex) { Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}"); } } /// /// 释放资源(定时器、TCP连接) /// public void Dispose() { _pressureReadTimer?.Dispose(); _settingReadTimer?.Dispose(); _settingReadTimer2?.Dispose(); _tcpClient?.Close(); _tcpClient?.Dispose(); _modbusMaster = null; } /// /// 窗口关闭时释放资源 /// protected override void OnClosed(EventArgs e) { base.OnClosed(e); Dispose(); } #endregion #region 定时器读取逻辑 /// /// 实时压力读取定时器 /// private void OnPressureTimerElapsed(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _pressureRegisterAddress, true, value => UpdateRealTimerPressureUI(value.ToString()) ); } /// /// 开阀压力设置定时器(带编辑锁判断) /// private void OnSettingReadTimerTimerElapsed(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _pressureSettingRegisterAddress, false, value => UpdateSettingUI(value.ToString()) ); } /// /// 关阀压力设置定时器(带编辑锁判断) /// private void OnSettingReadTimerTimerElapsed2(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _pressureCloseSettingRegisterAddress, false, value => UpdateSettingUI2(value.ToString()) ); } /// /// 通用寄存器读取并更新UI,支持16位整数和32位浮点数 /// private void ReadAndUpdateRegister(ushort address, bool isFloat, Action updateAction) { if (!IsModbusConnected()) { updateAction?.Invoke(isFloat ? (object)float.NaN : (ushort)0); return; } try { int registerCount = isFloat ? 2 : 1; ushort[] data = _modbusMaster?.ReadHoldingRegisters(0x01, address, (ushort)registerCount); if (isFloat) { if (data.Length >= 2) { ushort a = data[0]; // 高位寄存器值 ushort b = data[1]; // 低位寄存器值 float floatValue = c.UshortToFloat(b, a); floatValue = (float)Math.Round(floatValue, 2); updateAction?.Invoke(floatValue); } else { updateAction?.Invoke(float.NaN); } } else { updateAction?.Invoke(data.Length > 0 ? data[0] : (ushort)0); } } catch (Exception ex) { Console.WriteLine($"读取寄存器[{address:X4}]失败: {ex.Message}"); updateAction?.Invoke(isFloat ? (object)float.NaN : (ushort)0); } } #endregion #region UI更新(统一线程安全处理) private void UpdateConnectionTextUI(System.Windows.Controls.TextBox textBox, string value) { // 获取当前语言(默认中文) string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; // 多语言提示信息 string disconnectMsg = currentLanguage == "en-US" ? "Connection disconnected" : "连接断开"; // 安全更新UI UpdateUiSafely(() => textBox.Text = IsModbusConnected() ? value : disconnectMsg ); } private void UpdateConnectionTextUI(System.Windows.Controls.TextBlock textBox, string value) { // 获取当前语言(默认中文) string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; // 多语言提示信息 string disconnectMsg = currentLanguage == "en-US" ? "Connection disconnected" : "连接断开"; // 安全更新UI UpdateUiSafely(() => textBox.Text = IsModbusConnected() ? value : disconnectMsg ); } /// /// 更新实时压力UI /// private void UpdateRealTimerPressureUI(string value) { UpdateUiSafely(() => UpdateConnectionTextUI(TimerPressTxt, value) ); } /// /// 更新开阀压力设置UI /// private void UpdateSettingUI(string value) { UpdateUiSafely(() => UpdateConnectionTextUI(SettingPaTextBox, value) ); } /// /// 更新关阀压力设置UI /// private void UpdateSettingUI2(string value) { UpdateUiSafely(() => UpdateConnectionTextUI(clostTxt, value) ); } /// /// 更新开阀压力设置UI(旧方法兼容) /// private void UpdatePressureUI(string value) { UpdateUiSafely(() => SettingPaTextBox.Text = value); } /// /// 更新关阀压力设置UI(旧方法兼容) /// private void UpdateClosePressureUI(string value) { UpdateUiSafely(() => clostTxt.Text = value); } /// /// 更新测试按钮状态UI /// private void UpdateButtonStatus(string text, Brush color) { UpdateUiSafely(() => { TestStartButton.Content = text; TestStartButton.Foreground = color; }); } /// /// 线程安全的UI更新通用方法 /// private void UpdateUiSafely(Action action) { if (action == null) return; if (this.Dispatcher.HasShutdownStarted || this.Dispatcher.HasShutdownFinished) return; if (!Dispatcher.CheckAccess()) { try { Dispatcher.BeginInvoke(action); } catch (TaskCanceledException) { } } else { action.Invoke(); } } #endregion #region 按钮事件(功能操作) /// /// 开阀压差判别设置(SettingPaTextBox) /// private async void Button_Click(object sender, RoutedEventArgs e) { // 获取当前语言(默认中文) string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; // 多语言提示信息 string disconnectMsg = currentLanguage == "en-US" ? "Set valve opening pressure differential discrimination:" : "设置开阀压差判别:"; await WriteRegisterWithValidation( inputControl: SettingPaTextBox, registerAddress: _pressureSettingRegisterAddress, minValue: 0, maxValue: 10000, successMsg: value => $"{disconnectMsg}: {value} Pa", refreshAction: () => RefreshCurrentPressure() ); } /// /// 关阀压差判别设置(clostTxt) /// private async void Button_Click_1(object sender, RoutedEventArgs e) { // 获取当前语言(默认中文) string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; // 多语言提示信息 string disconnectMsg = currentLanguage == "en-US" ? "Set valve closing pressure differential discrimination:" : "设置关阀压差判别:"; await WriteRegisterWithValidation( inputControl: clostTxt, registerAddress: _pressureCloseSettingRegisterAddress, minValue: 0, maxValue: 10000, successMsg: value => $"{disconnectMsg}: {value} Pa", refreshAction: () => RefreshClosePressure() ); } /// /// 读取开阀/关阀压力记录并保存 /// private async void Button_Click_2(object sender, RoutedEventArgs e) { await ReadRegisterAndSave( address: _openRegisterAddress, address2: _closeRegisterAddress, control: recordOpenTxt ); } private async void Button_Click_4(object sender, RoutedEventArgs e) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string success = currentLanguage == "en-US" ? "Anti-suffocation pressure calibration received" : "防窒息压力校准指令已被设备接收并执行"; string fail = currentLanguage == "en-US" ? "Calibration timeout, abnormal status" : "校准执行超时,状态异常"; string log = currentLanguage == "en-US" ? "Anti-suffocation pressure calibration success" : "防窒息压力校准指令执行成功"; await WriteCoilWithCheck( coilAddress: _calibrationCoilAddress, value: true, successMsg: success, failMsg: fail, logMsg: log ); } /// /// 返回主窗口 /// private void Button_Click_5(object sender, RoutedEventArgs e) { var mainWindow = MainWindow.Instance; // 检查窗口状态,只在窗口未显示时调用ShowDialog if (!mainWindow.IsVisible) { mainWindow.ShowDialog(); } else { // 如果窗口已显示,可将其激活到前台 mainWindow.Activate(); } Close(); } /// /// 打开报告窗口 /// private void Button_Click_6(object sender, RoutedEventArgs e) { new ReportWindow3().Show(); } private async void Button_Click_7(object sender, RoutedEventArgs e) { // 获取当前语言(和你原有逻辑保持一致) string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (!IsModbusConnected()) { UpdateButtonStatus(currentLanguage == "en-US" ? "Disconnected" : "连接断开", Brushes.Red); ShowError(currentLanguage == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } try { UpdateButtonStatus(currentLanguage == "en-US" ? "Starting..." : "正在启动...", Brushes.LightGreen); // 写入启动线圈(脉冲式触发) await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStartAddress, true)); await Task.Delay(100); await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStartAddress, false)); UpdateButtonStatus(currentLanguage == "en-US" ? "Test started successfully" : "测试启动成功", Brushes.LightGreen); } catch (Exception ex) { UpdateButtonStatus(currentLanguage == "en-US" ? "Test start failed" : "测试启动失败", Brushes.Red); ShowError(currentLanguage == "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}"); } } /// /// 防窒息压力测试停止(M30线圈) /// private async void Button_Click_8(object sender, RoutedEventArgs e) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string failMsg = currentLanguage == "en-US" ? "Test stop timeout, abnormal status" : "防窒息压力测试停止超时,状态异常"; string logMsg = currentLanguage == "en-US" ? "Anti-suffocation test stopped" : "防窒息压力测试停止"; await WriteCoilWithCheck( coilAddress: _testStopAddress, value: true, successMsg: null, failMsg: failMsg, logMsg: logMsg ); TestStartButton.Content = currentLanguage == "en-US" ? "Test initiation" : "测试启动"; TestStartButton.Foreground = Brushes.White; } #endregion #region 通用操作方法(提取重复逻辑) /// /// 带输入验证的寄存器写入(带成功消息) /// private async Task WriteRegisterWithValidation( TextBox inputControl, ushort registerAddress, int minValue, int maxValue, Func successMsg, Action refreshAction) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string noConn = currentLanguage == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"; string invalid = currentLanguage == "en-US" ? $"Please enter integer between {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的整数"; string fail = currentLanguage == "en-US" ? "Operation failed" : "操作失败"; if (!IsModbusConnected()) { ShowError(noConn); return; } if (!int.TryParse(inputControl.Text.Trim(), out int value) || value < minValue || value > maxValue) { ShowWarning(invalid); return; } try { string operating = currentLanguage == "en-US" ? "Operating..." : "操作中..."; inputControl.Text = operating; await Task.Run(() => _modbusMaster.WriteSingleRegister(0x01, registerAddress, (ushort)value) ); await Task.Delay(300); refreshAction?.Invoke(); ShowSuccess(successMsg(value)); } catch (Exception ex) { ShowError($"{fail}: {ex.Message}"); } finally { inputControl.Text = value.ToString(); } } /// /// 带输入验证的寄存器写入(开阀压力专用) /// private async Task WriteRegisterWithValidation( TextBox inputControl, ushort registerAddress, float minValue, float maxValue) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string noConn = currentLanguage == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"; string invalid = currentLanguage == "en-US" ? $"Please enter number between {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的数字"; string fail = currentLanguage == "en-US" ? "Operation failed" : "操作失败"; string operating = currentLanguage == "en-US" ? "Operating..." : "操作中..."; if (!IsModbusConnected()) { ShowError(noConn); return; } if (!float.TryParse(inputControl.Text.Trim(), out float value) || value < minValue || value > maxValue) { ShowWarning(invalid); return; } try { inputControl.Text = operating; Function ma = new Function(_modbusMaster); ma.WriteToPLCForNew(value.ToString(), registerAddress, Function.DataType.整形); await Task.Delay(300); ReadAndUpdateRegister(registerAddress, true, v => UpdatePressureUI(v.ToString())); } catch (Exception ex) { ShowError($"{fail}: {ex.Message}"); } finally { inputControl.Text = value.ToString(); } } /// /// 带输入验证的寄存器写入(关阀压力专用) /// private async Task WriteRegisterWithValidation2( TextBox inputControl, ushort registerAddress, float minValue, float maxValue) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string noConn = currentLanguage == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"; string invalid = currentLanguage == "en-US" ? $"Please enter number between {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的数字"; string fail = currentLanguage == "en-US" ? "Operation failed" : "操作失败"; string operating = currentLanguage == "en-US" ? "Operating..." : "操作中..."; if (!IsModbusConnected()) { ShowError(noConn); return; } if (!float.TryParse(inputControl.Text.Trim(), out float value) || value < minValue || value > maxValue) { ShowWarning(invalid); return; } try { inputControl.Text = operating; Function ma = new Function(_modbusMaster); ma.WriteToPLCForNew(value.ToString(), registerAddress, Function.DataType.整形); await Task.Delay(300); ReadAndUpdateRegister(registerAddress, true, v => UpdateClosePressureUI(v.ToString())); } catch (Exception ex) { ShowError($"{fail}: {ex.Message}"); } finally { inputControl.Text = value.ToString(); } } /// /// 写入线圈并验证状态 /// private async Task WriteCoilWithCheck( ushort coilAddress, bool value, string successMsg, string failMsg, string logMsg) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string commError = currentLanguage == "en-US" ? "Communication error" : "通信错误"; if (!IsModbusConnected()) { InitializeModbusTcp(); return; } try { await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, coilAddress, value) ); Thread.Sleep(200); if (failMsg != null) { bool isStop = failMsg.Contains("停止") || failMsg.Equals("Stop", StringComparison.OrdinalIgnoreCase); bool isCalib = failMsg.Contains("校准") || failMsg.Equals("Calibrate", StringComparison.OrdinalIgnoreCase); if (isStop || isCalib) { await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, coilAddress, false) ); } } Thread.Sleep(100); await Task.Delay(500); bool[] status = await _modbusMaster?.ReadCoilsAsync(0x01, coilAddress, 1); if (failMsg != null) { bool isStop = failMsg.Contains("停止") || failMsg.Equals("Stop", StringComparison.OrdinalIgnoreCase); bool isCalib = failMsg.Contains("校准") || failMsg.Equals("Calibrate", StringComparison.OrdinalIgnoreCase); if (!isStop && !isCalib) { if (status[0] == value) { WriteLog($"{logMsg} - Address:{coilAddress},Status:{value}"); if (!string.IsNullOrEmpty(successMsg)) ShowSuccess(successMsg); } else { ShowError(failMsg); } } } } catch (Exception ex) { ShowError($"{commError}: {ex.Message}"); } } private async void CaptureModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender == null) return; var comboBox = sender as ComboBox; if (comboBox == null) return; var selectedItem = comboBox.SelectedItem as ComboBoxItem; if (selectedItem == null) return; var content = selectedItem.Content?.ToString(); if (string.IsNullOrEmpty(content)) return; // 获取当前语言 string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string stopMsg = currentLanguage == "en-US" ? "Stop" : "停止"; // 双语匹配:中文 + 英文都能识别 switch (content) { case "开阀捕捉": case "Open the valve to capture": await WriteCoilWithCheck(0, true, null, stopMsg, "开关阀选择"); break; case "关阀捕捉": case "Closing the valve to capture": await WriteCoilWithCheck(1, true, null, stopMsg, "开关阀选择"); break; default: return; } } /// /// 刷新开阀压力设置 /// private void RefreshCurrentPressure() { ReadAndUpdateRegister(_pressureSettingRegisterAddress, true, v => UpdatePressureUI(v.ToString())); } /// /// 刷新关阀压力设置 /// private void RefreshClosePressure() { ReadAndUpdateRegister(_pressureCloseSettingRegisterAddress, true, v => UpdateClosePressureUI(v.ToString())); } /// /// 读取寄存器并保存到数据库 /// private async Task ReadRegisterAndSave(ushort address, ushort address2, TextBlock control) { if (!IsModbusConnected()) { control.Text = "连接断开"; ShowError("Modbus TCP 未连接"); return; } try { control.Text = "读取中..."; recordCloseTxt.Text = "读取中..."; // 读取开阀压力 ushort[] data = await Task.Run(() => _modbusMaster?.ReadHoldingRegisters(0x01, address, 2) ); ushort a = data[0]; ushort b = data[1]; float value = c.UshortToFloat(b, a); value = (float)Math.Round(value, 2); control.Text = value.ToString("0.00"); // 读取关阀压力 ushort[] data2 = await Task.Run(() => _modbusMaster?.ReadHoldingRegisters(0x01, address2, 2) ); ushort a2 = data2[0]; ushort b2 = data2[1]; float value2 = c.UshortToFloat(b2, a2); value2 = (float)Math.Round(value2, 2); recordCloseTxt.Text = value2.ToString("0.00"); // 保存到数据库 SaveRecordToDatabase(value, value2); } catch (Exception ex) { control.Text = "读取失败"; recordCloseTxt.Text = "读取失败"; ShowError($"读取失败: {ex.Message}"); } } #endregion #region 数据库与日志操作 /// /// 初始化数据库 /// private void InitializeDatabase() { try { using (var conn = new SQLiteConnection(CSConstant.DbConnectionString)) { conn.Open(); string createSql = @" CREATE TABLE IF NOT EXISTS PreventSuffocation ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Flow REAL NOT NULL, Pressure REAL NOT NULL, RecordTime DATETIME NOT NULL );"; using (var cmd = new SQLiteCommand(createSql, conn)) { cmd.ExecuteNonQuery(); } } } catch (Exception ex) { Console.WriteLine($"数据库初始化失败: {ex.Message}"); ShowError($"数据库错误: {ex.Message}"); } } /// /// 保存记录到数据库 /// private void SaveRecordToDatabase(double flow, double pressure) { try { flow = Math.Round(flow, 2); pressure = Math.Round(pressure, 2); using (var conn = new SQLiteConnection(CSConstant.DbConnectionString)) { conn.Open(); string insertSql = @" INSERT INTO PreventSuffocation (Flow, Pressure, RecordTime) VALUES (@Flow, @Pressure, @RecordTime);"; using (var cmd = new SQLiteCommand(insertSql, conn)) { cmd.Parameters.AddWithValue("@Flow", flow); cmd.Parameters.AddWithValue("@Pressure", pressure); cmd.Parameters.AddWithValue("@RecordTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); cmd.ExecuteNonQuery(); } } } catch (Exception ex) { Console.WriteLine($"保存数据库失败: {ex.Message}"); ShowWarning($"数据保存失败: {ex.Message}"); } } /// /// 写入操作日志 /// private void WriteLog(string content) { try { string log = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {content}\r\n"; string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "modbus_log.txt"); System.IO.File.AppendAllText(logPath, log); } catch (Exception ex) { Console.WriteLine($"写入日志失败: {ex.Message}"); } } #endregion #region 输入框焦点与编辑事件(核心修复部分) /// /// SettingPaTextBox输入验证(仅允许数字和小数点) /// private void SettingPaTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { var isNumber = System.Text.RegularExpressions.Regex.IsMatch(e.Text, @"^[0-9]*(?:\.[0-9]*)?$"); e.Handled = !isNumber; } /// /// clostTxt输入验证(仅允许数字和小数点) /// private void ClostTxt_PreviewTextInput(object sender, TextCompositionEventArgs e) { var isNumber = System.Text.RegularExpressions.Regex.IsMatch(e.Text, @"^[0-9]*(?:\.[0-9]*)?$"); e.Handled = !isNumber; } #endregion #region 辅助方法(连接检查、消息提示、背景加载) /// /// 检查Modbus连接状态 /// private bool IsModbusConnected() { return _modbusMaster != null && _tcpClient?.Connected == true; } /// /// 加载窗口背景 /// private void Window_Loaded(object sender, RoutedEventArgs e) { try { string imgPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg"); if (System.IO.File.Exists(imgPath)) { Background = new ImageBrush { ImageSource = new BitmapImage(new Uri(imgPath, UriKind.Absolute)) }; } else { Console.WriteLine($"背景图片不存在: {imgPath}"); } } catch (Exception ex) { Console.WriteLine($"加载背景失败: {ex.Message}"); } } // 消息提示封装 private void ShowSuccess(string msg) => MessageBox.Show(msg, "成功", MessageBoxButton.OK, MessageBoxImage.Information); private void ShowWarning(string msg) => MessageBox.Show(msg, "提示", MessageBoxButton.OK, MessageBoxImage.Warning); private void ShowError(string msg) => MessageBox.Show(msg, "错误", MessageBoxButton.OK, MessageBoxImage.Error); #endregion private async void Button_Click_3(object sender, RoutedEventArgs e) { string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string noConn = currentLanguage == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"; string invalidData = currentLanguage == "en-US" ? "Invalid flow or pressure value" : "流量或压力值格式不正确,请检查输入"; string noData = currentLanguage == "en-US" ? "No data to export" : "防室息阀压力表中无数据,无法导出"; string exportSuccess = currentLanguage == "en-US" ? "Export success" : "数据已成功导出到Excel"; string exportFail = currentLanguage == "en-US" ? "Export failed" : "Excel导出失败,请检查文件是否被占用"; if (!IsModbusConnected()) { recordOpenTxt.Text = currentLanguage == "en-US" ? "Disconnected" : "连接断开"; ShowError(noConn); return; } fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 170); fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 170); ReadAndUpdateRegister(_openRegisterAddress, true, value => UpdateFlowFlowUI(value.ToString())); ReadAndUpdateRegister(_closeRegisterAddress, true, value => UpdateCloseFlowUI(value.ToString())); if (float.TryParse(recordOpenTxt.Text, out float flowValue) && float.TryParse(recordCloseTxt.Text, out float pressureValue)) { SaveRecordToDatabase(flowValue, pressureValue); } else { ShowError(invalidData); return; } List co2Records = ReadCO2RecordsFromDatabase(); if (co2Records == null || !co2Records.Any()) { MessageBox.Show(noData, currentLanguage == "en-US" ? "Info" : "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } bool exportSuccessFlag = ExportCO2RecordsToExcel(co2Records); if (exportSuccessFlag) { MessageBox.Show(exportSuccess, currentLanguage == "en-US" ? "Success" : "成功", MessageBoxButton.OK, MessageBoxImage.Information); } else { MessageBox.Show(exportFail, currentLanguage == "en-US" ? "Error" : "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private bool ExportCO2RecordsToExcel(List records) { try { // WPF 原生保存对话框(替代 WinForms) SaveFileDialog saveDialog = new SaveFileDialog { Filter = "Excel文件 (*.xlsx)|*.xlsx", FileName = $"防室息阀压力记录_{DateTime.Now:yyyyMMddHHmmss}.xlsx", Title = "保存防室息阀压力记录" }; // 用户取消选择则返回 bool? result = saveDialog.ShowDialog(); if (!(result ?? false)) return false; // EPPlus 许可设置(.NET 8 必须显式设置) ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // 非商业用途 // 创建并写入Excel using (ExcelPackage package = new ExcelPackage(new FileInfo(saveDialog.FileName))) { ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("防室息阀压力记录"); // 表头(对应DataGrid列) worksheet.Cells[1, 1].Value = "开阀压力pa"; worksheet.Cells[1, 2].Value = "关阀压力pa"; worksheet.Cells[1, 3].Value = "时间"; // 表头样式(加粗、居中) using (var headerRange = worksheet.Cells[1, 1, 1, 6]) { headerRange.Style.Font.Bold = true; headerRange.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center; } // 填充数据(从第2行开始) for (int i = 0; i < records.Count; i++) { int row = i + 2; var record = records[i]; worksheet.Cells[row, 1].Value = record.Flow; worksheet.Cells[row, 2].Value = record.Pressure; worksheet.Cells[row, 3].Value = record.RecordTime.ToString("yyyy-MM-dd HH:mm:ss"); } // 自动调整列宽 worksheet.Cells.AutoFitColumns(); // 保存文件 package.Save(); } return true; } catch (Exception ex) { MessageBox.Show($"导出失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return false; } } /// /// /// private List ReadCO2RecordsFromDatabase() { List records = new List(); try { using (SQLiteConnection conn = new SQLiteConnection(CSConstant.DbConnectionString)) { conn.Open(); // 查询CO2表所有记录(按时间排序) string query = "SELECT Flow, Pressure, RecordTime FROM PreventSuffocation ORDER BY RecordTime desc limit 1 "; using (SQLiteCommand cmd = new SQLiteCommand(query, conn)) { using (SQLiteDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { records.Add(new CO2Record { Flow = reader.GetDouble(0), // 二氧化碳浓度(%) Pressure = reader.GetDouble(1), // 压力(pa) RecordTime = reader.GetDateTime(2) // 时间 }); } } } } return records; } catch (Exception ex) { MessageBox.Show($"读取防室息阀压力表失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return null; } } /// /// 更新实时流量UI /// private void UpdateFlowFlowUI(string value) { UpdateUiSafely(() => recordOpenTxt.Text = IsModbusConnected() ? value : "连接断开" ); } /// /// 更新实时流量UI /// private void UpdateCloseFlowUI(string value) { UpdateUiSafely(() => recordCloseTxt.Text = IsModbusConnected() ? value : "连接断开" ); } private void Button_Click_9(object sender, RoutedEventArgs e) { if (!IsModbusConnected()) { recordOpenTxt.Text = "连接断开"; ShowError("Modbus TCP 未连接"); return; } fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 11); fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 11); ReadAndUpdateRegister( _openRegisterAddress, true, value => UpdateFlowFlowUI(value.ToString()) ); ReadAndUpdateRegister( _closeRegisterAddress, true, value => UpdateCloseFlowUI(value.ToString()) ); // 尝试将文本框内容转换为float if (float.TryParse(recordOpenTxt.Text, out float flowValue) && float.TryParse(recordCloseTxt.Text, out float pressureValue)) { SaveRecordToDatabase(flowValue, pressureValue); } else { // 转换失败,提示错误信息 ShowError("流量或压力值格式不正确,请检查输入"); } // 步骤2:读取CO2表数据 List co2Records = ReadCO2RecordsFromDatabase(); if (co2Records == null || !co2Records.Any()) { MessageBox.Show("防室息阀压力表中无数据,无法导出", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 步骤3:导出Excel bool exportSuccess = ExportCO2RecordsToExcel(co2Records); if (exportSuccess) { MessageBox.Show("数据已成功导出到Excel", "成功", MessageBoxButton.OK, MessageBoxImage.Information); } else { MessageBox.Show("Excel导出失败,请检查文件是否被占用", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void Button_Click_10(object sender, RoutedEventArgs e) { try { await WriteRegisterWithValidation( inputControl: SettingPaTextBox, registerAddress: _pressureSettingRegisterAddress, minValue: 0, maxValue: 10000 ); _settingReadTimer.Start(); } finally { } } private async void Button_Click_11(object sender, RoutedEventArgs e) { try { await WriteRegisterWithValidation2( inputControl: clostTxt, registerAddress: _pressureCloseSettingRegisterAddress, minValue: 0, maxValue: 10000 ); _settingReadTimer2.Start(); } finally { } } } }