Files
pressurediff/压差法气体渗透仪/CoefficientForm.cs
2026-02-07 10:07:45 +08:00

445 lines
16 KiB
C#
Raw Permalink 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 Modbus.Device;
using Sunny.UI;
using Sunny.UI.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using .Data;
namespace
{
public partial class CoefficientForm : UIForm
{
#region
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
private System.Windows.Forms.Timer _readTimer;
#endregion
private TestScreen _testScreen;
Function ma;
DataChange c = new DataChange();
public CoefficientForm()
{
InitializeComponent();
InitTimer();
}
private System.Windows.Forms.Timer InitTimer()
{
var timer = new System.Windows.Forms.Timer()
{
Interval = 1000
};
timer.Tick += async (s, e) =>
{
if (_modbusMaster != null)
{
try
{
await ReadAddr262DataAsync();
}
catch { }
}
};
//timer.Start();
return timer;
}
private async System.Threading.Tasks.Task ReadAddr262DataAsync()
{
try
{
// 添加空值检查
if (_modbusMaster == null || _tcpClient == null || !_tcpClient.Connected)
{
return;
}
// 创建任务列表
var tasks = new List<Task>
{
ReadAndUpdateFloatAsync(1218, 2, uiLabel6,"F2",""),
ReadLeakTestParametersAsync()
//ReadStatusAsync()
};
await Task.WhenAll(tasks);
}
catch (Exception ex)
{
ShowError($"读取数据失败:{ex.Message}");
}
}
private async Task ReadAndUpdateFloatAsync(int address, int length, Label control, string format, string unit)
{
// 空值校验避免传入空控件导致后续Invoke报错
if (control == null)
{
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败目标Label控件为空");
return;
}
try
{
ushort[] registers = await Task.Run(async () =>
{
// 双重空值校验避免_modbusMaster为空导致调用异常
if (_modbusMaster == null) return null;
return await _modbusMaster.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length);
});
// ===================== 数据有效性校验 & 解析 =====================
// 场景1读取到2个及以上寄存器 → 解析为浮点型数据占2个16位寄存器
if (registers != null && registers.Length >= 2)
{
// c.UshortToFloat自定义工具方法将2个16位无符号寄存器转换为32位浮点型
// 参数顺序registers[1]高位寄存器、registers[0](低位寄存器)→ 需匹配设备数据存储格式
float value = c.UshortToFloat(registers[1], registers[0]);
// ===================== WinForm UI控件更新 =====================
// Control.Invoke判断是否跨线程若跨线程则切换到UI线程更新控件
// WinForm中UI控件只能由创建它的主线程修改否则会抛出跨线程异常
if (control.InvokeRequired)
{
control.Invoke(new Action(() =>
{
control.Text = $"{value.ToString(format)}{unit}";
}));
}
else
{
// 若当前已是UI线程直接更新
control.Text = $"{value.ToString(format)}{unit}";
}
}
// 场景2读取到1个寄存器 → 解析为整型数据占1个16位寄存器
else if (registers != null && registers.Length >= 1)
{
// 直接取第一个寄存器的值作为整型数据
int value = registers[0];
// 同样通过Control.Invoke保证UI线程安全更新
if (control.InvokeRequired)
{
control.Invoke(new Action(() =>
{
control.Text = $"{value.ToString(format)}{unit}";
}));
}
else
{
control.Text = $"{value.ToString(format)}{unit}";
}
}
// 场景3registers为null或长度为0 → 无有效数据,不执行更新(静默处理)
else if (registers == null || registers.Length == 0)
{
System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:未获取到有效寄存器数据");
}
}
catch (Exception ex)
{
//System.Diagnostics.Debug.WriteLine($"读取地址{address}失败:{ex.Message}\n异常堆栈{ex.StackTrace}");
ShowError($"读取地址{address}失败:{ex.Message}\n异常堆栈{ex.StackTrace}");
}
}
private void ShowError(string msg) => MessageBox.Show(msg, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
private class RegisterConfig
{
public ushort Address { get; set; } // Modbus寄存器地址ushort类型匹配方法参数
public UITextBox TextBox { get; set; } // 适配SunnyUI的UITextBox控件
public string Format { get; set; } // 数值格式化字符串
public bool IsInteger { get; set; } // 是否为整数类型(新增)
}
private async Task ReadLeakTestParametersAsync()
{
// 前置校验:连接状态检查
if (_tcpClient == null || !_tcpClient.Connected || _modbusMaster == null)
{
return;
}
try
{
// 配置所有寄存器映射关系(统一管理,便于维护)
var registerConfigs = new List<RegisterConfig>
{
CreateRegisterConfig(1018, uiTextBox1, "F3",false), // 高压
CreateRegisterConfig(1118, uiTextBox3, "F3",false), // 低压
CreateRegisterConfig(1222, uiTextBox2, "F3",false), // 温度
CreateRegisterConfig(234, uiTextBox7, "F4",false), // 压降值
CreateRegisterConfig(230, uiTextBox12, "0",true), // 1#平稳系数
CreateRegisterConfig(242, uiTextBox9, "F2",false), // CM3 GTR系数
CreateRegisterConfig(246, uiTextBox10, "F2",false), // CM3 GT系数
CreateRegisterConfig(252, uiTextBox11, "F2",false), // mol GTR系数
CreateRegisterConfig(256, uiTextBox5, "F2",false) // mol GT系数
};
// 批量处理所有寄存器读取和UI更新
foreach (var config in registerConfigs)
{
await ReadRegisterAndUpdateUIAsync(config);
}
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
UIMessageBox.ShowError($"读取系数失败:{ex.Message}");
}));
}
}
/// <summary>
/// 辅助方法创建寄存器配置对象适配C# 7.3语法)
/// </summary>
/// <param name="address">寄存器地址</param>
/// <param name="textBox">绑定的SunnyUI文本框</param>
/// <param name="format">数值格式化字符串</param>
private RegisterConfig CreateRegisterConfig(int address, UITextBox textBox, string format, bool isInteger)
{
var config = new RegisterConfig();
config.Address = (ushort)address; // 显式转换为ushort解决类型不匹配
config.TextBox = textBox;
config.Format = format;
config.IsInteger = isInteger; // 赋值整数类型标识
return config;
}
/// <summary>
/// 通用方法读取寄存器并更新SunnyUI文本框核心逻辑复用
/// </summary>
/// <param name="config">寄存器配置</param>
private async Task ReadRegisterAndUpdateUIAsync(RegisterConfig config)
{
// 异步读取Modbus寄存器避免阻塞UI线程
ushort[] registers = await Task.Run(() =>
_modbusMaster?.ReadHoldingRegisters(1, config.Address, 2));
// 数据有效性校验 + 区分整数/浮点处理
if (registers != null && registers.Length >= 2)
{
this.Invoke(new Action(() =>
{
if (config.IsInteger)
{
// 整数处理合并两个ushort为32位整数根据Modbus字节序调整
// 注意字节序需匹配设备端此处为registers[1]高位 + registers[0]低位)
int intValue = (registers[1] << 16) | registers[0];
config.TextBox.Text = intValue.ToString(config.Format);
}
else
{
// 浮点处理:保持原有逻辑
float floatValue = c.UshortToFloat(registers[1], registers[0]);
config.TextBox.Text = floatValue.ToString(config.Format);
}
}));
}
}
private void ShowErrorMsg(string msg)
{
// 判断当前线程是否是UI线程避免跨线程访问控件异常
if (this.InvokeRequired)
{
// 跨线程时通过Invoke切换到UI线程执行
this.Invoke(new Action<string>(ShowErrorMsg), msg);
return;
}
// UI线程直接显示错误提示框
MessageBox.Show(msg, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
//返回
private void uiButton1_Click(object sender, EventArgs e)
{
SwitchWindow(ref _testScreen, () => new TestScreen());
}
private void CoefficientForm_Load(object sender, EventArgs e)
{
string plcIp = "192.168.1.10";
bool initSuccess = Data.ModbusResourceManager.Instance.Init(plcIp, 502);
if (!initSuccess)
{
MessageBox.Show("连接Modbus服务器失败", "错误");
this.Close();
return;
}
// 检查连接状态
if (_tcpClient == null || !_tcpClient.Connected)
{
MessageBox.Show("Modbus连接异常", "错误");
this.Close();
return;
}
ma = new Function(_modbusMaster);
_readTimer = InitTimer();
// 等_modbusMaster初始化完成后手动启动
if (_modbusMaster != null)
{
_readTimer.Start();
}
}
private void SwitchWindow<T>(ref T windowInstance, Func<T> createFunc) where T : UIForm
{
//1.停止当前窗口的定时器(不释放资源)
_readTimer?.Stop();
// 2. 检查资源是否可用(添加重连机制)
if (_tcpClient == null || !_tcpClient.Connected || _modbusMaster == null)
{
//尝试重新连接
bool reconnectSuccess = TryReconnect();
if (!reconnectSuccess)
{
MessageBox.Show("TCP连接已断开请重新连接", "提示");
return;
}
}
// 3. 复用窗口实例:不存在则创建,存在则激活
if (windowInstance == null)
{
windowInstance = createFunc();
// 添加窗口关闭事件处理
windowInstance.Closed += (s, args) =>
{
// 窗口关闭时重新启动定时器并显示当前窗口
//_readtimer?.Start();
//this.Show();
this.Activate();
};
}
else
{
// 激活已存在的窗口(前置显示)
windowInstance.Activate();
return;
}
// 4. 切换窗口:隐藏当前窗口,显示目标窗口(非模态)
this.Hide();
windowInstance.Show(); // 使用 Show() 而不是 ShowDialog()
}
private bool TryReconnect()
{
try
{
string plcIp = "192.168.1.10";
bool initSuccess = Data.ModbusResourceManager.Instance.Init(plcIp, 502);
if (initSuccess)
{
ma = new Function(_modbusMaster);
return true;
}
}
catch (Exception ex)
{
ShowError($"重新连接失败:{ex.Message}");
}
return false;
}
private void uiTextBox1_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox1.Text.Trim(), 1018, Function.DataType., 3);//高压
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox3_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox3.Text.Trim(), 1118, Function.DataType., 3);//低压
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox2_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox2.Text.Trim(), 1222, Function.DataType., 3);//温度
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox7_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox7.Text.Trim(), 234, Function.DataType., 4);//自身压降值
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox12_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox12.Text.Trim(), 230, Function.DataType.);//1#平稳系数
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox9_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox9.Text.Trim(), 242, Function.DataType.);//CM3 GTR系数
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox10_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox10.Text.Trim(), 246, Function.DataType.);//CM3 GT系数
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox11_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox11.Text.Trim(), 252, Function.DataType.);//mol GTR系数
System.Threading.Tasks.Task.Delay(50);
}
private void uiTextBox5_Click(object sender, EventArgs e)
{
ma?.WriteToPLCForNew(uiTextBox5.Text.Trim(), 256, Function.DataType.);//mol GT系数
System.Threading.Tasks.Task.Delay(50);
}
private void CoefficientForm_FormClosing(object sender, FormClosingEventArgs e)
{
_readTimer?.Stop();
_readTimer?.Dispose();
// 仅用户主动关闭时退出应用
if (e.CloseReason == CloseReason.UserClosing)
{
ModbusResourceManager.Instance?.Dispose();
Application.Exit();
}
}
}
}