FluentModbus icon indicating copy to clipboard operation
FluentModbus copied to clipboard

Modbus TCP poll not timing out

Open seikosantana opened this issue 2 years ago • 8 comments

I am performing read on holding registers with modbus TCP cllient. However, when device does not reply, i am not getting any timeout exception and it just stucks there. I'm using the preview 4 nuget package.

Is the expected behavior? If it is, how is the proper way to get timeout?

Thanks in advance!

seikosantana avatar May 30 '22 13:05 seikosantana

Do you have a small example I can use to reproduce it?

Apollo3zehn avatar Jun 01 '22 15:06 Apollo3zehn

I dont currently have something that can simulate timeout on the network. Could probably write a socket to simulate a slave socket and make no response to simulate timeout. However i'm not very familiar with network programming therefore it may not be simulating the same phenomenon.

However, there is a modbus slave that sometimes timeout, probably because it is located far away from the adapter. It usually times out for first try. I can see the timeout with modpoll tool.

I've set ReadTimeout on the client but the ReadHoldingRegistersAsync stucks there waiting forever for timeout.

I've looked into the source and made some searches about network readasync does not timeout and readtimeout only applies to synchronous read. https://stackoverflow.com/questions/62161695/why-is-networkstream-readasync-not-timing-out

For now this is the suspect. I wrapped the read invocation in try catch and timeouts does not throw. I'll send a code snipped when i have access to code next time but i hope this at least describes what is happening.

seikosantana avatar Jun 01 '22 17:06 seikosantana

Thanks for the link, the answer there could solve the problem. I had a similar issue with Modbus RTU and worked around it as shown here: https://github.com/Apollo3zehn/FluentModbus/blob/181586d88cbbef3b2b3e6ace7b29099e04b30627/src/FluentModbus/ModbusRtuSerialPort.cs#L54

I will try to solve it next week.

Apollo3zehn avatar Jun 01 '22 19:06 Apollo3zehn

I have temporarily switched to sync version

seikosantana avatar Jun 20 '22 03:06 seikosantana

Sorry for the delay, I have planned it for today

Apollo3zehn avatar Jun 20 '22 05:06 Apollo3zehn

No worries, thanks a lot for your effort on such a great library :D

seikosantana avatar Jun 20 '22 05:06 seikosantana

I have updated the code and pushed a new version to Nuget (v4.0.0-preview.4). Is it working for you with that version? The solution is now to create an additional cancellation token which closes the socket automatically after the specified timeout time. It is a combination of the solutions in your link and my link.

Apollo3zehn avatar Jun 20 '22 14:06 Apollo3zehn

I have finished the previous project with sync version of read, and now working on another project, which relies on version 4.1.0 of FluentModbus.

which closes the socket automatically after the specified timeout time

Does that mean that I will have to invoke Connect() again before performing another read? Also If the timeout is not set during the client instance creation, will it wait forever when there is no reply, or is there default timeout?

seikosantana avatar Sep 07 '22 03:09 seikosantana

Yes you would need to call connect again so that a new network stream instance is created. Here you can see that the network stream is closed when a timeout occurs: https://github.com/Apollo3zehn/FluentModbus/blob/ce86dbdddaeace89a609740b713695fa195c69c5/src/FluentModbus/Client/ModbusTcpClientAsync.cs#L67

The default timeout is Timeout.Infinite:

https://github.com/Apollo3zehn/FluentModbus/blob/ce86dbdddaeace89a609740b713695fa195c69c5/src/FluentModbus/Client/ModbusTcpClient.cs#L38-L53

Apollo3zehn avatar Sep 08 '22 11:09 Apollo3zehn

There seem to be an issue with calling Connect(). I am getting a lot of connection refused in a while loop polling modbus slave each 50ms, and i did Connect() only if the modbus client is disconnected, something like

while (true) {
  if (!mbClient.IsConnected)
  {
    mbClient.Disconnect();
    mbClient.Connect();
  }
  short currentBatch = (await mbClient.ReadHoldingRegistersAsync<short>(_address, RegCurrentBatch, 1)).ToArray().First();
  // do something with the value
  await Task.Delay(50);
}

However it could be something else (will investigate fruther).

seikosantana avatar Sep 09 '22 02:09 seikosantana

Connection refused means that the Modbus slave did not accept the connection request or there was no Modbus slave listening. I am not sure how this exception could be caused on the Modbus client side. Which FluentModbus version are you using right now?

Apollo3zehn avatar Sep 09 '22 06:09 Apollo3zehn

I am using 4.1.0 NuGet package.

I took a look at Connect() implementation and found out that if it is invoked without parameters then it tries to connect to loopback, which I thought it is to reconnect to last address specified. Now that I explicitly specify the correct address, I no longer get connection refused.

Sometimes polling could fail so I used Polly with RetryAsync policy to execute something similar to the code in the previous comment and so far it's working great. It's just that the poll could timeout sometimes, which I am not sure what the cause is (the TCP slave itself could be the cause I think) but i think that's for another issue if to be discussed?

I'm happy that this issue can be considered as fixed now, and one last thing to be sure, is the Disconnect() call necessary before trying to reconnect?

seikosantana avatar Sep 10 '22 04:09 seikosantana

Good to hear it is working now :-) Regarding your question: I need to improve exception handling for the clients. Until then It is good to call Disconnect().

Note: Your code above .ToArray().First() can be simplified by a simple [0].

Apollo3zehn avatar Sep 10 '22 07:09 Apollo3zehn

can be simplified by a simple [0]

Noted. I just liked it for readability.

Disconnect() call necessary before trying to reconnect?

Since I once worked with some devices and PLCs that accepts only one connection, another connect while having them previously still connected fails.

But for now, I think it's good to close this issue since the main problem (not timing out) is fixed. Thanks a lot! I enjoy working with this library :)

seikosantana avatar Sep 10 '22 07:09 seikosantana

Hello, I am not 100% sure, that using Syncronous reading will timeout actually. It still stucks.

In my environment I have 300+ devices connected to different remote locations reading/writing each every 1 second. After a certain time some of them gets stuck. I run thru the logs. They never return from client.ReadHoldingRegisters even thought client.ReadTimeout = 5000 is set.

Now I am moving to async with a CancellationTokenSource set to cts.CancelAfter(5000); I will report you back.

endrelovas avatar May 23 '24 06:05 endrelovas