using System.Net.Sockets; using InnovEnergy.Lib.Protocols.Modbus.Protocol; using InnovEnergy.Lib.Utils.Net; namespace InnovEnergy.Lib.Protocols.Modbus.Channels; public class TcpChannel : ConnectionChannel { private const Int32 TimeoutMs = 500; // TODO: parametrize public TcpChannel(Ip4Address ip4Address, Boolean closeAfterSuccessfulRead = false, Boolean closeAfterSuccessfulWrite = false) : this(ip4Address.Host ?? "null", ip4Address.Port.HasValue ? (Int32)ip4Address.Port.Value : 0, closeAfterSuccessfulRead, closeAfterSuccessfulWrite) { } public TcpChannel(String hostname, Int32 port, Boolean closeAfterSuccessfulRead = false, Boolean closeAfterSuccessfulWrite = false) : base(closeAfterSuccessfulRead, closeAfterSuccessfulWrite) { TcpClient Open() { var tcpClient = new TcpClient { LingerState = new LingerOption(false, 0), NoDelay = true, ReceiveTimeout = TimeoutMs, SendTimeout = TimeoutMs }; var cts = new CancellationTokenSource(); var connected = tcpClient .ConnectAsync(hostname, port, cts.Token) .AsTask() .Wait(TimeoutMs); if (connected) return tcpClient; cts.Cancel(); throw new Exception($"Failed to connect to {hostname}:{port}"); } _Open = Open; } private readonly Func _Open; protected override TcpClient Open() => _Open(); protected override void Close(TcpClient tcpClient) => tcpClient.Close(); protected override IReadOnlyList Read(TcpClient tcpClient, Int32 nToRead) { var buffer = new Byte[nToRead]; var stream = tcpClient.GetStream(); var nReceived = 0; do { var nRemaining = nToRead - nReceived; var read = stream.Read(buffer, nReceived, nRemaining); if (read <= 0) throw new NotConnectedException("The TCP Connection was closed"); nReceived += read; } while (nReceived < nToRead); return buffer; } protected override void Write(TcpClient tcpClient, IReadOnlyList data) { var array = data.ToArray(); tcpClient.GetStream().Write(array, 0, array.Length); } }