pull/2/merge
Martti T 8 years ago committed by GitHub
commit 37eaa19f4f
  1. 1
      .gitignore
  2. 26
      .travis.yml
  3. 25
      README.md
  4. 19
      composer.json
  5. 4
      examples/README.md
  6. 108
      examples/example_750841_Mmemory.php
  7. 33
      examples/example_datatype.php
  8. 24
      examples/example_fc1.php
  9. 38
      examples/example_fc15.php
  10. 32
      examples/example_fc16.php
  11. 36
      examples/example_fc2.php
  12. 20
      examples/example_fc22.php
  13. 34
      examples/example_fc23.php
  14. 32
      examples/example_fc3.php
  15. 18
      examples/example_fc4.php
  16. 33
      examples/example_fc5.php
  17. 32
      examples/example_fc6.php
  18. 23
      phpunit.xml
  19. 213
      src/IecType.php
  20. 10
      src/ModbusException.php
  21. 2121
      src/ModbusMaster.php
  22. 50
      src/ModbusMasterTcp.php
  23. 50
      src/ModbusMasterUdp.php
  24. 12
      src/Network/IOException.php
  25. 212
      src/Network/ModbusConnection.php
  26. 111
      src/Network/ModbusConnectionBuilder.php
  27. 120
      src/Network/ModbusConnectionProperties.php
  28. 65
      src/Packet/MaskWriteRegisterPacket.php
  29. 88
      src/Packet/ReadCoilsPacket.php
  30. 64
      src/Packet/ReadInputDiscretesPacket.php
  31. 68
      src/Packet/ReadMultipleInputRegistersPacket.php
  32. 67
      src/Packet/ReadMultipleRegistersPacket.php
  33. 102
      src/Packet/ReadWriteRegistersPacket.php
  34. 96
      src/Packet/WriteMultipleCoilsPacket.php
  35. 85
      src/Packet/WriteMultipleRegisterPacket.php
  36. 70
      src/Packet/WriteSingleCoilPacket.php
  37. 68
      src/Packet/WriteSingleRegisterPacket.php
  38. 452
      src/PhpType.php
  39. 256
      tests/Codesys/TEST.EXP
  40. 15
      tests/Codesys/_make_exp.cmd
  41. BIN
      tests/Codesys/test.pro
  42. 18
      tests/IecType/IecByteTest.php
  43. 31
      tests/IecType/IecDIntTest.php
  44. 22
      tests/IecType/IecIntTest.php
  45. 31
      tests/IecType/IecRealTest.php
  46. 11
      tests/IecType/_test.bat
  47. 1
      tests/IecType/ref/test.iecByte.php.html
  48. 1
      tests/IecType/ref/test.iecDInt.php.html
  49. 1
      tests/IecType/ref/test.iecInt.php.html
  50. 12
      tests/IecType/ref/test.iecReal.php.html
  51. 33
      tests/IecType/test.iecByte.php
  52. 50
      tests/IecType/test.iecDInt.php
  53. 50
      tests/IecType/test.iecInt.php
  54. 50
      tests/IecType/test.iecReal.php
  55. 25
      tests/ModbusMaster/BindClientToLocalIpAndPortTest.php
  56. 41
      tests/ModbusMaster/Fc15WriteMultipleCoilsTest.php
  57. 21
      tests/ModbusMaster/Fc16WriteMultipleRegistersTest.php
  58. 35
      tests/ModbusMaster/Fc1ReadCoilsTest.php
  59. 26
      tests/ModbusMaster/Fc22MaskWriteRegisterTest.php
  60. 24
      tests/ModbusMaster/Fc23ReadWriteRegistersTest.php
  61. 35
      tests/ModbusMaster/Fc2ReadInputDiscretesTest.php
  62. 35
      tests/ModbusMaster/Fc3ReadMultipleRegistersTest.php
  63. 35
      tests/ModbusMaster/Fc4ReadMultipleInputRegistersTest.php
  64. 35
      tests/ModbusMaster/Fc5WriteSingleCoilTest.php
  65. 35
      tests/ModbusMaster/Fc6WriteSingleRegisterTest.php
  66. 86
      tests/ModbusMaster/MockResponseServer.php
  67. 37
      tests/ModbusMaster/MockServerTestCase.php
  68. 67
      tests/ModbusMaster/ModbusExceptionTest.php
  69. 23
      tests/ModbusMaster/UdpFc1ReadCoilsTest.php
  70. 11
      tests/ModbusMaster/_test.bat
  71. 72
      tests/ModbusMaster/ref/test.tcp.fc16fc3.php.html
  72. 1
      tests/ModbusMaster/ref/test.tcp.fc26.php.html
  73. 1
      tests/ModbusMaster/ref/test.tcp.socket_protocol_mismatch.php.html
  74. 72
      tests/ModbusMaster/ref/test.udp.fc16fc3.php.html
  75. 1
      tests/ModbusMaster/ref/test.udp.fc26.php.html
  76. 44
      tests/ModbusMaster/test.tcp.fc16fc3.php
  77. 24
      tests/ModbusMaster/test.tcp.fc26.php
  78. 22
      tests/ModbusMaster/test.tcp.socket_protocol_mismatch.php
  79. 44
      tests/ModbusMaster/test.udp.fc16fc3.php
  80. 24
      tests/ModbusMaster/test.udp.fc26.php
  81. 11
      tests/ModbusMasterTcp/_test.bat
  82. 72
      tests/ModbusMasterTcp/ref/test.fc16fc3.php.html
  83. 1
      tests/ModbusMasterTcp/ref/test.fc26.php.html
  84. 44
      tests/ModbusMasterTcp/test.fc16fc3.php
  85. 24
      tests/ModbusMasterTcp/test.fc26.php
  86. 11
      tests/ModbusMasterUdp/_test.bat
  87. 66
      tests/ModbusMasterUdp/ref/test.fc15fc1.php.html
  88. 72
      tests/ModbusMasterUdp/ref/test.fc16fc3.php.html
  89. 72
      tests/ModbusMasterUdp/ref/test.fc16fc3bind.php.html
  90. 7
      tests/ModbusMasterUdp/ref/test.fc2.php.html
  91. 1
      tests/ModbusMasterUdp/ref/test.fc26.php.html
  92. 1
      tests/ModbusMasterUdp/ref/test.fc26bind.php.html
  93. 11
      tests/ModbusMasterUdp/ref/test.fc4.php.html
  94. 5
      tests/ModbusMasterUdp/ref/test.fc5.php.html
  95. 5
      tests/ModbusMasterUdp/ref/test.fc6fc3.php.html
  96. 22
      tests/ModbusMasterUdp/test.fc15fc1.php
  97. 44
      tests/ModbusMasterUdp/test.fc16fc3.php
  98. 45
      tests/ModbusMasterUdp/test.fc16fc3bind.php
  99. 14
      tests/ModbusMasterUdp/test.fc2.php
  100. 24
      tests/ModbusMasterUdp/test.fc26.php
  101. Some files were not shown because too many files have changed in this diff Show More

1
.gitignore vendored

@ -3,3 +3,4 @@ composer.lock
.idea/ .idea/
*~ *~
/report

@ -0,0 +1,26 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
- 5.4
- 5.5
- 5.6
- 7.0
branches:
only:
- /^dev-.*$/
before_install:
- travis_retry composer self-update
install:
- travis_retry composer update --no-interaction
script:
- composer test-ci

@ -2,11 +2,13 @@
Implementation of the basic functionality of the Modbus TCP and UDP based protocol using PHP. Implementation of the basic functionality of the Modbus TCP and UDP based protocol using PHP.
**NOTE: This is a fork to fix & update the library code (and code alone). Notably, the tests are probably all broken.** **NOTE: This is a fork to fix & update the library code.**
> **What's new** ##What's new
>
> This fork adds a namespace and fixes issues encountered when porting to PHP 7 * This fork adds a namespace and fixes issues encountered when porting to PHP 7
* Removes dependency to [sockets extension](http://www.php.net/manual/en/book.sockets.php). Now uses built-in [Stream API](http://www.php.net/manual/en/function.stream-socket-client.php)
* Fixes/replaces old MS Windows specific tests
## Implemented features ## Implemented features
@ -25,7 +27,7 @@ Implementation of the basic functionality of the Modbus TCP and UDP based protoc
## Requirements ## Requirements
* The PHP extension php_sockets.dll should be enabled (server php.ini file) * PHP 5.5+
## Example ## Example
@ -53,6 +55,19 @@ Use the `setTimeout($seconds)` and `setSocketTimeout($read_timeout_sec, $write_t
Most of the code is (to some extent) commented and documented with PhpDoc. You should get useful tooltips in your IDE. Most of the code is (to some extent) commented and documented with PhpDoc. You should get useful tooltips in your IDE.
## Tests
To run the test suite, you need install the dependencies via composer, then
run PHPUnit.
NB: PHP 5.6+ is required for tests
composer install
composer test
To report test coverage (created inside ./report/html):
composer test-coverage
## GoogleCode legacy docs & downloads ## GoogleCode legacy docs & downloads

@ -4,8 +4,13 @@
"description": "PhpModbus with namespaces and updated to PHP 7", "description": "PhpModbus with namespaces and updated to PHP 7",
"license": "LGPL", "license": "LGPL",
"require": { "require": {
"php": "^5.3.2 || ^7.0", "php": "^5.4 || ^7.0"
"ext-sockets": "*" },
"require-dev": {
"react/socket": "~0.4.0",
"react/child-process": "^0.4.1",
"react/datagram": "^1.1",
"phpunit/phpunit": "^5.6"
}, },
"authors": [ "authors": [
{ {
@ -21,5 +26,15 @@
"psr-4": { "psr-4": {
"PHPModbus\\": "src/" "PHPModbus\\": "src/"
} }
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-ci": "vendor/bin/phpunit --coverage-clover report/coverage.xml",
"test-coverage": "vendor/bin/phpunit --coverage-html report/html"
} }
} }

@ -1,3 +1 @@
Those examples have not been updated to use namespaces. Do not expose this folder publicly as you read/write modbus data with query parameters
They may not work. Just for reference.

@ -1,65 +1,75 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMaster;
use PHPModbus\PhpType;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$ip = "192.192.15.51"; $unitId = ((int)$_GET['unitid']) ?: 0;
$modbus = new ModbusMaster($ip, "UDP"); $reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 6;
$modbus = new ModbusMaster($ip, 'UDP');
try { try {
// FC 3 // FC 3
$moduleId = 0; $recData = $modbus->readMultipleRegisters($unitId, $reference, $quantity);
$reference = 12288;
$mw0address = 12288;
$quantity = 6;
$recData = $modbus->readMultipleRegisters($moduleId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
?> ?>
<html> <html>
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=windows-1250"> <meta http-equiv="content-type" content="text/html; charset=windows-1250">
<meta name="generator" content="PSPad editor, www.pspad.com"> <meta name="generator" content="PSPad editor, www.pspad.com">
<title>WAGO 750-841 M-memory dump</title> <title>WAGO 750-841 M-memory dump</title>
</head> </head>
<body> <body>
<h1>Dump of M-memory from WAGO 750-84x series coupler.</h1> <h1>Dump of M-memory from WAGO 750-84x series coupler.</h1>
<ul> <ul>
<li>PLC: 750-84x series</li> <li>PLC: 750-84x series</li>
<li>IP: <?php echo $ip ?></li> <li>IP: <?php echo $ip ?></li>
<li>Modbus module ID: <?php echo $moduleId ?></li> <li>Modbus module ID: <?php echo $unitId ?></li>
<li>Modbus memory reference: <?php echo $reference ?></li> <li>Modbus memory reference: <?php echo $reference ?></li>
<li>Modbus memory quantity: <?php echo $quantity ?></li> <li>Modbus memory quantity: <?php echo $quantity ?></li>
</ul> </ul>
<h2>M-memory dump</h2>
<table border="1px" width="400px"> <h2>M-memory dump</h2>
<tr>
<td>Modbus address</td>
<td>MWx</td>
<td>value</td>
</tr>
<?php
for ($i = 0; $i < count($recData); $i += 2) {
?>
<tr>
<td><?php echo $i + $reference ?></td>
<td>MW<?php echo ($i + $reference - $mw0address) / 2 ?></td>
<td><?php echo ($recData[$i] << 8) + $recData[$i + 1] ?></td>
</tr>
<?php
}
?>
</table>
<h2>Modbus class status</h2> <table border="1px">
<tr>
<td>WORD address</td>
<td>Int16</td>
<td>UInt16</td>
<td>high byte</td>
<td>low byte</td>
<td>high bits</td>
<td>low bits</td>
</tr>
<?php
for ($i = 0, $max = count($recData); $i < $max; $i += 2) {
?>
<tr>
<td><?php echo $reference+($i/2) ?></td>
<td><?php echo PhpType::bytes2signedInt([$recData[$i], $recData[$i+1]]) ?></td>
<td><?php echo PhpType::bytes2unsignedInt([$recData[$i], $recData[$i+1]]) ?></td>
<td><?php echo $recData[$i] ?></td>
<td><?php echo $recData[$i + 1] ?></td>
<td><?php echo sprintf("%08d", decbin($recData[$i])) ?></td>
<td><?php echo sprintf("%08d", decbin($recData[$i + 1])) ?></td>
</tr>
<?php
}
?>
</table>
<pre><?= $modbus ?></pre> <h2>Modbus class status</h2>
</body> <pre><?= $modbus ?></pre>
<h2>Data</h2>
<pre><?= print_r($recData); ?></pre>
</body>
</html> </html>

@ -1,20 +1,25 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMaster;
use PHPModbus\PhpType; use PHPModbus\PhpType;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 10;
$modbus = new ModbusMaster($ip, 'UDP');
try { try {
// FC 3 // FC 3
// read 10 words (20 bytes) from device ID=0, address=12288 // read 10 words (20 bytes) from device ID=0, address=12288
$recData = $modbus->readMultipleRegisters(0, 12288, 10); $recData = $modbus->readMultipleRegisters($unitId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Received data // Received data
@ -29,17 +34,17 @@ $values = array_chunk($recData, 4);
// Get float from REAL interpretation // Get float from REAL interpretation
echo "<h3>REAL to Float</h3>\n"; echo "<h3>REAL to Float</h3>\n";
foreach ($values as $bytes) foreach ($values as $bytes)
echo PhpType::bytes2float($bytes) . "</br>"; echo PhpType::bytes2float($bytes) . "</br>";
// Get integer from DINT interpretation // Get integer from DINT interpretation
echo "<h3>DINT to integer </h3>\n"; echo "<h3>DINT to integer </h3>\n";
foreach ($values as $bytes) foreach ($values as $bytes)
echo PhpType::bytes2signedInt($bytes) . "</br>"; echo PhpType::bytes2signedInt($bytes) . "</br>";
// Get integer of float from DINT interpretation // Get integer of float from DINT interpretation
echo "<h3>DWORD to integer (or float) </h3>\n"; echo "<h3>DWORD to integer (or float) </h3>\n";
foreach ($values as $bytes) foreach ($values as $bytes)
echo PhpType::bytes2unsignedInt($bytes) . "</br>"; echo PhpType::bytes2unsignedInt($bytes) . "</br>";
echo "<h2>16 bit types</h2>\n"; echo "<h2>16 bit types</h2>\n";
// Chunk the data array to set of 4 bytes // Chunk the data array to set of 4 bytes
@ -48,12 +53,12 @@ $values = array_chunk($recData, 2);
// Get signed integer from INT interpretation // Get signed integer from INT interpretation
echo "<h3>INT to integer </h3>\n"; echo "<h3>INT to integer </h3>\n";
foreach ($values as $bytes) foreach ($values as $bytes)
echo PhpType::bytes2signedInt($bytes) . "</br>"; echo PhpType::bytes2signedInt($bytes) . "</br>";
// Get unsigned integer from WORD interpretation // Get unsigned integer from WORD interpretation
echo "<h3>WORD to integer </h3>\n"; echo "<h3>WORD to integer </h3>\n";
foreach ($values as $bytes) foreach ($values as $bytes)
echo PhpType::bytes2unsignedInt($bytes) . "</br>"; echo PhpType::bytes2unsignedInt($bytes) . "</br>";
// Get string from STRING interpretation // Get string from STRING interpretation
echo "<h3>STRING to string </h3>\n"; echo "<h3>STRING to string </h3>\n";

@ -1,13 +1,18 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMaster;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 12;
$modbus = new ModbusMaster($ip, 'UDP');
try { try {
// FC 1 // FC 1
$recData = $modbus->readCoils(0, 12288, 12); $recData = $modbus->readCoils($unitId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
@ -15,10 +20,9 @@ try {
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo "</br>Status:</br>" . $modbus; print_r($modbus);
echo '</pre>';
// Print read data echo '<h1>Data</h1><pre>';
echo "</br>Data:</br>"; print_r($recData);
var_dump($recData); echo '</pre>';
echo "</br>";

@ -1,27 +1,35 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 0;
$modbus = new ModbusMasterUdp($ip);
// Data to be written - supports both 0/1 and booleans (true, false) // Data to be written - supports both 0/1 and booleans (true, false)
$data = array( $data = array(
1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
); );
try { try {
// FC15 // FC15
$modbus->writeMultipleCoils(0, 12288, $data); $recData = $modbus->writeMultipleCoils($unitId, $reference, $data);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo $modbus; print_r($modbus);
echo '</pre>';
echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -1,25 +1,31 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$modbus = new ModbusMasterUdp($ip);
// Data to be writen // Data to be writen
$data = array(10, -1000, 2000, 3.0); $data = array(10, -1000, 2000, 3.0);
$dataTypes = array("WORD", "INT", "DINT", "REAL"); $dataTypes = array("WORD", "INT", "DINT", "REAL");
try { try {
// FC16 // FC16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes); $recData = $modbus->writeMultipleRegister($unitId, $reference, $data, $dataTypes);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo $modbus; print_r($modbus);
echo '</pre>';
?> echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -1,25 +1,29 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMaster;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 0;
$quantity = ((int)$_GET['quantity']) ?: 2;
$modbus = new ModbusMaster($ip, 'UDP');
try { try {
// FC 2 // FC 2
// read 2 input bits from address 0x0 (Wago input image) // read 2 input bits from address 0x0 (Wago input image)
$recData = $modbus->readInputDiscretes(0, 0, 2); $recData = $modbus->readInputDiscretes($unitId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo "</br>Status:</br>" . $modbus; print_r($modbus);
echo '</pre>';
// Print read data echo '<h1>Data</h1><pre>';
echo "</br>Data:</br>"; print_r($recData);
var_dump($recData); echo '</pre>';
echo "</br>";

@ -1,9 +1,13 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$modbus = new ModbusMasterUdp($ip);
// Data to be writen // Data to be writen
$bitValue = true; $bitValue = true;
@ -13,7 +17,7 @@ $orMask = 0x0000 ^ (pow(2, $bitNumber) * $bitValue);
try { try {
// FC22 // FC22
$modbus->maskWriteRegister(0, 12288, $andMask, $orMask); $recData = $modbus->maskWriteRegister($unitId, $reference, $andMask, $orMask);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
@ -21,5 +25,9 @@ try {
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo $modbus; print_r($modbus);
echo '</pre>';
echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -1,28 +1,32 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 6;
$modbus = new ModbusMasterUdp($ip);
// Data to be writen // Data to be writen
$data = array(10, -1000, 2000, 3.0); $data = array(10, -1000, 2000, 3.0);
$dataTypes = array("WORD", "INT", "DINT", "REAL"); $dataTypes = array("WORD", "INT", "DINT", "REAL");
try { try {
// FC23 // FC23
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes); $recData = $modbus->readWriteRegisters($unitId, $reference, $quantity, $reference, $data, $dataTypes);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo "</br>Status:</br>" . $modbus; print_r($modbus);
echo '</pre>';
// Print read data echo '<h1>Data</h1><pre>';
echo "</br>Data:</br>";
print_r($recData); print_r($recData);
echo "</br>"; echo '</pre>';

@ -1,24 +1,28 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMaster;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 6;
$modbus = new ModbusMaster($ip, 'UDP');
try { try {
// FC 3 // FC 3
$recData = $modbus->readMultipleRegisters(0, 12288, 6); $recData = $modbus->readMultipleRegisters($unitId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo "</br>Status:</br>" . $modbus; print_r($modbus);
echo '</pre>';
// Print read data echo '<h1>Data</h1><pre>';
echo "</br>Data:</br>";
print_r($recData); print_r($recData);
echo "</br>"; echo '</pre>';

@ -1,13 +1,18 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMasterUdp; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMasterUdp("192.192.15.51"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 0;
$quantity = ((int)$_GET['quantity']) ?: 2;
$modbus = new ModbusMasterUdp($ip);
try { try {
// Read input discretes - FC 4 // Read input discretes - FC 4
$recData = $modbus->readMultipleInputRegisters(0, 0, 2); $recData = $modbus->readMultipleInputRegisters($unitId, $reference, $quantity);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
@ -15,4 +20,9 @@ try {
exit; exit;
} }
var_dump($recData); echo '<h1>Status</h1><pre>';
print_r($modbus);
echo '</pre>';
echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -1,23 +1,28 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMasterUdp; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMasterUdp("192.192.15.51"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$value = ((bool)$_GET['value']) ?: false;
// Data to be writen - TRUE, FALSE $modbus = new ModbusMasterUdp($ip);
$data_true = array(true);
$data_false = array(false);
try { try {
// Write single coil - FC5 // Write single coil - FC5
$modbus->writeSingleCoil(0, 12288, $data_true); $recData = $modbus->writeSingleCoil($unitId, $reference, [$value]);
$modbus->writeSingleCoil(0, 12289, $data_false);
$modbus->writeSingleCoil(0, 12290, $data_true);
$modbus->writeSingleCoil(0, 12291, $data_false);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
echo '<h1>Status</h1><pre>';
print_r($modbus);
echo '</pre>';
echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -1,22 +1,28 @@
<?php <?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster; use PHPModbus\ModbusMasterUdp;
// Create Modbus object $ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$modbus = new ModbusMaster("192.192.15.51", "UDP"); $unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$value = ((int)$_GET['value']) ?: -1000;
// Data to be writen $modbus = new ModbusMasterUdp($ip);
$data = array(-1000);
try { try {
// FC6 // FC6
$modbus->writeSingleRegister(0, 12288, $data); $recData = $modbus->writeSingleRegister($unitId, $reference, [$value]);
} catch (Exception $e) { } catch (Exception $e) {
// Print error information if any // Print error information if any
echo $modbus; echo $modbus;
echo $e; echo $e;
exit; exit;
} }
// Print status information echo '<h1>Status</h1><pre>';
echo $modbus; print_r($modbus);
echo '</pre>';
echo '<h1>Data</h1><pre>';
print_r($recData);
echo '</pre>';

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/5.6/phpunit.xsd"
bootstrap="./vendor/autoload.php"
backupGlobals="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutTodoAnnotatedTests="true"
timeoutForSmallTests="2"
timeoutForMediumTests="10"
timeoutForLargeTests="60"
verbose="true">
<testsuite>
<directory suffix="Test.php">./tests/*</directory>
</testsuite>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

@ -22,120 +22,117 @@ namespace PHPModbus;
* The class includes set of IEC-1131 data type functions that converts a PHP * The class includes set of IEC-1131 data type functions that converts a PHP
* data types to a IEC data type. * data types to a IEC data type.
* *
* @author Jan Krakora * @author Jan Krakora
* @copyright Copyright (c) 2004, 2010 Jan Krakora * @copyright Copyright (c) 2004, 2010 Jan Krakora
* @package Phpmodbus * @package Phpmodbus
*/ */
class IecType class IecType
{ {
/** /**
* iecBYTE * iecINT
* *
* Converts a value to IEC-1131 BYTE data type * Converts a value to IEC-1131 INT data type
* *
* @param int $value from 0 to 255 * @param int $value to be converted
* @return int IEC BYTE data type * @return int IEC-1131 INT data type
* */
*/ public static function iecINT($value)
public static function iecBYTE($value) {
{ return self::iecBYTE(($value >> 8) & 0x00FF) .
return chr($value & 0xFF); self::iecBYTE($value & 0x00FF);
} }
/** /**
* iecINT * iecBYTE
* *
* Converts a value to IEC-1131 INT data type * Converts a value to IEC-1131 BYTE data type
* *
* @param int $value to be converted * @param int $value from 0 to 255
* @return int IEC-1131 INT data type * @return int IEC BYTE data type
* */
*/ public static function iecBYTE($value)
public static function iecINT($value) {
{ return chr($value & 0xFF);
return self::iecBYTE(($value >> 8) & 0x00FF) . }
self::iecBYTE(($value & 0x00FF));
}
/** /**
* iecDINT * iecDINT
* *
* Converts a value to IEC-1131 DINT data type * Converts a value to IEC-1131 DINT data type
* *
* @param int $value to be converted * @param int $value to be converted
* @param int $bigEndian defines endian codding (little endian == 0, big endian == 1) * @param int $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @return int IEC-1131 INT data type * @return int IEC-1131 INT data type
* */
*/ public static function iecDINT($value, $bigEndian = 0)
public static function iecDINT($value, $bigEndian = 0) {
{ // result with right endianness
// result with right endianness return self::endianness($value, $bigEndian);
return self::endianness($value, $bigEndian); }
}
/** /**
* iecREAL * endianness
* *
* Converts a value to IEC-1131 REAL data type. The function uses function @use float2iecReal. * Make endianess as required.
* * For more see http://en.wikipedia.org/wiki/Endianness
* @param int $value to be converted *
* @param bool $bigEndian defines endian codding (little endian == 0, big endian == 1) * @param int $value
* @return int IEC-1131 REAL data type * @param bool $bigEndian
*/ * @return int
public static function iecREAL($value, $bigEndian = 0) */
{ private static function endianness($value, $bigEndian = 0)
// iecREAL representation {
$real = self::float2iecReal($value); if ($bigEndian == 0) {
// result with right endianness return
return self::endianness($real, $bigEndian); self::iecBYTE(($value >> 8) & 0x000000FF) .
} self::iecBYTE($value & 0x000000FF) .
self::iecBYTE(($value >> 24) & 0x000000FF) .
self::iecBYTE(($value >> 16) & 0x000000FF);
} else {
return
self::iecBYTE(($value >> 24) & 0x000000FF) .
self::iecBYTE(($value >> 16) & 0x000000FF) .
self::iecBYTE(($value >> 8) & 0x000000FF) .
self::iecBYTE($value & 0x000000FF);
}
}
/** /**
* float2iecReal * iecREAL
* *
* This function converts float value to IEC-1131 REAL single precision form. * Converts a value to IEC-1131 REAL data type. The function uses function @use float2iecReal.
* *
* For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or * @param int $value to be converted
* [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes * @param bool $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @ Georgia Tech 21-Nov-2007] or * @return int IEC-1131 REAL data type
* [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}] */
* public static function iecREAL($value, $bigEndian = 0)
* @param float $value to be converted {
* @return int IEC REAL data type // iecREAL representation
*/ $real = self::float2iecReal($value);
private static function float2iecReal($value) // result with right endianness
{ return self::endianness($real, $bigEndian);
// get float binary string }
$float = pack("f", $value);
// set 32-bit unsigned integer of the float
$w = unpack("L", $float);
return $w[1];
}
/** /**
* endianness * float2iecReal
* *
* Make endianess as required. * This function converts float value to IEC-1131 REAL single precision form.
* For more see http://en.wikipedia.org/wiki/Endianness *
* * For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or
* @param int $value * [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes
* @param bool $bigEndian * @ Georgia Tech 21-Nov-2007] or
* @return int * [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}]
*/ *
private static function endianness($value, $bigEndian = 0) * @param float $value to be converted
{ * @return int IEC REAL data type
if ($bigEndian == 0) { */
return private static function float2iecReal($value)
self::iecBYTE(($value >> 8) & 0x000000FF) . {
self::iecBYTE(($value & 0x000000FF)) . // get float binary string
self::iecBYTE(($value >> 24) & 0x000000FF) . $float = pack('f', $value);
self::iecBYTE(($value >> 16) & 0x000000FF); // set 32-bit unsigned integer of the float
} else { $w = unpack('L', $float);
return return $w[1];
self::iecBYTE(($value >> 24) & 0x000000FF) . }
self::iecBYTE(($value >> 16) & 0x000000FF) .
self::iecBYTE(($value >> 8) & 0x000000FF) .
self::iecBYTE(($value & 0x000000FF));
}
}
} }

@ -0,0 +1,10 @@
<?php
namespace PHPModbus;
/**
* Exception class thrown when a server sends packet with error code. Something is wrong with request.
*/
class ModbusException extends \RuntimeException
{
}

File diff suppressed because it is too large Load Diff

@ -8,14 +8,12 @@ namespace PHPModbus;
* This source file is subject to the "PhpModbus license" that is bundled * This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt. * with this package in the file license.txt.
* *
* * @copyright Copyright (c) 2004, 2012 Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora * @license PhpModbus license
* @license PhpModbus license * @category Phpmodbus
* @category Phpmodbus * @tutorial Phpmodbus.pkg
* @tutorial Phpmodbus.pkg * @package Phpmodbus
* @package Phpmodbus * @version $id$
* @version $id$
*
*/ */
/** /**
@ -23,29 +21,21 @@ namespace PHPModbus;
* *
* This class deals with the MODBUS master using TCP. Extends ModbusMaster class. * This class deals with the MODBUS master using TCP. Extends ModbusMaster class.
* *
* Implemented MODBUS functions: * @author Jan Krakora
* - FC 1: read coils * @copyright Copyright (c) 2004, 2012 Jan Krakora
* - FC 3: read multiple registers * @package Phpmodbus
* - FC 15: write multiple coils
* - FC 16: write multiple registers
* - FC 23: read write registers
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*
*/ */
class ModbusMasterTcp extends ModbusMaster class ModbusMasterTcp extends ModbusMaster
{ {
/** /**
* ModbusMasterTcp * ModbusMasterTcp
* *
* This is the constructor that defines {@link $host} IP address of the object. * This is the constructor that defines {@link $host} IP address of the object.
* *
* @param String $host An IP address of a Modbus TCP device. E.g. "192.168.1.1". * @param String $host An IP address of a Modbus TCP device. E.g. "192.168.1.1".
*/ */
public function __construct($host) public function __construct($host)
{ {
parent::__construct($host, "TCP"); parent::__construct($host, "TCP");
} }
} }

@ -8,14 +8,12 @@ namespace PHPModbus;
* This source file is subject to the "PhpModbus license" that is bundled * This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt. * with this package in the file license.txt.
* *
* * @copyright Copyright (c) 2004, 2012 Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora * @license PhpModbus license
* @license PhpModbus license * @category Phpmodbus
* @category Phpmodbus * @tutorial Phpmodbus.pkg
* @tutorial Phpmodbus.pkg * @package Phpmodbus
* @package Phpmodbus * @version $id$
* @version $id$
*
*/ */
/** /**
@ -23,29 +21,21 @@ namespace PHPModbus;
* *
* This class deals with the MODBUS master using UDP stack. * This class deals with the MODBUS master using UDP stack.
* *
* Implemented MODBUS master functions: * @author Jan Krakora
* - FC 1: read coils * @copyright Copyright (c) 2004, 2012 Jan Krakora
* - FC 3: read multiple registers * @package Phpmodbus
* - FC 15: write multiple coils
* - FC 16: write multiple registers
* - FC 23: read write registers
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*
*/ */
class ModbusMasterUdp extends ModbusMaster class ModbusMasterUdp extends ModbusMaster
{ {
/** /**
* ModbusMasterUdp * ModbusMasterUdp
* *
* This is the constructor that defines {@link $host} IP address of the object. * This is the constructor that defines {@link $host} IP address of the object.
* *
* @param String $host An IP address of a Modbus UDP device. E.g. "192.168.1.1". * @param String $host An IP address of a Modbus UDP device. E.g. "192.168.1.1".
*/ */
public function __construct($host) public function __construct($host)
{ {
parent::__construct($host, "UDP"); parent::__construct($host, "UDP");
} }
} }

@ -0,0 +1,12 @@
<?php
namespace PHPModbus\Network;
use PHPModbus\ModbusException;
/**
* Exception class thrown when a modbus network connection operation failure happens.
*/
class IOException extends ModbusException
{
}

@ -0,0 +1,212 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Network;
use InvalidArgumentException;
class ModbusConnection extends ModbusConnectionProperties
{
/**
* @var resource Communication socket
*/
private $streamSocket;
/**
* @var array status messages
*/
protected $statusMessages = [];
public function __construct(ModbusConnectionBuilder $builder)
{
$this->host = $builder->getHost();
$this->port = $builder->getPort();
$this->client = $builder->getClient();
$this->clientPort = $builder->getClientPort();
$this->timeoutSec = $builder->getTimeoutSec();
$this->connectTimeoutSec = $builder->getConnectTimeoutSec();
$this->readTimeoutSec = $builder->getReadTimeoutSec();
$this->writeTimeoutSec = $builder->getWriteTimeoutSec();
$this->protocol = $builder->getProtocol();
}
public static function getBuilder()
{
return new ModbusConnectionBuilder();
}
/**
* connect
*
* Connect the socket
*
* @return bool
* @throws \InvalidArgumentException
* @throws \PHPModbus\Network\IOException
*/
public function connect()
{
$protocol = null;
switch ($this->protocol) {
case 'TCP':
case 'UDP':
$protocol = strtolower($this->protocol);
break;
default:
throw new InvalidArgumentException("Unknown socket protocol, should be 'TCP' or 'UDP'");
}
$opts = [];
if (strlen($this->client) > 0) {
// Bind the client stream to a specific local port
$opts = array(
'socket' => array(
'bindto' => "{$this->client}:{$this->clientPort}",
),
);
}
$context = stream_context_create($opts);
$this->streamSocket = @stream_socket_client(
"$protocol://$this->host:$this->port",
$errno,
$errstr,
$this->connectTimeoutSec,
STREAM_CLIENT_CONNECT,
$context
);
if (false === $this->streamSocket) {
$message = "Unable to create client socket to {$protocol}://{$this->host}:{$this->port}: {$errstr}";
throw new IOException($message, $errno);
}
if (strlen($this->client) > 0) {
$this->statusMessages[] = 'Bound';
}
$this->statusMessages[] = 'Connected';
stream_set_blocking($this->streamSocket, false); // use non-blocking stream
$writeTimeoutParts = $this->secsToSecUsecArray($this->writeTimeoutSec);
// set as stream timeout as we use 'stream_select' to read data and this method has its own timeout
// this call will only affect our fwrite parts (send data method)
stream_set_timeout($this->streamSocket, $writeTimeoutParts['sec'], $writeTimeoutParts['usec']);
return true;
}
/**
* receive
*
* Receive data from the socket
*
* @return bool
* @throws \Exception
*/
public function receive()
{
$totalReadTimeout = $this->timeoutSec;
$lastAccess = microtime(true);
$readTimeout = $this->secsToSecUsecArray($this->readTimeoutSec);
while (true) {
$read = array($this->streamSocket);
$write = null;
$except = null;
$data = '';
if (false !== stream_select($read, $write, $except, $readTimeout['sec'], $readTimeout['usec'])) {
$this->statusMessages[] = 'Wait data ... ';
if (in_array($this->streamSocket, $read, false)) {
$data .= fread($this->streamSocket, 2048); // read max 2048 bytes
if (!empty($data)) {
$this->statusMessages[] = 'Data received';
return $data;
}
$lastAccess = microtime(true);
} else {
$timeSpentWaiting = microtime(true) - $lastAccess;
if ($timeSpentWaiting >= $totalReadTimeout) {
throw new IOException(
"Watchdog time expired [ {$totalReadTimeout} sec ]!!! " .
"Connection to {$this->host}:{$this->port} is not established."
);
}
}
} else {
throw new IOException("Failed to read data from {$this->host}:{$this->port}.");
}
}
return null;
}
/**
* send
*
* Send the packet via Modbus
*
* @param string $packet
*/
public function send($packet)
{
fwrite($this->streamSocket, $packet, strlen($packet));
$this->statusMessages[] = 'Send';
}
/**
* close
*
* Close the socket
*/
public function close()
{
if (is_resource($this->streamSocket)) {
fclose($this->streamSocket);
$this->statusMessages[] = 'Disconnected';
}
}
/**
* Close socket it still open
*/
public function __destruct()
{
$this->close();
}
/**
* Convert float in seconds to array
*
* @param float $secs
* @return array {sec: ..., usec: ...}
*/
private function secsToSecUsecArray($secs)
{
$remainder = $secs - floor($secs);
return [
'sec' => round($secs - $remainder),
'usec' => round($remainder * 1e6),
];
}
/**
* @return array
*/
public function getStatusMessages()
{
return $this->statusMessages;
}
}

@ -0,0 +1,111 @@
<?php
namespace PHPModbus\Network;
class ModbusConnectionBuilder extends ModbusConnectionProperties
{
/**
* Return built instance of ModbusConnection
*
* @return ModbusConnection built instance
* @throws \LogicException
*/
public function build()
{
if (empty($this->host)) {
throw new \LogicException('host property can not be left null or empty!');
}
return new ModbusConnection($this);
}
/**
* @param string $client
* @return ModbusConnectionBuilder
*/
public function setClient($client)
{
$this->client = $client;
return $this;
}
/**
* @param string $clientPort
* @return ModbusConnectionBuilder
*/
public function setClientPort($clientPort)
{
$this->clientPort = $clientPort;
return $this;
}
/**
* @param float $timeoutSec
* @return ModbusConnectionBuilder
*/
public function setTimeoutSec($timeoutSec)
{
$this->timeoutSec = $timeoutSec;
return $this;
}
/**
* @param float $connectTimeoutSec
* @return ModbusConnectionBuilder
*/
public function setConnectTimeoutSec($connectTimeoutSec)
{
$this->connectTimeoutSec = $connectTimeoutSec;
return $this;
}
/**
* @param float $readTimeoutSec
* @return ModbusConnectionBuilder
*/
public function setReadTimeoutSec($readTimeoutSec)
{
$this->readTimeoutSec = $readTimeoutSec;
return $this;
}
/**
* @param float $writeTimeoutSec
* @return ModbusConnectionBuilder
*/
public function setWriteTimeoutSec($writeTimeoutSec)
{
$this->writeTimeoutSec = $writeTimeoutSec;
return $this;
}
/**
* @param string $protocol
* @return ModbusConnectionBuilder
*/
public function setProtocol($protocol)
{
$this->protocol = $protocol;
return $this;
}
/**
* @param string $host
* @return ModbusConnectionBuilder
*/
public function setHost($host)
{
$this->host = $host;
return $this;
}
/**
* @param string $port
* @return ModbusConnectionBuilder
*/
public function setPort($port)
{
$this->port = $port;
return $this;
}
}

@ -0,0 +1,120 @@
<?php
namespace PHPModbus\Network;
/**
* ModbusConnection immutable properties base class
*/
abstract class ModbusConnectionProperties
{
/**
* @var string (optional) client IP address when binding client
*/
protected $client = '';
/**
* @var string client port set when binding client to local ip&port
*/
protected $clientPort = 502;
/**
* @var float Total response timeout (seconds, decimals allowed)
*/
protected $timeoutSec = 5;
/**
* @var float maximum timeout when establishing connection (seconds, decimals allowed)
*/
protected $connectTimeoutSec = 1;
/**
* @var float read timeout (seconds, decimals allowed)
*/
protected $readTimeoutSec = 0.3; // 300 ms
/**
* @var float maximum timeout for write operation on connection (seconds, decimals allowed)
*/
protected $writeTimeoutSec = 1;
/**
* @var string network protocol (TCP, UDP)
*/
protected $protocol = 'UDP';
/**
* @var string|null Modbus device IP address
*/
protected $host;
/**
* @var string gateway port
*/
protected $port = 502;
/**
* @return string (optional) client IP address when binding client
*/
public function getClient()
{
return $this->client;
}
/**
* @return string client port set when binding client to local ip&port
*/
public function getClientPort()
{
return $this->clientPort;
}
/**
* @return float Total response timeout (seconds, decimals allowed)
*/
public function getTimeoutSec()
{
return $this->timeoutSec;
}
/**
* @return float maximum timeout when establishing connection (seconds, decimals allowed)
*/
public function getConnectTimeoutSec()
{
return $this->connectTimeoutSec;
}
/**
* @return float read timeout (seconds, decimals allowed)
*/
public function getReadTimeoutSec()
{
return $this->readTimeoutSec;
}
/**
* @return float maximum timeout for write operation on connection (seconds, decimals allowed)
*/
public function getWriteTimeoutSec()
{
return $this->writeTimeoutSec;
}
/**
* @return string network protocol (TCP, UDP)
*/
public function getProtocol()
{
return $this->protocol;
}
/**
* @return string Modbus device IP address
*/
public function getHost()
{
return $this->host;
}
/**
* @return string gateway port
*/
public function getPort()
{
return $this->port;
}
}

@ -0,0 +1,65 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class MaskWriteRegisterPacket
{
/**
* Packet builder FC22 - MASK WRITE register
*
* @param int $unitId
* @param int $reference
* @param int $andMask
* @param int $orMask
* @return string
*/
public static function build($unitId, $reference, $andMask, $orMask)
{
$dataLen = 0;
// build data section
$buffer1 = '';
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(22); // FC 22 = 22(0x16)
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($andMask); // AND mask
$buffer2 .= IecType::iecINT($orMask); // OR mask
$dataLen += 7;
// build header
$buffer3 = '';
$buffer3 .= IecType::iecINT(mt_rand(0, 65000)); // transaction ID
$buffer3 .= IecType::iecINT(0); // protocol ID
$buffer3 .= IecType::iecINT($dataLen + 1); // lenght
$buffer3 .= IecType::iecBYTE($unitId); //unit ID
return $buffer3 . $buffer2 . $buffer1;
}
/**
* FC22 response parser
*
* @param string $packet
* @return bool
* @throws \Exception
*/
public static function parse($packet)
{
return true;
}
}

@ -0,0 +1,88 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class ReadCoilsPacket
{
/**
* FC1 packet builder - read coils
*
* @param int $unitId
* @param int $reference
* @param int $quantity
* @return string
*/
public static function build($unitId, $reference, $quantity)
{
$dataLen = 0;
// build data section
$buffer1 = '';
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(1); // FC 1 = 1(0x01)
// build body - read section
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($quantity); // quantity
$dataLen += 5;
// 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 $buffer3 . $buffer2 . $buffer1;
}
/**
* FC 1 response parser
*
* @param string $packet
* @param int $quantity
* @return bool[]
* @throws \Exception
*/
public static function parse($packet, $quantity)
{
$data = array();
for ($i = 0, $len = ord($packet[8]); $i < $len; $i++) {
$data[$i] = ord($packet[9 + $i]);
}
// get bool values to array
$data_boolean_array = array();
$di = 0;
foreach ($data as $value) {
for ($i = 0; $i < 8; $i++) {
if ($di == $quantity) {
continue;
}
// get boolean value
$v = ($value >> $i) & 0x01;
// build boolean array
if ($v == 0) {
$data_boolean_array[] = false;
} else {
$data_boolean_array[] = true;
}
$di++;
}
}
return $data_boolean_array;
}
}

@ -0,0 +1,64 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class ReadInputDiscretesPacket
{
/**
* FC2 packet builder - read coils
*
* @param int $unitId
* @param int $reference
* @param int $quantity
* @return string
*/
public static function build($unitId, $reference, $quantity)
{
$dataLen = 0;
// build data section
$buffer1 = '';
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(2); // FC 2 = 2(0x02)
// build body - read section
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($quantity); // quantity
$dataLen += 5;
// build header
$buffer3 = '';
$buffer3 .= IecType::iecINT(mt_rand(0, 65000)); // transaction ID
$buffer3 .= IecType::iecINT(0); // protocol ID
$buffer3 .= IecType::iecINT($dataLen + 1); // lenght
$buffer3 .= IecType::iecBYTE($unitId); //unit ID
return $buffer3 . $buffer2 . $buffer1;
}
/**
* FC 2 response parser, alias to FC 1 parser i.e. readCoilsParser.
*
* @param string $packet
* @param int $quantity
* @return bool[]
* @throws \Exception
*/
public static function parse($packet, $quantity)
{
return ReadCoilsPacket::parse($packet, $quantity);
}
}

@ -0,0 +1,68 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class ReadMultipleInputRegistersPacket
{
/**
* Packet FC 4 builder - read multiple input registers
*
* @param int $unitId
* @param int $reference
* @param int $quantity
* @return string
*/
public static function build($unitId, $reference, $quantity)
{
$dataLen = 0;
// build data section
$buffer1 = '';
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(4); // FC 4 = 4(0x04)
// build body - read section
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($quantity); // quantity
$dataLen += 5;
// 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 $buffer3 . $buffer2 . $buffer1;
}
/**
* FC 4 response parser
*
* @param string $packet
* @return array
* @throws \Exception
*/
public static function parse($packet)
{
$data = array();
for ($i = 0, $len = ord($packet[8]); $i < $len; $i++) {
$data[$i] = ord($packet[9 + $i]);
}
return $data;
}
}

@ -0,0 +1,67 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class ReadMultipleRegistersPacket
{
/**
* @param int $unitId
* @param int $reference
* @param int $quantity
* @return string
*/
public static function build($unitId, $reference, $quantity)
{
$dataLen = 0;
// build data section
$buffer1 = '';
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(3); // FC 3 = 3(0x03)
// build body - read section
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($quantity); // quantity
$dataLen += 5;
// 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;
}
/**
* FC 3 response parser
*
* @param string $packet
* @return array
* @throws \Exception
*/
public static function parse($packet)
{
$data = array();
// get data
for ($i = 0, $len = ord($packet[8]); $i < $len; $i++) {
$data[$i] = ord($packet[9 + $i]);
}
return $data;
}
}

@ -0,0 +1,102 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class ReadWriteRegistersPacket
{
/**
* Packet FC23 builder - READ WRITE registers
*
* @param int $unitId
* @param int $referenceRead
* @param int $quantity
* @param int $referenceWrite
* @param array $data
* @param array $dataTypes
* @param int $endianness (0 = little endian = 0, 1 = big endian)
* @return string
*/
public static function build(
$unitId,
$referenceRead,
$quantity,
$referenceWrite,
array $data,
array $dataTypes,
$endianness
)
{
$dataLen = 0;
// build data section
$buffer1 = '';
foreach ($data as $key => $dataitem) {
if ($dataTypes[$key] === 'INT') {
$buffer1 .= IecType::iecINT($dataitem); // register values x
$dataLen += 2;
} elseif ($dataTypes[$key] === 'DINT') {
$buffer1 .= IecType::iecDINT($dataitem, $endianness); // register values x
$dataLen += 4;
} elseif ($dataTypes[$key] === 'REAL') {
$buffer1 .= IecType::iecREAL($dataitem, $endianness); // register values x
$dataLen += 4;
} else {
$buffer1 .= IecType::iecINT($dataitem); // register values x
$dataLen += 2;
}
}
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(23); // FC 23 = 23(0x17)
// build body - read section
$buffer2 .= IecType::iecINT($referenceRead); // refnumber = 12288
$buffer2 .= IecType::iecINT($quantity); // quantity
// build body - write section
$buffer2 .= IecType::iecINT($referenceWrite); // refnumber = 12288
$buffer2 .= IecType::iecINT($dataLen / 2); // word count
$buffer2 .= IecType::iecBYTE($dataLen); // byte count
$dataLen += 10;
// 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;
}
/**
* FC23 response parser
*
* @param string $packet
* @return array|false
* @throws \Exception
*/
public static function parse($packet)
{
$data = array();
// get data
for ($i = 0, $len = ord($packet[8]); $i < $len; $i++) {
$data[$i] = ord($packet[9 + $i]);
}
return $data;
}
}

@ -0,0 +1,96 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
/**
* Builds/parses packet in Modbus TCP/IP format (http://www.simplymodbus.ca/TCP.htm)
*/
class WriteMultipleCoilsPacket
{
/**
* Packet builder FC15 - Write multiple coils
*
* @param int $unitId
* @param int $reference
* @param array $data
* @return string
*/
public static function build($unitId, $reference, array $data)
{
$dataLen = 0;
list($pduData, $wordCount) = self::getDataConvertedToIecWords($data);
$dataLen += $wordCount;
// build body
$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;
$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;
}
/**
* FC15 response parser
*
* @return bool
* @throws \Exception
*/
public static function parse()
{
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));
}
}

@ -0,0 +1,85 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class WriteMultipleRegisterPacket
{
/**
* Packet builder FC16 - WRITE multiple register
* e.g.: 4dd90000000d0010300000030603e807d00bb8
*
* @param int $unitId
* @param int $reference
* @param array $data
* @param array $dataTypes
* @param int $endianness (0 = little endian = 0, 1 = big endian)
* @return string
*/
public static function build($unitId, $reference, array $data, array $dataTypes, $endianness)
{
$dataLen = 0;
// build data section
$buffer1 = "";
foreach ($data as $key => $dataitem) {
if ($dataTypes[$key] === 'INT') {
$buffer1 .= IecType::iecINT($dataitem); // register values x
$dataLen += 2;
} elseif ($dataTypes[$key] === 'DINT') {
$buffer1 .= IecType::iecDINT($dataitem, $endianness); // register values x
$dataLen += 4;
} elseif ($dataTypes[$key] === 'REAL') {
$buffer1 .= IecType::iecREAL($dataitem, $endianness); // register values x
$dataLen += 4;
} else {
$buffer1 .= IecType::iecINT($dataitem); // register values x
$dataLen += 2;
}
}
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(16); // FC 16 = 16(0x10)
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$buffer2 .= IecType::iecINT($dataLen / 2); // word count
$buffer2 .= IecType::iecBYTE($dataLen); // 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;
}
/**
* FC16 response parser
*
* @return bool
* @throws \Exception
*/
public static function parse()
{
return true;
}
}

@ -0,0 +1,70 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class WriteSingleCoilPacket
{
/**
* Packet builder FC5 - WRITE single register
*
* @param int $unitId
* @param int $reference
* @param array $data
* @return string
*/
public static function build($unitId, $reference, array $data)
{
$dataLen = 0;
// build data section
$buffer1 = '';
foreach ($data as $key => $dataitem) {
if ($dataitem == true) {
$buffer1 = IecType::iecINT(0xFF00);
} else {
$buffer1 = IecType::iecINT(0x0000);
}
}
$dataLen += 2;
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(5); // FC5 = 5(0x05)
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$dataLen += 3;
// build header
$buffer3 = '';
$buffer3 .= IecType::iecINT(mt_rand(0, 65000)); // transaction ID
$buffer3 .= IecType::iecINT(0); // protocol ID
$buffer3 .= IecType::iecINT($dataLen + 1); // lenght
$buffer3 .= IecType::iecBYTE($unitId); //unit ID
// return packet string
return $buffer3 . $buffer2 . $buffer1;
}
/**
* FC5 response parser
*
* @return bool
* @throws \Exception
*/
public static function parse()
{
return true;
}
}

@ -0,0 +1,68 @@
<?php
/**
* Phpmodbus Copyright (c) 2004, 2013 Jan Krakora
*
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
* @copyright Copyright (c) 2004, 2013 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
namespace PHPModbus\Packet;
use PHPModbus\IecType;
class WriteSingleRegisterPacket
{
/**
* Packet builder FC6 - WRITE single register
*
* @param int $unitId
* @param int $reference
* @param array $data
* @return string
*/
public static function build($unitId, $reference, array $data)
{
$dataLen = 0;
// build data section
$buffer1 = '';
foreach ($data as $key => $dataitem) {
$buffer1 .= IecType::iecINT($dataitem); // register values x
$dataLen += 2;
break;
}
// build body
$buffer2 = '';
$buffer2 .= IecType::iecBYTE(6); // FC6 = 6(0x06)
$buffer2 .= IecType::iecINT($reference); // refnumber = 12288
$dataLen += 3;
// 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;
}
/**
* FC6 response parser
*
* @return bool
* @throws \Exception
*/
public static function parse()
{
return true;
}
}

@ -1,6 +1,7 @@
<?php <?php
namespace PHPModbus; namespace PHPModbus;
use Exception; use Exception;
/** /**
@ -15,7 +16,6 @@ use Exception;
* @category Phpmodbus * @category Phpmodbus
* @package Phpmodbus * @package Phpmodbus
* @version $id$ * @version $id$
*
*/ */
/** /**
@ -24,243 +24,243 @@ use Exception;
* The class includes set of methods that convert the received Modbus data * The class includes set of methods that convert the received Modbus data
* (array of bytes) to the PHP data type, i.e. signed int, unsigned int and float. * (array of bytes) to the PHP data type, i.e. signed int, unsigned int and float.
* *
* @author Jan Krakora * @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora * @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus * @package Phpmodbus
*
*/ */
class PhpType class PhpType
{ {
/** /**
* bytes2float * bytes2float
* *
* The function converts array of 4 bytes to float. The return value * The function converts array of 4 bytes to float. The return value
* depends on order of the input bytes (endianning). * depends on order of the input bytes (endianning).
* *
* @param array $values * @param array $values
* @param bool $bigEndian * @param bool $bigEndian
* @return float * @return float
*/ */
public static function bytes2float($values, $bigEndian = 0) public static function bytes2float($values, $bigEndian = 0)
{ {
// Set the array to correct form // Set the array to correct form
$data = self::checkData($values); $data = self::checkData($values);
// Combine bytes // Combine bytes
$real = self::combineBytes($data, $bigEndian); $real = self::combineBytes($data, $bigEndian);
// Convert the real value to float // Convert the real value to float
return (float)self::real2float($real); return (float)self::real2float($real);
} }
/** /**
* bytes2signedInt * bytes2signedInt
* *
* The function converts array of 2 or 4 bytes to signed integer. * The function converts array of 2 or 4 bytes to signed integer.
* The return value depends on order of the input bytes (endianning). * The return value depends on order of the input bytes (endianning).
* *
* @param array $values * @param array $values
* @param bool $bigEndian * @param bool $bigEndian
* @return int * @return int
*/ */
public static function bytes2signedInt($values, $bigEndian = 0) public static function bytes2signedInt($values, $bigEndian = 0)
{ {
$data = array(); $data = array();
$int = 0; $int = 0;
// Set the array to correct form // Set the array to correct form
$data = self::checkData($values); $data = self::checkData($values);
// Combine bytes // Combine bytes
$int = self::combineBytes($data, $bigEndian); $int = self::combineBytes($data, $bigEndian);
// In the case of signed 2 byte value convert it to 4 byte one // In the case of signed 2 byte value convert it to 4 byte one
if ((count($values) == 2) && ((0x8000 & $int) > 0)) { if ((count($values) === 2) && ((0x8000 & $int) > 0)) {
$int = 0xFFFF8000 | $int; $int = 0xFFFF8000 | $int;
} }
// Convert the value // Convert the value
return (int)self::dword2signedInt($int); return (int)self::dword2signedInt($int);
} }
/** /**
* bytes2unsignedInt * bytes2unsignedInt
* *
* The function converts array of 2 or 4 bytes to unsigned integer. * The function converts array of 2 or 4 bytes to unsigned integer.
* The return value depends on order of the input bytes (endianning). * The return value depends on order of the input bytes (endianning).
* *
* @param array $values * @param array $values
* @param bool $bigEndian * @param bool $bigEndian
* @return int|float * @return int|float
*/ */
public static function bytes2unsignedInt($values, $bigEndian = 0) public static function bytes2unsignedInt($values, $bigEndian = 0)
{ {
$data = array(); $data = array();
$int = 0; $int = 0;
// Set the array to correct form // Set the array to correct form
$data = self::checkData($values); $data = self::checkData($values);
// Combine bytes // Combine bytes
$int = self::combineBytes($data, $bigEndian); $int = self::combineBytes($data, $bigEndian);
// Convert the value // Convert the value
return self::dword2unsignedInt($int); return self::dword2unsignedInt($int);
} }
/** /**
* bytes2string * bytes2string
* *
* The function converts an values array to the string. The function detects * The function converts an values array to the string. The function detects
* the end of the string by 0x00 character as defined by string standards. * the end of the string by 0x00 character as defined by string standards.
* *
* @param array $values * @param array $values
* @param bool $bigEndian * @param bool $bigEndian
* @return string * @return string
*/ */
public static function bytes2string($values, $bigEndian = 0) public static function bytes2string($values, $bigEndian = 0)
{ {
// Prepare string variable // Prepare string variable
$str = ""; $str = "";
// Parse the received data word array // Parse the received data word array
for ($i = 0; $i < count($values); $i += 2) { for ($i = 0; $i < count($values); $i += 2) {
if ($bigEndian) { if ($bigEndian) {
if ($values[$i] != 0) { if ($values[$i] != 0) {
$str .= chr($values[$i]); $str .= chr($values[$i]);
} else { } else {
break; break;
} }
if ($values[$i + 1] != 0) { if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]); $str .= chr($values[$i + 1]);
} else { } else {
break; break;
} }
} else { } else {
if ($values[$i + 1] != 0) { if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]); $str .= chr($values[$i + 1]);
} else { } else {
break; break;
} }
if ($values[$i] != 0) { if ($values[$i] != 0) {
$str .= chr($values[$i]); $str .= chr($values[$i]);
} else { } else {
break; break;
} }
} }
} }
// return string // return string
return $str; return $str;
} }
/** /**
* real2float * real2float
* *
* This function converts a value in IEC-1131 REAL single precision form to float. * This function converts a value in IEC-1131 REAL single precision form to float.
* *
* For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or * For more see [{@link http://en.wikipedia.org/wiki/Single_precision Single precision on Wiki}] or
* [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes * [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes
* @ Georgia Tech 21-Nov-2007] or * @ Georgia Tech 21-Nov-2007] or
* [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}] * [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}]
* *
* @param int $value in IEC REAL data type to be converted * @param int $value in IEC REAL data type to be converted
* @return float float value * @return float float value
*/ */
private static function real2float($value) private static function real2float($value)
{ {
// get unsigned long // get unsigned long
$ulong = pack("L", $value); $ulong = pack('L', $value);
// set float // set float
$float = unpack("f", $ulong); $float = unpack('f', $ulong);
return $float[1]; return $float[1];
} }
/** /**
* dword2signedInt * dword2signedInt
* *
* Switch double word to signed integer * Switch double word to signed integer
* *
* @param int $value * @param int $value
* @return int * @return int
*/ */
private static function dword2signedInt($value) private static function dword2signedInt($value)
{ {
if ((0x80000000 & $value) != 0) { if ((0x80000000 & $value) !== 0) {
return -(0x7FFFFFFF & ~$value) - 1; return -(0x7FFFFFFF & ~$value) - 1;
} else { } else {
return (0x7FFFFFFF & $value); return (0x7FFFFFFF & $value);
} }
} }
/** /**
* dword2signedInt * dword2signedInt
* *
* Switch double word to unsigned integer * Switch double word to unsigned integer
* *
* @param int $value * @param int $value
* @return int|float * @return int|float
*/ */
private static function dword2unsignedInt($value) private static function dword2unsignedInt($value)
{ {
if ((0x80000000 & $value) != 0) { if ((0x80000000 & $value) !== 0) {
return ((float)(0x7FFFFFFF & $value)) + 2147483648; return ((float)(0x7FFFFFFF & $value)) + 2147483648;
} else { } else {
return (int)(0x7FFFFFFF & $value); return (int)(0x7FFFFFFF & $value);
} }
} }
/** /**
* checkData * checkData
* *
* Check if the data variable is array, and check if the values are numeric * Check if the data variable is array, and check if the values are numeric
* *
* @param int[] $data * @param int[] $data
* @return int * @return array|\int[]
* @throws Exception * @throws Exception
*/ */
private static function checkData($data) private static function checkData($data)
{ {
// Check the data // Check the data
if (!is_array($data) || $count = count($data);
count($data) < 2 || if (!is_array($data)
count($data) > 4 || || $count < 2
count($data) == 3 || $count > 4
) { || $count === 3
throw new Exception('The input data should be an array of 2 or 4 bytes.'); ) {
} throw new Exception('The input data should be an array of 2 or 4 bytes.');
// Fill the rest of array by zeroes }
if (count($data) == 2) { // Fill the rest of array by zeroes
$data[2] = 0; if ($count === 2) {
$data[3] = 0; $data[2] = 0;
} $data[3] = 0;
// Check the values to be number }
if (!is_numeric($data[0]) || // Check the values to be number
!is_numeric($data[1]) || if (!is_numeric($data[0])
!is_numeric($data[2]) || || !is_numeric($data[1])
!is_numeric($data[3]) || !is_numeric($data[2])
) { || !is_numeric($data[3])
throw new Exception('Data are not numeric or the array keys are not indexed by 0,1,2 and 3'); ) {
} throw new Exception('Data are not numeric or the array keys are not indexed by 0,1,2 and 3');
}
return $data; return $data;
} }
/** /**
* combineBytes * combineBytes
* *
* Combine bytes together * Combine bytes together
* *
* @param int $data * @param int $data
* @param bool $bigEndian * @param bool $bigEndian
* @return int * @return int
*/ */
private static function combineBytes($data, $bigEndian) private static function combineBytes($data, $bigEndian)
{ {
$value = 0; $value = 0;
// Combine bytes // Combine bytes
if ($bigEndian == 0) { if ($bigEndian == 0) {
$value = (($data[3] & 0xFF) << 16) | $value = (($data[3] & 0xFF) << 16) |
(($data[2] & 0xFF) << 24) | (($data[2] & 0xFF) << 24) |
(($data[1] & 0xFF)) | ($data[1] & 0xFF) |
(($data[0] & 0xFF) << 8); (($data[0] & 0xFF) << 8);
} else { } else {
$value = (($data[3] & 0xFF) << 24) | $value = (($data[3] & 0xFF) << 24) |
(($data[2] & 0xFF) << 16) | (($data[2] & 0xFF) << 16) |
(($data[1] & 0xFF) << 8) | (($data[1] & 0xFF) << 8) |
(($data[0] & 0xFF)); ($data[0] & 0xFF);
} }
return $value; return $value;
} }
} }

@ -1,256 +0,0 @@
(* @NESTEDCOMMENTS := 'Yes' *)
(* @PATH := '' *)
(* @OBJECTFLAGS := '0, 8' *)
(* @SYMFILEFLAGS := '2048' *)
PROGRAM PLC_PRG
VAR
(* BOOL, COIL *)
COIL1 AT %MX0.0 : BOOL := 0;
COIL2 AT %MX0.1 : BOOL := 0;
COIL3 AT %MX0.2 : BOOL := 0;
COIL4 AT %MX0.3 : BOOL := 0;
COIL5 AT %MX0.4 : BOOL := 0;
COIL6 AT %MX0.5 : BOOL := 0;
COIL7 AT %MX0.6 : BOOL := 0;
COIL8 AT %MX0.7 : BOOL := 0;
(* BYTE *)
BYTE1 AT %MB0 : BYTE := 0;
BYTE2 AT %MB1 : BYTE := 0;
BYTE3 AT %MB2 : BYTE := 0;
BYTE4 AT %MB3 : BYTE := 0;
BYTE5 AT %MB4 : BYTE := 0;
(* INT *)
INT1 AT %MW0 : INT := 0;
INT2 AT %MW1 : INT := 0;
INT3 AT %MW2 : INT := 0;
INT4 AT %MW3 : INT := 0;
INT5 AT %MW4 : INT := 0;
(* WORD *)
WORD1 AT %MW0 : WORD := 0;
WORD2 AT %MW1 : WORD := 0;
WORD3 AT %MW2 : WORD := 0;
WORD4 AT %MW3 : WORD := 0;
WORD5 AT %MW4 : WORD := 0;
(* DINT *)
DINT1 AT %MD0 : DINT := 0;
DINT2 AT %MD1 : DINT := 0;
DINT3 AT %MD2 : DINT := 0;
DINT4 AT %MD3 : DINT := 0;
DINT5 AT %MD4 : DINT := 0;
(* DWORD *)
DWORD1 AT %MD0 : DWORD := 0;
DWORD2 AT %MD1 : DWORD := 0;
DWORD3 AT %MD2 : DWORD := 0;
DWORD4 AT %MD3 : DWORD := 0;
DWORD5 AT %MD4 : DWORD := 0;
(* REAL *)
REAL1 AT %MD0 : REAL := 0;
REAL2 AT %MD1 : REAL := 0;
REAL3 AT %MD2 : REAL := 0;
REAL4 AT %MD3 : REAL := 0;
REAL5 AT %MD4 : REAL := 0;
(* String *)
STRING1 AT %MW0 : STRING := 'Hello word!!!';
END_VAR
(* @END_DECLARATION := '0' *)
(* Something to do *)
;
END_PROGRAM
(* @NESTEDCOMMENTS := 'Yes' *)
(* @GLOBAL_VARIABLE_LIST := 'Global_Variables' *)
(* @PATH := '' *)
(* @OBJECTFLAGS := '0, 8' *)
(* @SYMFILEFLAGS := '2048' *)
VAR_GLOBAL
END_VAR
(* @OBJECT_END := 'Global_Variables' *)
(* @CONNECTIONS := Global_Variables
FILENAME : ''
FILETIME : 0
EXPORT : 0
NUMOFCONNECTIONS : 0
*)
(* @NESTEDCOMMENTS := 'Yes' *)
(* @GLOBAL_VARIABLE_LIST := 'Variable_Configuration' *)
(* @PATH := '' *)
(* @OBJECTFLAGS := '0, 8' *)
(* @SYMFILEFLAGS := '2048' *)
VAR_CONFIG
END_VAR
(* @OBJECT_END := 'Variable_Configuration' *)
(* @CONNECTIONS := Variable_Configuration
FILENAME : ''
FILETIME : 0
EXPORT : 0
NUMOFCONNECTIONS : 0
*)
_ALARMCONFIG
_ALARMCONFIGNEXTTEXTID : 10002
_ALARMCONFIGFORMATS : 'HH$':$'mm$':$'ss','dd$'-$'MM$'-$'yyyy'
_ALARMCLASSLIST : 1
_ALARMCLASSID : 0
_ALARMCLASSACKTYPE : 0
_ALARMCLASSNAME : 'DEFAULT'
_ALARMCLASSDESCRIPTION : ''
_ALARMCLASSBGCOLORS : 16777215,16777215,16777215
_ALARMCLASSTEXTCOLORS : 3394560,255,16711680
_ALARMCLASSBITMAPS : '','',''
_ALARMACTIONLIST : 0
(* @ALARMCLASSRESETCOLORS := '_ALARMCLASSRESETCOLORS: 33023,16777215' *)
(* @ALARMCLASSRESETBITMAP := '_ALARMCLASSRESETBITMAP: $'$'' *)
_ALARMGROUPLISTNAME : 'System'
_ALARMGROUPPATH : 'System'
_ALARMGROUPLIST : 0
_VISUALSETTINGSFLAGS : 0,0,0,0
_VISUALSETTINGSFLAGS : '','',''
_VISUALSETTINGSDYNTEXTFILECOUNT : 0
(* @ALARMCONFIGFLAGS := '_ALARMCONFIGFLAGS: 0' *)
(* @ALARMCONFIGGLOBALDB_STR := '_ALARMCONFIGGLOBALDB_STRINGS: $'$',$'$',$'$',$'$'' *)
(* @ALARMCONFIGGLOBALDB_NUM := '_ALARMCONFIGGLOBALDB_NUMBERS: 0,0' *)
_END_ALARMCONFIG
LIBRARY
Standard.lib 2.12.10 14:48:34
(* @LIBRARYSYMFILEINFO := '0' *)
NumOfPOUs: 26
ASCIIBYTE_TO_STRING: 2048
CONCAT: 0
CTD: 0
CTU: 0
CTUD: 0
DELETE: 0
F_TRIG: 0
FIND: 0
INSERT: 0
LEFT: 0
LEN: 0
MID: 0
R_TRIG: 0
REAL_STATE: 2048
REPLACE: 0
RIGHT: 0
RS: 0
RTC: 0
SEMA: 0
SR: 0
STANDARD_VERSION: 2048
STRING_COMPARE: 2048
STRING_TO_ASCIIBYTE: 2048
TOF: 0
TON: 0
TP: 0
NumOfGVLs: 1
'Global Variables 0': 0
END_LIBRARY
LIBRARY
SYSLIBCALLBACK.LIB 2.12.10 14:48:32
(* @LIBRARYSYMFILEINFO := '0' *)
NumOfPOUs: 2
SysCallbackRegister: 0
SysCallbackUnregister: 0
NumOfGVLs: 2
Globale_Variablen: 0
Version: 0
END_LIBRARY
PLC_CONFIGURATION
_GLOBAL
_VERSION: 3
_AUTOADR: 0
_CHECKADR: 0
_SAVECONFIGFILESINPROJECT: 0
_END_GLOBAL
_MODULE: '3S'
_SECTION_NAME: 'Root'
_INDEX_IN_PARENT: '-1'
_MODULE_NAME: 'Hardware configuration'
_NODE_ID: -1
_IECIN: %IB0
_IECOUT: %QB0
_IECDIAG: %MB0
_DOWNLOAD: 1
_EXCLUDEFROMAUTOADR: 0
_COMMENT: ''
_MODULE: '3S'
_SECTION_NAME: 'K_Bus'
_INDEX_IN_PARENT: '1'
_MODULE_NAME: 'K-Bus'
_NODE_ID: 0
_IECIN: %IB0
_IECOUT: %QB0
_IECDIAG: %MB0
_DOWNLOAD: 1
_EXCLUDEFROMAUTOADR: 0
_COMMENT: ''
_END_MODULE
_MODULE: '3S'
_SECTION_NAME: 'FB_VARS'
_INDEX_IN_PARENT: '2'
_MODULE_NAME: 'Fieldbus variables'
_NODE_ID: 1
_IECIN: %IB0
_IECOUT: %QB0
_IECDIAG: %MB0
_DOWNLOAD: 1
_EXCLUDEFROMAUTOADR: 0
_COMMENT: ''
_END_MODULE
_END_MODULE
PLC_END
RESOURCE
{event_task : 'start','Called when program starts','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,1,11986}
{event_task : 'stop','Called when program stops','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,2,11986}
{event_task : 'before_reset','Called before reset takes place','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,3,11986}
{event_task : 'after_reset','Called after reset took place','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,4,11986}
{event_task : 'shutdown','Called before shutdown is performed (Firmware update over ethernet)','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,5,11986}
{event_task : 'excpt_watchdog','Software watchdog of IEC-task expired','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,7,11986}
{event_task : 'excpt_fieldbus','Fieldbus error','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,9,11986}
{event_task : 'excpt_ioupdate','KBus error','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,10,11986}
{event_task : 'excpt_dividebyzero','Division by zero. Only integer operations!','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,18,11986}
{event_task : 'excpt_noncontinuable','Exception handler','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,20,11986}
{event_task : 'after_reading_inputs','Called after reading of inputs','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,28,11986}
{event_task : 'before_writing_outputs','Called before writing of outputs','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,29,11986}
{event_task : 'debug_loop','Debug loop at breakpoint','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,31,11986}
{event_task : 'online_change','Is called after CodeInit() at Online-Change','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,33,11986}
{event_task : 'before_download','Is called before the Download starts','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,34,11986}
{event_task : 'event_login','Is called before the login service is performed','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,501,11986}
{event_task : 'eth_overload','Ethernet Overload','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,750,11986}
{event_task : 'eth_network_ready','Is called directly after the Network and the PLC are initialised','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,751,11986}
{event_task : 'blink_code','New blink code / Blink code cleared ( Call STATUS_GET_LAST_ERROR for details )','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,752,11986}
{event_task : 'interrupt_0','Interrupt Real Time Clock (every second)','','FUNCTION systemevent: DWORD VAR_INPUT dwEvent: DWORD; dwFilter: DWORD; dwOwner: DWORD; END_VAR '}{event_task_info : 0,1000,11986}
END_RESOURCE
_WORKSPACE
_GLOBALVISUALSETTINGS
_VISUALSETTINGSFLAGS : 0,0,0,0
_VISUALSETTINGSFLAGS : '','',''
_VISUALSETTINGSDYNTEXTFILECOUNT : 0
_VISUALBITMAPLISTCOUNT : 0
_END_GLOBALVISUALSETTINGS
_END_WORKSPACE

@ -1,15 +0,0 @@
rem Create Codesys EXP file
rem Build cmd file
set CODESYS="c:\Program Files (x86)\WAGO Software\CoDeSys V2.3\codesys.exe"
set PROJECT=test
del %PROJECT%.EXP
echo file open %PROJECT%.pro >> codesys_cmd_file.cmd
echo project export %PROJECT%.EXP >> codesys_cmd_file.cmd
rem echo file saveas %PROJECT%.lib internallib >> codesys_cmd_file.cmd
echo file close >> codesys_cmd_file.cmd
echo file quit >> codesys_cmd_file.cmd
%CODESYS% /noinfo /cmd codesys_cmd_file.cmd
rem Clean all when finished
del codesys_cmd_file.cmd

Binary file not shown.

@ -0,0 +1,18 @@
<?php
namespace Tests\IecType;
use PHPModbus\IecType;
use PHPUnit\Framework\TestCase;
class IecByteTest extends TestCase
{
public function testIecByte()
{
$this->assertEquals(125, ord(IecType::iecBYTE(125)));
$this->assertEquals(98, ord(IecType::iecBYTE(98)));
$this->assertEquals(0, ord(IecType::iecBYTE(0)));
$this->assertEquals(255, ord(IecType::iecBYTE(255)));
$this->assertEquals(88, ord(IecType::iecBYTE(88)));
}
}

@ -0,0 +1,31 @@
<?php
namespace Tests\IecType;
use PHPModbus\IecType;
use PHPUnit\Framework\TestCase;
class IecDIntTest extends TestCase
{
private static function unPackDInt2HexString($value, $bigEndian = 0)
{
return unpack('H*', IecType::iecDINT($value, $bigEndian))[1];
}
public function testIecDintEndianingOff()
{
$this->assertEquals('00000000', self::unPackDInt2HexString(0));
$this->assertEquals('00010000', self::unPackDInt2HexString(1));
$this->assertEquals('ffffffff', self::unPackDInt2HexString(-1));
$this->assertEquals('ffff7fff', self::unPackDInt2HexString(pow(2, 31) - 1));
$this->assertEquals('00008000', self::unPackDInt2HexString(-pow(2, 31)));
}
public function testIecDintEndianingOn()
{
$this->assertEquals('00000000', self::unPackDInt2HexString(0, 1));
$this->assertEquals('00000001', self::unPackDInt2HexString(1, 1));
$this->assertEquals('ffffffff', self::unPackDInt2HexString(-1, 1));
$this->assertEquals('7fffffff', self::unPackDInt2HexString(pow(2, 31) - 1, 1));
$this->assertEquals('80000000', self::unPackDInt2HexString(-pow(2, 31), 1));
}
}

@ -0,0 +1,22 @@
<?php
namespace Tests\IecType;
use PHPModbus\IecType;
use PHPUnit\Framework\TestCase;
class IecIntTest extends TestCase
{
private static function unPackInt2HexString($value)
{
return unpack('H*', IecType::iecINT($value))[1];
}
public function testIecInt()
{
$this->assertEquals('0000', self::unPackInt2HexString(0));
$this->assertEquals('0001', self::unPackInt2HexString(1));
$this->assertEquals('ffff', self::unPackInt2HexString(-1));
$this->assertEquals('7fff', self::unPackInt2HexString(pow(2, 15) - 1));
$this->assertEquals('8000', self::unPackInt2HexString(-pow(2, 15)));
}
}

@ -0,0 +1,31 @@
<?php
namespace Tests\IecType;
use PHPModbus\IecType;
use PHPUnit\Framework\TestCase;
class IecRealTest extends TestCase
{
private static function unPackReal2HexString($value, $bigEndian = 0)
{
return unpack('H*', IecType::iecREAL($value, $bigEndian))[1];
}
public function testIecRealEndianingOff()
{
$this->assertEquals('00000000', self::unPackReal2HexString(0));
$this->assertEquals('00003f80', self::unPackReal2HexString(1));
$this->assertEquals('0000c000', self::unPackReal2HexString(-2));
$this->assertEquals('aaab3eaa', self::unPackReal2HexString(0.333333333333));
$this->assertEquals('000041c8', self::unPackReal2HexString(25));
}
public function testIecRealEndianingOn()
{
$this->assertEquals('00000000', self::unPackReal2HexString(0, 1));
$this->assertEquals('3f800000', self::unPackReal2HexString(1, 1));
$this->assertEquals('c0000000', self::unPackReal2HexString(-2, 1));
$this->assertEquals('3eaaaaab', self::unPackReal2HexString(0.333333333333, 1));
$this->assertEquals('41c80000', self::unPackReal2HexString(25, 1));
}
}

@ -1,11 +0,0 @@
@echo off
call ../config.bat
for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html"
cd output
for %%f in (*.html) do %diff% "%%f" ../ref/"%%f"
cd ..
pause
@echo on

@ -1 +0,0 @@
125<br>98<br>0<br>0<br>0<br>0<br>0<br>0<br>0<br>0<br>0<br>0<br>255<br>255<br>255<br>255<br>158<br>88<br>97<br>168<br>

@ -1 +0,0 @@
Endianing off <hr>0 --> Packet: 0000_0000_</br>1 --> Packet: 0001_0000_</br>-1 --> Packet: ffff_ffff_</br>2147483647 --> Packet: ffff_7fff_</br>-2147483648 --> Packet: 0000_8000_</br>Endianing on <hr>0 --> Packet: 0000_0000_</br>1 --> Packet: 0000_0001_</br>-1 --> Packet: ffff_ffff_</br>2147483647 --> Packet: 7fff_ffff_</br>-2147483648 --> Packet: 8000_0000_</br>

@ -1 +0,0 @@
Endianing off <hr>0 --> Packet: 0000_</br>1 --> Packet: 0001_</br>-1 --> Packet: ffff_</br>32767 --> Packet: 7fff_</br>-32768 --> Packet: 8000_</br>Endianing on <hr>0 --> Packet: 0000_</br>1 --> Packet: 0001_</br>-1 --> Packet: ffff_</br>32767 --> Packet: 7fff_</br>-32768 --> Packet: 8000_</br>

@ -1,12 +0,0 @@
Endianing off <hr>
0 --> Packet: 0000_0000_<br>
1 --> Packet: 0000_3f80_<br>
-2 --> Packet: 0000_c000_<br>
0.333333333333 --> Packet: aaab_3eaa_<br>
25 --> Packet: 0000_41c8_<br>
Endianing on <hr>
0 --> Packet: 0000_0000_<br>
1 --> Packet: 3f80_0000_<br>
-2 --> Packet: c000_0000_<br>
0.333333333333 --> Packet: 3eaa_aaab_<br>
25 --> Packet: 41c8_0000_<br>

@ -1,33 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
// Received bytes interpreting Mixed values
$data = Array (
"0" => 125, // 32098 (DINT)
"1" => 98,
"2" => 0,
"3" => 0,
"4" => 0, // 0 (DINT)
"5" => 0,
"6" => 0,
"7" => 0,
"8" => 0, // 0 (DINT)
"9" => 0,
"10" => 0,
"11" => 0,
"12" => 255, // -1 (DINT)
"13" => 255,
"14" => 255,
"15" => 255,
"16" => 158, // -25000 (INT)
"17" => 88,
"18" => 97, // 25000 (INT)
"19" => 168
);
// Print mixed values
foreach($data as $d)
echo ord(IecType::iecBYTE($d)) . "<br>";
?>

@ -1,50 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
// Received bytes interpreting Mixed values
$data = Array (
"0" => 0,
"1" => 1,
"2" => -1,
"3" => pow(2,31)-1,
"4" => -pow(2,31)
);
function byte2hex($value){
$h = dechex(($value >> 4) & 0x0F);
$l = dechex($value & 0x0F);
return "$h$l";
}
function printPacket($packet){
$str = "";
$str .= "Packet: ";
for($i=0;$i<strlen($packet);$i++){
$str .= byte2hex(ord($packet[$i]));
if($i % 2)
$str .= "_";
}
$str .= "</br>";
return $str;
}
echo "Endianing off <hr>";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecDINT($data[$i], 0);
echo printPacket($v);
"<br>";
}
echo "Endianing on <hr>";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecDINT($data[$i], 1);
echo printPacket($v);
"<br>";
}
?>

@ -1,50 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
// Received bytes interpreting Mixed values
$data = Array (
"0" => 0,
"1" => 1,
"2" => -1,
"3" => pow(2,15)-1,
"4" => -pow(2,15)
);
function byte2hex($value){
$h = dechex(($value >> 4) & 0x0F);
$l = dechex($value & 0x0F);
return "$h$l";
}
function printPacket($packet){
$str = "";
$str .= "Packet: ";
for($i=0;$i<strlen($packet);$i++){
$str .= byte2hex(ord($packet[$i]));
if($i % 2)
$str .= "_";
}
$str .= "</br>";
return $str;
}
echo "Endianing off <hr>";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecINT($data[$i], 0);
echo printPacket($v);
"<br>";
}
echo "Endianing on <hr>";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecINT($data[$i], 1);
echo printPacket($v);
"<br>";
}
?>

@ -1,50 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
// http://en.wikipedia.org/wiki/Single_precision
$data = Array (
"0" => 0, // -> 0000 0000
"1" => 1, // -> 3f80 0000
"2" => -2, // -> c000 0000
"3" => 0.333333333333, //1/3 -> 3eaa aaab
"4" => 25 // -> 41c8 0000
);
function byte2hex($value){
$h = dechex(($value >> 4) & 0x0F);
$l = dechex($value & 0x0F);
return "$h$l";
}
function printPacket($packet){
$str = "";
$str .= "Packet: ";
for($i=0;$i<strlen($packet);$i++){
$str .= byte2hex(ord($packet[$i]));
if($i % 2)
$str .= "_";
}
$str .= "<br>\n";
return $str;
}
echo "Endianing off <hr>\n";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecREAL($data[$i], 0);
echo printPacket($v);
"<br>\n";
}
echo "Endianing on <hr>\n";
// Print mixed values
for($i=0;$i<count($data);$i++) {
echo $data[$i] . " --> ";
$v = IecType::iecREAL($data[$i], 1);
echo printPacket($v);
"<br>\n";
}
?>

@ -0,0 +1,25 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class BindClientToLocalIpAndPortTest extends MockServerTestCase
{
public function testBindClientToLocalIpAndPort()
{
$mockResponse = '89130000000400010101';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
// use wildcard as multiple IPs on same machine ala VirtualBox network adapter installed may result ip that you cannot bind to
// good enough for test that no thing throws exception.
$modbus->client = '0.0.0.0';
$modbus->client_port = mt_rand(30000, 40000);
$this->assertEquals([1], $modbus->readCoils(0, 256, 1));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000101000001', $packetWithoutTransactionId);
}
}

@ -0,0 +1,41 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc15WriteMultipleCoilsTest extends MockServerTestCase
{
public function testFc15WriteMultipleCoils()
{
$mockResponse = '455000000006000f30000003';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->fc15(0, 12288, [1, 0, 1]));
});
$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);
}
}

@ -0,0 +1,21 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc16WriteMultipleRegistersTest extends MockServerTestCase
{
public function testFc16WriteMultipleRegisters()
{
$mockResponse = 'facf00000006001030000005';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->fc16(0, 12288, [-1,100001,1.3], ['INT', 'DINT', 'REAL']));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('000000110010300000050affff86a1000166663fa6', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc1ReadCoilsTest extends MockServerTestCase
{
public function testFc1Read1Coil()
{
$mockResponse = '89130000000400010101'; // respond with 1 byte (00000001 bits set) [1]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([1], $modbus->readCoils(0, 256, 1));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000101000001', $packetWithoutTransactionId);
}
public function testFc1Read3Coils()
{
$mockResponse = '31be0000000400010103'; // respond with 1 byte (00000011 bits set) [1, 1, 0]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([1, 1, 0], $modbus->fc1(0, 256, 3));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000101000003', $packetWithoutTransactionId);
}
}

@ -0,0 +1,26 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc22MaskWriteRegisterTest extends MockServerTestCase
{
public function testFc22MaskWriteRegister()
{
$mockResponse = 'd4350000000800163000fffb0004';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$bitValue = true;
$bitNumber = 2;
$andMask = 0xFFFF ^ pow(2, $bitNumber) ;
$orMask = 0x0000 ^ (pow(2, $bitNumber) * $bitValue ) ;
$this->assertTrue($modbus->fc22(0, 12288, $andMask, $orMask));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('0000000800163000fffb0004', $packetWithoutTransactionId);
}
}

@ -0,0 +1,24 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc23ReadWriteRegistersTest extends MockServerTestCase
{
public function testFc23ReadWriteRegisters()
{
$mockResponse = '9aa80000000f00170c000afc1807d0000000004040';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$data = array(10, -1000, 2000, 3.0);
$dataTypes = array("INT", "INT", "DINT", "REAL");
$this->assertEquals([0, 10, 252, 24, 7, 208, 0, 0, 0, 0, 64, 64], $modbus->fc23(0, 12288, 6, 12288, $data, $dataTypes));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000017001730000006300000060c000afc1807d0000000004040', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc2ReadInputDiscretesTest extends MockServerTestCase
{
public function testFc1Read1InputDiscrete()
{
$mockResponse = '6ae30000000400020101';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([1], $modbus->readInputDiscretes(0, 256, 1));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000201000001', $packetWithoutTransactionId);
}
public function testFc1Read3InputDiscretes()
{
$mockResponse = 'b5110000000400020103';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([1, 1, 0], $modbus->fc2(0, 256, 3));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000201000003', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc3ReadMultipleRegistersTest extends MockServerTestCase
{
public function testFc3Read1Word()
{
$mockResponse = '8180000000050003020003'; // respond with 1 WORD (2 bytes) [0, 3]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([0, 3], $modbus->readMultipleRegisters(0, 256, 1));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000301000001', $packetWithoutTransactionId);
}
public function testFc3Read3Words()
{
$mockResponse = 'e4710000000900030693e000040000'; // respond with 3 WORD (3*2 bytes) [147, 224, 0, 4, 0, 0]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([147, 224, 0, 4, 0, 0], $modbus->fc3(0, 268, 3));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('000000060003010c0003', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc4ReadMultipleInputRegistersTest extends MockServerTestCase
{
public function testFc4Read1Word()
{
$mockResponse = '8180000000050003020003'; // respond with 1 WORD (2 bytes) [0, 3]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([0, 3], $modbus->readMultipleInputRegisters(0, 256, 1));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000401000001', $packetWithoutTransactionId);
}
public function testFc4Read3Words()
{
$mockResponse = 'e4710000000900030693e000040000'; // respond with 3 WORD (3*2 bytes) [147, 224, 0, 4, 0, 0]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertEquals([147, 224, 0, 4, 0, 0], $modbus->fc4(0, 268, 3));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('000000060004010c0003', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc5WriteSingleCoilTest extends MockServerTestCase
{
public function testFc5WriteSingleCoilWith1()
{
$mockResponse = '952d0000000600051000ff00';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->writeSingleCoil(0, 4096, [1]));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('0000000600051000ff00', $packetWithoutTransactionId);
}
public function testFc5WriteSingleCoilWith0()
{
$mockResponse = '489c00000006000510000000';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->fc5(0, 4096, [0]));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000510000000', $packetWithoutTransactionId);
}
}

@ -0,0 +1,35 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
class Fc6WriteSingleRegisterTest extends MockServerTestCase
{
public function testFc6WriteSingleRegister()
{
$mockResponse = 'ecd10000000600061000000f';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->writeSingleRegister(0, 4096, [15]));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('0000000600061000000f', $packetWithoutTransactionId);
}
public function testFc6WriteSingleRegisterWith0()
{
$mockResponse = '489c00000006000510000000';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
$this->assertTrue($modbus->fc6(0, 4096, [0]));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000610000000', $packetWithoutTransactionId);
}
}

@ -0,0 +1,86 @@
<?php
namespace Tests\ModbusMaster;
use React\Datagram\Socket;
use React\EventLoop\Factory;
use React\Socket\Server;
require __DIR__ . '/../../vendor/autoload.php';
class MockResponseServer
{
const MAX_WAIT_TIMEOUT = 5;
private $port;
public function __construct($protocol, $port, $answerTimeout, $responsePacket)
{
$this->port = $port;
$loop = Factory::create();
if ('TCP' === $protocol) {
$this->startTcpServer($loop, $answerTimeout, $responsePacket);
} else {
$this->startUdpServer($loop, $answerTimeout, $responsePacket);
}
$loop->addPeriodicTimer(self::MAX_WAIT_TIMEOUT, function () use ($loop) {
$loop->stop();
});
$loop->run();
}
private function startTcpServer($loop, $answerTimeout, $responsePacket)
{
$socket = new Server($loop);
$socket->on('connection', function ($conn) use ($socket, $loop, $answerTimeout, $responsePacket) {
$conn->on('data', function ($data) use ($conn, $answerTimeout, $responsePacket) {
if ($answerTimeout) {
sleep($answerTimeout);
}
$conn->write(pack('H*', $responsePacket));
echo unpack('H*', $data)[1];
});
$conn->on('close', function () use ($socket, $loop) {
$socket->shutdown();
$loop->stop();
});
});
$socket->listen($this->port);
}
private function startUdpServer($loop, $answerTimeout, $responsePacket)
{
$factory = new \React\Datagram\Factory($loop);
$factory->createServer('127.0.0.1:' . $this->port)->then(function (Socket $server) use ($loop, $answerTimeout, $responsePacket) {
$server->on('message', function ($message, $address, Socket $server) use ($loop, $answerTimeout, $responsePacket) {
if ($answerTimeout > 0) {
sleep($answerTimeout);
}
$server->send(pack('H*', $responsePacket), $address);
echo unpack('H*', $message)[1];
$loop->addTimer(0.001, function () use ($server, $loop) {
$server->emit('shutdown', [$server]);
});
});
//silly but otherwise client will not receive packets from server. probably server is closed before stream is flushed etc
$server->on('shutdown', function () use ($server, $loop) {
$loop->addTimer(0.002, function () use ($server, $loop) {
$server->close();
$loop->stop();
});
});
});
}
}
new MockResponseServer($argv[1], $argv[2], $argv[3], $argv[4]);

@ -0,0 +1,37 @@
<?php
namespace Tests\ModbusMaster;
use PHPUnit\Framework\TestCase;
use React\ChildProcess\Process;
use React\EventLoop\Factory;
use React\EventLoop\Timer\Timer;
abstract class MockServerTestCase extends TestCase
{
public static function executeWithMockServer($packetToRespond, \Closure $closure, $protocol = 'TCP', $answerTimeout = 0, $port = 0)
{
$loop = Factory::create();
$port = $port ?: mt_rand(40000, 50000);
$process = new Process(PHP_BINARY . ' ' . __DIR__ . DIRECTORY_SEPARATOR . "MockResponseServer.php {$protocol} {$port} {$answerTimeout} {$packetToRespond}");
$clientData = [];
$loop->addTimer(0.001, function (Timer $timer) use ($process, $closure, $port, &$clientData) {
$process->start($timer->getLoop());
$process->stdout->on('data', function ($output) use (&$clientData) {
$clientData[] = $output;
});
if (strpos(PHP_OS, 'WIN') === false || getenv('MOCKSERVER_TIMEOUT_USEC') !== false) {
// wait to spin up. needed for linux. unnessecary on Windows 10.
// Ugly but even with 150ms sleep test run faster on Linux
usleep(getenv('MOCKSERVER_TIMEOUT_USEC') ?: 150000);
}
$closure($port);
});
$loop->run();
return $clientData;
}
}

@ -0,0 +1,67 @@
<?php
namespace Tests\ModbusMaster;
use InvalidArgumentException;
use PHPModbus\ModbusMaster;
use PHPModbus\ModbusMasterTcp;
class ModbusExceptionTest extends MockServerTestCase
{
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Unknown socket protocol, should be 'TCP' or 'UDP'
*/
public function testThrowProtocolMismatchException()
{
$modbus = new ModbusMaster('127.0.0.1', 'Mismatch');
$modbus->readCoils(0, 256, 1);
}
/**
* @expectedException \PHPModbus\Network\IOException
* @expectedExceptionMessage Unable to create client socket to
*/
public function testPortClosedException()
{
$modbus = new ModbusMasterTcp('127.0.0.1');
$modbus->setSocketTimeout(0.2, 0.2);
$modbus->readCoils(0, 256, 1);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessageRegExp /Watchdog time expired \[ 0\.5 sec \]!!! Connection to 127\.0\.0\.1:.* is not established/
*/
public function testTimeoutException()
{
$mockResponse = '89130000000400010101'; // respond with 1 byte (00000001 bits set) [1]
static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'UDP');
$modbus->port = $port;
$modbus->setTimeout(0.5);
$modbus->setSocketTimeout(0.2, 0.2);
$modbus->readCoils(0, 256, 1);
}, 'UDP', 1);
}
/**
* @expectedException \PHPModbus\ModbusException
* @expectedExceptionMessage Modbus response error code: 3 (ILLEGAL DATA VALUE)
*/
public function testThrowIllegalDataValueException()
{
$mockResponse = 'da8700000003008303'; // respond with 1 WORD (2 bytes) [0, 3]
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMaster('127.0.0.1', 'TCP');
$modbus->port = $port;
//can not query more than 124 WORDs. Wago response is ILLEGAL DATA VALUE
$this->assertEquals([0, 3], $modbus->readMultipleRegisters(0, 256, 140));
});
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('0000000600030100008c', $packetWithoutTransactionId);
}
}

@ -0,0 +1,23 @@
<?php
namespace Tests\ModbusMaster;
use PHPModbus\ModbusMaster;
use PHPModbus\ModbusMasterUdp;
class UdpFc1ReadCoilsTest extends MockServerTestCase
{
public function testUdpFc1Read1Coil()
{
$mockResponse = '89130000000400010101';
$clientData = static::executeWithMockServer($mockResponse, function ($port) {
$modbus = new ModbusMasterUdp('127.0.0.1');
$modbus->port = $port;
usleep(150000); // no idea how to fix this. wait for server to "warm" up or modbus UDP socket will timeout. does not occur with TCP
$this->assertEquals([1], $modbus->readCoils(0, 256, 1));
}, 'UDP');
$packetWithoutTransactionId = substr($clientData[0], 4);
$this->assertEquals('00000006000101000001', $packetWithoutTransactionId);
}
}

@ -1,11 +0,0 @@
@echo off
call ../config.bat
for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html"
cd output
for %%f in (*.html) do %diff% "%%f" ../ref/"%%f"
cd ..
pause
@echo on

@ -1,72 +0,0 @@
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 1
[6] => 0
[7] => 255
[8] => 0
[9] => 255
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 255
[5] => 255
[6] => 127
[7] => 255
[8] => 128
[9] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 255
[9] => 255
[10] => 255
[11] => 255
[12] => 255
[13] => 255
[14] => 127
[15] => 255
[16] => 0
[17] => 0
[18] => 128
[19] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 0
[6] => 63
[7] => 128
[8] => 0
[9] => 0
[10] => 192
[11] => 0
[12] => 170
[13] => 171
[14] => 62
[15] => 170
[16] => 0
[17] => 0
[18] => 65
[19] => 200
)

@ -1 +0,0 @@
writeMultipleRegister (FC26): DONE

@ -1 +0,0 @@
Caught exception: Unknown socket protocol, should be 'TCP' or 'UDP'

@ -1,72 +0,0 @@
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 1
[6] => 0
[7] => 255
[8] => 0
[9] => 255
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 255
[5] => 255
[6] => 127
[7] => 255
[8] => 128
[9] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 255
[9] => 255
[10] => 255
[11] => 255
[12] => 255
[13] => 255
[14] => 127
[15] => 255
[16] => 0
[17] => 0
[18] => 128
[19] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 0
[6] => 63
[7] => 128
[8] => 0
[9] => 0
[10] => 192
[11] => 0
[12] => 170
[13] => 171
[14] => 62
[15] => 170
[16] => 0
[17] => 0
[18] => 65
[19] => 200
)

@ -1 +0,0 @@
writeMultipleRegister (FC26): DONE

@ -1,44 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMaster.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMaster($test_host_ip, "TCP");
// Data to be writen - BYTE
$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1);
$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - INT
$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15));
$dataTypes = array("INT", "INT", "INT", "INT", "INT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - DINT
$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31));
$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
// Data to be writen - REAL
$data = array(0, 1, -2, 1/3, 25);
$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
?>

@ -1,24 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMaster.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMaster($test_host_ip, "TCP");
// Data to be writen
$data = array(1000, 2000, 1.250, 1.250);
$dataTypes = array("REAL", "REAL", "REAL", "REAL");
// FC23
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes);
if(!$recData) {
// Print error information if any
echo "</br>Error:</br>" . $modbus->errstr . "</br>";
//
exit();
}
// Print status information
echo "writeMultipleRegister (FC26): DONE";
?>

@ -1,22 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMaster.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMaster($test_host_ip, "Mismatch");
// Data to be writen
$data = array(1000, 2000, 1.250, 1.250);
$dataTypes = array("REAL", "REAL", "REAL", "REAL");
// FC23
try {
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
exit();
}
// Should through an Exception
// Print status information
echo "Should never reach this line!";

@ -1,44 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMaster.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMaster($test_host_ip, "UDP");
// Data to be writen - BYTE
$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1);
$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - INT
$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15));
$dataTypes = array("INT", "INT", "INT", "INT", "INT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - DINT
$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31));
$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
// Data to be writen - REAL
$data = array(0, 1, -2, 1/3, 25);
$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
?>

@ -1,24 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMaster.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMaster($test_host_ip, "UDP");
// Data to be writen
$data = array(1000, 2000, 1.250, 1.250);
$dataTypes = array("REAL", "REAL", "REAL", "REAL");
// FC23
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes);
if(!$recData) {
// Print error information if any
echo "</br>Error:</br>" . $modbus->errstr . "</br>";
//
exit();
}
// Print status information
echo "writeMultipleRegister (FC26): DONE";
?>

@ -1,11 +0,0 @@
@echo off
call ../config.bat
for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html"
cd output
for %%f in (*.html) do %diff% "%%f" ../ref/"%%f"
cd ..
pause
@echo on

@ -1,72 +0,0 @@
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 1
[6] => 0
[7] => 255
[8] => 0
[9] => 255
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 255
[5] => 255
[6] => 127
[7] => 255
[8] => 128
[9] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 255
[9] => 255
[10] => 255
[11] => 255
[12] => 255
[13] => 255
[14] => 127
[15] => 255
[16] => 0
[17] => 0
[18] => 128
[19] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 0
[6] => 63
[7] => 128
[8] => 0
[9] => 0
[10] => 192
[11] => 0
[12] => 170
[13] => 171
[14] => 62
[15] => 170
[16] => 0
[17] => 0
[18] => 65
[19] => 200
)

@ -1 +0,0 @@
writeMultipleRegister (FC26): DONE

@ -1,44 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterTcp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterTcp($test_host_ip);
// Data to be writen - BYTE
$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1);
$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - INT
$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15));
$dataTypes = array("INT", "INT", "INT", "INT", "INT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - DINT
$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31));
$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
// Data to be writen - REAL
$data = array(0, 1, -2, 1/3, 25);
$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
?>

@ -1,24 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterTcp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterTcp($test_host_ip);
// Data to be writen
$data = array(1000, 2000, 1.250, 1.250);
$dataTypes = array("REAL", "REAL", "REAL", "REAL");
// FC23
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes);
if(!$recData) {
// Print error information if any
echo "</br>Error:</br>" . $modbus->errstr . "</br>";
//
exit();
}
// Print status information
echo "writeMultipleRegister (FC26): DONE";
?>

@ -1,11 +0,0 @@
@echo off
call ../config.bat
for %%f in (test.*.php) do %php% -q "%%f" > "output/%%f.html"
cd output
for %%f in (*.html) do %diff% "%%f" ../ref/"%%f"
cd ..
pause
@echo on

@ -1,66 +0,0 @@
array(32) {
[0]=>
bool(true)
[1]=>
bool(false)
[2]=>
bool(true)
[3]=>
bool(true)
[4]=>
bool(false)
[5]=>
bool(true)
[6]=>
bool(true)
[7]=>
bool(true)
[8]=>
bool(true)
[9]=>
bool(true)
[10]=>
bool(true)
[11]=>
bool(true)
[12]=>
bool(false)
[13]=>
bool(false)
[14]=>
bool(false)
[15]=>
bool(false)
[16]=>
bool(false)
[17]=>
bool(false)
[18]=>
bool(false)
[19]=>
bool(false)
[20]=>
bool(true)
[21]=>
bool(true)
[22]=>
bool(true)
[23]=>
bool(true)
[24]=>
bool(true)
[25]=>
bool(true)
[26]=>
bool(true)
[27]=>
bool(true)
[28]=>
bool(true)
[29]=>
bool(true)
[30]=>
bool(true)
[31]=>
bool(true)
}

@ -1,72 +0,0 @@
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 1
[6] => 0
[7] => 255
[8] => 0
[9] => 255
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 255
[5] => 255
[6] => 127
[7] => 255
[8] => 128
[9] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 255
[9] => 255
[10] => 255
[11] => 255
[12] => 255
[13] => 255
[14] => 127
[15] => 255
[16] => 0
[17] => 0
[18] => 128
[19] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 0
[6] => 63
[7] => 128
[8] => 0
[9] => 0
[10] => 192
[11] => 0
[12] => 170
[13] => 171
[14] => 62
[15] => 170
[16] => 0
[17] => 0
[18] => 65
[19] => 200
)

@ -1,72 +0,0 @@
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 1
[6] => 0
[7] => 255
[8] => 0
[9] => 255
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 1
[4] => 255
[5] => 255
[6] => 127
[7] => 255
[8] => 128
[9] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 1
[6] => 0
[7] => 0
[8] => 255
[9] => 255
[10] => 255
[11] => 255
[12] => 255
[13] => 255
[14] => 127
[15] => 255
[16] => 0
[17] => 0
[18] => 128
[19] => 0
)
Array
(
[0] => 0
[1] => 0
[2] => 0
[3] => 0
[4] => 0
[5] => 0
[6] => 63
[7] => 128
[8] => 0
[9] => 0
[10] => 192
[11] => 0
[12] => 170
[13] => 171
[14] => 62
[15] => 170
[16] => 0
[17] => 0
[18] => 65
[19] => 200
)

@ -1,7 +0,0 @@
Test should pass when %IX0.0==FALSE and %IX0.1==TRUE
array(2) {
[0]=>
bool(false)
[1]=>
bool(true)
}

@ -1 +0,0 @@
writeMultipleRegister (FC26): DONE

@ -1 +0,0 @@
writeMultipleRegister (FC26): DONE

@ -1,11 +0,0 @@
Test should pass when %IX0.0==FALSE and %IX0.1==TRUE
array(4) {
[0]=>
int(0)
[1]=>
int(2)
[2]=>
int(0)
[3]=>
int(0)
}

@ -1,5 +0,0 @@
Array
(
[0] => 0
[1] => 5
)

@ -1,5 +0,0 @@
Array
(
[0] => 207
[1] => 199
)

@ -1,22 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterUdp($test_host_ip);
// Data to be writen - BOOL array
$data = array(1, 0, TRUE, TRUE, 0, 1, TRUE, TRUE,
1, 1, TRUE, TRUE, 0, 0, FALSE, FALSE,
0, 0, FALSE, FALSE, 1, 1, TRUE, TRUE,
1, 1, TRUE, TRUE, 1, 1, TRUE, TRUE);
// Write data - FC 15
$modbus->writeMultipleCoils(0, 12288, $data);
//echo $modbus->status;
//$modbus->status = "";
//echo "\n\n";
// Read data - FC 1
$recData = $modbus->readCoils(0, 12288, 32);
//echo $modbus->status;
//echo "\n\n";
var_dump($recData);

@ -1,44 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterUdp($test_host_ip);
// Data to be writen - BYTE
$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1);
$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - INT
$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15));
$dataTypes = array("INT", "INT", "INT", "INT", "INT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - DINT
$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31));
$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
// Data to be writen - REAL
$data = array(0, 1, -2, 1/3, 25);
$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
?>

@ -1,45 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterUdp($test_host_ip);
$modbus->client = $test_bind_client_ip;
// Data to be writen - BYTE
$data = array(0, 1, 1, pow(2,8)-1, pow(2,8)-1);
$dataTypes = array("BYTE", "BYTE", "BYTE", "BYTE", "BYTE");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - INT
$data = array(0, 1, -1, pow(2,15)-1, -pow(2,15));
$dataTypes = array("INT", "INT", "INT", "INT", "INT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 5);
print_r($recData);
// Data to be writen - DINT
$data = array(0, 1, -1, pow(2,31)-1, -pow(2,31));
$dataTypes = array("DINT", "DINT", "DINT", "DINT", "DINT");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
// Data to be writen - REAL
$data = array(0, 1, -2, 1/3, 25);
$dataTypes = array("REAL", "REAL", "REAL", "REAL", "REAL");
// Write data - FC 16
$modbus->writeMultipleRegister(0, 12288, $data, $dataTypes);
// Read data - FC3
$recData = $modbus->readMultipleRegisters(0, 12288, 10);
print_r($recData);
?>

@ -1,14 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterUdp($test_host_ip);
// Test requirements
echo "Test should pass when %IX0.0==FALSE and %IX0.1==TRUE\n";
// Read input discretes - FC 2
$recData = $modbus->readInputDiscretes(0, 0, 2);
var_dump($recData);

@ -1,24 +0,0 @@
<?php
require_once dirname(__FILE__) . '/../../Phpmodbus/ModbusMasterUdp.php';
require_once dirname(__FILE__) . '/../config.php';
// Create Modbus object
$modbus = new ModbusMasterUdp($test_host_ip);
// Data to be writen
$data = array(1000, 2000, 1.250, 1.250);
$dataTypes = array("REAL", "REAL", "REAL", "REAL");
// FC23
$recData = $modbus->readWriteRegisters(0, 12288, 6, 12288, $data, $dataTypes);
if(!$recData) {
// Print error information if any
echo "</br>Error:</br>" . $modbus->errstr . "</br>";
//
exit();
}
// Print status information
echo "writeMultipleRegister (FC26): DONE";
?>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save