aldas / modbus-tcp-client

PHP client for Modbus TCP and Modbus RTU over TCP (can be used for serial)
Apache License 2.0
191 stars 55 forks source link

stream_select interrupted by an incoming signal error with Modbus RTU over TCP #140

Closed haha8x closed 1 year ago

haha8x commented 1 year ago

Hi

I am trying to read some data from the PowerLogic™ Power Meter PM5350 Using Modbus RTU Over TCP

I use the FC3, with an address from 3000 - 3110 Packet to be sent (in hex): 9e72000000060c030bb80070

It returns An exception that occurred stream_select interrupted by an incoming signal

So if I skip the address 3110, just read from 3000 - 3076, then it works Packet to be sent (in hex): d943000000060c030bb8004e RTU Binary received (in hex): 0c039c37804199a4ec41a357fe3f6af18cffc00000419d66ce3fb1eb854018f5c34071eb854071eb8543c6f6e243c75d7343c6bfa143c706a74364aea84366f91d4365cf32ffffffff4365d2533cf5c28f3e2e147b3e0f5c293e2e147b3f0000003f0000003c23d70a3f000000405f01bd40628d9a40735c2b412d3ae1402fcab0402b187c402ff2e34102b584408dfb37408df363409626d3415902a53f49f4d3

But I add more an address 3110, it returns error I think it is the gap between the 3076 -> 3110

So I tried to change the

abstract class AddressSplitter
modified const MAX_REGISTERS_PER_MODBUS_REQUEST = 100;

Then I have 2 requests and it works flawlessly.

So What is the problem here? Can I somehow get a way to the issue, without changing MAX_REGISTERS_PER_MODBUS_REQUEST in AddressSplitter?

My Env is PHP: 8.1.17 Memory limit: 2048M Max execution time (s): 9999 nginx/1.21.4 Linux UbuntuSRV 5.19.0-43-generic #44~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon May 22 13:39:36 UTC 2 x86_64

aldas commented 1 year ago

I think you are dealing with https://github.com/aldas/modbus-tcp-client/issues/135 here. Some Modbus servers have certain range of addresses that can not be requested. So when startAddress + Quantity overlaps that area you will received an error from server. So you need to "avoid" requesting these ranges by splitting requested range into multiple requests.

aldas commented 1 year ago

If you are not doing something dynamic. just adjust you start address and quantity to values that can request successfully before that inaccessible range and another startAddress + quantity after that range.

haha8x commented 1 year ago

I tried to make my code dymamic By request all meter param from database (already filled with modbus address and param name)

and tried to read all param and put it all to the array, every time it request.

So maybe I should split the request on my own? By spiting the address into group array, so in the foreach loop, it can build the request for each 10 param @ one time.

aldas commented 1 year ago

If your modbus server (PLC) does not allow certain ranges to be requested then there is no way to do this with single request. You need to split that address range (startAddress + quantity) into 2 requests - manually or trying to use latest version.

Latest version should be able to split it into multiple requests

$fc3 = ReadRegistersBuilder::newReadHoldingRegisters($address, $unitID)
    ->unaddressableRanges([[3076,3110]])
...

but this can be done by you own logic also.

haha8x commented 1 year ago

I don't get the new function

$fc3 = ReadRegistersBuilder::newReadHoldingRegisters($address, $unitID)
    ->unaddressableRanges([[3076,3110]])

What does it mean 3076,3110? (from and to right?) As I understand, if the range is from 3076 - 3110 then it would split into another request, starting from address 3076 or 3110

aldas commented 1 year ago

[[3076,3110]] creates 1 range starting from 3076 (including) to 3110 (including) that splitter would try to avoid

That method has doc comment. maybe it helps https://github.com/aldas/modbus-tcp-client/blob/09bd467b9821d394f4b29f5bf5b0984c005fbdb0/src/Composer/Read/ReadRegistersBuilder.php#L72-L82

aldas commented 1 year ago

@haha8x you might want to try setting delay between sending the request packet and reading the response. I just saw something strange with USB serial device that needed delay to request some addresses. I have never seen this kind of stuff before that some address need explicit delay but others work without it. This should not happen for TCP but you never know.

replace this line

$binaryData = $connection->connect()->sendAndReceive($packet);

with

    $connection->connect()->send($packet);
    // delay this is crucial for some serial devices and delay needs to be long as 100ms (depending on the quantity)
    // or you will experience read errors ("stream_select interrupted") or invalid CRCs
    usleep(100_000); // 100ms
    $binaryData = $connection->receive();

and see if it helps

aldas commented 1 year ago

@haha8x did you get your problem solved?