2026-05-04 14:46:58 +08:00
|
|
|
|
using Microsoft.Win32;
|
|
|
|
|
|
using Modbus;
|
2026-05-07 16:51:45 +08:00
|
|
|
|
using Modbus.Device;
|
2026-05-04 14:46:58 +08:00
|
|
|
|
using OfficeOpenXml;
|
2026-05-07 16:51:45 +08:00
|
|
|
|
using OxyPlot;
|
2026-05-04 14:46:58 +08:00
|
|
|
|
using Sunny.UI;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
|
|
|
|
using System.Configuration;
|
|
|
|
|
|
using System.Data.SQLite;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Diagnostics.Metrics;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using System.Timers;
|
|
|
|
|
|
using System.Windows;
|
|
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
|
using System.Windows.Input;
|
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
|
using 口罩泄露定制款;
|
|
|
|
|
|
using static ShanghaiEnvironmentalTechnology.Window5;
|
|
|
|
|
|
|
|
|
|
|
|
namespace ShanghaiEnvironmentalTechnology
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 气流压力测试窗口(Modbus通信+数据记录)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public partial class Window1 : Window, IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
#region 寄存器/线圈地址定义(按功能分组)
|
|
|
|
|
|
// 压力相关地址
|
|
|
|
|
|
private readonly ushort _pressureSettingRegisterAddress = 0x0138; // 压力设置地址
|
|
|
|
|
|
private readonly ushort _pressureRegisterAddress = 1372; // 实时压力读取地址
|
|
|
|
|
|
private readonly ushort _calibrationCoilAddress = 72; // 校准线圈(M72)
|
|
|
|
|
|
private readonly ushort _settingRegisterAddress = 312; // 流量记录地址
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 流量相关地址
|
|
|
|
|
|
private readonly ushort _flowAddress = 0x0078; // 实时流量读取地址
|
|
|
|
|
|
private readonly ushort _flowRegisterAddress = 0x0170; // 流量记录地址
|
|
|
|
|
|
private readonly ushort _flow2Address = 368; // 实时流量读取地址
|
|
|
|
|
|
|
|
|
|
|
|
// 测试控制线圈
|
|
|
|
|
|
private readonly ushort _testStartAddress = 60; // 测试启动(M60)
|
|
|
|
|
|
private readonly ushort _testStatusAddress = 0x003C; // 测试状态读取(同启动地址)
|
|
|
|
|
|
private readonly ushort _testStopAddress = 62; // 测试停止(M62)
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 私有字段
|
|
|
|
|
|
private TcpClient _tcpClient;
|
|
|
|
|
|
private IModbusMaster _modbusMaster;
|
|
|
|
|
|
private System.Timers.Timer _pressureReadTimer; // 实时压力定时器
|
|
|
|
|
|
private System.Timers.Timer _settingReadTimer; // 设定实时压力定时器
|
|
|
|
|
|
private System.Timers.Timer _flowTimer; // 实时流量定时器
|
|
|
|
|
|
|
|
|
|
|
|
private System.Timers.Timer startTimer; // 启动状态实时定时器
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
DataChange c = new DataChange();
|
|
|
|
|
|
Function fc;
|
|
|
|
|
|
public Window1()
|
|
|
|
|
|
{
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化定时器(1秒间隔)
|
|
|
|
|
|
_pressureReadTimer = CreateTimer(1000, OnPressureTimerElapsed);
|
|
|
|
|
|
_settingReadTimer = CreateTimer(1000, OnSettingReadTimerTimerElapsed);
|
|
|
|
|
|
_flowTimer = CreateTimer(1000, OnFlowTimerElapsed);
|
|
|
|
|
|
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>
|
|
|
|
|
|
/// 释放资源(定时器、TCP连接)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
_pressureReadTimer?.Dispose();
|
|
|
|
|
|
_flowTimer?.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 => UpdatePressureUI(value.ToString())
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设定实时压力读取定时器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnSettingReadTimerTimerElapsed(object sender, ElapsedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadAndUpdateRegister(
|
|
|
|
|
|
_settingRegisterAddress, true,
|
|
|
|
|
|
value => UpdateSettingUI(value.ToString())
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 实时流量读取定时器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnFlowTimerElapsed(object sender, ElapsedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReadAndUpdateRegister(
|
|
|
|
|
|
_flowAddress, true,
|
|
|
|
|
|
value => UpdateFlowPressureUI(value.ToString())
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 实时流量读取定时器(修正跨线程访问UI问题)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnStartTimerElapsed(object sender, ElapsedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
bool[] result = _modbusMaster?.ReadCoils(0x01, 61, 1);
|
|
|
|
|
|
bool isTestRunning = result != null && result.Length > 0 && result[0];
|
|
|
|
|
|
|
2026-05-07 16:51:45 +08:00
|
|
|
|
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 = "测试启动";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
TestStartButton.Dispatcher.Invoke(() =>
|
2026-05-07 16:51:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (isTestRunning)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
TestStartButton.Content = testStartButtonText;
|
|
|
|
|
|
TestStartButton.Foreground = Brushes.LightGreen;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
TestStartButton.Content = ButtonStatus;
|
|
|
|
|
|
TestStartButton.Foreground = Brushes.White;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 通用寄存器读取并更新UI,支持16位整数和32位浮点数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="address">起始地址</param>
|
|
|
|
|
|
/// <param name="isFloat">是否为浮点型(占用2个寄存器)</param>
|
|
|
|
|
|
/// <param name="updateAction">更新UI的回调函数</param>
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 浮点型转换(2个16位寄存器组合为32位浮点数)
|
|
|
|
|
|
if (data.Length >= 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 2. 解析寄存器值(data[0]是D312,data[1]是D313)
|
|
|
|
|
|
ushort a = data[0]; // 高位寄存器值
|
|
|
|
|
|
ushort b = data[1]; // 低位寄存器值
|
|
|
|
|
|
|
|
|
|
|
|
float floatValue = c.UshortToFloat(b, a);
|
|
|
|
|
|
floatValue = (float)Math.Round(floatValue, 2);
|
|
|
|
|
|
updateAction?.Invoke(floatValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
updateAction?.Invoke(float.NaN);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 16位整数直接返回
|
|
|
|
|
|
updateAction?.Invoke(data.Length > 0 ? data[0] : (ushort)0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"读取寄存器[{address:X4}]失败: {ex.Message}");
|
|
|
|
|
|
// 异常时根据类型返回对应的值
|
|
|
|
|
|
updateAction?.Invoke(isFloat ? (object)float.NaN : (ushort)0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region UI更新(统一线程安全处理)
|
2026-05-07 16:51:45 +08:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新实时压力UI
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdatePressureUI(string value)
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
UpdateConnectionTextUI(RealTimePressureTextBox, value);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新实时压力UI
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateSettingUI(string value)
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
UpdateConnectionTextUI(SettingPaTextBox, value);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新实时流量UI
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateFlowPressureUI(string value)
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
UpdateConnectionTextUI(txtFolw, value);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
2026-05-07 16:51:45 +08:00
|
|
|
|
//private void UpdateStartPressureUI(string value)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// UpdateUiSafely(() =>
|
|
|
|
|
|
// TestStartButton.Content = IsModbusConnected() ? value : "连接断开"
|
|
|
|
|
|
// );
|
|
|
|
|
|
//}
|
2026-05-04 14:46:58 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新实时流量UI
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateFlowFlowUI(string value)
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
UpdateConnectionTextUI(saveFlowTxt, value);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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 (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>
|
|
|
|
|
|
/// 校准指令(M41线圈)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void Button_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
string success = lang == "en-US" ? "Calibration command received (verified)" : "校准指令已被设备接收并执行(二次验证通过)";
|
|
|
|
|
|
string fail = lang == "en-US" ? "Calibration timeout, abnormal status" : "校准执行超时,状态异常";
|
|
|
|
|
|
string log = lang == "en-US" ? "Calibration successful" : "校准指令执行成功";
|
|
|
|
|
|
|
|
|
|
|
|
await WriteCoilWithCheck(_calibrationCoilAddress, true, success, fail, log);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 压力设置(SettingPaTextBox)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void SetPressureButton_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
await WriteRegisterWithValidation(
|
|
|
|
|
|
inputControl: SettingPaTextBox,
|
|
|
|
|
|
registerAddress: _pressureSettingRegisterAddress,
|
|
|
|
|
|
minValue: 0,
|
|
|
|
|
|
maxValue: 10000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 返回主窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void Button_Click_1(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
var mainWindow = MainWindow.Instance;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查窗口状态,只在窗口未显示时调用ShowDialog
|
|
|
|
|
|
if (!mainWindow.IsVisible)
|
|
|
|
|
|
{
|
|
|
|
|
|
mainWindow.ShowDialog();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 如果窗口已显示,可将其激活到前台
|
|
|
|
|
|
mainWindow.Activate();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 测试启动(M60线圈)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void Button_Click_2(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
if (!IsModbusConnected())
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +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-07 21:17:08 +08:00
|
|
|
|
UpdateButtonStatus(lang == "en-US" ? "Starting..." : "正在启动...", Brushes.LightGreen);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStartAddress, true));
|
2026-05-04 14:46:58 +08:00
|
|
|
|
await Task.Delay(100);
|
2026-05-07 21:17:08 +08:00
|
|
|
|
await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStatusAddress, false));
|
2026-05-04 14:46:58 +08:00
|
|
|
|
await Task.Delay(100);
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
UpdateButtonStatus(lang == "en-US" ? "Test Started" : "测试启动成功", Brushes.LightGreen);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
UpdateButtonStatus(lang == "en-US" ? "Start Failed" : "测试启动失败", Brushes.Red);
|
|
|
|
|
|
ShowError(lang == "en-US" ? $"Error: {ex.Message}" : $"操作异常: {ex.Message}");
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 测试停止(M62线圈)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async void Button_Click_3(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
string fail = lang == "en-US" ? "Airflow test stop timeout" : "气流测试停止超时,状态异常";
|
|
|
|
|
|
string log = lang == "en-US" ? "Airflow test stopped" : "气流测试停止";
|
2026-05-04 14:46:58 +08:00
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
await WriteCoilWithCheck(_testStopAddress, true, null, fail, log);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
TestStartButton.Content = lang == "en-US" ? "Test Start" : "测试启动";
|
|
|
|
|
|
TestStartButton.Foreground = Brushes.White;
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 流量数据读取并保存(saveFlowTxt)- 适配浮点型流量
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void Button_Click_4(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
if (!IsModbusConnected())
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
saveFlowTxt.Text = lang == "en-US" ? "Disconnected" : "连接断开";
|
|
|
|
|
|
ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
|
2026-05-04 14:46:58 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 190);
|
|
|
|
|
|
fc.BtnClickFunctionForNew(Function.ButtonType.切换型, 190);
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ReadAndUpdateRegister(_flow2Address, true, value => UpdateFlowFlowUI(value.ToString()));
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
if (float.TryParse(txtFolw.Text, out float flowValue) &&
|
|
|
|
|
|
float.TryParse(RealTimePressureTextBox.Text, out float pressureValue))
|
|
|
|
|
|
{
|
|
|
|
|
|
SaveRecordToDatabase(flowValue, pressureValue);
|
|
|
|
|
|
saveFlowTxt.Text = txtFolw.Text;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ShowError(lang == "en-US" ? "Invalid flow or pressure" : "流量或压力值格式不正确,请检查输入");
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
List<CO2Record> records = ReadCO2RecordsFromDatabase();
|
|
|
|
|
|
if (records == null || records.Count == 0)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string msg = lang == "en-US" ? "No data to export" : "排气流表中无数据,无法导出";
|
|
|
|
|
|
string title = lang == "en-US" ? "Info" : "提示";
|
|
|
|
|
|
MessageBox.Show(msg, title, MessageBoxButton.OK, MessageBoxImage.Warning);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
bool exportOk = ExportCO2RecordsToExcel(records);
|
|
|
|
|
|
if (exportOk)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string msg = lang == "en-US" ? "Export successful" : "数据已成功导出到Excel";
|
|
|
|
|
|
string title = lang == "en-US" ? "Success" : "成功";
|
|
|
|
|
|
MessageBox.Show(msg, title, MessageBoxButton.OK, MessageBoxImage.Information);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
private bool ExportCO2RecordsToExcel(List<CO2Record> records)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
SaveFileDialog saveDialog = new SaveFileDialog
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
Filter = "Excel(*.xlsx)|*.xlsx",
|
2026-05-04 14:46:58 +08:00
|
|
|
|
FileName = $"排气流记录_{DateTime.Now:yyyyMMddHHmmss}.xlsx",
|
2026-05-07 21:17:08 +08:00
|
|
|
|
Title = "Export Airflow Data"
|
2026-05-04 14:46:58 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
if (saveDialog.ShowDialog() != true) return false;
|
|
|
|
|
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
2026-05-07 16:51:45 +08:00
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
using (var package = new ExcelPackage(new FileInfo(saveDialog.FileName)))
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
var ws = package.Workbook.Worksheets.Add("Records");
|
|
|
|
|
|
ws.Cells[1, 1].Value = "Flow L/min";
|
|
|
|
|
|
ws.Cells[1, 2].Value = "Pressure Pa";
|
|
|
|
|
|
ws.Cells[1, 3].Value = "Time";
|
2026-05-04 14:46:58 +08:00
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < records.Count; i++)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ws.Cells[i + 2, 1].Value = records[i].Flow;
|
|
|
|
|
|
ws.Cells[i + 2, 2].Value = records[i].Pressure;
|
|
|
|
|
|
ws.Cells[i + 2, 3].Value = records[i].RecordTime.ToString("yyyy-MM-dd HH:mm:ss");
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ws.Cells.AutoFitColumns();
|
2026-05-04 14:46:58 +08:00
|
|
|
|
package.Save();
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
string err = lang == "en-US" ? "Export failed" : "导出失败";
|
|
|
|
|
|
MessageBox.Show($"{err}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
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 FlowPressureRecords 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>
|
|
|
|
|
|
/// 打开报告窗口
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void Button_Click_5(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
new ReportWindow1().ShowDialog();
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 通用操作方法(提取重复逻辑)
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 带输入验证的寄存器写入
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task WriteRegisterWithValidation(
|
2026-05-07 21:17:08 +08:00
|
|
|
|
TextBox inputControl,
|
|
|
|
|
|
ushort registerAddress,
|
|
|
|
|
|
float minValue,
|
|
|
|
|
|
float maxValue)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
if (!IsModbusConnected())
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
|
2026-05-04 14:46:58 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-07 21:17:08 +08:00
|
|
|
|
if (!float.TryParse(inputControl.Text.Trim(), out float val) || val < minValue || val > maxValue)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string msg = lang == "en-US" ? $"Enter {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的数字";
|
|
|
|
|
|
ShowWarning(msg);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
inputControl.Text = lang == "en-US" ? "Processing..." : "操作中...";
|
|
|
|
|
|
var ma = new Function(_modbusMaster);
|
|
|
|
|
|
ma.WriteToPLCForNew(val.ToString(), registerAddress, Function.DataType.浮点型);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
|
|
|
|
|
|
await Task.Delay(300);
|
|
|
|
|
|
ReadAndUpdateRegister(registerAddress, true, v => UpdatePressureUI(v.ToString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
string err = lang == "en-US" ? $"Failed: {ex.Message}" : $"操作失败: {ex.Message}";
|
|
|
|
|
|
ShowError(err);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
2026-05-07 21:17:08 +08:00
|
|
|
|
inputControl.Text = val.ToString();
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 写入线圈并验证状态
|
|
|
|
|
|
/// </summary>
|
2026-05-07 21:17:08 +08:00
|
|
|
|
|
2026-05-04 14:46:58 +08:00
|
|
|
|
private async Task WriteCoilWithCheck(
|
2026-05-07 21:17:08 +08:00
|
|
|
|
ushort coilAddress,
|
|
|
|
|
|
bool value,
|
|
|
|
|
|
string successMsg,
|
|
|
|
|
|
string failMsg,
|
|
|
|
|
|
string logMsg)
|
2026-05-04 14:46:58 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (!IsModbusConnected())
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowError("Modbus TCP 未连接");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 写入线圈
|
|
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
|
_modbusMaster.WriteSingleCoilAsync(0x01, coilAddress, value)
|
|
|
|
|
|
);
|
|
|
|
|
|
Thread.Sleep(200);
|
|
|
|
|
|
if (failMsg != null && (failMsg.Contains("停止") || failMsg.Contains("校准")))
|
|
|
|
|
|
{
|
|
|
|
|
|
// 写入线圈
|
|
|
|
|
|
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 && !failMsg.Contains("停止") && !failMsg.Contains("校准"))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (status[0] == value)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 写入日志
|
|
|
|
|
|
WriteLog($"{logMsg} - 地址:{coilAddress},状态:{value}");
|
|
|
|
|
|
if (!string.IsNullOrEmpty(successMsg))
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
//ShowSuccess(successMsg);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-07 16:51:45 +08:00
|
|
|
|
//ShowError(failMsg);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
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 FlowPressureRecords (
|
|
|
|
|
|
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 FlowPressureRecords (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 string logPath => 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";
|
|
|
|
|
|
File.AppendAllText(logPath, log);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"写入日志失败: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#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 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg");
|
|
|
|
|
|
if (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}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 消息提示封装
|
2026-05-07 16:51:45 +08:00
|
|
|
|
//private void ShowSuccess(string msg) => MessageBox.Show(msg, "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
|
|
|
|
private void ShowWarning(string msg) => MessageBox.Show(msg, "Instruction", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
|
|
|
|
private void ShowError(string msg) => MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
2026-05-04 14:46:58 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void SettingPaTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 正则:只允许数字和一个小数点(如“123”“123.45”)
|
|
|
|
|
|
var isNumber = System.Text.RegularExpressions.Regex.IsMatch(e.Text, @"^[0-9]*(?:\.[0-9]*)?$");
|
|
|
|
|
|
e.Handled = !isNumber; // 非数字则阻止输入
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async void SettingPaTextBox_MouseLeave(object sender, RoutedEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
await WriteRegisterWithValidation(
|
|
|
|
|
|
inputControl: SettingPaTextBox,
|
|
|
|
|
|
registerAddress: _pressureSettingRegisterAddress,
|
|
|
|
|
|
minValue: 0,
|
|
|
|
|
|
maxValue: 10000);
|
|
|
|
|
|
|
|
|
|
|
|
_settingReadTimer.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-07 16:51:45 +08:00
|
|
|
|
|
|
|
|
|
|
public void RefreshLanguage()
|
|
|
|
|
|
{
|
|
|
|
|
|
string currentLanguage = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetLocalizedString(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
var resource = Application.Current.TryFindResource(key);
|
|
|
|
|
|
return resource?.ToString() ?? key;
|
|
|
|
|
|
}
|
2026-05-04 14:46:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|