Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Packet::isCompleteLengthRTU to differentiate fix sized response p… #129

Merged
merged 1 commit into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
38 changes: 29 additions & 9 deletions src/Utils/Packet.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use ModbusTcpClient\Network\IOException;
use ModbusTcpClient\Packet\ErrorResponse;
use ModbusTcpClient\Packet\ModbusPacket;

final class Packet
{
Expand Down Expand Up @@ -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
Expand All @@ -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');
}
Expand Down