2026-05-04 14:46:58 +08:00
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 )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
try
{
bool [ ] result = _modbusMaster ? . ReadCoils ( 0x01 , 98 , 1 ) ;
2026-05-08 19:10:44 +08:00
bool isRunning = result ! = null & & result . Length > 0 & & result [ 0 ] ;
2026-05-04 14:46:58 +08:00
TestStartButton . Dispatcher . Invoke ( ( ) = >
{
2026-05-08 19:10:44 +08:00
if ( isRunning )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
TestStartButton . Content = _lang = = "en-US" ? "Test Started" : "测试启动成功" ;
2026-05-04 14:46:58 +08:00
TestStartButton . Foreground = Brushes . LightGreen ;
}
else
{
2026-05-08 19:10:44 +08:00
TestStartButton . Content = _lang = = "en-US" ? "Start Test" : "测试启动" ;
2026-05-04 14:46:58 +08:00
TestStartButton . Foreground = Brushes . White ;
}
} ) ;
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
Console . WriteLine ( _lang = = "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败: {ex.Message}" ) ;
2026-05-04 14:46:58 +08:00
}
}
private void OnResetTimerElapsed ( object sender , ElapsedEventArgs e )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
try
{
bool [ ] result = _modbusMaster ? . ReadCoils ( 0x01 , 3 , 1 ) ;
2026-05-08 19:10:44 +08:00
bool isRunning = result ! = null & & result . Length > 0 & & result [ 0 ] ;
2026-05-04 14:46:58 +08:00
ResetBtn . Dispatcher . Invoke ( ( ) = >
{
2026-05-08 19:10:44 +08:00
if ( isRunning )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
ResetBtn . Content = _lang = = "en-US" ? "Reset Success" : "复位成功" ;
2026-05-04 14:46:58 +08:00
ResetBtn . Foreground = Brushes . LightGreen ;
}
else
{
2026-05-08 19:10:44 +08:00
ResetBtn . Content = _lang = = "en-US" ? "Reset" : "复位" ;
2026-05-04 14:46:58 +08:00
ResetBtn . Foreground = Brushes . White ;
}
} ) ;
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
Console . WriteLine ( _lang = = "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败: {ex.Message}" ) ;
2026-05-04 14:46:58 +08:00
}
}
//int i = 0;
// 新增:记录当前状态(避免重复触发)
private string _currentBreathState = "" ; // 可能的值:"吸气"、"呼气"、"异常"
private void OnbreathTimerElapsed ( object sender , ElapsedEventArgs e )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
try
{
2026-05-08 19:10:44 +08:00
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 ] ;
2026-05-04 14:46:58 +08:00
string newState ;
2026-05-08 19:10:44 +08:00
if ( ( isIn & & isEx ) | | ( ! isIn & & ! isEx ) )
newState = _lang = = "en-US" ? "Error" : "异常" ;
else if ( isIn )
newState = _lang = = "en-US" ? "Inhale" : "吸气" ;
else
newState = _lang = = "en-US" ? "Exhale" : "呼气" ;
2026-05-04 14:46:58 +08:00
if ( newState ! = _currentBreathState )
{
breathType . Dispatcher . Invoke ( ( ) = >
{
_currentBreathState = newState ;
2026-05-08 19:10:44 +08:00
if ( newState = = ( _lang = = "en-US" ? "Error" : "异常" ) )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
breathType . Text = _lang = = "en-US" ? "Standby" : "待机" ;
2026-05-04 14:46:58 +08:00
StopAllAnimations ( ) ;
2026-05-08 19:10:44 +08:00
breathType . Foreground = Brushes . Orange ;
2026-05-04 14:46:58 +08:00
breathType . FontSize = 20 ;
breathType . Opacity = 1.0 ;
}
2026-05-08 19:10:44 +08:00
else if ( newState = = ( _lang = = "en-US" ? "Inhale" : "吸气" ) )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
breathType . Text = _lang = = "en-US" ? "Inhale" : "吸气" ;
2026-05-04 14:46:58 +08:00
StopAllAnimations ( ) ;
2026-05-08 19:10:44 +08:00
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 ) ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
else
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
breathType . Text = _lang = = "en-US" ? "Exhale" : "呼气" ;
2026-05-04 14:46:58 +08:00
StopAllAnimations ( ) ;
2026-05-08 19:10:44 +08:00
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 ) ;
2026-05-04 14:46:58 +08:00
}
} ) ;
}
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
Console . WriteLine ( _lang = = "en-US" ? $"Failed to read coil: {ex.Message}" : $"读取线圈或更新UI失败: {ex.Message}" ) ;
2026-05-04 14:46:58 +08:00
}
}
// 新增:停止所有动画的辅助方法
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]是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 ) ;
}
}
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]是D312, data[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]是D312, data[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]是D312, data[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更新 ( 统 一 线 程 安 全 处 理 )
2026-05-08 19:10:44 +08:00
private void UpdateConnectionTextUI ( System . Windows . Controls . TextBox textBox , string value )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
// 获取当前语言(默认中文)
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
2026-05-04 14:46:58 +08:00
UpdateUiSafely ( ( ) = >
2026-05-08 19:10:44 +08:00
textBox . Text = IsModbusConnected ( ) ? value : disconnectMsg
2026-05-04 14:46:58 +08:00
) ;
}
2026-05-08 19:10:44 +08:00
private void UpdateOutUI ( string val )
{
string currentLanguage = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
UpdateUiSafely ( ( ) = > respiratoryCountTxt . Text = IsModbusConnected ( ) ? val : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ) ;
}
2026-05-04 14:46:58 +08:00
/// <summary>
/// 更新呼吸计时UI
/// </summary>
private void UpdateTimerUI ( string minute , string second )
{
2026-05-08 19:10:44 +08:00
string currentLanguage = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
UpdateUiSafely ( ( ) = >
{
2026-05-08 19:10:44 +08:00
breathTimeBegin . Text = IsModbusConnected ( ) ? minute : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
breathTimeEnd . Text = IsModbusConnected ( ) ? second : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
2026-05-04 14:46:58 +08:00
} ) ;
}
/// <summary>
/// 更新CO2和呼路管压力UI
/// </summary>
private void UpdateCo2TimerUI ( string pressure , string co2 )
{
2026-05-08 19:10:44 +08:00
string currentLanguage = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
UpdateUiSafely ( ( ) = >
{
2026-05-08 19:10:44 +08:00
RespiratoryPressureTxt . Text = IsModbusConnected ( ) ? pressure : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
CO2PressureTxt . Text = IsModbusConnected ( ) ? co2 : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
2026-05-04 14:46:58 +08:00
} ) ;
}
/// <summary>
/// 更新CO2开始/结束浓度UI
/// </summary>
private void UpdateCO2BeginEndTimerUI ( string begin , string end )
{
2026-05-08 19:10:44 +08:00
string currentLanguage = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
UpdateUiSafely ( ( ) = >
{
2026-05-08 19:10:44 +08:00
CO2BeginTb . Text = IsModbusConnected ( ) ? begin : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
CO2EndTb . Text = IsModbusConnected ( ) ? end : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" ) ;
2026-05-04 14:46:58 +08:00
} ) ;
}
/// <summary>
/// 更新CO2增加值UI
/// </summary>
private void UpdateCO2AddTimerUI ( string value )
{
2026-05-08 19:10:44 +08:00
string currentLanguage = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
UpdateUiSafely ( ( ) = >
2026-05-08 19:10:44 +08:00
CO2AddTxt . Text = IsModbusConnected ( ) ? value : ( currentLanguage = = "en-US" ? "Disconnected" : "连接断开" )
2026-05-04 14:46:58 +08:00
) ;
}
/// <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
2026-05-07 16:51:45 +08:00
2026-05-04 14:46:58 +08:00
/// <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 )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
UpdateResetButtonStatus ( _lang = = "en-US" ? "Disconnected" : "连接断开" , Brushes . Red ) ;
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
2026-05-04 14:46:58 +08:00
return ;
}
try
{
2026-05-08 19:10:44 +08:00
UpdateResetButtonStatus ( _lang = = "en-US" ? "Resetting..." : "正在复位..." , Brushes . LightGreen ) ;
await Task . Run ( ( ) = > _modbusMaster . WriteSingleCoil ( 0x01 , _resetAddress , true ) ) ;
2026-05-04 14:46:58 +08:00
fc . BtnClickFunctionForNew ( Function . ButtonType . 复 位 型 , _resetAddress ) ;
await Task . Delay ( 100 ) ;
2026-05-08 19:10:44 +08:00
await Task . Run ( ( ) = > _modbusMaster . WriteSingleCoil ( 0x01 , _resetAddress , false ) ) ;
2026-05-04 14:46:58 +08:00
fc . BtnClickFunctionForNew ( Function . ButtonType . 复 位 型 , _resetAddress ) ;
2026-05-08 19:10:44 +08:00
UpdateResetButtonStatus ( _lang = = "en-US" ? "Reset Success" : "复位成功" , Brushes . LightGreen ) ;
2026-05-04 14:46:58 +08:00
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
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}" ) ;
2026-05-04 14:46:58 +08:00
}
}
/// <summary>
/// 二氧化碳测试启动( Button11)
/// </summary>
private async void Button_Click_11 ( object sender , RoutedEventArgs e )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
UpdateButtonStatus ( _lang = = "en-US" ? "Disconnected" : "连接断开" , Brushes . Red ) ;
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
2026-05-04 14:46:58 +08:00
return ;
}
try
{
2026-05-08 19:10:44 +08:00
UpdateButtonStatus ( _lang = = "en-US" ? "Starting..." : "正在启动..." , Brushes . LightGreen ) ;
2026-05-04 14:46:58 +08:00
// 写入启动线圈
await Task . Run ( ( ) = >
_modbusMaster . WriteSingleCoilAsync ( 0x01 , _testStartAddress , true )
) ;
// 等待并验证状态
await Task . Delay ( 200 ) ;
// 写入启动线圈
await Task . Run ( ( ) = >
_modbusMaster . WriteSingleCoilAsync ( 0x01 , _testStartAddress , false )
) ;
// 等待并验证状态
await Task . Delay ( 200 ) ;
2026-05-08 19:10:44 +08:00
UpdateButtonStatus ( _lang = = "en-US" ? "Test Start Success" : "测试启动成功" , Brushes . LightGreen ) ;
2026-05-04 14:46:58 +08:00
}
catch ( Exception ex )
{
Console . WriteLine ( $"测试启动异常: {ex.Message}" ) ;
2026-05-08 19:10:44 +08:00
UpdateButtonStatus ( _lang = = "en-US" ? "Test Start Failed" : "测试启动失败" , Brushes . Red ) ;
2026-05-04 14:46:58 +08:00
ShowError ( $"操作异常: {ex.Message}" ) ;
}
}
/// <summary>
/// 测试停止( Button12)
/// </summary>
private async void Button_Click_12 ( object sender , RoutedEventArgs e )
{
2026-05-08 19:10:44 +08:00
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" : "测试启动" ;
2026-05-04 14:46:58 +08:00
TestStartButton . Foreground = Brushes . White ;
}
/// <summary>
/// 开始数据记录( Button13)
/// </summary>
private async void Button_Click_13 ( object sender , RoutedEventArgs e )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
2026-05-04 14:46:58 +08:00
return ;
}
2026-05-08 19:10:44 +08:00
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" : "已开始记录" ) ;
2026-05-04 14:46:58 +08:00
_beginRecordTimer = CreateTimer ( 1000 , OnBeginRecordTimerElapsed ) ;
}
private async void Button_Click_14 ( object sender , RoutedEventArgs e )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
2026-05-04 14:46:58 +08:00
return ;
}
2026-05-08 19:10:44 +08:00
try { await Task . Delay ( 100 ) ; fc . BtnClickFunctionForNew ( Function . ButtonType . 切 换 型 , 64 ) ; }
catch ( Exception ex ) { ShowError ( _lang = = "en-US" ? $"Operation error: {ex.Message}" : $"操作异常: {ex.Message}" ) ; }
2026-05-04 14:46:58 +08:00
if ( _beginRecordTimer ! = null )
{
2026-05-08 19:10:44 +08:00
MessageBox . Show ( _lang = = "en-US" ? "Recording stopped" : "已停止记录" , _lang = = "en-US" ? "Info" : "提示" , MessageBoxButton . OK , MessageBoxImage . Information ) ;
_beginRecordTimer . Stop ( ) ; _beginRecordTimer . Dispose ( ) ; _beginRecordTimer = null ;
2026-05-04 14:46:58 +08:00
}
else
{
2026-05-08 19:10:44 +08:00
MessageBox . Show ( _lang = = "en-US" ? "Not recording" : "未在记录状态,无需停止" , _lang = = "en-US" ? "Warning" : "提示" , MessageBoxButton . OK , MessageBoxImage . Warning ) ;
2026-05-04 14:46:58 +08:00
return ;
}
2026-05-08 19:10:44 +08:00
var records = ReadCO2RecordsFromDatabase ( ) ;
if ( records = = null | | ! records . Any ( ) )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
MessageBox . Show ( _lang = = "en-US" ? "No CO2 data to export" : "CO2表中无数据, 无法导出" , _lang = = "en-US" ? "Warning" : "提示" , MessageBoxButton . OK , MessageBoxImage . Warning ) ;
2026-05-04 14:46:58 +08:00
return ;
}
2026-05-08 19:10:44 +08:00
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 ) ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
2026-05-04 14:46:58 +08:00
/// <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数据导出到Excel( WPF .NET 8 适配版)
/// </summary>
private bool ExportCO2RecordsToExcel ( List < CO2Record > records )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
try
{
2026-05-08 19:10:44 +08:00
SaveFileDialog dlg = new SaveFileDialog
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
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记录"
2026-05-04 14:46:58 +08:00
} ;
2026-05-08 19:10:44 +08:00
if ( dlg . ShowDialog ( ) ! = true ) return false ;
ExcelPackage . LicenseContext = LicenseContext . NonCommercial ;
using ( var pkg = new ExcelPackage ( new FileInfo ( dlg . FileName ) ) )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
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 ;
2026-05-04 14:46:58 +08:00
for ( int i = 0 ; i < records . Count ; i + + )
{
2026-05-08 19:10:44 +08:00
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" ) ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
ws . Cells . AutoFitColumns ( ) ; pkg . Save ( ) ;
2026-05-04 14:46:58 +08:00
}
return true ;
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
MessageBox . Show ( _lang = = "en-US" ? $"Export failed: {ex.Message}" : $"导出失败:{ex.Message}" , _lang = = "en-US" ? "Error" : "错误" , MessageBoxButton . OK , MessageBoxImage . Error ) ;
2026-05-04 14:46:58 +08:00
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>
2026-05-08 19:10:44 +08:00
private async Task WriteCoilWithCheck ( ushort coilAddress , bool value , string successMsg , string failMsg , string logMsg )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
2026-05-04 14:46:58 +08:00
return ;
}
try
{
2026-05-08 19:10:44 +08:00
await Task . Run ( ( ) = > _modbusMaster ? . WriteSingleCoilAsync ( 0x01 , coilAddress , value ) ) ;
2026-05-04 14:46:58 +08:00
Thread . Sleep ( 200 ) ;
2026-05-08 19:10:44 +08:00
bool needReset = false ;
if ( failMsg ! = null )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
bool stop = failMsg . Contains ( "停止" ) | | failMsg . Contains ( "Stop" ) | | failMsg . Contains ( "stop" ) ;
bool calib = failMsg . Contains ( "校准" ) | | failMsg . Contains ( "Calib" ) | | failMsg . Contains ( "calib" ) ;
needReset = stop | | calib ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
if ( needReset )
{
await Task . Run ( ( ) = > _modbusMaster ? . WriteSingleCoilAsync ( 0x01 , coilAddress , false ) ) ;
}
Thread . Sleep ( 100 ) ;
2026-05-04 14:46:58 +08:00
await Task . Delay ( 500 ) ;
bool [ ] status = await _modbusMaster ? . ReadCoilsAsync ( 0x01 , coilAddress , 1 ) ;
2026-05-08 19:10:44 +08:00
bool noReset = false ;
if ( failMsg ! = null )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
bool stop = failMsg . Contains ( "停止" ) | | failMsg . Contains ( "Stop" ) | | failMsg . Contains ( "stop" ) ;
bool calib = failMsg . Contains ( "校准" ) | | failMsg . Contains ( "Calib" ) | | failMsg . Contains ( "calib" ) ;
noReset = ! stop & & ! calib ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
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 ) ;
2026-05-04 14:46:58 +08:00
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
ShowError ( _lang = = "en-US" ? $"Communication error: {ex.Message}" : $"通信错误: {ex.Message}" ) ;
2026-05-04 14:46:58 +08:00
}
}
/// <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
2026-05-08 19:10:44 +08:00
private async Task WriteRegisterWithValidation ( TextBox inputControl , ushort registerAddress , float minValue , float maxValue )
2026-05-04 14:46:58 +08:00
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
if ( ! IsModbusConnected ( ) ) { ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ; return ; }
2026-05-04 14:46:58 +08:00
try
{
2026-05-08 19:10:44 +08:00
inputControl . Text = _lang = = "en-US" ? "Processing..." : "操作中..." ;
2026-05-04 14:46:58 +08:00
Function ma = new Function ( _modbusMaster ) ;
ma . WriteToPLCForNew ( "" , registerAddress , Function . DataType . 浮 点 型 ) ;
await Task . Delay ( 300 ) ;
}
2026-05-08 19:10:44 +08:00
catch ( Exception ex ) { ShowError ( _lang = = "en-US" ? $"Operation failed: {ex.Message}" : $"操作失败: {ex.Message}" ) ; }
2026-05-04 14:46:58 +08:00
}
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 ( )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
2026-05-04 14:46:58 +08:00
if ( ! IsModbusConnected ( ) )
{
2026-05-08 19:10:44 +08:00
UpdateAddButtonStatus ( _lang = = "en-US" ? "Disconnected" : "连接断开" , Brushes . Red ) ;
ShowError ( _lang = = "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接" ) ;
_isPressurizing = false ; return ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
_pressurizeCts = new CancellationTokenSource ( ) ; var token = _pressurizeCts . Token ;
2026-05-04 14:46:58 +08:00
try
{
2026-05-08 19:10:44 +08:00
UpdateAddButtonStatus ( _lang = = "en-US" ? "Pressurizing..." : "正在加压..." , Brushes . LightGreen ) ;
2026-05-04 14:46:58 +08:00
while ( _isPressurizing & & ! token . IsCancellationRequested )
{
2026-05-08 19:10:44 +08:00
await Task . Run ( ( ) = > _modbusMaster . WriteSingleCoilAsync ( 0x01 , 657 , true ) ) ;
2026-05-04 14:46:58 +08:00
await Task . Delay ( 200 ) ;
2026-05-08 19:10:44 +08:00
if ( ! IsModbusConnected ( ) ) throw new Exception ( _lang = = "en-US" ? "Modbus disconnected" : "Modbus连接中途断开" ) ;
2026-05-04 14:46:58 +08:00
}
2026-05-08 19:10:44 +08:00
if ( ! token . IsCancellationRequested ) UpdateAddButtonStatus ( _lang = = "en-US" ? "Pressurize" : "加压" , Brushes . White ) ;
2026-05-04 14:46:58 +08:00
}
catch ( Exception ex )
{
2026-05-08 19:10:44 +08:00
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}" ) ;
2026-05-04 14:46:58 +08:00
_isPressurizing = false ;
}
}
/// <summary>
/// 停止加压(复位状态和资源)
/// </summary>
private void StopPressurize ( )
{
2026-05-08 19:10:44 +08:00
string _lang = ConfigurationManager . AppSettings [ "Language" ] ? ? "zh-CN" ;
if ( _pressurizeCts ! = null & & ! _pressurizeCts . IsCancellationRequested ) { _pressurizeCts . Cancel ( ) ; _pressurizeCts . Dispose ( ) ; }
2026-05-04 14:46:58 +08:00
_isPressurizing = false ;
2026-05-08 19:10:44 +08:00
UpdateAddButtonStatus ( _lang = = "en-US" ? "Pressurize" : "加压" , Brushes . White ) ;
try { if ( IsModbusConnected ( ) ) _modbusMaster . WriteSingleCoilAsync ( 0x01 , 657 , false ) . Wait ( ) ; } catch { }
2026-05-04 14:46:58 +08:00
}
private async void Button_Click_17 ( object sender , RoutedEventArgs e )
{
2026-05-08 19:10:44 +08:00
2026-05-04 14:46:58 +08:00
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
{
}
}
}
}