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/
*~
/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.
**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**
>
> This fork adds a namespace and fixes issues encountered when porting to PHP 7
##What's new
* 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
@ -25,7 +27,7 @@ Implementation of the basic functionality of the Modbus TCP and UDP based protoc
## Requirements
* The PHP extension php_sockets.dll should be enabled (server php.ini file)
* PHP 5.5+
## 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.
## 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

@ -4,8 +4,13 @@
"description": "PhpModbus with namespaces and updated to PHP 7",
"license": "LGPL",
"require": {
"php": "^5.3.2 || ^7.0",
"ext-sockets": "*"
"php": "^5.4 || ^7.0"
},
"require-dev": {
"react/socket": "~0.4.0",
"react/child-process": "^0.4.1",
"react/datagram": "^1.1",
"phpunit/phpunit": "^5.6"
},
"authors": [
{
@ -21,5 +26,15 @@
"psr-4": {
"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.
They may not work. Just for reference.
Do not expose this folder publicly as you read/write modbus data with query parameters

@ -1,65 +1,75 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster;
use PHPModbus\PhpType;
// Create Modbus object
$ip = "192.192.15.51";
$modbus = new ModbusMaster($ip, "UDP");
$ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$quantity = ((int)$_GET['quantity']) ?: 6;
$modbus = new ModbusMaster($ip, 'UDP');
try {
// FC 3
$moduleId = 0;
$reference = 12288;
$mw0address = 12288;
$quantity = 6;
$recData = $modbus->readMultipleRegisters($moduleId, $reference, $quantity);
// FC 3
$recData = $modbus->readMultipleRegisters($unitId, $reference, $quantity);
} catch (Exception $e) {
echo $modbus;
echo $e;
exit;
echo $modbus;
echo $e;
exit;
}
?>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1250">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title>WAGO 750-841 M-memory dump</title>
</head>
<body>
<h1>Dump of M-memory from WAGO 750-84x series coupler.</h1>
<ul>
<li>PLC: 750-84x series</li>
<li>IP: <?php echo $ip ?></li>
<li>Modbus module ID: <?php echo $moduleId ?></li>
<li>Modbus memory reference: <?php echo $reference ?></li>
<li>Modbus memory quantity: <?php echo $quantity ?></li>
</ul>
<h2>M-memory dump</h2>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1250">
<meta name="generator" content="PSPad editor, www.pspad.com">
<title>WAGO 750-841 M-memory dump</title>
</head>
<body>
<h1>Dump of M-memory from WAGO 750-84x series coupler.</h1>
<ul>
<li>PLC: 750-84x series</li>
<li>IP: <?php echo $ip ?></li>
<li>Modbus module ID: <?php echo $unitId ?></li>
<li>Modbus memory reference: <?php echo $reference ?></li>
<li>Modbus memory quantity: <?php echo $quantity ?></li>
</ul>
<table border="1px" width="400px">
<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>M-memory dump</h2>
<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>

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

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

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

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

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

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

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

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

@ -1,13 +1,18 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMasterUdp;
// Create Modbus object
$modbus = new ModbusMasterUdp("192.192.15.51");
$ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 0;
$quantity = ((int)$_GET['quantity']) ?: 2;
$modbus = new ModbusMasterUdp($ip);
try {
// Read input discretes - FC 4
$recData = $modbus->readMultipleInputRegisters(0, 0, 2);
$recData = $modbus->readMultipleInputRegisters($unitId, $reference, $quantity);
} catch (Exception $e) {
// Print error information if any
echo $modbus;
@ -15,4 +20,9 @@ try {
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
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMasterUdp;
// Create Modbus object
$modbus = new ModbusMasterUdp("192.192.15.51");
$ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '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
$data_true = array(true);
$data_false = array(false);
$modbus = new ModbusMasterUdp($ip);
try {
// Write single coil - FC5
$modbus->writeSingleCoil(0, 12288, $data_true);
$modbus->writeSingleCoil(0, 12289, $data_false);
$modbus->writeSingleCoil(0, 12290, $data_true);
$modbus->writeSingleCoil(0, 12291, $data_false);
// Write single coil - FC5
$recData = $modbus->writeSingleCoil($unitId, $reference, [$value]);
} catch (Exception $e) {
// Print error information if any
echo $modbus;
echo $e;
exit;
// Print error information if any
echo $modbus;
echo $e;
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
require __DIR__ . '/../vendor/autoload.php';
use PHPModbus\ModbusMaster;
use PHPModbus\ModbusMasterUdp;
// Create Modbus object
$modbus = new ModbusMaster("192.192.15.51", "UDP");
$ip = filter_var($_GET['ip'], FILTER_VALIDATE_IP) ? $_GET['ip'] : '192.192.15.51';
$unitId = ((int)$_GET['unitid']) ?: 0;
$reference = ((int)$_GET['reference']) ?: 12288;
$value = ((int)$_GET['value']) ?: -1000;
// Data to be writen
$data = array(-1000);
$modbus = new ModbusMasterUdp($ip);
try {
// FC6
$modbus->writeSingleRegister(0, 12288, $data);
// FC6
$recData = $modbus->writeSingleRegister($unitId, $reference, [$value]);
} catch (Exception $e) {
// Print error information if any
echo $modbus;
echo $e;
exit;
// Print error information if any
echo $modbus;
echo $e;
exit;
}
// Print status information
echo $modbus;
echo '<h1>Status</h1><pre>';
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
* data types to a IEC data type.
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2010 Jan Krakora
* @package Phpmodbus
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2010 Jan Krakora
* @package Phpmodbus
*/
class IecType
{
/**
* iecBYTE
*
* Converts a value to IEC-1131 BYTE data type
*
* @param int $value from 0 to 255
* @return int IEC BYTE data type
*
*/
public static function iecBYTE($value)
{
return chr($value & 0xFF);
}
/**
* iecINT
*
* Converts a value to IEC-1131 INT data type
*
* @param int $value to be converted
* @return int IEC-1131 INT data type
*/
public static function iecINT($value)
{
return self::iecBYTE(($value >> 8) & 0x00FF) .
self::iecBYTE($value & 0x00FF);
}
/**
* iecINT
*
* Converts a value to IEC-1131 INT data type
*
* @param int $value to be converted
* @return int IEC-1131 INT data type
*
*/
public static function iecINT($value)
{
return self::iecBYTE(($value >> 8) & 0x00FF) .
self::iecBYTE(($value & 0x00FF));
}
/**
* iecBYTE
*
* Converts a value to IEC-1131 BYTE data type
*
* @param int $value from 0 to 255
* @return int IEC BYTE data type
*/
public static function iecBYTE($value)
{
return chr($value & 0xFF);
}
/**
* iecDINT
*
* Converts a value to IEC-1131 DINT data type
*
* @param int $value to be converted
* @param int $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @return int IEC-1131 INT data type
*
*/
public static function iecDINT($value, $bigEndian = 0)
{
// result with right endianness
return self::endianness($value, $bigEndian);
}
/**
* iecDINT
*
* Converts a value to IEC-1131 DINT data type
*
* @param int $value to be converted
* @param int $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @return int IEC-1131 INT data type
*/
public static function iecDINT($value, $bigEndian = 0)
{
// result with right endianness
return self::endianness($value, $bigEndian);
}
/**
* iecREAL
*
* Converts a value to IEC-1131 REAL data type. The function uses function @use float2iecReal.
*
* @param int $value to be converted
* @param bool $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @return int IEC-1131 REAL data type
*/
public static function iecREAL($value, $bigEndian = 0)
{
// iecREAL representation
$real = self::float2iecReal($value);
// result with right endianness
return self::endianness($real, $bigEndian);
}
/**
* endianness
*
* Make endianess as required.
* For more see http://en.wikipedia.org/wiki/Endianness
*
* @param int $value
* @param bool $bigEndian
* @return int
*/
private static function endianness($value, $bigEndian = 0)
{
if ($bigEndian == 0) {
return
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
*
* This function converts float value to IEC-1131 REAL single precision form.
*
* 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
* @ Georgia Tech 21-Nov-2007] or
* [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}]
*
* @param float $value to be converted
* @return int IEC REAL data type
*/
private static function float2iecReal($value)
{
// get float binary string
$float = pack("f", $value);
// set 32-bit unsigned integer of the float
$w = unpack("L", $float);
return $w[1];
}
/**
* iecREAL
*
* Converts a value to IEC-1131 REAL data type. The function uses function @use float2iecReal.
*
* @param int $value to be converted
* @param bool $bigEndian defines endian codding (little endian == 0, big endian == 1)
* @return int IEC-1131 REAL data type
*/
public static function iecREAL($value, $bigEndian = 0)
{
// iecREAL representation
$real = self::float2iecReal($value);
// result with right endianness
return self::endianness($real, $bigEndian);
}
/**
* endianness
*
* Make endianess as required.
* For more see http://en.wikipedia.org/wiki/Endianness
*
* @param int $value
* @param bool $bigEndian
* @return int
*/
private static function endianness($value, $bigEndian = 0)
{
if ($bigEndian == 0) {
return
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
*
* This function converts float value to IEC-1131 REAL single precision form.
*
* 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
* @ Georgia Tech 21-Nov-2007] or
* [{@link http://www.php.net/manual/en/function.pack.php PHP pack/unpack functionality}]
*
* @param float $value to be converted
* @return int IEC REAL data type
*/
private static function float2iecReal($value)
{
// get float binary string
$float = pack('f', $value);
// set 32-bit unsigned integer of the float
$w = unpack('L', $float);
return $w[1];
}
}

@ -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
* with this package in the file license.txt.
*
*
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
/**
@ -23,29 +21,21 @@ namespace PHPModbus;
*
* This class deals with the MODBUS master using TCP. Extends ModbusMaster class.
*
* Implemented MODBUS functions:
* - FC 1: read coils
* - FC 3: read multiple registers
* - 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
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*/
class ModbusMasterTcp extends ModbusMaster
{
/**
* ModbusMasterTcp
*
* 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".
*/
public function __construct($host)
{
parent::__construct($host, "TCP");
}
/**
* ModbusMasterTcp
*
* 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".
*/
public function __construct($host)
{
parent::__construct($host, "TCP");
}
}

@ -8,14 +8,12 @@ namespace PHPModbus;
* This source file is subject to the "PhpModbus license" that is bundled
* with this package in the file license.txt.
*
*
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @license PhpModbus license
* @category Phpmodbus
* @tutorial Phpmodbus.pkg
* @package Phpmodbus
* @version $id$
*/
/**
@ -23,29 +21,21 @@ namespace PHPModbus;
*
* This class deals with the MODBUS master using UDP stack.
*
* Implemented MODBUS master functions:
* - FC 1: read coils
* - FC 3: read multiple registers
* - 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
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*/
class ModbusMasterUdp extends ModbusMaster
{
/**
* ModbusMasterUdp
*
* 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".
*/
public function __construct($host)
{
parent::__construct($host, "UDP");
}
/**
* ModbusMasterUdp
*
* 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".
*/
public function __construct($host)
{
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
namespace PHPModbus;
use Exception;
/**
@ -15,7 +16,6 @@ use Exception;
* @category Phpmodbus
* @package Phpmodbus
* @version $id$
*
*/
/**
@ -24,243 +24,243 @@ use Exception;
* 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.
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*
* @author Jan Krakora
* @copyright Copyright (c) 2004, 2012 Jan Krakora
* @package Phpmodbus
*/
class PhpType
{
/**
* bytes2float
*
* The function converts array of 4 bytes to float. The return value
* depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return float
*/
public static function bytes2float($values, $bigEndian = 0)
{
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$real = self::combineBytes($data, $bigEndian);
// Convert the real value to float
return (float)self::real2float($real);
}
/**
* bytes2float
*
* The function converts array of 4 bytes to float. The return value
* depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return float
*/
public static function bytes2float($values, $bigEndian = 0)
{
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$real = self::combineBytes($data, $bigEndian);
// Convert the real value to float
return (float)self::real2float($real);
}
/**
* bytes2signedInt
*
* The function converts array of 2 or 4 bytes to signed integer.
* The return value depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return int
*/
public static function bytes2signedInt($values, $bigEndian = 0)
{
$data = array();
$int = 0;
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$int = self::combineBytes($data, $bigEndian);
// In the case of signed 2 byte value convert it to 4 byte one
if ((count($values) == 2) && ((0x8000 & $int) > 0)) {
$int = 0xFFFF8000 | $int;
}
// Convert the value
return (int)self::dword2signedInt($int);
}
/**
* bytes2signedInt
*
* The function converts array of 2 or 4 bytes to signed integer.
* The return value depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return int
*/
public static function bytes2signedInt($values, $bigEndian = 0)
{
$data = array();
$int = 0;
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$int = self::combineBytes($data, $bigEndian);
// In the case of signed 2 byte value convert it to 4 byte one
if ((count($values) === 2) && ((0x8000 & $int) > 0)) {
$int = 0xFFFF8000 | $int;
}
// Convert the value
return (int)self::dword2signedInt($int);
}
/**
* bytes2unsignedInt
*
* The function converts array of 2 or 4 bytes to unsigned integer.
* The return value depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return int|float
*/
public static function bytes2unsignedInt($values, $bigEndian = 0)
{
$data = array();
$int = 0;
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$int = self::combineBytes($data, $bigEndian);
// Convert the value
return self::dword2unsignedInt($int);
}
/**
* bytes2unsignedInt
*
* The function converts array of 2 or 4 bytes to unsigned integer.
* The return value depends on order of the input bytes (endianning).
*
* @param array $values
* @param bool $bigEndian
* @return int|float
*/
public static function bytes2unsignedInt($values, $bigEndian = 0)
{
$data = array();
$int = 0;
// Set the array to correct form
$data = self::checkData($values);
// Combine bytes
$int = self::combineBytes($data, $bigEndian);
// Convert the value
return self::dword2unsignedInt($int);
}
/**
* bytes2string
*
* 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.
*
* @param array $values
* @param bool $bigEndian
* @return string
*/
public static function bytes2string($values, $bigEndian = 0)
{
// Prepare string variable
$str = "";
// Parse the received data word array
for ($i = 0; $i < count($values); $i += 2) {
if ($bigEndian) {
if ($values[$i] != 0) {
$str .= chr($values[$i]);
} else {
break;
}
if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]);
} else {
break;
}
} else {
if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]);
} else {
break;
}
if ($values[$i] != 0) {
$str .= chr($values[$i]);
} else {
break;
}
}
}
// return string
return $str;
}
/**
* bytes2string
*
* 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.
*
* @param array $values
* @param bool $bigEndian
* @return string
*/
public static function bytes2string($values, $bigEndian = 0)
{
// Prepare string variable
$str = "";
// Parse the received data word array
for ($i = 0; $i < count($values); $i += 2) {
if ($bigEndian) {
if ($values[$i] != 0) {
$str .= chr($values[$i]);
} else {
break;
}
if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]);
} else {
break;
}
} else {
if ($values[$i + 1] != 0) {
$str .= chr($values[$i + 1]);
} else {
break;
}
if ($values[$i] != 0) {
$str .= chr($values[$i]);
} else {
break;
}
}
}
// return string
return $str;
}
/**
* real2float
*
* 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
* [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes
* @ Georgia Tech 21-Nov-2007] or
* [{@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
* @return float float value
*/
private static function real2float($value)
{
// get unsigned long
$ulong = pack("L", $value);
// set float
$float = unpack("f", $ulong);
/**
* real2float
*
* 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
* [{@link http://de.php.net/manual/en/function.base-convert.php PHP base_convert function commentary}, Todd Stokes
* @ Georgia Tech 21-Nov-2007] or
* [{@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
* @return float float value
*/
private static function real2float($value)
{
// get unsigned long
$ulong = pack('L', $value);
// set float
$float = unpack('f', $ulong);
return $float[1];
}
return $float[1];
}
/**
* dword2signedInt
*
* Switch double word to signed integer
*
* @param int $value
* @return int
*/
private static function dword2signedInt($value)
{
if ((0x80000000 & $value) != 0) {
return -(0x7FFFFFFF & ~$value) - 1;
} else {
return (0x7FFFFFFF & $value);
}
}
/**
* dword2signedInt
*
* Switch double word to signed integer
*
* @param int $value
* @return int
*/
private static function dword2signedInt($value)
{
if ((0x80000000 & $value) !== 0) {
return -(0x7FFFFFFF & ~$value) - 1;
} else {
return (0x7FFFFFFF & $value);
}
}
/**
* dword2signedInt
*
* Switch double word to unsigned integer
*
* @param int $value
* @return int|float
*/
private static function dword2unsignedInt($value)
{
if ((0x80000000 & $value) != 0) {
return ((float)(0x7FFFFFFF & $value)) + 2147483648;
} else {
return (int)(0x7FFFFFFF & $value);
}
}
/**
* dword2signedInt
*
* Switch double word to unsigned integer
*
* @param int $value
* @return int|float
*/
private static function dword2unsignedInt($value)
{
if ((0x80000000 & $value) !== 0) {
return ((float)(0x7FFFFFFF & $value)) + 2147483648;
} else {
return (int)(0x7FFFFFFF & $value);
}
}
/**
* checkData
*
* Check if the data variable is array, and check if the values are numeric
*
* @param int[] $data
* @return int
* @throws Exception
*/
private static function checkData($data)
{
// Check the data
if (!is_array($data) ||
count($data) < 2 ||
count($data) > 4 ||
count($data) == 3
) {
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) {
$data[2] = 0;
$data[3] = 0;
}
// Check the values to be number
if (!is_numeric($data[0]) ||
!is_numeric($data[1]) ||
!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');
}
/**
* checkData
*
* Check if the data variable is array, and check if the values are numeric
*
* @param int[] $data
* @return array|\int[]
* @throws Exception
*/
private static function checkData($data)
{
// Check the data
$count = count($data);
if (!is_array($data)
|| $count < 2
|| $count > 4
|| $count === 3
) {
throw new Exception('The input data should be an array of 2 or 4 bytes.');
}
// Fill the rest of array by zeroes
if ($count === 2) {
$data[2] = 0;
$data[3] = 0;
}
// Check the values to be number
if (!is_numeric($data[0])
|| !is_numeric($data[1])
|| !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');
}
return $data;
}
return $data;
}
/**
* combineBytes
*
* Combine bytes together
*
* @param int $data
* @param bool $bigEndian
* @return int
*/
private static function combineBytes($data, $bigEndian)
{
$value = 0;
// Combine bytes
if ($bigEndian == 0) {
$value = (($data[3] & 0xFF) << 16) |
(($data[2] & 0xFF) << 24) |
(($data[1] & 0xFF)) |
(($data[0] & 0xFF) << 8);
} else {
$value = (($data[3] & 0xFF) << 24) |
(($data[2] & 0xFF) << 16) |
(($data[1] & 0xFF) << 8) |
(($data[0] & 0xFF));
}
/**
* combineBytes
*
* Combine bytes together
*
* @param int $data
* @param bool $bigEndian
* @return int
*/
private static function combineBytes($data, $bigEndian)
{
$value = 0;
// Combine bytes
if ($bigEndian == 0) {
$value = (($data[3] & 0xFF) << 16) |
(($data[2] & 0xFF) << 24) |
($data[1] & 0xFF) |
(($data[0] & 0xFF) << 8);
} else {
$value = (($data[3] & 0xFF) << 24) |
(($data[2] & 0xFF) << 16) |
(($data[1] & 0xFF) << 8) |
($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