Files
Sleep-Multi-functionality/Window3.xaml.cs
2026-05-09 14:34:05 +08:00

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