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.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 Window4 : Window, IDisposable { DataChange c = new DataChange(); #region 寄存器/线圈地址定义(按功能分组,对应物理意义) // 基础参数地址 private readonly ushort _pressureSettingRegisterAddress = 0x013A; // 单一故障气阻设置流量(D314) private readonly ushort _noseRegisterAddress = 0x055C; // 鼻口压力地址 private readonly ushort _modifiedNoseRegisterAddress = 0x0048; // 鼻口压力校准地址(M72) private readonly ushort _outRegisterAddress = 120; // 呼流量地址 private readonly ushort _inRegisterAddress = 1400; // 吸流量地址 private readonly ushort _BreathOutRegisterAddress = 316; // 记录呼气阻力实时显示 private readonly ushort _BreathInRegisterAddress = 318; // 记录吸气阻力实时显示 // 线圈地址(控制指令) private readonly ushort _testStartAddress = 0x0020; // 单一故障气阻测试启动(M32) #endregion #region 私有字段 private TcpClient _tcpClient; private IModbusMaster _modbusMaster; Function fc; // 定时器 private System.Timers.Timer _outReadTimer; // 呼流量读取 private System.Timers.Timer _inReadTimer; // 吸流量读取 private System.Timers.Timer _noseReadTimer; // 鼻口压力读取 private System.Timers.Timer _settingReadTimer2; // private System.Timers.Timer startTimer; // 启动状态实时定时器 #endregion public Window4() { 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; // 初始化定时器 InitializeTimers(); fc = new Function(_modbusMaster); // 初始化数据库 InitializeDatabase(); } catch (Exception ex) { ShowError($"Modbus初始化失败: {ex.Message}"); } } /// /// 统一初始化所有定时器 /// private void InitializeTimers() { _outReadTimer = CreateTimer(1000, OnOutTimerElapsed); _inReadTimer = CreateTimer(1000, OnInTimerElapsed); _noseReadTimer = CreateTimer(1000, OnNoseTimerElapsed); _settingReadTimer2 = CreateTimer(1000, OnSettingReadTimerTimerElapsed2); startTimer = CreateTimer(1000, OnStartTimerElapsed); } /// /// 实时流量读取定时器(修正跨线程访问UI问题) /// private void OnStartTimerElapsed(object sender, ElapsedEventArgs e) { try { bool[] result = _modbusMaster?.ReadCoils(0x01, 33, 1); bool isTestRunning = result != null && result.Length > 0 && result[0]; string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string running = lang == "en-US" ? "Test Running" : "测试启动成功"; string ready = lang == "en-US" ? "Start Test" : "测试启动"; TestStartButton.Dispatcher.Invoke(() => { if (isTestRunning) { btnsetting.IsEnabled = false; btnsetting2.IsEnabled = false; TestStartButton.Content = running; TestStartButton.Foreground = Brushes.LightGreen; } else { btnsetting.IsEnabled = true; btnsetting2.IsEnabled = true; TestStartButton.Content = ready; TestStartButton.Foreground = Brushes.White; } }); } catch (Exception ex) { Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}"); } } private void OnSettingReadTimerTimerElapsed2(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _pressureSettingRegisterAddress, true, value => UpdateSettingUI2(value.ToString()) ); } private void UpdateSettingUI2(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => clostTxt.Text = IsModbusConnected() ? value : disconnect); } /// /// 通用定时器创建方法 /// 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; } /// /// 释放资源 /// public void Dispose() { // 释放定时器 _outReadTimer?.Dispose(); _inReadTimer?.Dispose(); _noseReadTimer?.Dispose(); // 释放Modbus连接 _tcpClient?.Close(); _tcpClient?.Dispose(); _modbusMaster = null; } /// /// 窗口关闭时释放资源 /// protected override void OnClosed(EventArgs e) { base.OnClosed(e); Dispose(); } #endregion #region 定时器读取逻辑(寄存器读取+UI更新) /// /// 呼流量读取定时器 /// private void OnOutTimerElapsed(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _outRegisterAddress, true, value => UpdateOutUI(value.ToString()) ); } /// /// 吸流量读取定时器 /// private void OnInTimerElapsed(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _inRegisterAddress, true, value => UpdateInUI(value.ToString()) ); } /// /// 鼻口压力读取定时器 /// private void OnNoseTimerElapsed(object sender, ElapsedEventArgs e) { ReadAndUpdateRegister( _noseRegisterAddress, true, value => UpdateNoseUI(value.ToString()) ); } /// /// 通用寄存器读取并更新UI,支持16位整数和32位浮点数 /// /// 起始地址 /// 是否为浮点型(占用2个寄存器) /// 更新UI的回调函数 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) { // 浮点型转换(2个16位寄存器组合为32位浮点数) if (data.Length >= 2) { // 2. 解析寄存器值(data[0]是D312,data[1]是D313) 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 { // 16位整数直接返回 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更新(线程安全处理) /// /// 更新呼流量UI /// private void UpdateOutUI(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => outTxt.Text = IsModbusConnected() ? value : disconnect); } /// /// 更新吸流量UI /// private void UpdateInUI(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => InTxt.Text = IsModbusConnected() ? value : disconnect); } /// /// 更新鼻口压力UI /// private void UpdateNoseUI(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => NoseTxt.Text = IsModbusConnected() ? value : disconnect); } /// /// 更新测试按钮状态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 (Dispatcher.HasShutdownStarted) { return; } try { if (!Dispatcher.CheckAccess()) { Dispatcher.Invoke(action, TimeSpan.FromSeconds(2)); } else { action.Invoke(); } } catch (TaskCanceledException) { } catch (Exception ex) { Console.WriteLine($"UI更新失败:{ex.Message}"); } } #endregion #region 按钮事件(功能操作) ///// ///// 流量值设置(settingTxt) ///// //private async void Button_Click(object sender, RoutedEventArgs e) //{ // await WriteRegisterWithValidation( // inputControl: settingTxt, // registerAddress: _pressureSettingRegisterAddress, // minValue: 0, // maxValue: 10000, // successMsg: value => $"设置流量值: {value} L/min" // ); //} /// /// 鼻口压力校准(M72线圈) /// private async void Button_Click_1(object sender, RoutedEventArgs e) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string success = lang == "en-US" ? "Calibration OK (verified)" : "校准指令已被设备接收并执行(二次验证通过)"; string fail = lang == "en-US" ? "Calibration timeout" : "校准执行超时,状态异常"; string log = lang == "en-US" ? "Calibration done" : "校准指令执行成功"; await WriteCoilWithCheck(_modifiedNoseRegisterAddress, true, success, fail, log); } /// /// 单一故障气阻测试停止(M34线圈) /// private async void Button_Click_3(object sender, RoutedEventArgs e) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string fail = lang == "en-US" ? "Test stop timeout" : "单一故障气阻测试停止超时,状态异常"; string log = lang == "en-US" ? "Test stopped" : "单一故障气阻测试停止"; string start = lang == "en-US" ? "Start Test" : "测试启动"; await WriteCoilWithCheck(0x0022, true, null, fail, log); TestStartButton.Content = start; TestStartButton.Foreground = Brushes.White; } /// /// 单一故障气阻测试启动(M20线圈) /// private async void Button_Click_4(object sender, RoutedEventArgs e) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (!IsModbusConnected()) { UpdateButtonStatus(lang == "en-US" ? "Disconnected" : "连接断开", Brushes.Red); ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } try { UpdateButtonStatus(lang == "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)); await Task.Delay(100); UpdateButtonStatus(lang == "en-US" ? "Test Started" : "测试启动成功", Brushes.LightGreen); } catch (Exception ex) { UpdateButtonStatus(lang == "en-US" ? "Start Failed" : "测试启动失败", Brushes.Red); ShowError(lang == "en-US" ? $"Error: {ex.Message}" : $"操作异常: {ex.Message}"); } } /// /// 读取呼气阻力(D136)并保存 /// private async void Button_Click_5(object sender, RoutedEventArgs e) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (!IsModbusConnected()) { saveFlowOutTxt.Text = lang == "en-US" ? "Disconnected" : "连接断开"; ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 192); fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 192); ReadAndUpdateRegister(_BreathOutRegisterAddress, true, value => UpdateFlowFlowUI(value.ToString())); //ReadAndUpdateRegister(_BreathInRegisterAddress, true, value => UpdateCloseFlowUI2(value.ToString())); if (float.TryParse(saveFlowOutTxt.Text, out float flowValue)) { SaveRecordToDatabase(flowValue, 0); } else { ShowError(lang == "en-US" ? "Invalid data format" : "流量或压力值格式不正确,请检查输入"); } List records = ReadCO2RecordsFromDatabase(); if (records == null || records.Count == 0) { string msg = lang == "en-US" ? "No data to export" : "单一故障气阻表中无数据,无法导出"; MessageBox.Show(msg, "Info", MessageBoxButton.OK, MessageBoxImage.Warning); return; } bool exportSuccess = ExportCO2RecordsToExcel(records); if (exportSuccess) { string msg = lang == "en-US" ? "Export successful" : "数据已成功导出到Excel"; MessageBox.Show(msg, "Success", MessageBoxButton.OK, MessageBoxImage.Information); } else { //string msg = lang == "en-US" ? "Export failed" : "Excel导出失败,请检查文件是否被占用"; //MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } private async void Button_Click_6(object sender, RoutedEventArgs e) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (!IsModbusConnected()) { saveFlowOutTxt.Text = lang == "en-US" ? "Disconnected" : "连接断开"; ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 193); fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 193); //ReadAndUpdateRegister(_BreathOutRegisterAddress, true, value => UpdateFlowFlowUI(value.ToString())); ReadAndUpdateRegister(_BreathInRegisterAddress, true, value => UpdateCloseFlowUI2(value.ToString())); if ( float.TryParse(saveFlowInTxt.Text, out float pressureValue)) { SaveRecordToDatabase(0, pressureValue); } else { ShowError(lang == "en-US" ? "Invalid data format" : "流量或压力值格式不正确,请检查输入"); } List records = ReadCO2RecordsFromDatabase(); if (records == null || records.Count == 0) { string msg = lang == "en-US" ? "No data to export" : "单一故障气阻表中无数据,无法导出"; MessageBox.Show(msg, "Info", MessageBoxButton.OK, MessageBoxImage.Warning); return; } bool exportSuccess = ExportCO2RecordsToExcel(records); if (exportSuccess) { string msg = lang == "en-US" ? "Export successful" : "数据已成功导出到Excel"; MessageBox.Show(msg, "Success", MessageBoxButton.OK, MessageBoxImage.Information); } else { //string msg = lang == "en-US" ? "Export failed" : "Excel导出失败,请检查文件是否被占用"; //MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } private bool ExportCO2RecordsToExcel(List records) { try { SaveFileDialog saveDialog = new SaveFileDialog { Filter = "Excel(*.xlsx)|*.xlsx", FileName = $"AirResistance_{DateTime.Now:yyyyMMddHHmmss}.xlsx", Title = "Export Air Resistance Data" }; if (saveDialog.ShowDialog() != true) return false; ExcelPackage.LicenseContext = LicenseContext.NonCommercial; using (var package = new ExcelPackage(new FileInfo(saveDialog.FileName))) { var ws = package.Workbook.Worksheets.Add("Records"); ws.Cells[1, 1].Value = "Expiratory Pa"; ws.Cells[1, 2].Value = "Inspiratory Pa"; ws.Cells[1, 3].Value = "Time"; for (int i = 0; i < records.Count; i++) { ws.Cells[i + 2, 1].Value = records[i].Flow; ws.Cells[i + 2, 2].Value = records[i].Pressure; ws.Cells[i + 2, 3].Value = records[i].RecordTime.ToString("yyyy-MM-dd HH:mm:ss"); } ws.Cells.AutoFitColumns(); package.Save(); } return true; } catch (Exception ex) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string err = lang == "en-US" ? "Export failed" : "导出失败"; MessageBox.Show($"{err}: {ex.Message}", "Error", 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 ExpiratoryResistance 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; } } private void UpdateFlowFlowUI(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => saveFlowOutTxt.Text = IsModbusConnected() ? value : disconnect); } private void UpdateCloseFlowUI2(string value) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string disconnect = lang == "en-US" ? "Disconnected" : "连接断开"; UpdateUiSafely(() => saveFlowInTxt.Text = IsModbusConnected() ? value : disconnect); } ///// ///// 读取吸气阻力(D318)并保存 ///// //private async void Button_Click_6(object sender, RoutedEventArgs e) //{ // await ReadAndSaveRegister( // address: _flowInRegisterAddress, // control: saveFlowInTxt, // isFlow: false // ); //} /// /// 返回主窗口 /// private void Button_Click_2(object sender, RoutedEventArgs e) { var mainWindow = MainWindow.Instance; // 检查窗口状态,只在窗口未显示时调用ShowDialog if (!mainWindow.IsVisible) { mainWindow.ShowDialog(); } else { // 如果窗口已显示,可将其激活到前台 mainWindow.Activate(); } Close(); } /// /// 打开报告窗口 /// private void Button_Click_7(object sender, RoutedEventArgs e) { new ReportWindow4().ShowDialog(); } #endregion #region 通用操作方法(提取重复逻辑) private async Task WriteRegisterWithValidation( TextBox inputControl, ushort registerAddress, float minValue, float maxValue) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (!IsModbusConnected()) { ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } if (!float.TryParse(inputControl.Text.Trim(), out float value) || value < minValue || value > maxValue) { string msg = lang == "en-US" ? $"Enter {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的数字"; ShowWarning(msg); return; } try { inputControl.Text = lang == "en-US" ? "Processing..." : "操作中..."; 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) { string err = lang == "en-US" ? $"Failed: {ex.Message}" : $"操作失败: {ex.Message}"; ShowError(err); } finally { inputControl.Text = value.ToString(); } } private void UpdatePressureUI(string value) { UpdateUiSafely(() => clostTxt.Text = value); } /// /// 写入线圈并验证状态 /// private async Task WriteCoilWithCheck( ushort coilAddress, bool value, string successMsg, string failMsg, string logMsg) { string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; if (_modbusMaster == null) { // ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接"); return; } if (!IsModbusConnected()) { InitializeModbusTcp(); return; } try { // 写入线圈 await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, coilAddress, value) ); Thread.Sleep(200); // ============================== // 兼容:中文 停止/校准 + 英文 Stop/Calib // ============================== bool needReset = false; if (failMsg != null) { bool hasStop = failMsg.Contains("停止") || failMsg.Contains("Stop") || failMsg.Contains("stop"); bool hasCalib = failMsg.Contains("校准") || failMsg.Contains("Calib") || failMsg.Contains("calib"); needReset = hasStop || hasCalib; } if (failMsg != null && needReset) { // 写入线圈 await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, coilAddress, false) ); } Thread.Sleep(100); // 等待并验证 await Task.Delay(500); bool[] status = await _modbusMaster?.ReadCoilsAsync(0x01, coilAddress, 1); // ============================== // 同样兼容中英文判断 // ============================== bool noResetCheck = false; if (failMsg != null) { bool hasStop = failMsg.Contains("停止") || failMsg.Contains("Stop") || failMsg.Contains("stop"); bool hasCalib = failMsg.Contains("校准") || failMsg.Contains("Calib") || failMsg.Contains("calib"); noResetCheck = !hasStop && !hasCalib; } if (failMsg != null && noResetCheck) { if (status != null && status.Length > 0 && status[0] == value) { // 写入日志 WriteLog($"{logMsg} - 地址:{coilAddress},状态:{value}"); if (!string.IsNullOrEmpty(successMsg)) { ShowSuccess(successMsg); } } else { ShowError(failMsg); } } } catch (Exception ex) { ShowError(lang == "en-US" ? $"Communication error: {ex.Message}" : $"通信错误: {ex.Message}"); } } #endregion #region 数据库与日志操作 /// /// 初始化数据库 /// private void InitializeDatabase() { try { using (var conn = new SQLiteConnection(CSConstant.DbConnectionString)) { conn.Open(); string createSql = @" CREATE TABLE IF NOT EXISTS ExpiratoryResistance ( 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 ExpiratoryResistance(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 string logPath => System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "modbus_log.txt"); /// /// 写入操作日志 /// private void WriteLog(string content) { try { string log = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {content}\r\n"; System.IO.File.AppendAllText(logPath, log); } catch (Exception ex) { Console.WriteLine($"写入日志失败: {ex.Message}"); } } #endregion #region 辅助方法(连接检查、消息提示、背景加载) /// /// 检查Modbus连接状态 /// private bool IsModbusConnected() { return _modbusMaster != null && _tcpClient?.Connected == true; } /// /// 加载窗口背景 /// private async 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}"); } await LoadBreathTypeSelection(); } catch (Exception ex) { Console.WriteLine($"加载背景失败: {ex.Message}"); } } private async Task LoadBreathTypeSelection() { try { // 读取线圈 36(假设 36 为呼吸类型:true=吸气/Inhale,false=呼气/Exhale) bool[] result = await _modbusMaster?.ReadCoilsAsync(1, 36, 1); bool isInhale = result != null && result.Length > 0 && result[0]; // 获取当前语言 string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN"; string expectedContent; if (currentLanguage == "en-US") { expectedContent = isInhale ? "Inhale" : "Exhale"; } else { expectedContent = isInhale ? "吸气" : "呼气"; } // 查找并设置选中项 foreach (ComboBoxItem item in CaptureModeComboBox.Items) { if (item.Content?.ToString() == expectedContent) { CaptureModeComboBox.SelectedItem = item; break; } } } catch (Exception ex) { // 记录错误,可选用日志或 Console Console.WriteLine($"加载呼吸类型失败: {ex.Message}"); } } /// /// 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; } private void UpdateClosePressureUI(string value) { UpdateUiSafely(() => clostTxt.Text = value); } // 消息提示封装 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 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; switch (content) { case "呼气": case "Exhale": await WriteCoilWithCheck(36, false, null, "Breath Type", "Breath Type"); break; case "吸气": case "Inhale": await WriteCoilWithCheck(36, true, null, "Breath Type", "Breath Type"); break; default: return; } } private async void Button_Click(object sender, RoutedEventArgs e) { try { await WriteRegisterWithValidation( inputControl: clostTxt, registerAddress: _pressureSettingRegisterAddress, minValue: 0, maxValue: 10000 ); //_settingReadTimer2.Start(); } finally { } } } }