Files
CSI-Z420-Tablet-Multi-Funct…/Services/ModbusTcpPlcService.cs
2026-05-15 11:13:06 +08:00

123 lines
4.3 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 Modbus.Device;
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using TabletTester2025.Models;
namespace TabletTester2025.Services
{
public class ModbusTcpPlcService : IPlcService
{
private readonly PlcConfiguration _config;
private TcpClient _tcpClient;
private IModbusMaster _master;
public ModbusTcpPlcService(PlcConfiguration config)
{
_config = config;
}
public async Task ConnectAsync() => await EnsureConnectedAsync();
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);
_master.Transport.ReadTimeout = 1000;
_master.Transport.WriteTimeout = 1000;
return;
}
catch (Exception ex) when (i < retryCount - 1)
{
System.Diagnostics.Debug.WriteLine($"连接失败500ms后重试... {ex.Message}");
await Task.Delay(500);
}
}
throw new Exception($"无法连接到 PLC ({_config.IpAddress}:{_config.Port})");
}
//读取寄存器返回浮点型
public async Task<float> ReadFloatAsync(ushort startAddress)
{
await EnsureConnectedAsync();
var registers = await ReadHoldingRegistersAsync(startAddress, 2);
return UshortToFloat(registers[1], registers[0]);
}
//读取返回整型
public async Task<int> ReadIntAsync(ushort startAddress)
{
await EnsureConnectedAsync();
var registers = await ReadHoldingRegistersAsync(startAddress, 1);
return registers[0];
}
public async Task WriteCoilAsync(ushort coilAddress, bool value)
{
await EnsureConnectedAsync();
await _master.WriteSingleCoilAsync(_config.SlaveId, coilAddress, value);
}
public async Task<bool> ReadCoilAsync(ushort coilAddress)
{
await EnsureConnectedAsync();
bool[] result = await _master.ReadCoilsAsync(_config.SlaveId, coilAddress, 1);
return result[0];
}
public async Task WriteRegisterAsync(ushort registerAddress, ushort value)
{
await EnsureConnectedAsync();
await _master.WriteSingleRegisterAsync(_config.SlaveId, registerAddress, value);
}
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort count)
{
await EnsureConnectedAsync();
return await _master.ReadHoldingRegistersAsync(_config.SlaveId, startAddress, count);
}
public bool IsConnected => _tcpClient != null && _tcpClient.Connected;
private float UshortToFloat(ushort high, ushort low)
{
// Modbus 大端模式高16位在前低16位在后
byte[] bytes = new byte[4];
bytes[0] = (byte)(high >> 8);
bytes[1] = (byte)(high & 0xFF);
bytes[2] = (byte)(low >> 8);
bytes[3] = (byte)(low & 0xFF);
return BitConverter.ToSingle(bytes, 0);
}
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<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
}
await task;
}
}
}