186 lines
6.9 KiB
C#
186 lines
6.9 KiB
C#
using NModbus;
|
||
using System;
|
||
using System.Net;
|
||
using System.Net.Sockets;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace DeviceCommand.Base
|
||
{
|
||
public class ModbusTcp : IModbusDevice
|
||
{
|
||
public string IPAddress { get; private set; } = "127.0.0.1";
|
||
public int Port { get; private set; } = 502;
|
||
public int SendTimeout { get; private set; } = 3000;
|
||
public int ReceiveTimeout { get; private set; } = 3000;
|
||
|
||
private TcpClient _tcpClient;
|
||
public IModbusMaster Modbus { get; private set; }
|
||
public bool IsConnected => _tcpClient?.Connected ?? false;
|
||
|
||
protected readonly SemaphoreSlim _commLock = new(1, 1);
|
||
|
||
public ModbusTcp()
|
||
{
|
||
_tcpClient = new TcpClient();
|
||
}
|
||
|
||
public void ConfigureDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000)
|
||
{
|
||
IPAddress = ipAddress;
|
||
Port = port;
|
||
SendTimeout = sendTimeout;
|
||
ReceiveTimeout = receiveTimeout;
|
||
}
|
||
|
||
public virtual async Task<bool> ConnectAsync(CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 开始连接 - IP: {IPAddress}, 端口: {Port}");
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 超时设置 - 发送: {SendTimeout}ms, 接收: {ReceiveTimeout}ms");
|
||
|
||
if (_tcpClient.Connected)
|
||
{
|
||
var remoteEndPoint = (IPEndPoint)_tcpClient.Client.RemoteEndPoint!;
|
||
string currentIp = remoteEndPoint.Address.MapToIPv4().ToString();
|
||
int currentPort = remoteEndPoint.Port;
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 已有连接: {currentIp}:{currentPort}");
|
||
|
||
if (currentIp == IPAddress && currentPort == Port)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 参数匹配,复用现有连接");
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 参数不匹配,需要重新连接");
|
||
}
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 关闭并释放旧连接");
|
||
_tcpClient.Close();
|
||
_tcpClient.Dispose();
|
||
_tcpClient = new TcpClient();
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 调用 ConnectAsync({IPAddress}, {Port})");
|
||
await _tcpClient.ConnectAsync(IPAddress, Port, ct);
|
||
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 创建ModbusMaster");
|
||
Modbus = new ModbusFactory().CreateMaster(_tcpClient);
|
||
|
||
bool isConnected = _tcpClient.Connected;
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 连接结果: {(isConnected ? "成功" : "失败")}");
|
||
return isConnected;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 连接异常: {ex.Message}");
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 异常类型: {ex.GetType().Name}");
|
||
System.Diagnostics.Debug.WriteLine($"[ModbusTcp] 异常堆栈: {ex.StackTrace}");
|
||
return false;
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public virtual void Close()
|
||
{
|
||
if (_tcpClient.Connected) _tcpClient.Close();
|
||
}
|
||
|
||
public async Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
// 修复:FromMinutes 改为 FromMilliseconds
|
||
await Modbus.WriteSingleRegisterAsync(slaveAddress, registerAddress, value)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
await Modbus.WriteMultipleRegistersAsync(slaveAddress, startAddress, values)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
return await Modbus.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
await Modbus.WriteSingleCoilAsync(slaveAddress, coilAddress, value)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<bool[]> ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
return await Modbus.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<ushort[]> ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default)
|
||
{
|
||
await _commLock.WaitAsync(ct);
|
||
try
|
||
{
|
||
return await Modbus.ReadInputRegistersAsync(slaveAddress, startAddress, numberOfPoints)
|
||
.WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct);
|
||
}
|
||
finally
|
||
{
|
||
_commLock.Release();
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_tcpClient?.Dispose();
|
||
_commLock?.Dispose();
|
||
}
|
||
}
|
||
} |