From f6818a21c49ac68ff24202fc5e85e836c880c9e4 Mon Sep 17 00:00:00 2001 From: toimtoimtoim Date: Sun, 27 Nov 2016 22:01:51 +0200 Subject: [PATCH] let modbus add send packet to status message, clean a little WriteMultipleCoilsPacket methods --- src/ModbusMaster.php | 5 +- src/Packet/WriteMultipleCoilsPacket.php | 87 ++++++++++--------- .../Fc15WriteMultipleCoilsTest.php | 20 +++++ 3 files changed, 70 insertions(+), 42 deletions(-) diff --git a/src/ModbusMaster.php b/src/ModbusMaster.php index a4349ff..e35b586 100644 --- a/src/ModbusMaster.php +++ b/src/ModbusMaster.php @@ -179,7 +179,7 @@ class ModbusMaster */ private function printPacket($packet) { - return 'Packet: ' . unpack('H*', $packet)[1] . "\n"; + return 'packet: ' . unpack('H*', $packet)[1] . "\n"; } /** @@ -480,11 +480,12 @@ class ModbusMaster $socket->connect(); $packet = $buildRequest(); + $this->status .= 'Sending ' . $this->printPacket($packet); $socket->send($packet); $data = $socket->receive(); - $this->status .= $this->printPacket($data); + $this->status .= 'Received ' . $this->printPacket($data); $this->validateResponseCode($data); return $parseResponse($data); diff --git a/src/Packet/WriteMultipleCoilsPacket.php b/src/Packet/WriteMultipleCoilsPacket.php index 5851bf5..fd5dab0 100644 --- a/src/Packet/WriteMultipleCoilsPacket.php +++ b/src/Packet/WriteMultipleCoilsPacket.php @@ -17,6 +17,9 @@ namespace PHPModbus\Packet; use PHPModbus\IecType; +/** + * Builds/parses packet in Modbus TCP/IP format (http://www.simplymodbus.ca/TCP.htm) + */ class WriteMultipleCoilsPacket { /** @@ -30,49 +33,25 @@ class WriteMultipleCoilsPacket public static function build($unitId, $reference, array $data) { $dataLen = 0; - // build bool stream to the WORD array - $data_word_stream = array(); - $data_word = 0; - $shift = 0; - for ($i = 0, $len = count($data); $i < $len; $i++) { - if ((($i % 8) === 0) && ($i > 0)) { - $data_word_stream[] = $data_word; - $shift = 0; - $data_word = 0; - $data_word |= (0x01 && $data[$i]) << $shift; - $shift++; - } else { - $data_word |= (0x01 && $data[$i]) << $shift; - $shift++; - } - } - $data_word_stream[] = $data_word; - // show binary stream to status string -// foreach ($data_word_stream as $d) { -// $this->status .= sprintf("byte=b%08b\n", $d); -// } - // build data section - $buffer1 = ''; - foreach ($data_word_stream as $key => $dataitem) { - $buffer1 .= IecType::iecBYTE($dataitem); // register values x - $dataLen += 1; - } + + list($pduData, $wordCount) = self::getDataConvertedToIecWords($data); + $dataLen += $wordCount; + // build body - $buffer2 = ''; - $buffer2 .= IecType::iecBYTE(15); // FC 15 = 15(0x0f) - $buffer2 .= IecType::iecINT($reference); // refnumber = 12288 - $buffer2 .= IecType::iecINT(count($data)); // bit count - $buffer2 .= IecType::iecBYTE((count($data) + 7) / 8); // byte count + $pduHeader = ''; + $pduHeader .= IecType::iecBYTE(15); // FC 15 = 15(0x0f) + $pduHeader .= IecType::iecINT($reference); // refnumber = 12288 + $pduHeader .= IecType::iecINT(count($data)); // bit count + $pduHeader .= IecType::iecBYTE((count($data) + 7) / 8); // byte count $dataLen += 6; - // build header - $buffer3 = ''; - $buffer3 .= IecType::iecINT(mt_rand(0, 65000)); // transaction ID - $buffer3 .= IecType::iecINT(0); // protocol ID - $buffer3 .= IecType::iecINT($dataLen + 1); // length - $buffer3 .= IecType::iecBYTE($unitId); // unit ID - // return packet string - return $buffer3 . $buffer2 . $buffer1; + $mbapHeader = ''; + $mbapHeader .= IecType::iecINT(mt_rand(0, 65000)); // transaction ID + $mbapHeader .= IecType::iecINT(0); // protocol ID + $mbapHeader .= IecType::iecINT($dataLen + 1); // length + $mbapHeader .= IecType::iecBYTE($unitId); // unit ID + + return $mbapHeader . $pduHeader . $pduData; } /** @@ -86,4 +65,32 @@ class WriteMultipleCoilsPacket return true; } + /** + * @param array $data + * @return array + */ + private static function getDataConvertedToIecWords(array $data) + { + $data_word_stream = array(); + $data_word = 0; + $shift = 0; + for ($i = 0, $len = count($data); $i < $len; $i++) { + if ((($i % 8) === 0) && ($i > 0)) { + //shift to next word + $data_word_stream[] = $data_word; + $shift = 0; + $data_word = 0; + } + $data_word |= (0x01 && $data[$i]) << $shift; + $shift++; + } + $data_word_stream[] = $data_word; + + $pduData = ''; + foreach ($data_word_stream as $key => $dataitem) { + $pduData .= IecType::iecBYTE($dataitem); + } + return array($pduData, count($data_word_stream)); + } + } \ No newline at end of file diff --git a/tests/ModbusMaster/Fc15WriteMultipleCoilsTest.php b/tests/ModbusMaster/Fc15WriteMultipleCoilsTest.php index bc59828..2d7a178 100644 --- a/tests/ModbusMaster/Fc15WriteMultipleCoilsTest.php +++ b/tests/ModbusMaster/Fc15WriteMultipleCoilsTest.php @@ -18,4 +18,24 @@ class Fc15WriteMultipleCoilsTest extends MockServerTestCase $packetWithoutTransactionId = substr($clientData[0], 4); $this->assertEquals('00000008000f300000030105', $packetWithoutTransactionId); } + + public function testFc15WriteMultipleCoilsWithMultiWordPacket() + { + $mockResponse = 'a51100000006000f00000020'; + $clientData = static::executeWithMockServer($mockResponse, function ($port) { + $modbus = new ModbusMaster('127.0.0.1', 'TCP'); + $modbus->port = $port; + + $this->assertTrue($modbus->fc15(0, 0, + [ + 1, 0, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + ])); + }); + + $packetWithoutTransactionId = substr($clientData[0], 4); + $this->assertEquals('0000000b000f0000002004ed0ff0ff', $packetWithoutTransactionId); + } } \ No newline at end of file