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

Uncaught ModbusTcpClient\Network\IOException: stream_select interrupted by an incoming signal #150

Closed zeConehead closed 1 year ago

zeConehead commented 1 year ago

Hello there,

I am trying to receive a modtcp value from my Huawei inverter (Huawai-SUN2000-8KTL-M1). This is my sample code:

$unitID = 1;
$uri = 'tcp://192.127.0.33:502';
$requests = ReadRegistersBuilder::newReadHoldingRegisters($uri, $unitID)
    ->int16(32017, 'current1')
    ->build();
$responseContainer = (new NonBlockingClient(['readTimeoutSec' => 3]))->sendRequests($requests);
print_r($responseContainer->getData());
print_r($responseContainer->getErrors());

Every time, no matter what I do, I receive this error.

Fatal error: Uncaught ModbusTcpClient\Network\IOException: stream_select interrupted by an incoming signal in C:\php\modbus\vendor\aldas\modbus-tcp-client\src\Network\StreamHandler.php:56 Stack trace: #0 C:\php\modbus\vendor\aldas\modbus-tcp-client\src\Network\NonBlockingClient.php(143): ModbusTcpClient\Network\NonBlockingClient->receiveFrom(Array, 3.0, NULL) #1 C:\php\modbus\index.php(15): ModbusTcpClient\Network\NonBlockingClient->sendRequests(Array) #2 {main} thrown in C:\php\modbus\vendor\aldas\modbus-tcp-client\src\Network\StreamHandler.php on line 56

I do know that it is working in general, as I can use this tool EasyModbusClient (https://github.com/rossmann-engineering/EasyModbusTCP.NET). With it I can connect and read the value 32017 without any problems. So I compared the sent commands and what I found a bit strange as well was that receiving the value 32016 with this library, sent the same code as receiving the value 32017 when using EasyModTcp.

Here is the commands that were sent:

Command FC03
Tx: 
    00010000000601037D100001 (32017 in EasyModbus Client)
    b5120000000601037d100001 (32016 in PHP-Library)
    a4ae0000000601037d110001 (32017 in PHP-Library)
Rx:
    0001000000050103021AED (Value: 6893 in EasyModbus Client) 
        Error (PHP-Library)

Additionally I tried it with this code, but without any success either:

$connection = BinaryStreamConnection::getBuilder()
    ->setHost('192.127.0.33')
    ->setPort(502)
    ->build();

$packet = new ReadHoldingRegistersRequest(32017, 1);
$binaryData = $connection->connect()->sendAndReceive($packet);
print_r($binaryData);

Another tool is showing the correct data as well.

Am I doing something wrong or is this a bug?

Thank you so much.

aldas commented 1 year ago

you could try to increase time between sending the packet and starting to read it with delayRead property.

This can be passed as option as (new NonBlockingClient(['readTimeoutSec' => 3, 'delayRead' => 100_000])) or as setter, as this block does https://github.com/aldas/modbus-tcp-client/blob/436d85d7c4281c1d1334746a5266c81f568ad55b/examples/rtu_usb_to_serial_stream.php#L18-L20

I have not seen this for TCP devices but you never know.

zeConehead commented 1 year ago

Thank you for the quick response. I forgot to mention that I already tried this as it was said in this ticket: https://github.com/aldas/modbus-tcp-client/issues/140#issuecomment-1591082960

Unfortunately without any success. Not even with delayRead or setDelayRead

aldas commented 1 year ago

I is very hard for me to test this. Could you try something similar to

Replace temporarily his block https://github.com/aldas/modbus-tcp-client/blob/436d85d7c4281c1d1334746a5266c81f568ad55b/src/Network/StreamHandler.php#L48-L57

with

            // See https://github.com/symfony/symfony/pull/6540
            // https://github.com/PHPMailer/PHPMailer/blob/57f994d89eacac82dd0e40e4657eb6054136a7ea/src/SMTP.php#L1236
            $errorMessage = null;
            set_error_handler(function ($errno, $errmsg, $errfile = '', $errline = 0) use (&$errorMessage) {
                $errorMessage = $errmsg;
            });
            $modifiedStreams = stream_select(
                $read,
                $write,
                $except,
                (int)$timeout,
                $timeoutUsec
            );
            restore_error_handler();

            if (false == $modifiedStreams) {
                // interrupted stream_select should be ok for retry
                if ($errorMessage !== null && stripos($errorMessage, 'interrupted system call') !== false) {
                    $errorMessage = null;
                    continue;
                }
                throw new IOException('stream_select interrupted by an incoming signal');
            }

and maybe check what the actual message is in $errorMessage. I might be wrong here.

if it works. I could add this and tag new release

aldas commented 1 year ago

just edit that file in you vendor directory.

zeConehead commented 1 year ago

I just did that. Unfortunately the $errorMessage is empty and I still get the same error. Maybe I should add that I was not able to connect with the library adduc/phpmodbus as well. Is this maybe even a problem with PHP/Streams. Just strange that it works with 2 different tools

aldas commented 1 year ago

this has to be some kind of timing thing. Does https://www.modbusdriver.com/modpoll.html work for you?

and about address '32017' and '32016'. This library uses '0' based addresses. Some modbus clients use '1' based addresses. In protocol level addresses start from 0 but in "modbus domain" first address is 1, which is '0' in protocol level.

zeConehead commented 1 year ago

Modpoll does not work for me neither. But the more I google about it...it looks like a problem with the inverter and not with your library. Sorry for that but still thanks for helping me out so quick!

aldas commented 1 year ago

If you have raspberry Pis or some Linux machines (WSL maybe) you could trying sending packets from Linux. It would be interesting to know is this is Windows specific network thing.

zeConehead commented 1 year ago

I am way for the next days, but I could try it with a mac soon. Although I do not think it is a Windows thing as 2 other tools on Windows are working fine. Just to add some more info: I just tried the tool QModMaster. There I get Timeouts, Invalid Data, Slave device or server is busy...or sometimes the correct value.

aldas commented 1 year ago

Looking at some OpenHAB thread https://community.openhab.org/t/reading-data-from-huawei-inverter-sun-2000-3ktl-10ktl-via-modbus-tcp-and-rtu/87670/155?page=8 they talk about 'afterConnectionDelayMillis'

You could try adding 100ms sleep between connecting and sending

    $connection->connect();
    usleep(100_000); // wait 100ms after connecting and before sending the packet
    $binaryData = $connection->sendAndReceive($packet);
zeConehead commented 1 year ago

Already tried that as well. Even between sending and receiving and delays up to 10 seconds. No luck :/