using Modbus.Device; using System; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using System.Threading; using System.Threading.Tasks; namespace MembranePoreTester.Communication { public class ModbusTcpPlcService : IPlcService, IDisposable { private readonly PlcConfiguration _config; private TcpClient _tcpClient; private IModbusMaster _master; public ModbusTcpPlcService(PlcConfiguration config) { _config = config; } public async Task EnsureConnectedAsync(int retryCount = 3) { if (_tcpClient != null && _tcpClient.Connected) return; for (int i = 0; i < retryCount; i++) { try { _tcpClient?.Close(); _tcpClient = new TcpClient(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)); // 现在可以直接使用扩展方法 await _tcpClient.ConnectAsync(_config.IpAddress, _config.Port).WithCancellation(cts.Token); _master = ModbusIpMaster.CreateIp(_tcpClient); return; } catch (Exception ex) when (i < retryCount - 1) { System.Diagnostics.Debug.WriteLine($"连接失败,{500}ms 后重试... {ex.Message}"); await Task.Delay(500); } } throw new Exception($"无法连接到 PLC ({_config.IpAddress}:{_config.Port}),请检查网络和 PLC 状态。"); } // 读取两个连续的保持寄存器,转换为32位浮点数(假设大端模式) public async Task ReadFloatAsync(ushort startAddress) { await EnsureConnectedAsync(); var registers = await ReadHoldingRegistersAsync(startAddress, 2); return UshortToFloat(registers[1], registers[0]); } public async Task ReadPressureAsync() => await ReadFloatAsync(_config.PressureRegister); public async Task ReadWetFlowAsync(int stationId) { ushort startAddress = stationId switch { 1 => _config.WetFlowRegister, 2 => _config.WetFlowRegister2, 3 => _config.WetFlowRegister3, }; return await ReadFloatAsync(startAddress); } public async Task ReadDryFlowAsync(int stationId) { ushort startAddress = stationId switch { 1 => _config.DryFlowRegister, 2 => _config.DryFlowRegister2, 3 => _config.DryFlowRegister3, _ => throw new ArgumentException("Invalid station") }; return await ReadFloatAsync(startAddress); } public async Task WriteCoilAsync(ushort coilAddress, bool value) { await EnsureConnectedAsync(); await _master.WriteSingleCoilAsync(_config.SlaveId, coilAddress, value); } public async Task WriteRegisterAsync(ushort registerAddress, ushort value) { await EnsureConnectedAsync(); await Task.Delay(100); await _master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value); } public async Task ReadCoilAsync(ushort coilAddress) { await EnsureConnectedAsync(); await Task.Delay(100); bool[] result = await _master?.ReadCoilsAsync(_config.SlaveId, coilAddress, 1); return result[0]; } public bool IsConnected => _tcpClient != null && _tcpClient.Connected; public async Task ReadHoldingRegistersAsync(ushort startAddress, ushort count) { await EnsureConnectedAsync(); // await Task.Delay(100); return await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count); } public async Task WriteSingleRegisterAsync(ushort registerAddress, ushort value) { await EnsureConnectedAsync(); int val = (int)value; await Task.Delay(100); await _master.WriteMultipleRegistersAsync(1, registerAddress, intToushorts(val)); } public async Task WriteMultipleRegistersAsync(ushort registerAddress, float value) { await EnsureConnectedAsync(); await Task.Delay(100); await _master.WriteMultipleRegistersAsync(_config.SlaveId, registerAddress, SplitFloatToUShortArray((float)value)); } /// /// Int转为ushort数组发送 /// /// /// 返回ushort数组 private ushort[] intToushorts(int res) { ushort ust1 = (ushort)(res >> 16); ushort ust2 = (ushort)res; return new ushort[] { ust2, ust1 }; } /// /// Float转为Ushort数组发送 /// /// /// 返回ushort数组 public ushort[] SplitFloatToUShortArray(float value) { byte[] floatBytes = BitConverter.GetBytes(value); ushort[] ushortArray = new ushort[floatBytes.Length / 2]; for (int i = 0, j = 0; i < floatBytes.Length; i += 2, j++) { ushortArray[j] = BitConverter.ToUInt16(floatBytes, i); } return ushortArray; } /// /// ushort转为float类型 /// /// /// /// float型数据 public float UshortToFloat(ushort P1, ushort P2) { int intSign, intSignRest, intExponent, intExponentRest; float faResult, faDigit; intSign = P1 / 32768; intSignRest = P1 % 32768; intExponent = intSignRest / 128; intExponentRest = intSignRest % 128; faDigit = (float)(intExponentRest * 65536 + P2) / 8388608; faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1); return faResult; } // 新增读取压力(根据工位) public async Task ReadPressureAsync(int stationId) { ushort startAddress = stationId switch { 1 => _config.PressureRegisterStation1, 2 => _config.PressureRegisterStation2, 3 => _config.PressureRegisterStation3, _ => throw new ArgumentException("Invalid station") }; return await ReadFloatAsync(startAddress); } public void Dispose() { _master?.Dispose(); _tcpClient?.Close(); _tcpClient?.Dispose(); } } public static class TaskExtensions { public static async Task WithCancellation(this Task task, CancellationToken cancellationToken) { var tcs = new TaskCompletionSource(); using (cancellationToken.Register(s => ((TaskCompletionSource)s).TrySetResult(true), tcs)) { if (task != await Task.WhenAny(task, tcs.Task)) throw new OperationCanceledException(cancellationToken); } await task; } } }