1040 lines
35 KiB
C#
1040 lines
35 KiB
C#
using Modbus.Device;
|
||
using Modbus;
|
||
using OxyPlot.Series;
|
||
using OxyPlot;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Configuration;
|
||
using System.Linq;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Timers;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Data;
|
||
using System.Windows.Documents;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Media.Imaging;
|
||
using System.Windows.Shapes;
|
||
using System.Windows.Threading;
|
||
using OxyPlot.Axes;
|
||
using OxyPlot.Legends;
|
||
using 口罩泄露定制款;
|
||
|
||
namespace ShanghaiEnvironmentalTechnology
|
||
{
|
||
/// <summary>
|
||
/// Window6.xaml 的交互逻辑
|
||
/// </summary>
|
||
public partial class Window6 : Window
|
||
{
|
||
DataChange c = new DataChange();
|
||
#region 寄存器/线圈地址定义(按功能分组,标注物理意义)
|
||
// 控制线圈(M代码)
|
||
private readonly ushort _OutBreathUpAddress = 0x0011; // 呼上(M17)
|
||
private readonly ushort _OutBreathDownAddress = 0x0010; // 呼下(M16)
|
||
|
||
private readonly ushort _InBreathUpAddress = 0x001E; // 吸上(M30)
|
||
private readonly ushort _InBreathDownAddress = 0x001F; // 吸下(M31)
|
||
|
||
private readonly ushort _HandInBreathUpAddress = 0x0013; // 手动吸(M19)
|
||
private readonly ushort _HandInBreathDownAddress = 0x0012; // 手动呼(M18)
|
||
|
||
|
||
private readonly ushort _BreathUpAddress = 0x002A; // 呼气校准(M42)
|
||
private readonly ushort _BreathDownAddress = 0x002B; // 吸气校准(M43)
|
||
private readonly ushort _PressCheckAddress = 41; // 压力校准(M41)
|
||
Function fc;
|
||
#endregion
|
||
|
||
#region 寄存器/线圈地址定义
|
||
// 控制线圈(D代码)
|
||
private readonly ushort _handSpeedAddress = 218; // 手动速度(D218)
|
||
|
||
//潮气量系数
|
||
private readonly ushort _moistureAddress = 3700;
|
||
|
||
//设置呼气流量系数 D1218呼流量系数
|
||
private readonly ushort _OutFowRateAddress = 1218; // 手动速度(D1218呼流量系数)
|
||
|
||
//设置吸气流量系数 D1318呼流量系数
|
||
private readonly ushort _InFowRateAddress = 1318; // 手动速度(D1318呼流量系数)
|
||
|
||
//设置压力系数 D1118
|
||
private readonly ushort _PressRateAddress = 1118; // 手动速度(D1118)
|
||
|
||
|
||
//位置
|
||
private readonly ushort _PositionAddress = 0x002A; // D42
|
||
|
||
//压力
|
||
private readonly ushort _PressAddress = 0x0BD6; // 手动速度(D3030)
|
||
|
||
//呼气流量
|
||
private readonly ushort _OutBreathAddress = 0x1398; // D5016
|
||
|
||
//吸气流量
|
||
private readonly ushort _InBreathAddress = 0x1396; // D5014
|
||
|
||
//复位
|
||
private readonly ushort _resetAddress = 0x0002; // D5014
|
||
|
||
#endregion
|
||
|
||
// 定时器(按功能分组)
|
||
private System.Timers.Timer _ReadTimer; // 实时监控数据
|
||
|
||
// Modbus通信
|
||
private TcpClient _tcpClient;
|
||
private IModbusMaster _modbusMaster;
|
||
private System.Timers.Timer resetTimer; // 启动状态实时定时器
|
||
|
||
#region 统计相关
|
||
|
||
private PlotModel _plotModel;
|
||
private LineSeries _exhalationSeries;
|
||
private LineSeries _inhalationSeries;
|
||
private List<DataPoint> _exhalationData;
|
||
private List<DataPoint> _inhalationData;
|
||
private int _timeCounter;
|
||
|
||
|
||
#region 私有字段(编辑状态+定时器+寄存器地址)
|
||
// 编辑状态标志位(控制定时器是否更新)
|
||
private bool _isEditingHandmovement; // 手动速度
|
||
private bool _isEditingMoisture; // 潮气量系数
|
||
private bool _isEditingOutFlow; // 呼气流量系数
|
||
private bool _isEditingInFlow; // 吸气流量系数
|
||
private bool _isEditingPress; // 压力系数
|
||
|
||
// 实时读取定时器(1个定时器管理5个参数)
|
||
private System.Timers.Timer _paramReadTimer;
|
||
|
||
// 下位机寄存器地址(根据实际PLC地址修改)
|
||
//private readonly ushort _handmovementAddr = 0x0300; // 手动速度
|
||
//private readonly ushort _moistureAddr = 0x0301; // 潮气量系数
|
||
//private readonly ushort _outFlowAddr = 0x0302; // 呼气流量系数
|
||
//private readonly ushort _inFlowAddr = 0x0303; // 吸气流量系数
|
||
//private readonly ushort _pressAddr = 0x0304; // 压力系数
|
||
#endregion
|
||
|
||
|
||
private void OnResetTimerElapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
bool[] result = _modbusMaster?.ReadCoils(0x01, 3, 1);
|
||
bool isTestRunning = result != null && result.Length > 0 && result[0];
|
||
|
||
ResetBtn.Dispatcher.Invoke(() =>
|
||
{
|
||
if (isTestRunning)
|
||
{
|
||
ResetBtn.Content = "复位成功";
|
||
ResetBtn.Foreground = Brushes.LightGreen;
|
||
}
|
||
else
|
||
{
|
||
ResetBtn.Content = "复位";
|
||
ResetBtn.Foreground = Brushes.White;
|
||
}
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"读取线圈或更新UI失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
#endregion
|
||
public Window6()
|
||
{
|
||
InitializeComponent();
|
||
InitializeModbusTcp();
|
||
InitializePlot();
|
||
StartDataUpdate();
|
||
|
||
|
||
_paramReadTimer = new System.Timers.Timer(1000) { AutoReset = true, Enabled = true };
|
||
_paramReadTimer.Elapsed += OnParamTimerElapsed; // 绑定定时读取事件
|
||
resetTimer = CreateTimer(1000, OnResetTimerElapsed);
|
||
Loaded += (s, e) => _paramReadTimer.Start();
|
||
|
||
//Hertbean();
|
||
}
|
||
|
||
#region 定时器实时读取下位机数据(核心:根据编辑状态判断是否更新UI)
|
||
private void OnParamTimerElapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
// 1. 手动速度(焦点不在时更新)
|
||
if (!_isEditingHandmovement)
|
||
{
|
||
ReadAndUpdateParam(
|
||
address: _handSpeedAddress,
|
||
isFloat: true,
|
||
updateAction: (value) => UpdateTextBox(handmovementTxt, value.ToString())
|
||
);
|
||
}
|
||
|
||
// 2. 潮气量系数
|
||
if (!_isEditingMoisture)
|
||
{
|
||
ReadAndUpdateParam(
|
||
address: _moistureAddress,
|
||
isFloat: true,
|
||
updateAction: (value) => UpdateTextBox(moistureTxt, value.ToString())
|
||
);
|
||
}
|
||
|
||
// 3. 呼气流量系数
|
||
if (!_isEditingOutFlow)
|
||
{
|
||
ReadAndUpdateParam(
|
||
address: _OutFowRateAddress,
|
||
isFloat: true,
|
||
updateAction: (value) => UpdateTextBox(OutFowRateTxt, value.ToString())
|
||
);
|
||
}
|
||
|
||
// 4. 吸气流量系数
|
||
if (!_isEditingInFlow)
|
||
{
|
||
ReadAndUpdateParam(
|
||
address: _InFowRateAddress,
|
||
isFloat: true,
|
||
updateAction: (value) => UpdateTextBox(InFowRateTxt, value.ToString())
|
||
);
|
||
}
|
||
|
||
// 5. 压力系数
|
||
if (!_isEditingPress)
|
||
{
|
||
ReadAndUpdateParam(
|
||
address: _PressRateAddress,
|
||
isFloat: true,
|
||
updateAction: (value) => UpdateTextBox(PressRateTxt, value.ToString())
|
||
);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
// 读取下位机参数通用方法
|
||
private void ReadAndUpdateParam(ushort address, bool isFloat, Action<object> updateAction)
|
||
{
|
||
if (!IsModbusConnected())
|
||
{
|
||
updateAction("离线");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
ushort[] data = _modbusMaster?.ReadHoldingRegisters(0x01, address, isFloat ? (ushort)2 : (ushort)1);
|
||
object value = isFloat
|
||
? Convert.ToSingle(c.UshortToFloat(data[1], data[0])) // 浮点数(需转换)
|
||
: data[0]; // 整数
|
||
updateAction(value);
|
||
}
|
||
catch
|
||
{
|
||
updateAction("读取失败");
|
||
}
|
||
}
|
||
|
||
private void UpdateTextBox(TextBox textBox, string value)
|
||
{
|
||
Dispatcher.Invoke(() =>
|
||
{
|
||
if (textBox.IsVisible) // 控件可见时才更新
|
||
{
|
||
textBox.Text = value;
|
||
}
|
||
});
|
||
}
|
||
|
||
private void InitializePlot()
|
||
{
|
||
_plotModel = new PlotModel
|
||
{
|
||
Title = "呼气流量与吸气流量",
|
||
Background = OxyColor.FromRgb(240, 240, 240) // 设置背景色
|
||
};
|
||
|
||
_exhalationSeries = new LineSeries
|
||
{
|
||
Title = "呼气流量 (D5016)",
|
||
Color = OxyColor.FromRgb(255, 0, 0),
|
||
StrokeThickness = 2, // 设置线条宽度
|
||
MarkerType = MarkerType.Circle, // 添加标记
|
||
MarkerSize = 4
|
||
};
|
||
|
||
_inhalationSeries = new LineSeries
|
||
{
|
||
Title = "吸气流量 (D5014)",
|
||
Color = OxyColor.FromRgb(0, 0, 255),
|
||
StrokeThickness = 2,
|
||
MarkerType = MarkerType.Circle,
|
||
MarkerSize = 4
|
||
};
|
||
|
||
_exhalationData = new List<DataPoint>();
|
||
_inhalationData = new List<DataPoint>();
|
||
|
||
_plotModel.Series.Add(_exhalationSeries);
|
||
_plotModel.Series.Add(_inhalationSeries);
|
||
|
||
// 添加轴标签
|
||
_plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "时间 (秒)" });
|
||
_plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "流量 (单位)" });
|
||
|
||
|
||
// 在InitializePlot方法的轴配置中添加
|
||
_plotModel.Axes.Add(new LinearAxis
|
||
{
|
||
Position = AxisPosition.Bottom,
|
||
Title = "时间 (秒)",
|
||
IsZoomEnabled = false, // 禁用手动缩放
|
||
IsPanEnabled = false, // 禁用手动平移
|
||
MaximumPadding = 0.1, // 轴最大值留10%余量
|
||
MinimumPadding = 0.1 // 轴最小值留10%余量
|
||
});
|
||
|
||
// 添加图例
|
||
_plotModel.IsLegendVisible = true; // 设置图例可见
|
||
|
||
plotView.Model = _plotModel;
|
||
}
|
||
|
||
|
||
|
||
private void StartDataUpdate()
|
||
{
|
||
DispatcherTimer timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||
timer.Tick += async (s, e) => await UpdateData();
|
||
timer.Start();
|
||
}
|
||
|
||
private async Task UpdateData()
|
||
{
|
||
if (!IsModbusConnected()) return;
|
||
|
||
try
|
||
{
|
||
// 读取寄存器值(保持不变)
|
||
ushort[] exhalationData = await Task.Run(() => _modbusMaster?.ReadHoldingRegisters(0x01, _OutBreathAddress, 2));
|
||
ushort[] inhalationData = await Task.Run(() => _modbusMaster?.ReadHoldingRegisters(0x01, _InBreathAddress, 2));
|
||
|
||
// 解析呼气流量(保持不变)
|
||
ushort a = exhalationData[0];
|
||
ushort b = exhalationData[1];
|
||
float floatValue = c.UshortToFloat(b, a);
|
||
floatValue = (float)Math.Round(floatValue, 2);
|
||
|
||
// 解析吸气流量(保持不变)
|
||
ushort a1 = inhalationData[0];
|
||
ushort b1 = inhalationData[1];
|
||
float floatValue1 = c.UshortToFloat(b1, a1);
|
||
floatValue1 = (float)Math.Round(floatValue1, 2);
|
||
|
||
double exhalationValue = floatValue;
|
||
double inhalationValue = floatValue1;
|
||
|
||
// 关键修改:添加新数据后,只保留最新的10个数据点
|
||
_exhalationData.Add(new DataPoint(_timeCounter, exhalationValue));
|
||
_inhalationData.Add(new DataPoint(_timeCounter, inhalationValue));
|
||
_timeCounter++; // 时间计数器统一递增(避免两条线时间轴不一致)
|
||
|
||
// 限制数据列表长度为10(只保留最新数据)
|
||
if (_exhalationData.Count > 10)
|
||
{
|
||
_exhalationData.RemoveRange(0, _exhalationData.Count - 10); // 移除旧数据
|
||
}
|
||
if (_inhalationData.Count > 10)
|
||
{
|
||
_inhalationData.RemoveRange(0, _inhalationData.Count - 10);
|
||
}
|
||
|
||
// 更新图表数据(保持不变)
|
||
_exhalationSeries.Points.Clear();
|
||
_inhalationSeries.Points.Clear();
|
||
|
||
foreach (var point in _exhalationData)
|
||
{
|
||
_exhalationSeries.Points.Add(point);
|
||
}
|
||
|
||
foreach (var point in _inhalationData)
|
||
{
|
||
_inhalationSeries.Points.Add(point);
|
||
}
|
||
|
||
_plotModel.InvalidatePlot(true);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"读取数据失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 初始化Modbus连接和定时器
|
||
/// </summary>
|
||
private void InitializeModbusTcp()
|
||
{
|
||
try
|
||
{
|
||
// 从配置读取PLC连接信息
|
||
string plcIp = ConfigurationManager.AppSettings["PLC2_IP"];
|
||
int plcPort = int.Parse(ConfigurationManager.AppSettings["PLC2_Port"]);
|
||
|
||
_tcpClient = new TcpClient(plcIp, plcPort);
|
||
_modbusMaster = ModbusIpMaster.CreateIp(_tcpClient);
|
||
_modbusMaster.Transport.ReadTimeout = 3000;
|
||
_modbusMaster.Transport.WriteTimeout = 3000;
|
||
fc = new Function(_modbusMaster);
|
||
// 初始化定时器
|
||
InitializeTimers();
|
||
// 初始化数据库
|
||
//InitializeDatabase();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"Modbus初始化失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一初始化所有定时器(避免重复配置)
|
||
/// </summary>
|
||
private void InitializeTimers()
|
||
{
|
||
_ReadTimer = CreateTimer(1000, OnRealTimerElapsed);
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
private void OnRealTimerElapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
ReadAndUpdateDualRegister(
|
||
_PositionAddress,
|
||
_PressAddress, _OutBreathAddress, _InBreathAddress,
|
||
(a, b, c, d) => UpdateTimerUI(a.ToString(), b.ToString(), c.ToString(), d.ToString())
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新呼吸计时UI
|
||
/// </summary>
|
||
private void UpdateTimerUI(string a, string b, string c, string d)
|
||
{
|
||
UpdateUiSafely(() =>
|
||
{
|
||
positionTxt.Text = IsModbusConnected() ? a : "连接断开";
|
||
pressTxt.Text = IsModbusConnected() ? b : "连接断开";
|
||
OutFowTxt.Text = IsModbusConnected() ? c : "连接断开";
|
||
InFlowTxt.Text = IsModbusConnected() ? d : "连接断开";
|
||
});
|
||
}
|
||
|
||
/// <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}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取两个寄存器并更新UI(统一处理连接状态)
|
||
/// </summary>
|
||
private void ReadAndUpdateDualRegister(ushort address1, ushort address2, ushort address3, ushort address4, Action<float, float, float, float> updateAction)
|
||
{
|
||
if (!IsModbusConnected())
|
||
{
|
||
updateAction?.Invoke(0, 0, 0, 0); // 触发UI显示"连接断开"
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
ushort[] data1 = _modbusMaster?.ReadHoldingRegisters(0x01, address1, 2);
|
||
ushort[] data2 = _modbusMaster?.ReadHoldingRegisters(0x01, address2, 2);
|
||
ushort[] data3 = _modbusMaster?.ReadHoldingRegisters(0x01, address3, 2);
|
||
ushort[] data4 = _modbusMaster?.ReadHoldingRegisters(0x01, address4, 2);
|
||
|
||
|
||
|
||
// 2. 解析寄存器值(data[0]是D312,data[1]是D313)
|
||
ushort a1 = data1[0]; // 高位寄存器值
|
||
ushort b1 = data1[1]; // 低位寄存器值
|
||
|
||
float floatValue1 = c.UshortToFloat(b1, a1);
|
||
floatValue1 = (float)Math.Round(floatValue1, 2);
|
||
|
||
|
||
|
||
// 2. 解析寄存器值(data[0]是D312,data[1]是D313)
|
||
ushort a2 = data2[0]; // 高位寄存器值
|
||
ushort b2 = data2[1]; // 低位寄存器值
|
||
|
||
float floatValue2 = c.UshortToFloat(b2, a2);
|
||
floatValue2 = (float)Math.Round(floatValue2, 2);
|
||
|
||
|
||
|
||
|
||
// 2. 解析寄存器值(data[0]是D312,data[1]是D313)
|
||
ushort a3 = data3[0]; // 高位寄存器值
|
||
ushort b3 = data3[1]; // 低位寄存器值
|
||
|
||
float floatValue3 = c.UshortToFloat(b3, a3);
|
||
floatValue3 = (float)Math.Round(floatValue3, 2);
|
||
|
||
|
||
|
||
// 2. 解析寄存器值(data[0]是D312,data[1]是D313)
|
||
ushort a4 = data4[0]; // 高位寄存器值
|
||
ushort b4 = data4[1]; // 低位寄存器值
|
||
|
||
float floatValue4 = c.UshortToFloat(b4, a4);
|
||
floatValue4 = (float)Math.Round(floatValue4, 2);
|
||
|
||
|
||
updateAction?.Invoke(floatValue1, floatValue2, floatValue3, floatValue4);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"读取寄存器[{address1:X4},{address2:X4}]失败: {ex.Message}");
|
||
updateAction?.Invoke(0, 0, 0, 0); // 触发UI显示"读取失败"
|
||
}
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
|
||
private void Button_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
|
||
}
|
||
|
||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
string imagePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources/sleep2.jpg");
|
||
if (System.IO.File.Exists(imagePath))
|
||
{
|
||
Background = new ImageBrush
|
||
{
|
||
ImageSource = new BitmapImage(new Uri(imagePath, UriKind.Absolute))
|
||
};
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"背景图片不存在: {imagePath}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"加载背景失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void Button_Click_1(object sender, RoutedEventArgs e)
|
||
{
|
||
new Window5().ShowDialog();
|
||
Close();
|
||
}
|
||
|
||
private async Task WriteRegisterWithValidation(
|
||
TextBox inputControl,
|
||
ushort registerAddress,
|
||
float minValue,
|
||
float maxValue)
|
||
{
|
||
if (!IsModbusConnected())
|
||
{
|
||
ShowError("Modbus TCP 未连接");
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
try
|
||
{
|
||
inputControl.Text = "操作中...";
|
||
|
||
Function ma = new Function(_modbusMaster);
|
||
ma.WriteToPLCForNew("", registerAddress, Function.DataType.浮点型);
|
||
|
||
//// 等待并刷新
|
||
//await Task.Delay(300);
|
||
//ReadAndUpdateRegister(registerAddress, true, v => UpdatePressureUI(v.ToString()));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"操作失败: {ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 写入单个寄存器
|
||
/// </summary>
|
||
private async Task WriteSingleRegisterWithOutUI(
|
||
ushort registerAddress,
|
||
Func<int, string> successMessage)
|
||
{
|
||
if (!IsModbusConnected())
|
||
{
|
||
ShowError("Modbus TCP 未连接");
|
||
return;
|
||
}
|
||
try
|
||
{
|
||
await _modbusMaster.WriteSingleCoilAsync(0x01, registerAddress, true);
|
||
await Task.Delay(100); // 等待PLC处理
|
||
|
||
await _modbusMaster.WriteSingleCoilAsync(0x01, registerAddress, false);
|
||
await Task.Delay(100); // 等待PLC处理
|
||
|
||
//var data = _modbusMaster?.ReadHoldingRegisters(0x01, registerAddress, 1);
|
||
//if (data != null && data.Length > 0 && data[0] == 1)
|
||
//{
|
||
// ShowSuccess("写入成功,读取验证值为 1");
|
||
//}
|
||
//else
|
||
//{
|
||
// ShowSuccess("写入成功,但读取验证值异常,建议检查");
|
||
//}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
ShowError($"设置失败: {ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 检查Modbus连接状态(统一判断逻辑)
|
||
/// </summary>
|
||
private bool IsModbusConnected()
|
||
{
|
||
return _modbusMaster != null && _tcpClient?.Connected == true;
|
||
}
|
||
|
||
// 消息提示封装(统一风格)
|
||
private void ShowSuccess(string message) => MessageBox.Show(message, "成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
private void ShowWarning(string message) => MessageBox.Show(message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||
private void ShowError(string message) => MessageBox.Show(message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
private void Button_Click_2(object sender, RoutedEventArgs e)
|
||
{
|
||
var mainWindow = MainWindow.Instance;
|
||
|
||
// 检查窗口状态,只在窗口未显示时调用ShowDialog
|
||
if (!mainWindow.IsVisible)
|
||
{
|
||
mainWindow.ShowDialog();
|
||
}
|
||
else
|
||
{
|
||
// 如果窗口已显示,可将其激活到前台
|
||
mainWindow.Activate();
|
||
}
|
||
|
||
Close();
|
||
}
|
||
|
||
private void Button_Click_3(object sender, RoutedEventArgs e)
|
||
{
|
||
new ReportWindow5().ShowDialog();
|
||
Close();
|
||
}
|
||
|
||
private async void Button_Click_4(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
|
||
registerAddress: _OutBreathUpAddress,
|
||
successMessage: value => $"呼上: {value}");
|
||
}
|
||
|
||
private async void Button_Click_5(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
registerAddress: _OutBreathDownAddress,
|
||
successMessage: value => $"呼下: {value}");
|
||
}
|
||
|
||
private async void Button_Click_6(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
registerAddress: _InBreathUpAddress,
|
||
successMessage: value => $"吸上: {value}");
|
||
}
|
||
|
||
private async void Button_Click_7(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
registerAddress: _InBreathDownAddress,
|
||
successMessage: value => $"吸下: {value}");
|
||
}
|
||
|
||
private async void Button_Click_8(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
registerAddress: _HandInBreathUpAddress,
|
||
successMessage: value => $"手动吸: {value}");
|
||
}
|
||
|
||
private async void Button_Click_9(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteSingleRegisterWithOutUI(
|
||
registerAddress: _HandInBreathDownAddress,
|
||
successMessage: value => $"手动呼: {value}");
|
||
}
|
||
|
||
private async void Button_Click_10(object sender, RoutedEventArgs e)
|
||
{
|
||
if (!IsModbusConnected())
|
||
{
|
||
UpdateResetButtonStatus("连接断开", Brushes.Red);
|
||
ShowError("Modbus TCP 未连接");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
UpdateResetButtonStatus("正在复位...", Brushes.LightGreen);
|
||
|
||
// 写入复位线圈
|
||
await Task.Run(() =>
|
||
_modbusMaster.WriteSingleCoil(0x01, _resetAddress, true)
|
||
);
|
||
|
||
fc.BtnClickFunctionForNew(Function.ButtonType.复位型, _resetAddress);
|
||
await Task.Delay(100);
|
||
// 写入复位线圈
|
||
await Task.Run(() =>
|
||
_modbusMaster.WriteSingleCoil(0x01, _resetAddress, false)
|
||
);
|
||
|
||
|
||
fc.BtnClickFunctionForNew(Function.ButtonType.复位型, _resetAddress);
|
||
|
||
|
||
UpdateResetButtonStatus("复位成功", Brushes.LightGreen);
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"复位异常: {ex.Message}");
|
||
UpdateResetButtonStatus("复位失败", Brushes.Red);
|
||
ShowError($"操作异常: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新复位按钮状态UI
|
||
/// </summary>
|
||
private void UpdateResetButtonStatus(string text, Brush color)
|
||
{
|
||
UpdateUiSafely(() =>
|
||
{
|
||
ResetBtn.Content = text;
|
||
ResetBtn.Foreground = color;
|
||
});
|
||
}
|
||
|
||
private async void Button_Click_11(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteCoilWithCheck(
|
||
coilAddress: _BreathUpAddress,
|
||
value: true,
|
||
successMsg: "校准指令已被设备接收并执行(二次验证通过)",
|
||
failMsg: "校准执行超时,状态异常",
|
||
logMsg: "校准指令执行成功");
|
||
}
|
||
|
||
private async void Button_Click_12(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteCoilWithCheck(
|
||
coilAddress: _BreathDownAddress,
|
||
value: true,
|
||
successMsg: "校准指令已被设备接收并执行(二次验证通过)",
|
||
failMsg: "校准执行超时,状态异常",
|
||
logMsg: "校准指令执行成功"
|
||
);
|
||
}
|
||
|
||
private async void Button_Click_13(object sender, RoutedEventArgs e)
|
||
{
|
||
await WriteCoilWithCheck(
|
||
coilAddress: _PressCheckAddress,
|
||
value: true,
|
||
successMsg: "校准指令已被设备接收并执行(二次验证通过)",
|
||
failMsg: "校准执行超时,状态异常",
|
||
logMsg: "校准指令执行成功"
|
||
);
|
||
}
|
||
|
||
/// <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}");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 日志路径
|
||
/// </summary>
|
||
private string logPath => System.IO.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";
|
||
System.IO.File.AppendAllText(logPath, log);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"写入日志失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
|
||
//public void Hertbean()
|
||
//{
|
||
// while (true)
|
||
// {
|
||
// try
|
||
// {
|
||
// if (!IsModbusConnected())
|
||
// { // 断线检测
|
||
// string plcIp = ConfigurationManager.AppSettings["PLC2_IP"];
|
||
// int plcPort = int.Parse(ConfigurationManager.AppSettings["PLC2_Port"]);
|
||
|
||
// _tcpClient = new TcpClient(plcIp, plcPort);
|
||
// _modbusMaster = ModbusIpMaster.CreateIp(_tcpClient);
|
||
// }
|
||
// // 执行读写操作(如 master.ReadHoldingRegisters )
|
||
// Thread.Sleep(100); // 控制采集频率
|
||
// }
|
||
// catch (Exception ex)
|
||
// {
|
||
// Console.WriteLine($"断线/异常: {ex.Message},尝试重连...");
|
||
// _tcpClient.Close(); // 清理旧连接
|
||
// Thread.Sleep(1000); // 间隔重连
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
private async void Button_Click_14(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await WriteRegisterWithValidation(
|
||
inputControl: handmovementTxt,
|
||
registerAddress: _handSpeedAddress, // 手动速度寄存器地址(根据实际修改)
|
||
minValue: 1, // 手动速度最小值
|
||
maxValue: 1000 // 手动速度最大值
|
||
);
|
||
}
|
||
finally
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
private async void Button_Click_15(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await WriteRegisterWithValidation(
|
||
inputControl: moistureTxt,
|
||
registerAddress: _moistureAddress, // 手动速度寄存器地址(根据实际修改)
|
||
minValue: 1, // 手动速度最小值
|
||
maxValue: 1000 // 手动速度最大值
|
||
);
|
||
|
||
}
|
||
finally
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
private async void Button_Click_16(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await WriteRegisterWithValidation(
|
||
inputControl: OutFowRateTxt,
|
||
registerAddress: _OutFowRateAddress, // 手动速度寄存器地址(根据实际修改)
|
||
minValue: 1, // 手动速度最小值
|
||
maxValue: 1000 // 手动速度最大值
|
||
);
|
||
}
|
||
finally
|
||
{
|
||
}
|
||
|
||
}
|
||
|
||
private async void Button_Click_17(object sender, RoutedEventArgs e)
|
||
{
|
||
|
||
try
|
||
{
|
||
await WriteRegisterWithValidation(
|
||
inputControl: InFowRateTxt,
|
||
registerAddress: _InFowRateAddress, // 手动速度寄存器地址(根据实际修改)
|
||
minValue: 1, // 手动速度最小值
|
||
maxValue: 1000 // 手动速度最大值
|
||
);
|
||
|
||
}
|
||
finally
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
private async void Button_Click_18(object sender, RoutedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
await WriteRegisterWithValidation(
|
||
inputControl: PressRateTxt,
|
||
registerAddress: _PressRateAddress, // 手动速度寄存器地址(根据实际修改)
|
||
minValue: 1, // 手动速度最小值
|
||
maxValue: 1000 // 手动速度最大值
|
||
);
|
||
|
||
}
|
||
finally
|
||
{
|
||
|
||
}
|
||
}
|
||
}
|
||
} |