From 95b0aa68a475badd3de6c62ae9729e2511a5b4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 26 May 2024 15:44:36 +0200 Subject: [PATCH 1/5] feat(CustomModbusTcpClient): use read timeout as connect timeout as well --- .../Services/Modbus/Contracts/IModbusTcpClient.cs | 2 +- .../Services/Modbus/CustomModbusTcpClient.cs | 3 ++- .../Services/Modbus/ModbusClientHandlingService.cs | 13 +++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs index d13911917..e0cd9e4c8 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs @@ -6,7 +6,7 @@ namespace TeslaSolarCharger.Services.Services.Modbus.Contracts; public interface IModbusTcpClient : IDisposable { bool IsConnected { get; } - void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess); + void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout); void Disconnect(); Task GetByteArrayFromHoldingRegisters(byte unitIdentifier, ushort startingAddress, ushort quantity, TimeSpan readTimeout); Task GetByteArrayFromInputRegisters(byte unitIdentifier, ushort startingAddress, ushort quantity, TimeSpan readTimeout); diff --git a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs index f06959e11..c53181277 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs @@ -49,7 +49,7 @@ public void Demo() Connect(); } - public void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess) + public void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout) { var fluentEndianness = endianess switch { @@ -57,6 +57,7 @@ public void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess) ModbusEndianess.LittleEndian => ModbusEndianness.LittleEndian, _ => throw new ArgumentOutOfRangeException(nameof(endianess), endianess, "Endianess not known"), }; + ConnectTimeout = (int)connectTimeout.TotalMilliseconds; base.Connect(ipEndPoint, fluentEndianness); } } diff --git a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs index 047b4f62c..b640cfc24 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs @@ -16,7 +16,7 @@ public async Task GetByteArray(byte unitIdentifier, string host, int por { logger.LogTrace("{method}({unitIdentifier}, {host}, {port}, {endianess}, {connectDelay}, {readTimeout}, {registerType}, {address}, {length})", nameof(GetByteArray), unitIdentifier, host, port, endianess, connectDelay, readTimeout, registerType, address, length); - var client = await GetConnectedModbusTcpClient(host, port, endianess, connectDelay); + var client = await GetConnectedModbusTcpClient(host, port, endianess, connectDelay, readTimeout); byte[] byteArray; if (registerType == ModbusRegisterType.HoldingRegister) { @@ -62,7 +62,8 @@ private static byte[] ConvertToCorrectEndianess(ModbusEndianess endianess, byte[ return tempArray; } - private async Task GetConnectedModbusTcpClient(string host, int port, ModbusEndianess endianess, TimeSpan connectDelay) + private async Task GetConnectedModbusTcpClient(string host, int port, ModbusEndianess endianess, + TimeSpan connectDelay, TimeSpan connectTimeout) { logger.LogTrace("{method}({host}, {port})", nameof(GetConnectedModbusTcpClient), host, port); var ipAddress = GetIpAddressFromHost(host); @@ -71,21 +72,21 @@ private async Task GetConnectedModbusTcpClient(string host, in { if (!modbusClient.IsConnected) { - await ConnectModbusClient(modbusClient, ipAddress, port, endianess, connectDelay); + await ConnectModbusClient(modbusClient, ipAddress, port, endianess, connectDelay, connectTimeout); } return modbusClient; } var client = serviceProvider.GetRequiredService(); - await ConnectModbusClient(client, ipAddress, port, endianess, connectDelay); + await ConnectModbusClient(client, ipAddress, port, endianess, connectDelay, connectTimeout); _modbusClients.Add(key, client); return client; } private async Task ConnectModbusClient(IModbusTcpClient modbusClient, IPAddress ipAddress, int port, ModbusEndianess endianess, - TimeSpan connectDelay) + TimeSpan connectDelay, TimeSpan connectTimeout) { - modbusClient.Connect(new IPEndPoint(ipAddress, port), endianess); + modbusClient.Connect(new IPEndPoint(ipAddress, port), endianess, connectTimeout); await Task.Delay(connectDelay).ConfigureAwait(false); } From 0442d39838ffe6a284944e93caff6e6087d8d6b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 26 May 2024 17:50:18 +0200 Subject: [PATCH 2/5] fix(CustomModbusTcpClient): prevent concurrent modbus connection --- .../Modbus/Contracts/IModbusTcpClient.cs | 2 +- .../Services/Modbus/CustomModbusTcpClient.cs | 25 +++++++++++++------ .../Modbus/ModbusClientHandlingService.cs | 4 +-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs index e0cd9e4c8..5a40b438d 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/Contracts/IModbusTcpClient.cs @@ -6,7 +6,7 @@ namespace TeslaSolarCharger.Services.Services.Modbus.Contracts; public interface IModbusTcpClient : IDisposable { bool IsConnected { get; } - void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout); + Task Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout); void Disconnect(); Task GetByteArrayFromHoldingRegisters(byte unitIdentifier, ushort startingAddress, ushort quantity, TimeSpan readTimeout); Task GetByteArrayFromInputRegisters(byte unitIdentifier, ushort startingAddress, ushort quantity, TimeSpan readTimeout); diff --git a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs index c53181277..124cd4a28 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs @@ -49,15 +49,24 @@ public void Demo() Connect(); } - public void Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout) + public async Task Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout) { - var fluentEndianness = endianess switch + await _semaphoreSlim.WaitAsync().ConfigureAwait(false); + try + { + var fluentEndianness = endianess switch + { + ModbusEndianess.BigEndian => ModbusEndianness.BigEndian, + ModbusEndianess.LittleEndian => ModbusEndianness.LittleEndian, + _ => throw new ArgumentOutOfRangeException(nameof(endianess), endianess, "Endianess not known"), + }; + ConnectTimeout = (int)connectTimeout.TotalMilliseconds; + base.Connect(ipEndPoint, fluentEndianness); + } + finally { - ModbusEndianess.BigEndian => ModbusEndianness.BigEndian, - ModbusEndianess.LittleEndian => ModbusEndianness.LittleEndian, - _ => throw new ArgumentOutOfRangeException(nameof(endianess), endianess, "Endianess not known"), - }; - ConnectTimeout = (int)connectTimeout.TotalMilliseconds; - base.Connect(ipEndPoint, fluentEndianness); + _semaphoreSlim.Release(); + } + } } diff --git a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs index b640cfc24..fab322bc6 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs @@ -78,15 +78,15 @@ private async Task GetConnectedModbusTcpClient(string host, in } var client = serviceProvider.GetRequiredService(); - await ConnectModbusClient(client, ipAddress, port, endianess, connectDelay, connectTimeout); _modbusClients.Add(key, client); + await ConnectModbusClient(client, ipAddress, port, endianess, connectDelay, connectTimeout); return client; } private async Task ConnectModbusClient(IModbusTcpClient modbusClient, IPAddress ipAddress, int port, ModbusEndianess endianess, TimeSpan connectDelay, TimeSpan connectTimeout) { - modbusClient.Connect(new IPEndPoint(ipAddress, port), endianess, connectTimeout); + await modbusClient.Connect(new IPEndPoint(ipAddress, port), endianess, connectTimeout); await Task.Delay(connectDelay).ConfigureAwait(false); } From 6cb2328e7df8f3a104723516e064cd3f73c3afaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 26 May 2024 19:06:41 +0200 Subject: [PATCH 3/5] feat(Modbus): improve logging --- .../Services/Modbus/CustomModbusTcpClient.cs | 1 + .../Services/Modbus/ModbusClientHandlingService.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs index 124cd4a28..ab92cc082 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs @@ -51,6 +51,7 @@ public void Demo() public async Task Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, TimeSpan connectTimeout) { + logger.LogTrace("{method}({ipEndPoint}, {endianess}, {connectTimeout})", nameof(Connect), ipEndPoint, endianess, connectTimeout); await _semaphoreSlim.WaitAsync().ConfigureAwait(false); try { diff --git a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs index fab322bc6..8ee39b679 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/ModbusClientHandlingService.cs @@ -86,6 +86,7 @@ private async Task GetConnectedModbusTcpClient(string host, in private async Task ConnectModbusClient(IModbusTcpClient modbusClient, IPAddress ipAddress, int port, ModbusEndianess endianess, TimeSpan connectDelay, TimeSpan connectTimeout) { + logger.LogTrace("{method}(modbusClient, {ipAddress}, {port}, {endianess}, {connectDelay}, {connectTimeout})", nameof(ConnectModbusClient), ipAddress, port, endianess, connectDelay, connectTimeout); await modbusClient.Connect(new IPEndPoint(ipAddress, port), endianess, connectTimeout); await Task.Delay(connectDelay).ConfigureAwait(false); } From 4355d4f6c037260a6ec04d2f98f18d4271349819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 26 May 2024 19:09:31 +0200 Subject: [PATCH 4/5] feat(CustomModbusTcpClient): further improve logging --- .../Services/Modbus/CustomModbusTcpClient.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs index ab92cc082..1be7d0232 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/CustomModbusTcpClient.cs @@ -18,12 +18,14 @@ public async Task GetByteArrayFromHoldingRegisters(byte unitIdentifier, try { ReadTimeout = (int)readTimeout.TotalMilliseconds; + logger.LogTrace("ReadTimeout: {ReadTimeout}", ReadTimeout); var result = await base.ReadHoldingRegistersAsync(unitIdentifier, startingAddress, quantity); return result.ToArray(); } finally { _semaphoreSlim.Release(); + logger.LogTrace("Semaphore released"); } } @@ -35,12 +37,14 @@ public async Task GetByteArrayFromInputRegisters(byte unitIdentifier, us try { ReadTimeout = (int)readTimeout.TotalMilliseconds; + logger.LogTrace("ReadTimeout: {ReadTimeout}", ReadTimeout); var result = await base.ReadInputRegistersAsync(unitIdentifier, startingAddress, quantity); return result.ToArray(); } finally { _semaphoreSlim.Release(); + logger.LogTrace("Semaphore released"); } } @@ -62,11 +66,13 @@ public async Task Connect(IPEndPoint ipEndPoint, ModbusEndianess endianess, Time _ => throw new ArgumentOutOfRangeException(nameof(endianess), endianess, "Endianess not known"), }; ConnectTimeout = (int)connectTimeout.TotalMilliseconds; + logger.LogTrace("ConnectTimeout: {ConnectTimeout}", ConnectTimeout); base.Connect(ipEndPoint, fluentEndianness); } finally { _semaphoreSlim.Release(); + logger.LogTrace("Semaphore relesed"); } } From b1c2c68ff52d10d38b8655d814cce5a8f03a484b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20K=C3=BChnel?= Date: Sun, 26 May 2024 19:33:47 +0200 Subject: [PATCH 5/5] fix(ModbusValueExecutionService): use TimeSpan.FromMilliseconds --- .../Services/Modbus/ModbusValueExecutionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TeslaSolarCharger.Services/Services/Modbus/ModbusValueExecutionService.cs b/TeslaSolarCharger.Services/Services/Modbus/ModbusValueExecutionService.cs index 6ded266b1..483dae7d2 100644 --- a/TeslaSolarCharger.Services/Services/Modbus/ModbusValueExecutionService.cs +++ b/TeslaSolarCharger.Services/Services/Modbus/ModbusValueExecutionService.cs @@ -20,7 +20,7 @@ public async Task GetResult(DtoModbusConfiguration modbusConfig, DtoModb { logger.LogTrace("{method}({modbusConfig})", nameof(GetResult), modbusConfig); var byteArray = await modbusClientHandlingService.GetByteArray((byte)modbusConfig.UnitIdentifier!, modbusConfig.Host, - modbusConfig.Port, modbusConfig.Endianess, TimeSpan.FromSeconds(modbusConfig.ConnectDelayMilliseconds), + modbusConfig.Port, modbusConfig.Endianess, TimeSpan.FromMilliseconds(modbusConfig.ConnectDelayMilliseconds), TimeSpan.FromMilliseconds(modbusConfig.ReadTimeoutMilliseconds), resultConfiguration.RegisterType, (ushort)resultConfiguration.Address, (ushort)resultConfiguration.Length); return byteArray;