FluentModbus
FluentModbus copied to clipboard
Modbus TCP poll not timing out
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!
Do you have a small example I can use to reproduce it?
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.
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.
I have temporarily switched to sync version
Sorry for the delay, I have planned it for today
No worries, thanks a lot for your effort on such a great library :D
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.
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?
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
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).
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?
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?
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]
.
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 :)
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.