using Modbus.Device; using RobotNet10.Script.IO; using System.Net.Sockets; namespace RobotNet10.ScriptEngine.IO; /// /// Implementation of ModbusTCP connection using NModbus4 library. /// 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 ReadHoldingRegistersAsync(ushort startAddress, ushort numberOfPoints) { EnsureConnected(); return await Task.Run(() => _modbusMaster!.ReadHoldingRegisters(SlaveId, startAddress, numberOfPoints)); } public async Task ReadInputRegistersAsync(ushort startAddress, ushort numberOfPoints) { EnsureConnected(); return await Task.Run(() => _modbusMaster!.ReadInputRegisters(SlaveId, startAddress, numberOfPoints)); } public async Task ReadCoilsAsync(ushort startAddress, ushort numberOfPoints) { EnsureConnected(); return await Task.Run(() => _modbusMaster!.ReadCoils(SlaveId, startAddress, numberOfPoints)); } public async Task 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); } }