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
{
///
/// 呼吸相关参数监控窗口(Modbus通信+CO2数据记录)
///
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 初始化与资源释放(强化资源管理)
///
/// 初始化Modbus连接和定时器
///
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 输入验证(仅允许数字输入)
///
/// 通用数字输入过滤(支持整数和小数)
///
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
///
/// 统一初始化所有定时器(避免重复配置)
///
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)
{
try
{
bool[] result = _modbusMaster?.ReadCoils(0x01, 98, 1);
bool isTestRunning = result != null && result.Length > 0 && result[0];
TestStartButton.Dispatcher.Invoke(() =>
{
if (isTestRunning)
{
TestStartButton.Content = "测试启动成功";
TestStartButton.Foreground = Brushes.LightGreen;
}
else
{
TestStartButton.Content = "测试启动";
TestStartButton.Foreground = Brushes.White;
}
});
}
catch (Exception ex)
{
Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}");
}
}
private void OnResetTimerElapsed(object sender, ElapsedEventArgs e)
{
try
{
bool[] result = _modbusMaster?.ReadCoils(0x01, 3, 1);
bool isTestRunning = result != null && result.Length > 0 && result[0];
ResetBtn.Dispatcher.Invoke(() =>
{
if (isTestRunning)
{
ResetBtn.Content = "复位成功";
ResetBtn.Foreground = Brushes.LightGreen;
}
else
{
ResetBtn.Content = "复位";
ResetBtn.Foreground = Brushes.White;
}
});
}
catch (Exception ex)
{
Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}");
}
}
//int i = 0;
// 新增:记录当前状态(避免重复触发)
private string _currentBreathState = ""; // 可能的值:"吸气"、"呼气"、"异常"
private void OnbreathTimerElapsed(object sender, ElapsedEventArgs e)
{
try
{
// 读取线圈状态
bool[] inhaleResult = _modbusMaster?.ReadCoils(0x01, 51, 1);
bool[] exhaleResult = _modbusMaster?.ReadCoils(0x01, 55, 1);
bool isInhale = inhaleResult != null && inhaleResult.Length > 0 && inhaleResult[0];
bool isExhale = exhaleResult != null && exhaleResult.Length > 0 && exhaleResult[0];
// 确定新状态(先判断异常,再判断正常状态)
string newState;
if (isInhale && isExhale || !isInhale && !isExhale)
{
newState = "异常";
}
else if (isInhale)
{
newState = "吸气";
}
else // isExhale
{
newState = "呼气";
}
// 关键:仅当新状态与当前状态不同时,才更新UI(避免重复触发)
if (newState != _currentBreathState)
{
breathType.Dispatcher.Invoke(() =>
{
// 更新当前状态记录
_currentBreathState = newState;
if (newState == "异常")
{
breathType.Text = "待机";
// 停止所有动画,避免异常状态下动画混乱
StopAllAnimations();
breathType.Foreground = new SolidColorBrush(Colors.Orange);
breathType.FontSize = 20;
breathType.Opacity = 1.0;
}
else if (newState == "吸气")
{
breathType.Text = "吸气";
// 停止现有动画(避免与新动画冲突)
StopAllAnimations();
// 颜色动画
ColorAnimation colorAnimation = new ColorAnimation
{
From = Colors.Gray,
To = Colors.Green,
Duration = TimeSpan.FromSeconds(2.5)
};
SolidColorBrush colorBrush = new SolidColorBrush(Colors.Gray);
breathType.Foreground = colorBrush;
colorBrush.BeginAnimation(SolidColorBrush.ColorProperty, colorAnimation);
// 大小动画
DoubleAnimation sizeAnimation = new DoubleAnimation
{
From = 16,
To = 40,
Duration = TimeSpan.FromSeconds(2.5)
};
breathType.BeginAnimation(TextBlock.FontSizeProperty, sizeAnimation);
// 透明度动画
DoubleAnimation opacityAnimation = new DoubleAnimation
{
From = 0.5,
To = 1.0,
Duration = TimeSpan.FromSeconds(2.5)
};
breathType.BeginAnimation(TextBlock.OpacityProperty, opacityAnimation);
}
else // 呼气
{
breathType.Text = "呼气";
// 停止现有动画
StopAllAnimations();
// 颜色动画
ColorAnimation colorAnimation = new ColorAnimation
{
From = Colors.Green,
To = Colors.Red,
Duration = TimeSpan.FromSeconds(2.5)
};
SolidColorBrush colorBrush = new SolidColorBrush(Colors.Green);
breathType.Foreground = colorBrush;
colorBrush.BeginAnimation(SolidColorBrush.ColorProperty, colorAnimation);
// 大小动画
DoubleAnimation sizeAnimation = new DoubleAnimation
{
From = 40,
To = 16,
Duration = TimeSpan.FromSeconds(2.5)
};
breathType.BeginAnimation(TextBlock.FontSizeProperty, sizeAnimation);
// 透明度动画
DoubleAnimation opacityAnimation = new DoubleAnimation
{
From = 1.0,
To = 0.5,
Duration = TimeSpan.FromSeconds(2.5)
};
breathType.BeginAnimation(TextBlock.OpacityProperty, opacityAnimation);
}
});
}
}
catch (Exception ex)
{
Console.WriteLine($"读取线圈或更新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);
}
}
///
/// 通用定时器创建方法(统一配置)
///
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;
}
///
/// 初始化数据库(创建CO2记录表)
///
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}");
}
}
///
/// 释放所有资源(避免内存泄漏)
///
public void Dispose()
{
// 释放定时器(含数据记录定时器)
_outReadTimer?.Dispose();
_breathTimer?.Dispose();
_co2Timer?.Dispose();
_co2BeginEndTimer?.Dispose();
_co2AddTimer?.Dispose();
_beginRecordTimer?.Dispose();
// 释放Modbus连接
_tcpClient?.Close();
_tcpClient?.Dispose();
_modbusMaster = null;
}
///
/// 窗口关闭时强制释放资源
///
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Dispose();
}
#endregion
#region 定时器读取逻辑(优化异常处理)
///
/// 鼻口参数读取(呼吸次数)
///
private void OnOutTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateSingleRegister(
_outRegisterAddress, false,
value => UpdateOutUI(value.ToString())
);
}
///
/// 呼吸计时读取(分+秒)
///
private void OnBreathTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegisterUshort(
_breathMinuteAddress,
_breathSecondAddress,
(min, sec) => UpdateTimerUI(min.ToString(), sec.ToString())
);
}
///
/// 呼路管压力+CO2浓度读取
///
private void OnCo2TimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegister(
_breathOutAddress,
_co2Address,
(pressure, co2) => UpdateCo2TimerUI(pressure.ToString(), co2.ToString())
);
}
///
/// 数据记录定时器(定时保存CO2相关参数)
///
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}");
}
}
///
/// CO2开始/结束浓度读取
///
private void OnCO2BeginEndTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateDualRegister(
_co2BeginAddress,
_co2EndAddress,
(begin, end) => UpdateCO2BeginEndTimerUI(begin.ToString(), end.ToString())
);
}
///
/// CO2增加值读取
///
private void OnCO2AddTimerElapsed(object sender, ElapsedEventArgs e)
{
ReadAndUpdateSingleRegister(
_co2AddAddress, true,
value => UpdateCO2AddTimerUI(value.ToString())
);
}
#endregion
#region Modbus通用操作(提取重复逻辑)
///
/// 通用寄存器读取并更新UI,支持16位整数和32位浮点数
///
/// 起始地址
/// 是否为浮点型(占用2个寄存器)
/// 更新UI的回调函数
private void ReadAndUpdateSingleRegister(ushort address, bool isFloat, Action