diff --git a/CHANGELOG.md b/CHANGELOG.md index f20a43b..22b3a83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +# [3.1.1] - 2023-01-25 + +* Fixed `Packet::isCompleteLengthRTU()` to differentiate fix sized function code responses from variable sized responses. + # [3.1.0] - 2022-10-16 * Added `Packet::isCompleteLengthRTU()` to help checking if packet is complete RTU packet. Helps when receiving fragmented packets. diff --git a/src/Utils/Packet.php b/src/Utils/Packet.php index b7f2ce5..3493388 100644 --- a/src/Utils/Packet.php +++ b/src/Utils/Packet.php @@ -6,6 +6,7 @@ use ModbusTcpClient\Network\IOException; use ModbusTcpClient\Packet\ErrorResponse; +use ModbusTcpClient\Packet\ModbusPacket; final class Packet { @@ -39,8 +40,8 @@ public static function isCompleteLength(string|null $binaryData): bool } /** - * isCompleteLengthRTU checks if binary string is complete modbus RTU packet - * NB: this function works only for MODBUS RTU packets + * isCompleteLengthRTU checks if binary string is complete modbus RTU response packet + * NB: this function works only for MODBUS RTU response packets * * @param string|null $binaryData binary string to be checked * @return bool true if data is actual error packet @@ -52,17 +53,36 @@ public static function isCompleteLengthRTU(string|null $binaryData): bool if ($length < 5) { return false; } - if ((ord($binaryData[1]) & ErrorResponse::EXCEPTION_BITMASK) > 0) { // seems to be error response + $functionCode = ord($binaryData[1]); + if (($functionCode & ErrorResponse::EXCEPTION_BITMASK) > 0) { // seems to be error response return true; } - // if it is not error response then 3rd byte contains data length in bytes + switch ($functionCode) { + case ModbusPacket::READ_COILS: // + case ModbusPacket::READ_INPUT_DISCRETES: // + case ModbusPacket::READ_HOLDING_REGISTERS: // + case ModbusPacket::READ_INPUT_REGISTERS: // + case ModbusPacket::READ_WRITE_MULTIPLE_REGISTERS: // + // if it is not error response then 3rd byte contains data length in bytes - // trailing 3 bytes are = unit id + function code + data length in bytes - // next is N bytes of data that should match 3rd byte value - // and 2 bytes for CRC - // so adding these number is what complete packet would be - $expectedLength = 3 + ord($binaryData[2]) + 2; + // trailing 3 bytes are = unit id (1) + function code (1) + data length in bytes (1) + (N) + // next is N bytes of data that should match 3rd byte value + $responseBytesLen = 3 + ord($binaryData[2]); + break; + case ModbusPacket::WRITE_SINGLE_COIL: // unit id (1) + function code (1) + start address (2) + coil data (2) + case ModbusPacket::WRITE_SINGLE_REGISTER: // unit id (1) + function code (1) + start address (2) + register data (2) + case ModbusPacket::WRITE_MULTIPLE_COILS: // unit id (1) + function code (1) + start address (2) + count of coils written (2) + case ModbusPacket::WRITE_MULTIPLE_REGISTERS: // unit id (1) + function code (1) + start address (2) + count of registers written (2) + $responseBytesLen = 6; + break; + case ModbusPacket::MASK_WRITE_REGISTER: + $responseBytesLen = 8; // unit id (1) + function code (1) + start address (2) + AND mask (2) + OR mask (2) + break; + default: + throw new IOException('can not determine complete length for unsupported modbus function code'); + } + $expectedLength = $responseBytesLen + 2; // and 2 bytes for CRC if ($length > $expectedLength) { throw new IOException('packet length more bytes than expected'); }