Files
Sleep-Multi-functionality/Window1.xaml.cs
2026-05-07 21:17:08 +08:00

898 lines
32 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;
using Modbus.Device;
using OfficeOpenXml;
using OxyPlot;
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];
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)
{
TestStartButton.Content = testStartButtonText;
TestStartButton.Foreground = Brushes.LightGreen;
}
else
{
TestStartButton.Content = ButtonStatus;
TestStartButton.Foreground = Brushes.White;
}
});
}
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]是D312data[1]是D313
ushort a = data[0]; // 高位寄存器值
ushort b = data[1]; // 低位寄存器值
float floatValue = c.UshortToFloat(b, a);
floatValue = (float)Math.Round(floatValue, 2);
updateAction?.Invoke(floatValue);
}
else
{
updateAction?.Invoke(float.NaN);
}
}
else
{
// 16位整数直接返回
updateAction?.Invoke(data.Length > 0 ? data[0] : (ushort)0);
}
}
catch (Exception ex)
{
Console.WriteLine($"读取寄存器[{address:X4}]失败: {ex.Message}");
// 异常时根据类型返回对应的值
updateAction?.Invoke(isFloat ? (object)float.NaN : (ushort)0);
}
}
#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 UpdatePressureUI(string value)
{
UpdateConnectionTextUI(RealTimePressureTextBox, value);
}
/// <summary>
/// 更新实时压力UI
/// </summary>
private void UpdateSettingUI(string value)
{
UpdateConnectionTextUI(SettingPaTextBox, value);
}
/// <summary>
/// 更新实时流量UI
/// </summary>
private void UpdateFlowPressureUI(string value)
{
UpdateConnectionTextUI(txtFolw, value);
}
//private void UpdateStartPressureUI(string value)
//{
// UpdateUiSafely(() =>
// TestStartButton.Content = IsModbusConnected() ? value : "连接断开"
// );
//}
/// <summary>
/// 更新实时流量UI
/// </summary>
private void UpdateFlowFlowUI(string value)
{
UpdateConnectionTextUI(saveFlowTxt, 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 (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)
{
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);
}
/// <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)
{
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
UpdateButtonStatus(lang == "en-US" ? "Disconnected" : "连接断开", Brushes.Red);
ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
try
{
UpdateButtonStatus(lang == "en-US" ? "Starting..." : "正在启动...", Brushes.LightGreen);
await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStartAddress, true));
await Task.Delay(100);
await Task.Run(() => _modbusMaster.WriteSingleCoilAsync(0x01, _testStatusAddress, false));
await Task.Delay(100);
UpdateButtonStatus(lang == "en-US" ? "Test Started" : "测试启动成功", Brushes.LightGreen);
}
catch (Exception ex)
{
UpdateButtonStatus(lang == "en-US" ? "Start Failed" : "测试启动失败", Brushes.Red);
ShowError(lang == "en-US" ? $"Error: {ex.Message}" : $"操作异常: {ex.Message}");
}
}
/// <summary>
/// 测试停止M62线圈
/// </summary>
private async void Button_Click_3(object sender, RoutedEventArgs e)
{
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
string fail = lang == "en-US" ? "Airflow test stop timeout" : "气流测试停止超时,状态异常";
string log = lang == "en-US" ? "Airflow test stopped" : "气流测试停止";
await WriteCoilWithCheck(_testStopAddress, true, null, fail, log);
TestStartButton.Content = lang == "en-US" ? "Test Start" : "测试启动";
TestStartButton.Foreground = Brushes.White;
}
/// <summary>
/// 流量数据读取并保存saveFlowTxt- 适配浮点型流量
/// </summary>
private void Button_Click_4(object sender, RoutedEventArgs e)
{
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
saveFlowTxt.Text = lang == "en-US" ? "Disconnected" : "连接断开";
ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
fc.BtnClickFunctionForNew(Function.ButtonType., 190);
fc.BtnClickFunctionForNew(Function.ButtonType., 190);
ReadAndUpdateRegister(_flow2Address, true, value => UpdateFlowFlowUI(value.ToString()));
if (float.TryParse(txtFolw.Text, out float flowValue) &&
float.TryParse(RealTimePressureTextBox.Text, out float pressureValue))
{
SaveRecordToDatabase(flowValue, pressureValue);
saveFlowTxt.Text = txtFolw.Text;
}
else
{
ShowError(lang == "en-US" ? "Invalid flow or pressure" : "流量或压力值格式不正确,请检查输入");
}
List<CO2Record> records = ReadCO2RecordsFromDatabase();
if (records == null || records.Count == 0)
{
string msg = lang == "en-US" ? "No data to export" : "排气流表中无数据,无法导出";
string title = lang == "en-US" ? "Info" : "提示";
MessageBox.Show(msg, title, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
bool exportOk = ExportCO2RecordsToExcel(records);
if (exportOk)
{
string msg = lang == "en-US" ? "Export successful" : "数据已成功导出到Excel";
string title = lang == "en-US" ? "Success" : "成功";
MessageBox.Show(msg, title, MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private bool ExportCO2RecordsToExcel(List<CO2Record> records)
{
try
{
SaveFileDialog saveDialog = new SaveFileDialog
{
Filter = "Excel(*.xlsx)|*.xlsx",
FileName = $"排气流记录_{DateTime.Now:yyyyMMddHHmmss}.xlsx",
Title = "Export Airflow Data"
};
if (saveDialog.ShowDialog() != true) return false;
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage(new FileInfo(saveDialog.FileName)))
{
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";
for (int i = 0; i < records.Count; i++)
{
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");
}
ws.Cells.AutoFitColumns();
package.Save();
}
return true;
}
catch (Exception ex)
{
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
string err = lang == "en-US" ? "Export failed" : "导出失败";
MessageBox.Show($"{err}: {ex.Message}", "Error", 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 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(
TextBox inputControl,
ushort registerAddress,
float minValue,
float maxValue)
{
string lang = ConfigurationManager.AppSettings["Language"] ?? "zh-CN";
if (!IsModbusConnected())
{
ShowError(lang == "en-US" ? "Modbus TCP not connected" : "Modbus TCP 未连接");
return;
}
if (!float.TryParse(inputControl.Text.Trim(), out float val) || val < minValue || val > maxValue)
{
string msg = lang == "en-US" ? $"Enter {minValue}~{maxValue}" : $"请输入{minValue}~{maxValue}的数字";
ShowWarning(msg);
return;
}
try
{
inputControl.Text = lang == "en-US" ? "Processing..." : "操作中...";
var ma = new Function(_modbusMaster);
ma.WriteToPLCForNew(val.ToString(), registerAddress, Function.DataType.);
await Task.Delay(300);
ReadAndUpdateRegister(registerAddress, true, v => UpdatePressureUI(v.ToString()));
}
catch (Exception ex)
{
string err = lang == "en-US" ? $"Failed: {ex.Message}" : $"操作失败: {ex.Message}";
ShowError(err);
}
finally
{
inputControl.Text = val.ToString();
}
}
/// <summary>
/// 写入线圈并验证状态
/// </summary>
private async Task WriteCoilWithCheck(
ushort coilAddress,
bool value,
string successMsg,
string failMsg,
string logMsg)
{
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))
{
//ShowSuccess(successMsg);
}
}
else
{
//ShowError(failMsg);
}
}
}
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}");
}
}
// 消息提示封装
//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);
#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
{
}
}
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;
}
}
}