Files
I150/srcs/RobotNet10/Commons/RobotNet10.ScriptEngine/IO/ModbusTcpConnection.cs
2026-07-03 16:37:12 +07:00

135 lines
4.2 KiB
C#

using Modbus.Device;
using RobotNet10.Script.IO;
using System.Net.Sockets;
namespace RobotNet10.ScriptEngine.IO;
/// <summary>
/// Implementation of ModbusTCP connection using NModbus4 library.
/// </summary>
public class ModbusTcpConnection(string ipAddress, int port = 502, byte slaveId = 1, int connectTimeoutMs = 5000) : IModbusTcpConnection
{
private TcpClient? _tcpClient;
private ModbusMaster? _modbusMaster;
private bool _disposed;
public string IpAddress { get; } = ipAddress;
public int Port { get; } = port;
public byte SlaveId { get; } = slaveId;
public int ConnectTimeoutMs { get; } = connectTimeoutMs;
public bool IsConnected { get; private set; } = false;
public async Task ConnectAsync()
{
if (IsConnected)
return;
try
{
_tcpClient = new TcpClient();
// Sử dụng ConnectTimeout
using (var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(ConnectTimeoutMs)))
{
await _tcpClient.ConnectAsync(IpAddress, Port).WaitAsync(cts.Token);
}
_modbusMaster = ModbusIpMaster.CreateIp(_tcpClient);
IsConnected = true;
}
catch (OperationCanceledException)
{
Disconnect();
throw new TimeoutException($"Connection to {IpAddress}:{Port} timed out after {ConnectTimeoutMs}ms");
}
catch
{
Disconnect();
throw;
}
}
public Task DisconnectAsync()
{
Disconnect();
return Task.CompletedTask;
}
private void Disconnect()
{
IsConnected = false;
_modbusMaster?.Dispose();
_modbusMaster = null;
_tcpClient?.Close();
_tcpClient?.Dispose();
_tcpClient = null;
}
public async Task<ushort[]> ReadHoldingRegistersAsync(ushort startAddress, ushort numberOfPoints)
{
EnsureConnected();
return await Task.Run(() => _modbusMaster!.ReadHoldingRegisters(SlaveId, startAddress, numberOfPoints));
}
public async Task<ushort[]> ReadInputRegistersAsync(ushort startAddress, ushort numberOfPoints)
{
EnsureConnected();
return await Task.Run(() => _modbusMaster!.ReadInputRegisters(SlaveId, startAddress, numberOfPoints));
}
public async Task<bool[]> ReadCoilsAsync(ushort startAddress, ushort numberOfPoints)
{
EnsureConnected();
return await Task.Run(() => _modbusMaster!.ReadCoils(SlaveId, startAddress, numberOfPoints));
}
public async Task<bool[]> ReadDiscreteInputsAsync(ushort startAddress, ushort numberOfPoints)
{
EnsureConnected();
return await Task.Run(() => _modbusMaster!.ReadInputs(SlaveId, startAddress, numberOfPoints));
}
public async Task WriteSingleCoilAsync(ushort coilAddress, bool value)
{
EnsureConnected();
await Task.Run(() => _modbusMaster!.WriteSingleCoil(SlaveId, coilAddress, value));
}
public async Task WriteMultipleCoilsAsync(ushort startAddress, bool[] values)
{
EnsureConnected();
await Task.Run(() => _modbusMaster!.WriteMultipleCoils(SlaveId, startAddress, values));
}
public async Task WriteSingleRegisterAsync(ushort registerAddress, ushort value)
{
EnsureConnected();
await Task.Run(() => _modbusMaster!.WriteSingleRegister(SlaveId, registerAddress, value));
}
public async Task WriteMultipleRegistersAsync(ushort startAddress, ushort[] values)
{
EnsureConnected();
await Task.Run(() => _modbusMaster!.WriteMultipleRegisters(SlaveId, startAddress, values));
}
private void EnsureConnected()
{
if (!IsConnected || _modbusMaster == null)
{
throw new InvalidOperationException("ModbusTCP connection is not connected. Call ConnectAsync() first.");
}
}
public void Dispose()
{
if (!_disposed)
{
Disconnect();
_disposed = true;
}
GC.SuppressFinalize(this);
}
}