Files
Sleep-Multi-functionality/Window5.xaml.cs
2026-05-09 16:11:05 +08:00

1518 lines
59 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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.Runtime.Intrinsics;
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.Animation;
using System.Windows.Media.Imaging;
using ;
namespace ShanghaiEnvironmentalTechnology
{
/// <summary>
/// 呼吸相关参数监控窗口Modbus通信+CO2数据记录
/// </summary>
public partial class Window5 : Window, IDisposable
{
DataChange c = new DataChange();
#region /线
// 鼻口相关
private readonly ushort _outRegisterAddress = 0x0038; // 呼吸次数D56
private readonly ushort _breathMinuteAddress = 0x09CA; // 呼吸计时分
private readonly ushort _breathSecondAddress = 0x09C6; // 呼吸计时秒
// 呼路管与CO2相关
private readonly ushort _breathOutAddress = 3030; // 呼路管压力
private readonly ushort _co2Address = 3006; // CO2浓度
private readonly ushort _co2BeginAddress = 0x0190; // CO2开始浓度
private readonly ushort _co2EndAddress = 0x019A; // CO2结束浓度
private readonly ushort _co2AddAddress = 0x0198; // CO2浓度增加值
// 控制线圈M代码
private readonly ushort _testStartAddress = 97; // 二氧化碳测试启动M97
private readonly ushort _resetAddress = 0x0003; // 复位M2
private readonly ushort _testStopAddress = 0x0008; // 测试停止M8
Function fc;
#endregion
#region
// Modbus通信
private TcpClient _tcpClient;
private IModbusMaster _modbusMaster;
// 定时器(按功能分组)
private System.Timers.Timer _outReadTimer; // 鼻口参数读取
private System.Timers.Timer _breathTimer; // 呼吸计时读取
private System.Timers.Timer _co2Timer; // 呼路管+CO2浓度读取
private System.Timers.Timer _co2BeginEndTimer; // CO2开始/结束浓度读取
private System.Timers.Timer _co2AddTimer; // CO2增加值读取
private System.Timers.Timer _beginRecordTimer; // 数据记录定时器
private System.Timers.Timer startTimer; // 启动状态实时定时器
private System.Timers.Timer resetTimer; // 启动状态实时定时器
private System.Timers.Timer breathTimer;
#endregion
#region
// 编辑状态标志位控制是否更新TextBox
private bool _isEditingOut; // 呼吸比-呼气
private bool _isEditingIn; // 呼吸比-吸气
private bool _isEditingMoisture; // 潮气量
private bool _isEditingRespRate; // 呼吸频率
private bool _isEditingFrequency; // 频率系数
// 定时器1个定时器管理4个参数的实时读取
private System.Timers.Timer _paramReadTimer;
#endregion
public Window5()
{
InitializeComponent();
InitializeModbusTcp();
Loaded += Window_Loaded; // 延迟加载背景,避免初始化阻塞
}
#region
/// <summary>
/// 初始化Modbus连接和定时器
/// </summary>
private void InitializeModbusTcp()
{
try
{
// 从配置读取PLC连接信息
string plcIp = ConfigurationManager.AppSettings["PLC2_IP"];
int plcPort = int.Parse(ConfigurationManager.AppSettings["PLC2_Port"]);
_tcpClient = new TcpClient(plcIp, plcPort);
_modbusMaster = ModbusIpMaster.CreateIp(_tcpClient);
_modbusMaster.Transport.ReadTimeout = 3000;
_modbusMaster.Transport.WriteTimeout = 3000;
_paramReadTimer = CreateTimer(1000, OnParamTimerElapsed);
// 初始化定时器
InitializeTimers();
fc = new Function(_modbusMaster);
// 初始化数据库
InitializeDatabase();
}
catch (Exception ex)
{
ShowError($"Modbus初始化失败: {ex.Message}");
}
}
private void OnParamTimerElapsed(object sender, ElapsedEventArgs e)
{
// 1. 读取呼吸比(呼气)
if (!_isEditingOut)
{
ReadAndUpdateSingleRegister(342, true, v1 =>
{
UpdatePressureUI1(v1.ToString());
});
}
// 2. 读取呼吸比(吸气)
if (!_isEditingIn)
{
ReadAndUpdateSingleRegister(
340,
isFloat: true, value =>
UpdatePressureUI2(value.ToString())
);
}
// 3. 读取潮气量
if (!_isEditingMoisture)
{
ReadAndUpdateSingleRegister(
300,
isFloat: true,
value => UpdatePressureUI3(value.ToString())
);
}
// 4. 读取呼吸频率
if (!_isEditingRespRate)
{
ReadAndUpdateSingleRegister(
210,
isFloat: true,
value => UpdatePressureUI4(value.ToString())
);
}
// 5. 读取频率系数
if (!_isEditingFrequency)
{
ReadAndUpdateSingleRegister(
368,
isFloat: true,
value => UpdatePressureUI5(value.ToString())
);
}
}
#region
/// <summary>
/// 通用数字输入过滤(支持整数和小数)
/// </summary>
private void NumberPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = sender as TextBox;
// 允许数字和一个小数点如“123”“123.45”)
var isNumber = System.Text.RegularExpressions.Regex.IsMatch(e.Text, @"^[0-9]*(?:\.[0-9]*)?$");
// 禁止重复输入小数点
if (e.Text == "." && textBox?.Text.Contains(".") == true)
{
isNumber = false;
}
e.Handled = !isNumber;
}
#endregion
/// <summary>
/// 统一初始化所有定时器(避免重复配置)
/// </summary>
private void InitializeTimers()
{
_outReadTimer = CreateTimer(1000, OnOutTimerElapsed);
_breathTimer = CreateTimer(1000, OnBreathTimerElapsed);
_co2Timer = CreateTimer(500, OnCo2TimerElapsed);
_co2BeginEndTimer = CreateTimer(1000, OnCO2BeginEndTimerElapsed);
_co2AddTimer = CreateTimer(1000, OnCO2AddTimerElapsed);
startTimer = CreateTimer(1000, OnStartTimerElapsed);
resetTimer = CreateTimer(1000, OnResetTimerElapsed);
breathTimer = CreateTimer(1000, OnbreathTimerElapsed);
}
private void OnStartTimerElapsed(object sender, ElapsedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
try
{
bool[] result = _modbusMaster?.ReadCoils(0x01, 98, 1);
bool isRunning = result != null && result.Length > 0 && result[0];
TestStartButton.Dispatcher.Invoke(() =>
{
if (isRunning)
{
btn1.IsEnabled = false;
btn2.IsEnabled = false;
btn3.IsEnabled = false;
btn4.IsEnabled = false;
btn5.IsEnabled = false;
TestStartButton.Content = _lang == "en-US" ? "Test Started" : "测试启动成功";
TestStartButton.Foreground = Brushes.LightGreen;
}
else
{
btn1.IsEnabled = true;
btn2.IsEnabled = true;
btn3.IsEnabled = true;
btn4.IsEnabled = true;
btn5.IsEnabled = true;
TestStartButton.Content = _lang == "en-US" ? "Start Test" : "测试启动";
TestStartButton.Foreground = Brushes.White;
}
});
}
catch (Exception ex)
{
Console.WriteLine(_lang == "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败{ex.Message}");
}
}
private void OnResetTimerElapsed(object sender, ElapsedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
try
{
bool[] result = _modbusMaster?.ReadCoils(0x01, 3, 1);
bool isRunning = result != null && result.Length > 0 && result[0];
ResetBtn.Dispatcher.Invoke(() =>
{
if (isRunning)
{
ResetBtn.Content = _lang == "en-US" ? "Reset Success" : "复位成功";
ResetBtn.Foreground = Brushes.LightGreen;
}
else
{
ResetBtn.Content = _lang == "en-US" ? "Reset" : "复位";
ResetBtn.Foreground = Brushes.White;
}
});
}
catch (Exception ex)
{
Console.WriteLine(_lang == "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败{ex.Message}");
}
}
//int i = 0;
// 新增:记录当前状态(避免重复触发)
private string _currentBreathState = ""; // 可能的值:"吸气"、"呼气"、"异常"
private void OnbreathTimerElapsed(object sender, ElapsedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
try
{
bool[] inhale = _modbusMaster?.ReadCoils(0x01, 51, 1);
bool[] exhale = _modbusMaster?.ReadCoils(0x01, 55, 1);
bool isIn = inhale != null && inhale.Length > 0 && inhale[0];
bool isEx = exhale != null && exhale.Length > 0 && exhale[0];
string newState;
if ((isIn && isEx) || (!isIn && !isEx))
newState = _lang == "en-US" ? "Error" : "异常";
else if (isIn)
newState = _lang == "en-US" ? "Inhale" : "吸气";
else
newState = _lang == "en-US" ? "Exhale" : "呼气";
if (newState != _currentBreathState)
{
breathType.Dispatcher.Invoke(() =>
{
_currentBreathState = newState;
if (newState == (_lang == "en-US" ? "Error" : "异常"))
{
breathType.Text = _lang == "en-US" ? "Standby" : "待机";
StopAllAnimations();
breathType.Foreground = Brushes.Orange;
breathType.FontSize = 20;
breathType.Opacity = 1.0;
}
else if (newState == (_lang == "en-US" ? "Inhale" : "吸气"))
{
breathType.Text = _lang == "en-US" ? "Inhale" : "吸气";
StopAllAnimations();
var colorAnim = new ColorAnimation(Colors.Gray, Colors.Green, TimeSpan.FromSeconds(2.5));
var brush = new SolidColorBrush(Colors.Gray);
breathType.Foreground = brush;
brush.BeginAnimation(SolidColorBrush.ColorProperty, colorAnim);
var sizeAnim = new DoubleAnimation(16, 40, TimeSpan.FromSeconds(2.5));
var opacAnim = new DoubleAnimation(0.5, 1.0, TimeSpan.FromSeconds(2.5));
breathType.BeginAnimation(TextBlock.FontSizeProperty, sizeAnim);
breathType.BeginAnimation(TextBlock.OpacityProperty, opacAnim);
}
else
{
breathType.Text = _lang == "en-US" ? "Exhale" : "呼气";
StopAllAnimations();
var colorAnim = new ColorAnimation(Colors.Green, Colors.Red, TimeSpan.FromSeconds(2.5));
var brush = new SolidColorBrush(Colors.Green);
breathType.Foreground = brush;
brush.BeginAnimation(SolidColorBrush.ColorProperty, colorAnim);
var sizeAnim = new DoubleAnimation(40, 16, TimeSpan.FromSeconds(2.5));
var opacAnim = new DoubleAnimation(1.0, 0.5, TimeSpan.FromSeconds(2.5));
breathType.BeginAnimation(TextBlock.FontSizeProperty, sizeAnim);
breathType.BeginAnimation(TextBlock.OpacityProperty, opacAnim);
}
});
}
}
catch (Exception ex)
{
Console.WriteLine(_lang == "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败{ex.Message}");
}
}
// 新增:停止所有动画的辅助方法
private void StopAllAnimations()
{
// 停止动画null表示移除动画
breathType.BeginAnimation(TextBlock.FontSizeProperty, null);
breathType.BeginAnimation(TextBlock.OpacityProperty, null);
if (breathType.Foreground is SolidColorBrush brush)
{
//brush.BeginAnimation(SolidColorBrush.ColorProperty, null);
}
}
/// <summary>
/// 通用定时器创建方法(统一配置)
/// </summary>
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;
}
/// <summary>
/// 初始化数据库创建CO2记录表
/// </summary>
private void InitializeDatabase()
{
try
{
using (var connection = new SQLiteConnection(CSConstant.DbConnectionString))
{
connection.Open();
string createTableSql = @"
CREATE TABLE IF NOT EXISTS CO2 (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Flow REAL NOT NULL,
Pressure REAL NOT NULL,
BeginCO2 REAL NOT NULL,
EndCO2 REAL NOT NULL,
CO2Added REAL NOT NULL,
RecordTime DATETIME NOT NULL
);";
using (var command = new SQLiteCommand(createTableSql, connection))
{
command.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
Console.WriteLine($"数据库初始化失败: {ex.Message}");
ShowError($"数据库错误: {ex.Message}");
}
}
/// <summary>
/// 释放所有资源(避免内存泄漏)
/// </summary>
public void Dispose()
{
// 释放定时器(含数据记录定时器)
_outReadTimer?.Dispose();
_breathTimer?.Dispose();
_co2Timer?.Dispose();
_co2BeginEndTimer?.Dispose();
_co2AddTimer?.Dispose();
_beginRecordTimer?.Dispose();
// 释放Modbus连接
_tcpClient?.Close();
_tcpClient?.Dispose();
_modbusMaster = null;
}
/// <summary>
/// 窗口关闭时强制释放资源
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Dispose();
}
#endregion
#region
/// <summary>
/// 鼻口参数读取(呼吸次数)
/// </summary>
private void OnOutTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateSingleRegister(
_outRegisterAddress, false,
value => UpdateOutUI(value.ToString())
);
}
/// <summary>
/// 呼吸计时读取(分+秒)
/// </summary>
private void OnBreathTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegisterUshort(
_breathMinuteAddress,
_breathSecondAddress,
(min, sec) => UpdateTimerUI(min.ToString(), sec.ToString())
);
}
/// <summary>
/// 呼路管压力+CO2浓度读取
/// </summary>
private void OnCo2TimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegister(
_breathOutAddress,
_co2Address,
(pressure, co2) => UpdateCo2TimerUI(pressure.ToString(), co2.ToString())
);
}
/// <summary>
/// 数据记录定时器定时保存CO2相关参数
/// </summary>
private void OnBeginRecordTimerElapsed(object sender, ElapsedEventArgs e)
{
if (!IsModbusConnected()) return;
try
{
float flowData = ReadAndUpdateSingleRegisterWithNoUI(_co2Address, true);
float pressureData = ReadAndUpdateSingleRegisterWithNoUI(_breathOutAddress, true);
float beginCo2Data = ReadAndUpdateSingleRegisterWithNoUI(_co2BeginAddress, true);
float endCo2Data = ReadAndUpdateSingleRegisterWithNoUI(_co2EndAddress, true);
float addCo2Data = ReadAndUpdateSingleRegisterWithNoUI(_co2AddAddress, true);
// 保存到数据库
SaveRecordToDatabase(
flowData,
pressureData,
beginCo2Data,
endCo2Data,
addCo2Data
);
}
catch (Exception ex)
{
Console.WriteLine($"数据记录失败: {ex.Message}");
}
}
/// <summary>
/// CO2开始/结束浓度读取
/// </summary>
private void OnCO2BeginEndTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegister(
_co2BeginAddress,
_co2EndAddress,
(begin, end) => UpdateCO2BeginEndTimerUI(begin.ToString(), end.ToString())
);
}
/// <summary>
/// CO2增加值读取
/// </summary>
private void OnCO2AddTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateSingleRegister(
_co2AddAddress, true,
value => UpdateCO2AddTimerUI(value.ToString())
);
}
#endregion
#region Modbus通用操作
/// <summary>
/// 通用寄存器读取并更新UI支持16位整数和32位浮点数
/// </summary>
/// <param name="address">起始地址</param>
/// <param name="isFloat">是否为浮点型占用2个寄存器</param>
/// <param name="updateAction">更新UI的回调函数</param>
private void ReadAndUpdateSingleRegister(ushort address, bool isFloat, Action<object> 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]是D312data[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);
}
}
private float ReadAndUpdateSingleRegisterWithNoUI(ushort address, bool isFloat)
{
if (!IsModbusConnected())
{
return 0;
}
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]是D312data[1]是D313
ushort a = data[0]; // 高位寄存器值
ushort b = data[1]; // 低位寄存器值
float floatValue = c.UshortToFloat(b, a);
floatValue = (float)Math.Round(floatValue, 2);
return floatValue;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
catch (Exception ex)
{
Console.WriteLine($"读取寄存器[{address:X4}]失败: {ex.Message}");
return 0;
}
}
/// <summary>
/// 读取两个寄存器并更新UI统一处理连接状态
/// </summary>
private void ReadAndUpdateDualRegister(ushort address1, ushort address2, Action<float, float> updateAction)
{
if (!IsModbusConnected())
{
updateAction?.Invoke(0, 0); // 触发UI显示"连接断开"
return;
}
try
{
ushort[] data1 = _modbusMaster?.ReadHoldingRegisters(0x01, address1, 2);
ushort[] data2 = _modbusMaster?.ReadHoldingRegisters(0x01, address2, 2);
// 2. 解析寄存器值data[0]是D312data[1]是D313
ushort a = data1[0]; // 高位寄存器值
ushort b = data1[1]; // 低位寄存器值
float floatValue = c.UshortToFloat(b, a);
floatValue = (float)Math.Round(floatValue, 2);
// 2. 解析寄存器值data[0]是D312data[1]是D313
ushort a1 = data2[0]; // 高位寄存器值
ushort b1 = data2[1]; // 低位寄存器值
float floatValue2 = c.UshortToFloat(b1, a1);
floatValue2 = (float)Math.Round(floatValue2, 2);
updateAction?.Invoke(floatValue, floatValue2);
}
catch (Exception ex)
{
Console.WriteLine($"读取寄存器[{address1:X4},{address2:X4}]失败: {ex.Message}");
updateAction?.Invoke(0, 0); // 触发UI显示"读取失败"
}
}
/// <summary>
/// 读取两个寄存器并更新UI统一处理连接状态
/// </summary>
private void ReadAndUpdateDualRegisterUshort(ushort address1, ushort address2, Action<float, float> updateAction)
{
if (!IsModbusConnected())
{
updateAction?.Invoke(0, 0); // 触发UI显示"连接断开"
return;
}
try
{
ushort[] data1 = _modbusMaster?.ReadHoldingRegisters(0x01, address1, 2);
ushort[] data2 = _modbusMaster?.ReadHoldingRegisters(0x01, address2, 2);
ushort floatValue = data1[0];
ushort floatValue2 = data2[0];
updateAction?.Invoke(floatValue, floatValue2);
}
catch (Exception ex)
{
Console.WriteLine($"读取寄存器[{address1:X4},{address2:X4}]失败: {ex.Message}");
updateAction?.Invoke(0, 0); // 触发UI显示"读取失败"
}
}
/// <summary>
/// 检查Modbus连接状态统一判断逻辑
/// </summary>
private bool IsModbusConnected()
{
return _modbusMaster != null && _tcpClient?.Connected == true;
}
#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
);
}
private void UpdateOutUI(string val)
{
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
UpdateUiSafely(() => respiratoryCountTxt.Text = IsModbusConnected() ? val : (currentLanguage == "en-US" ? "Disconnected" : "连接断开"));
}
/// <summary>
/// 更新呼吸计时UI
/// </summary>
private void UpdateTimerUI(string minute, string second)
{
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
UpdateUiSafely(() =>
{
breathTimeBegin.Text = IsModbusConnected() ? minute : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
breathTimeEnd.Text = IsModbusConnected() ? second : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
});
}
/// <summary>
/// 更新CO2和呼路管压力UI
/// </summary>
private void UpdateCo2TimerUI(string pressure, string co2)
{
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
UpdateUiSafely(() =>
{
RespiratoryPressureTxt.Text = IsModbusConnected() ? pressure : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
CO2PressureTxt.Text = IsModbusConnected() ? co2 : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
});
}
/// <summary>
/// 更新CO2开始/结束浓度UI
/// </summary>
private void UpdateCO2BeginEndTimerUI(string begin, string end)
{
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
UpdateUiSafely(() =>
{
CO2BeginTb.Text = IsModbusConnected() ? begin : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
CO2EndTb.Text = IsModbusConnected() ? end : (currentLanguage == "en-US" ? "Disconnected" : "连接断开");
});
}
/// <summary>
/// 更新CO2增加值UI
/// </summary>
private void UpdateCO2AddTimerUI(string value)
{
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
UpdateUiSafely(() =>
CO2AddTxt.Text = IsModbusConnected() ? value : (currentLanguage == "en-US" ? "Disconnected" : "连接断开")
);
}
/// <summary>
/// 更新潮气UI按钮写入后刷新
/// </summary>
private void UpdatePressureUI1(string value)
{
UpdateUiSafely(() => OutTxt.Text = value);
}
/// <summary>
/// 更新潮气UI按钮写入后刷新
/// </summary>
private void UpdatePressureUI2(string value)
{
UpdateUiSafely(() => InTxt.Text = value);
}
/// <summary>
/// 更新潮气UI按钮写入后刷新
/// </summary>
private void UpdatePressureUI3(string value)
{
UpdateUiSafely(() => moistureTxt.Text = value);
}
/// <summary>
/// 更新潮气UI按钮写入后刷新
/// </summary>
private void UpdatePressureUI4(string value)
{
UpdateUiSafely(() => respiratoryRateTxt.Text = value);
}
/// <summary>
/// 更新潮气UI按钮写入后刷新
/// </summary>
private void UpdatePressureUI5(string value)
{
UpdateUiSafely(() => frequencyTxt.Text = value);
}
/// <summary>
/// 线程安全的UI更新通用方法统一实现
/// </summary>
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
/// <summary>
/// 返回主窗口
/// </summary>
private void Button_Click_4(object sender, RoutedEventArgs e)
{
var mainWindow = MainWindow.Instance;
// 检查窗口状态只在窗口未显示时调用ShowDialog
if (!mainWindow.IsVisible)
{
mainWindow.ShowDialog();
}
else
{
// 如果窗口已显示,可将其激活到前台
mainWindow.Activate();
}
Close();
}
// 未实现的按钮事件(保留原空实现)
private void Button_Click_5(object sender, RoutedEventArgs e) { }
private void Button_Click_6(object sender, RoutedEventArgs e)
{
new LineChartDemo.MainWindow().ShowDialog();
Close();
}
private void Button_Click_7(object sender, RoutedEventArgs e)
{
new Window6().ShowDialog();
Close();
}
/// <summary>
/// 打开报告窗口Button8
/// </summary>
private void Button_Click_8(object sender, RoutedEventArgs e)
{
new ReportWindow5().ShowDialog();
}
/// <summary>
/// 打开报告窗口Button9与Button8功能一致
/// </summary>
private void Button_Click_9(object sender, RoutedEventArgs e)
{
new ReportWindow6().ShowDialog();
}
/// <summary>
/// 复位操作Button10
/// </summary>
private async void Button_Click_10(object sender, RoutedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
UpdateResetButtonStatus(_lang == "en-US" ? "Disconnected" : "连接断开", Brushes.Red);
ShowError(_lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
try
{
UpdateResetButtonStatus(_lang == "en-US" ? "Resetting..." : "正在复位...", Brushes.LightGreen);
await Task.Run(() => _modbusMaster.WriteSingleCoil(0x01, _resetAddress, true));
fc.BtnClickFunctionForNew(Function.ButtonType., _resetAddress);
await Task.Delay(100);
await Task.Run(() => _modbusMaster.WriteSingleCoil(0x01, _resetAddress, false));
fc.BtnClickFunctionForNew(Function.ButtonType., _resetAddress);
UpdateResetButtonStatus(_lang == "en-US" ? "Reset Success" : "复位成功", Brushes.LightGreen);
}
catch (Exception ex)
{
Console.WriteLine(_lang == "en-US" ? $"Reset error: {ex.Message}" : $"复位异常: {ex.Message}");
UpdateResetButtonStatus(_lang == "en-US" ? "Reset Failed" : "复位失败", Brushes.Red);
ShowError(_lang == "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}");
}
}
/// <summary>
/// 二氧化碳测试启动Button11
/// </summary>
private async void Button_Click_11(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(200);
// 写入启动线圈
await Task.Run(() =>
_modbusMaster.WriteSingleCoilAsync(0x01, _testStartAddress, false)
);
// 等待并验证状态
await Task.Delay(200);
UpdateButtonStatus(_lang == "en-US" ? "Test Start Success" : "测试启动成功", Brushes.LightGreen);
}
catch (Exception ex)
{
Console.WriteLine($"测试启动异常: {ex.Message}");
UpdateButtonStatus(_lang == "en-US" ? "Test Start Failed" : "测试启动失败", Brushes.Red);
ShowError($"操作异常: {ex.Message}");
}
}
/// <summary>
/// 测试停止Button12
/// </summary>
private async void Button_Click_12(object sender, RoutedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
await WriteCoilWithCheck(_testStopAddress, true, null, _lang == "en-US" ? "Test stop timeout" : "测试停止超时,状态异常", _lang == "en-US" ? "Test Stop" : "测试停止");
TestStartButton.Content = _lang == "en-US" ? "Start Test" : "测试启动";
TestStartButton.Foreground = Brushes.White;
}
/// <summary>
/// 开始数据记录Button13
/// </summary>
private async void Button_Click_13(object sender, RoutedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
ShowError(_lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
try { await Task.Delay(100); fc.BtnClickFunctionForNew(Function.ButtonType., 63); }
catch (Exception ex) { ShowError(_lang == "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}"); }
ShowWarning(_lang == "en-US" ? "Recording started" : "已开始记录");
_beginRecordTimer = CreateTimer(1000, OnBeginRecordTimerElapsed);
}
private async void Button_Click_14(object sender, RoutedEventArgs e)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
ShowError(_lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
try { await Task.Delay(100); fc.BtnClickFunctionForNew(Function.ButtonType., 64); }
catch (Exception ex) { ShowError(_lang == "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}"); }
if (_beginRecordTimer != null)
{
MessageBox.Show(_lang == "en-US" ? "Recording stopped" : "已停止记录", _lang == "en-US" ? "Info" : "提示", MessageBoxButton.OK, MessageBoxImage.Information);
_beginRecordTimer.Stop(); _beginRecordTimer.Dispose(); _beginRecordTimer = null;
}
else
{
MessageBox.Show(_lang == "en-US" ? "Not recording" : "未在记录状态,无需停止", _lang == "en-US" ? "Warning" : "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var records = ReadCO2RecordsFromDatabase();
if (records == null || !records.Any())
{
MessageBox.Show(_lang == "en-US" ? "No CO2 data to export" : "CO2表中无数据无法导出", _lang == "en-US" ? "Warning" : "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
bool ok = ExportCO2RecordsToExcel(records);
if (ok) MessageBox.Show(_lang == "en-US" ? "Export to Excel success" : "数据已成功导出到Excel", _lang == "en-US" ? "Success" : "成功", MessageBoxButton.OK, MessageBoxImage.Information);
else MessageBox.Show(_lang == "en-US" ? "Export failed" : "Excel导出失败请检查文件是否被占用", _lang == "en-US" ? "Error" : "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
/// <summary>
/// 从数据库CO2表读取数据不变
/// </summary>
private List<CO2Record> ReadCO2RecordsFromDatabase()
{
List<CO2Record> records = new List<CO2Record>();
try
{
using (SQLiteConnection conn = new SQLiteConnection(CSConstant.DbConnectionString))
{
conn.Open();
// 查询CO2表所有记录按时间排序
string query = "SELECT Flow, Pressure, BeginCO2, EndCO2, CO2Added, RecordTime FROM CO2 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
BeginCO2 = reader.GetDouble(2), // 开始CO2浓度%
EndCO2 = reader.GetDouble(3), // 终CO2浓度%
CO2Added = reader.GetDouble(4), // CO2浓度相对增加%
RecordTime = reader.GetDateTime(5) // 时间
});
}
}
}
}
return records;
}
catch (Exception ex)
{
MessageBox.Show($"读取CO2表失败{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return null;
}
}
/// <summary>
/// 将CO2数据导出到ExcelWPF .NET 8 适配版)
/// </summary>
private bool ExportCO2RecordsToExcel(List<CO2Record> records)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
try
{
SaveFileDialog dlg = new SaveFileDialog
{
Filter = _lang == "en-US" ? "Excel File (*.xlsx)|*.xlsx" : "Excel文件 (*.xlsx)|*.xlsx",
FileName = $"CO2_Record_{DateTime.Now:yyyyMMddHHmmss}.xlsx",
Title = _lang == "en-US" ? "Save CO2 Record" : "保存CO2记录"
};
if (dlg.ShowDialog() != true) return false;
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var pkg = new ExcelPackage(new FileInfo(dlg.FileName)))
{
var ws = pkg.Workbook.Worksheets.Add(_lang == "en-US" ? "CO2 Records" : "CO2记录");
ws.Cells[1, 1].Value = _lang == "en-US" ? "CO2(%)" : "二氧化碳浓度(%";
ws.Cells[1, 2].Value = _lang == "en-US" ? "Pressure(Pa)" : "压力pa";
ws.Cells[1, 3].Value = _lang == "en-US" ? "Begin CO2(%)" : "开始CO2浓度%";
ws.Cells[1, 4].Value = _lang == "en-US" ? "End CO2(%)" : "终CO2浓度%";
ws.Cells[1, 5].Value = _lang == "en-US" ? "CO2 Added(%)" : "CO2浓度相对增加%";
ws.Cells[1, 6].Value = _lang == "en-US" ? "Time" : "时间";
var head = ws.Cells[1, 1, 1, 6]; head.Style.Font.Bold = true; head.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
for (int i = 0; i < records.Count; i++)
{
var row = i + 2; var r = records[i];
ws.Cells[row, 1].Value = r.Flow; ws.Cells[row, 2].Value = r.Pressure;
ws.Cells[row, 3].Value = r.BeginCO2; ws.Cells[row, 4].Value = r.EndCO2;
ws.Cells[row, 5].Value = r.CO2Added; ws.Cells[row, 6].Value = r.RecordTime.ToString("yyyy-MM-dd HH:mm:ss");
}
ws.Cells.AutoFitColumns(); pkg.Save();
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(_lang == "en-US" ? $"Export failed: {ex.Message}" : $"导出失败:{ex.Message}", _lang == "en-US" ? "Error" : "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
/// <summary>
/// CO2记录实体类与数据库字段对应
/// </summary>
public class CO2Record
{
public double Flow { get; set; } // 二氧化碳浓度(%
public double Pressure { get; set; } // 压力pa
public double BeginCO2 { get; set; } // 开始CO2浓度%
public double EndCO2 { get; set; } // 终CO2浓度%
public double CO2Added { get; set; } // CO2浓度相对增加%
public DateTime RecordTime { get; set; } // 时间
}
private void Button_Click_15(object sender, RoutedEventArgs e) { }
#endregion
#region
/// <summary>
/// 写入线圈并验证状态(通用方法)
/// </summary>
private async Task WriteCoilWithCheck(ushort coilAddress, bool value, string successMsg, string failMsg, string logMsg)
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
ShowError(_lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
try
{
await Task.Run(() => _modbusMaster?.WriteSingleCoilAsync(0x01, coilAddress, value));
Thread.Sleep(200);
bool needReset = false;
if (failMsg != null)
{
bool stop = failMsg.Contains("停止") || failMsg.Contains("Stop") || failMsg.Contains("stop");
bool calib = failMsg.Contains("校准") || failMsg.Contains("Calib") || failMsg.Contains("calib");
needReset = stop || calib;
}
if (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 noReset = false;
if (failMsg != null)
{
bool stop = failMsg.Contains("停止") || failMsg.Contains("Stop") || failMsg.Contains("stop");
bool calib = failMsg.Contains("校准") || failMsg.Contains("Calib") || failMsg.Contains("calib");
noReset = !stop && !calib;
}
if (noReset && status != null && status.Length > 0 && status[0] == value)
{
WriteLog($"{logMsg} - Address: {coilAddress}, State: {value}");
if (!string.IsNullOrEmpty(successMsg)) ShowSuccess(successMsg);
}
else if (noReset) ShowError(failMsg);
}
catch (Exception ex)
{
ShowError(_lang == "en-US" ? $"Communication error: {ex.Message}" : $"通信错误: {ex.Message}");
}
}
/// <summary>
/// 保存记录到数据库
/// </summary>
private void SaveRecordToDatabase(double flow, double pressure, double beginCo2, double endCo2, double co2Added)
{
try
{
flow = Math.Round(flow, 2);
pressure = Math.Round(pressure, 2);
beginCo2 = Math.Round(beginCo2, 2);
endCo2 = Math.Round(endCo2, 2);
co2Added = Math.Round(co2Added, 2);
using (var conn = new SQLiteConnection(CSConstant.DbConnectionString))
{
conn.Open();
string insertSql = @"
INSERT INTO CO2(Flow, Pressure, BeginCO2, EndCO2, CO2Added, RecordTime)
VALUES (@Flow, @Pressure, @BeginCO2, @EndCO2, @CO2Added, @RecordTime);";
using (var cmd = new SQLiteCommand(insertSql, conn))
{
cmd.Parameters.AddWithValue("@Flow", flow);
cmd.Parameters.AddWithValue("@Pressure", pressure);
cmd.Parameters.AddWithValue("@BeginCO2", beginCo2);
cmd.Parameters.AddWithValue("@EndCO2", endCo2);
cmd.Parameters.AddWithValue("@CO2Added", co2Added);
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}");
}
}
#endregion
#region UI状态更新
/// <summary>
/// 更新测试按钮状态UI
/// </summary>
private void UpdateButtonStatus(string text, Brush color)
{
UpdateUiSafely(() =>
{
TestStartButton.Content = text;
TestStartButton.Foreground = color;
});
}
private void UpdateAddButtonStatus(string text, Brush color)
{
UpdateUiSafely(() =>
{
addPaBtn.Content = text;
addPaBtn.Foreground = color;
});
}
/// <summary>
/// 更新复位按钮状态UI
/// </summary>
private void UpdateResetButtonStatus(string text, Brush color)
{
UpdateUiSafely(() =>
{
ResetBtn.Content = text;
ResetBtn.Foreground = color;
});
}
/// <summary>
/// 日志路径
/// </summary>
private string logPath => System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "modbus_log.txt");
/// <summary>
/// 写入操作日志
/// </summary>
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}");
}
}
/// <summary>
/// 加载窗口背景
/// </summary>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
string imagePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg");
if (System.IO.File.Exists(imagePath))
{
Background = new ImageBrush
{
ImageSource = new BitmapImage(new Uri(imagePath, UriKind.Absolute))
};
}
else
{
Console.WriteLine($"背景图片不存在: {imagePath}");
}
}
catch (Exception ex)
{
Console.WriteLine($"加载背景失败: {ex.Message}");
}
}
bool _isWriting;
// 消息提示封装(统一风格)
private void ShowSuccess(string message) => MessageBox.Show(message, "成功", MessageBoxButton.OK, MessageBoxImage.Information);
private void ShowWarning(string message) => MessageBox.Show(message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
private void ShowError(string message) => MessageBox.Show(message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
#endregion
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; }
try
{
inputControl.Text = _lang == "en-US" ? "Processing..." : "操作中...";
Function ma = new Function(_modbusMaster);
ma.WriteToPLCForNew("", registerAddress, Function.DataType.);
await Task.Delay(300);
}
catch (Exception ex) { ShowError(_lang == "en-US" ? $"Operation failed: {ex.Message}" : $"操作失败: {ex.Message}"); }
}
private bool _isPressurizing = false;
private CancellationTokenSource _pressurizeCts;
private async void Button_Click_16(object sender, RoutedEventArgs e)
{
_isPressurizing = !_isPressurizing;
if (_isPressurizing)
{
// 开始持续加压
await StartPressurizeLoop();
}
else
{
// 停止加压(取消循环并复位状态)
StopPressurize();
}
}
/// <summary>
/// 开始持续加压循环
/// </summary>
private async Task StartPressurizeLoop()
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
UpdateAddButtonStatus(_lang == "en-US" ? "Disconnected" : "连接断开", Brushes.Red);
ShowError(_lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
_isPressurizing = false; return;
}
_pressurizeCts = new CancellationTokenSource(); var token = _pressurizeCts.Token;
try
{
UpdateAddButtonStatus(_lang == "en-US" ? "Pressurizing..." : "正在加压...", Brushes.LightGreen);
while (_isPressurizing && !token.IsCancellationRequested)
{
await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, 657, true));
await Task.Delay(200);
if (!IsModbusConnected()) throw new Exception(_lang == "en-US" ? "Modbus disconnected" : "Modbus连接中途断开");
}
if (!token.IsCancellationRequested) UpdateAddButtonStatus(_lang == "en-US" ? "Pressurize" : "加压", Brushes.White);
}
catch (Exception ex)
{
Console.WriteLine(_lang == "en-US" ? $"Pressurize error: {ex.Message}" : $"加压异常: {ex.Message}");
UpdateAddButtonStatus(_lang == "en-US" ? "Press Failed" : "加压失败", Brushes.Red);
ShowError(_lang == "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}");
_isPressurizing = false;
}
}
/// <summary>
/// 停止加压(复位状态和资源)
/// </summary>
private void StopPressurize()
{
string _lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (_pressurizeCts != null && !_pressurizeCts.IsCancellationRequested) { _pressurizeCts.Cancel(); _pressurizeCts.Dispose(); }
_isPressurizing = false;
UpdateAddButtonStatus(_lang == "en-US" ? "Pressurize" : "加压", Brushes.White);
try { if (IsModbusConnected()) _modbusMaster.WriteSingleCoilAsync(0x01, 657, false).Wait(); } catch { }
}
private async void Button_Click_17(object sender, RoutedEventArgs e)
{
try
{
// 验证并写入呼吸比-呼气值
await WriteRegisterWithValidation(
inputControl: OutTxt,
registerAddress: 342, // 呼吸比-呼气寄存器地址
minValue: 1, // 呼吸比最小1
maxValue: 10000 // 呼吸比最大20
);
}
finally
{
}
}
private async void Button_Click_21(object sender, RoutedEventArgs e)
{
try
{
// 验证并写入频率系数值
await WriteRegisterWithValidation(
inputControl: frequencyTxt,
registerAddress: 368, // 频率系数寄存器地址(根据实际地址修改)
minValue: 0.1f, // 频率系数最小0.1
maxValue: 10f // 频率系数最大10
);
}
finally
{
}
}
private async void Button_Click_20(object sender, RoutedEventArgs e)
{
try
{
// 验证并写入呼吸频率值
await WriteRegisterWithValidation(
inputControl: respiratoryRateTxt,
registerAddress: 210, // 呼吸频率寄存器地址(根据实际地址修改)
minValue: 1, // 呼吸频率最小5
maxValue: 10000 // 呼吸频率最大60
);
}
finally
{
}
}
private async void Button_Click_19(object sender, RoutedEventArgs e)
{
try
{
// 验证并写入潮气量值
await WriteRegisterWithValidation(
inputControl: moistureTxt,
registerAddress: 300,
minValue: 0.1f, // 潮气量最小0.1
maxValue: 10000f // 潮气量最大20
);
}
finally
{
}
}
private async void Button_Click_18(object sender, RoutedEventArgs e)
{
try
{
// 验证并写入呼吸比-吸气值
await WriteRegisterWithValidation(
inputControl: InTxt,
registerAddress: 340,
minValue: 1, // 呼吸比最小1
maxValue: 10000 // 呼吸比最大500
);
}
finally
{
}
}
}
}