PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/PHPModbusTcp/class/PHPModbusTcp.php

https://github.com/kohntark/PHPModbusTCP
PHP | 761 lines | 213 code | 197 blank | 351 comment | 47 complexity | cad2c1f91eeb7dd1c9ef2ad91858c198 MD5 | raw file
  1. <?php
  2. /**
  3. * PHPModbusTcp
  4. *
  5. * @author Kohntark <kohntark@kohntark.fr> <kohntark@kohntark.fr>
  6. *
  7. * @link http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf MODBUS MESSAGING ON TCP/IP IMPLEMENTATION GUIDE V1.0b
  8. * @link http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf MODBUS APPLICATION PROTOCOL SPECIFICATION V1.1b
  9. * @link http://www.modbus.org/docs/MBConformanceTestSpec_v3.0.pdf Modbus Conformance Test Specifications v3.0
  10. *
  11. */
  12. /**
  13. *
  14. */
  15. class PHPModbusTcp {
  16. // mode debug
  17. const DEBUG = false;
  18. /**#@+
  19. *
  20. * @access public
  21. */
  22. /**
  23. * @var int $iStartAddress adresse de départ
  24. */
  25. public $iStartAddress = 0;
  26. /**
  27. * @var int $iQuantity quantité d'adresses succesives à lire
  28. */
  29. public $iQuantity = 16;
  30. /**#@-*/
  31. /**#@+
  32. *
  33. * @access protected
  34. */
  35. /**
  36. * @var string $sDeviceIp adresse IP de l'équipement
  37. */
  38. protected $sDeviceIp;
  39. /**
  40. *
  41. * @var int $iDevicePort port de l'équipement
  42. */
  43. protected $iDevicePort;
  44. /**
  45. *
  46. * @var int $iRcvTimeout timeout pour la reception
  47. */
  48. protected $iRcvTimeout;
  49. /**
  50. *
  51. * @var int $iSndTimeout timeout pour l'envoi
  52. */
  53. protected $iSndTimeout;
  54. /**
  55. *
  56. * (for Modbus Serial gateway or for reding registers)
  57. * The host client uses 255 as the default value for Unit Identifier
  58. * The range of acceptable addresses
  59. * for Modbus Serial devices is 1 to 247.
  60. *
  61. * @var $iDeviceId ID équipement ou pour accès registres
  62. * @todo à implémenter
  63. */
  64. protected $iDeviceId = 255;
  65. protected $aReceivedFrame = array();
  66. /**
  67. *
  68. * @var array $aModbusException tableau des exceptions Modbus
  69. *
  70. * @todo
  71. */
  72. protected $aMBException = array (
  73. 1 => 'Illegal Function Code. The function code is unknown by the server',
  74. 2 => 'Illegal Data Address. Dependant on the request',
  75. 3 => 'Illegal Data Value. Dependant on the request',
  76. 4 => 'Server Failure. The server failed during the execution',
  77. 5 => 'Acknowledge. The server accepted the service invocation but the service requires a relatively long time to execute. The server therefore returns only an acknowledgement of the service invocation receipt.',
  78. 6 => 'Server Busy. The server was unable to accept the MB Request PDU. The client application has the responsibility of deciding if and when to re-send the request.',
  79. 10 => 'Gateway problem. Gateway paths not available.',
  80. 11 => 'Gateway problem. The targeted device failed to respond. The gateway generates this exception'
  81. );
  82. /**#@-*/
  83. /**#@+
  84. *
  85. * @access private
  86. */
  87. /**
  88. *
  89. * @var resource socket
  90. */
  91. private $rSocket;
  92. /**#@-*/
  93. /**
  94. *
  95. * @param string $sDeviceIp IP de l'équipement modbus
  96. * @param int $iDevicePort port de l'équipement modbus
  97. * @param int optional $iSndTimeout timeout envoi
  98. * @param int optional $iRcvTimeout timeout reception
  99. *
  100. *
  101. * @todo
  102. * - gestion IPV6
  103. * - gestion modbus / Jbus
  104. * - gestion routage
  105. */
  106. public function __construct($sDeviceIp, $iDevicePort = 502, $iDeviceId = 255, $iSndTimeout = 400, $iRcvTimeout = 300) {
  107. /*
  108. * crée le tableau à passer à la fonction filter_var_array()
  109. */
  110. $vars = compact('sDeviceIp', 'iDevicePort', 'iDeviceId', 'iSndTimeout', 'iRcvTimeout');
  111. /*
  112. * filtres à appliquer aux arguments passés
  113. */
  114. $filters = array(
  115. 'sDeviceIp' => array('filter' => FILTER_VALIDATE_IP, 'flags' => FILTER_FLAG_IPV4),
  116. 'iDevicePort' => FILTER_VALIDATE_INT,
  117. 'iDeviceId' => FILTER_VALIDATE_INT,
  118. 'iSndTimeout' => FILTER_VALIDATE_INT,
  119. 'iRcvTimeout' => FILTER_VALIDATE_INT
  120. );
  121. /*
  122. * contrôle que les arguments passés à l'objet son corrects
  123. */
  124. if (in_array(FALSE, filter_var_array($vars, $filters))) throw new Exception("arguments passés incorrects : ".implode(', ', func_get_args()));
  125. // ID de l'équipement
  126. $this->iDeviceId = $iDeviceId;
  127. /*
  128. * création de la socket
  129. */
  130. if (false === $this->rSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
  131. throw new Exception(socket_strerror(socket_last_error()));
  132. }
  133. /*
  134. * option de la socket
  135. */
  136. //socket_set_option($this->rSocket, SOL_SOCKET, SO_KEEPALIVE, 300);
  137. socket_set_option($this->rSocket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => (int)($iSndTimeout / 1000), 'usec' => (int)($iSndTimeout%1000)*1000));
  138. socket_set_option($this->rSocket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => (int)($iRcvTimeout / 1000), 'usec' => (int)($iRcvTimeout%1000)*1000));
  139. //socket_set_option($this->rSocket, SOL_SOCKET, SO_REUSEADDR, 1);
  140. if (false === socket_connect($this->rSocket, $sDeviceIp, $iDevicePort)) {
  141. $this->socketClose();
  142. throw new Exception(socket_strerror(socket_last_error()));
  143. }
  144. $linger = array('l_linger' => 0, 'l_onoff' => 0);
  145. socket_set_option($this->rSocket, SOL_SOCKET, SO_LINGER, $linger);
  146. // contrôle d'éventuelles erreurs
  147. if (socket_last_error($this->rSocket) != 0)
  148. throw new Exception(socket_strerror(socket_last_error($this->rSocket)));
  149. }
  150. /**
  151. * connexion à l'équipement distant
  152. */
  153. public function connect() {
  154. return true;
  155. if (false === socket_connect($this->rSocket, $this->sDeviceIp, $this->iDevicePort)) {
  156. $this->socketClose();
  157. throw new Exception(socket_strerror(socket_last_error()));
  158. }
  159. }
  160. /**
  161. * écrit les données $aOutBuf sur la socket
  162. *
  163. * @param array $sOutBuf tableau de la trame à écrire
  164. * return mixed false si erreur, nombre d'octets écrits sur la socket si réussi
  165. */
  166. public function writeSocket($aFrameToSend) {
  167. return socket_write($this->rSocket, implode("", $aFrameToSend ));
  168. }
  169. /**
  170. * lir les données de la socket
  171. *
  172. * @return string
  173. */
  174. function readSocket () {
  175. return $sRecBuf = socket_read($this->rSocket, 2048);
  176. }
  177. /**
  178. * Lis l'état d'une plage de sorties TOR
  179. *
  180. * @param int $iStartAddress adresse de début de lecture
  181. * @param int $iQuantity nombre d'entrées à lire
  182. * @return array résultat de la lecture sous la forme (dec)adresse=>(bool)valeur
  183. */
  184. public function readCoils($iStartAddress, $iQuantity) {
  185. if (!is_numeric($iStartAddress) || !is_numeric($iQuantity))
  186. throw new Exception('paramètres incorrects : l\'adresse de début ou le nombre de mots à lire ne sont pas des entiers');
  187. if ($iQuantity > 2000) $iQuantity = 2000; // nb d'inputs max lisible dans le standard modbus
  188. if ($iQuantity == 0) $iQuantity = 1; // quantité d'adresses min à lire
  189. /*
  190. * trame à envoyer
  191. */
  192. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), 5=>chr(6), 6=>chr($this->iDeviceId), 7=>chr(1)) ;
  193. /*
  194. * adding datas (big endian) :
  195. * $obuf[8], $obuf[9] : start address
  196. * $obuf[10], $obuf[11] : quantity of address to read
  197. *
  198. */
  199. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iStartAddress);
  200. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes((int)$iQuantity);
  201. $this->writeSocket($this->aFrameToSend);
  202. $sBuf = $this->readSocket();
  203. /**
  204. * contrôle réponse
  205. * si le 8eme bit reçu (code fonction) n'est pas égal au 8eme envoyé il s'agit d'une exception MB (function code + 0x80)
  206. */
  207. if ($sBuf[7] != $this->aFrameToSend[7]) {
  208. // code de l'exception renvoyée
  209. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Start Adress : ".$iStartAddress." Quantity of outputs : ".$iQuantity);
  210. }
  211. //debug
  212. // echo 'nb de Bytes reçus : '.ord($abuf[8])."\r\n";
  213. // explose la chaîne reçue en un tableau de Bytes
  214. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  215. // ne conserve que les datas (== Byte 9 et suivants)
  216. $aDatasBytes = array_slice($this->aReceivedFrame, 9, ord($sBuf[8]));
  217. // converti chaque valeur (Byte) du tableau en format binaire
  218. $aDatasBits = array_map("Convert::ByteToBits", $aDatasBytes);
  219. // explose chaque bit de chaque octet dans un tableau unique
  220. $aDatasBits = preg_split('``', implode('', $aDatasBits), NULL, PREG_SPLIT_NO_EMPTY);
  221. // limite le nombre de bits demandés (le protocole MB n'envoyant que des octets complets)
  222. $aDatasBits = array_splice($aDatasBits, 0, $iQuantity);
  223. // crée le tableau des adresses
  224. $aAddress = range($iStartAddress, $iStartAddress + $iQuantity - 1);
  225. // retourne un tableau avec comme clés les adresses
  226. return array_combine($aAddress, $aDatasBits);
  227. }
  228. /**
  229. * Lis l'état d'entrées TOR
  230. *
  231. * @param int $iStartAddress adresse de début de lecture
  232. * @param int $iQuantity nombre d'entrées à lire
  233. * @return array résultat de la lecture sous la forme (dec)adresse=>(bool)valeur
  234. */
  235. public function readDiscreteInputs($iStartAddress, $iQuantity) {
  236. if (!is_numeric($iStartAddress) || !is_numeric($iQuantity))
  237. throw new Exception('paramètres incorrects : l\'adresse de début ou le nombre de mots à lire ne sont pas des entiers');
  238. if ($iQuantity > 2000) $iQuantity = 2000; // nb d'inputs max lisible dans le standard modbus
  239. if ($iQuantity == 0) $iQuantity = 1; // quantité d'adresses min à lire
  240. /*
  241. * Définition de la trame Modbus à envoyer sous forme d'un array qui sera écrit ensuite sur la socket
  242. *
  243. * MBAP (MODBUS Application Protocol) :
  244. * (!! Big Endian !!)
  245. *
  246. * Transaction Identifier (2 Bytes) :
  247. * Identification of a MODBUS Request / Response transaction.
  248. * Initialized by the client, Recopied by the server from the received request
  249. *
  250. * Protocol Identifier (2 Bytes) :
  251. * MODBUS protocol = 0
  252. * Initialized by the client, Recopied by the server from the received request
  253. *
  254. * Length (2 Bytes) :
  255. * Number of following bytes
  256. * Initialized by the client (request) and by the server (response)
  257. *
  258. * Unit Identifier (1 Byte) :
  259. * Identification of the remote slave (for sub-network on Modbus+ or Modbus serial line)
  260. *
  261. * ModBus Function (1 Byte)
  262. *
  263. *
  264. * $abuf = array ( // define an array for the MBAP header + the function (Byte 8)
  265. * 0=>chr(0), // Transaction Identifier, first Byte
  266. * chr(0), // Transaction Identifier, second Byte
  267. * chr(0), // Protocol Identifier, first Byte
  268. * chr(0), // Protocol Identifier, second Byte
  269. * chr(0), // Length, first Byte
  270. * 5=>chr(6), // Length, second Byte
  271. * 6=>chr($this->Unit), // Unit Identifier
  272. * 7=>chr(2) ) ; // Modbus Function (here : Read Discrete Inputs (2))
  273. *
  274. */
  275. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), 5=>chr(6), 6=>chr($this->iDeviceId), 7=>chr(2)) ;
  276. /*
  277. * adding datas (big endian) :
  278. * $obuf[8], $obuf[9] : start address
  279. * $obuf[10], $obuf[11] : quantity of address to read
  280. *
  281. */
  282. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iStartAddress);
  283. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes((int)$iQuantity);
  284. $this->writeSocket($this->aFrameToSend);
  285. $sBuf = $this->readSocket();
  286. //debug
  287. // echo $abuf.'<br />';
  288. //
  289. // for ($i=0; $i < strlen($abuf); $i++) {
  290. // echo $i.'=>'.ord($abuf[$i]).'<br />';
  291. // }
  292. /**
  293. * contrôle réponse
  294. * si le 8eme bit reçu (code fonction) n'est pas égal au 8eme envoyé il s'agit d'une exception MB (function code + 0x80)
  295. */
  296. if ($sBuf[7] != $this->aFrameToSend[7]) {
  297. // code de l'exception renvoyée
  298. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Start Adress : ".$iStartAddress." Quantity of inputs : ".$iQuantity);
  299. }
  300. //debug
  301. // echo 'nb de Bytes reçus : '.ord($abuf[8])."\r\n";
  302. // explose la chaîne reçue en un tableau de Bytes
  303. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  304. // print_r($this->aReceivedFrame);
  305. //
  306. // ne conserve que les datas (== Byte 9 et suivants)
  307. $aDatasBytes = array_slice($this->aReceivedFrame, 9, ord($sBuf[8]));
  308. // converti chaque valeur (Byte) du tableau en format binaire
  309. $aDatasBits = array_map("Convert::ByteToBits", $aDatasBytes);
  310. // explose chaque bit de chaque octet dans un tableau unique
  311. $aDatasBits = preg_split('``', implode('', $aDatasBits), NULL, PREG_SPLIT_NO_EMPTY);
  312. // limite le nombre de bits demandés (le protocole MB n'envoyant que des octets complets)
  313. $aDatasBits = array_splice($aDatasBits, 0, $iQuantity);
  314. // crée le tableau des adresses
  315. $aAddress = range($iStartAddress, $iStartAddress + $iQuantity - 1);
  316. // retourne un tableau avec comme clés les adresses
  317. return array_combine($aAddress, $aDatasBits);
  318. }
  319. /**
  320. * écrit une plage de bits ou de sorties TOR
  321. *
  322. * @param int $iAddress adresse du bit ou de la sortie physique
  323. * @param bool $bState false pour désactiver, true pour activer
  324. */
  325. public function WriteSingleCoil($iAddress, $bState) {
  326. PHPModbusTcpDebug::writeLogs(func_get_args(), 1);
  327. $state = (false == $bState)? 0 : 0xFF00; // 0xFF00 pour mettre un bit ou une sortie à 0
  328. if (!ctype_digit($iAddress))
  329. throw new Exception('paramètre incorrect : l\'adresse n\'est pas un entier');
  330. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), chr(6), 6=>chr($this->iDeviceId), chr(5));
  331. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iAddress);
  332. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes($state);
  333. $this->writeSocket($this->aFrameToSend);
  334. $sBuf = $this->readSocket();
  335. /**
  336. * contrôle réponse
  337. * la trame renvoyée doit être identique à la trame émise
  338. */
  339. if ($sBuf != implode('', $this->aFrameToSend)) {
  340. // code de l'exception renvoyée
  341. echo $sBuf."\r\n".implode('',$this->aFrameToSend)."\r\n";
  342. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Address : ".$iStartAddress." State : ".$bState);
  343. }
  344. // explose la chaîne reçue en un tableau de Bytes
  345. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  346. return array(true);
  347. }
  348. public function writeMultipleCoils($iStartAddress, $sStates) {
  349. if (!is_numeric($iStartAddress) || !is_string($sStates))
  350. throw new Exception('paramètres incorrects : l\'adresse de début ou le nombre de bits à écrire sont incorrects');
  351. if (strlen($sStates) > 1968) $sStates = 1968; // nb d'écritures max dans le standard modbus
  352. if (0 == preg_match('`^[0|1]*$`', $sStates)) throw new Exception('"'.$sStates.'" n\'est pas une chaîne binaire');
  353. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), chr(9), 6=>chr($this->iDeviceId), chr(15));
  354. $aBytesStates = Convert::bitsToBytes($sStates);
  355. $aBytesStatesLength = count($aBytesStates); // nombre de Bytes envoyés
  356. // adresse de début
  357. //list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iStartAddress);
  358. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes($iStartAddress);
  359. // quantité de bits à écrire
  360. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes(strlen($sStates));
  361. /**
  362. * nombre de Bytes
  363. */
  364. $this->aFrameToSend[12] = chr($aBytesStatesLength);
  365. /**
  366. * ajoute les données de la chaîne binaire
  367. *
  368. */
  369. for ($i = 0; $i < $aBytesStatesLength; $i++) {
  370. $this->aFrameToSend[$i+13] = $aBytesStates[$i];
  371. }
  372. $this->writeSocket($this->aFrameToSend);
  373. $sBuf = $this->readSocket();
  374. /**
  375. * contrôle réponse
  376. * la trame renvoyée doit être identique à la trame émise
  377. */
  378. if ($sBuf[7] != $this->aFrameToSend[7]) {
  379. // code de l'exception renvoyée
  380. echo $sBuf."\r\n".implode('',$this->aFrameToSend)."\r\n";
  381. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Address : ".$iStartAddress." State : ".$bState);
  382. }
  383. // explose la chaîne reçue en un tableau de Bytes
  384. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  385. return array(true);
  386. }
  387. /**
  388. *
  389. * MB function 03
  390. */
  391. public function readHoldingRegisters($iStartAddress, $iQuantity) {
  392. if (!is_numeric($iStartAddress) || !is_numeric($iQuantity))
  393. throw new Exception('paramètres incorrects : l\'adresse de début ou le nombre de registres à lire ne sont pas des entiers');
  394. if ($iQuantity > 125) $iQuantity = 125; // nb de registres max lisible dans le standard modbus
  395. if ($iQuantity == 0) $iQuantity = 1; // quantité d'adresses min à lire
  396. /*
  397. * Définition de la trame Modbus à envoyer
  398. */
  399. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), 5=>chr(6), 6=>chr($this->iDeviceId), 7=>chr(3)) ;
  400. /*
  401. * adding datas (big endian) :
  402. * $obuf[8], $obuf[9] : starting address
  403. * $obuf[10], $obuf[11] : quantity of registers to read
  404. *
  405. */
  406. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iStartAddress);
  407. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes((int)$iQuantity);
  408. $this->writeSocket($this->aFrameToSend);
  409. $sBuf = $this->readSocket();
  410. //debug
  411. // echo $abuf.'<br />';
  412. //
  413. // for ($i=0; $i < strlen($abuf); $i++) {
  414. // echo $i.'=>'.ord($abuf[$i]).'<br />';
  415. // }
  416. /**
  417. * contrôle réponse
  418. * si le 8eme bit reçu (code fonction) n'est pas égal au 8eme envoyé il s'agit d'une exception MB (function code + 0x80)
  419. */
  420. if ($sBuf[7] != $this->aFrameToSend[7]) {
  421. // code de l'exception renvoyée
  422. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Start Adress : ".$iStartAddress." Quantity of registers : ".$iQuantity);
  423. }
  424. //debug
  425. // echo 'nb de Bytes reçus : '.ord($abuf[8])."\r\n";
  426. // explose la chaîne reçue en un tableau de Bytes
  427. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  428. // print_r($this->aReceivedFrame);
  429. //
  430. // ne conserve que les datas (== Byte 9 et suivants)
  431. $aDatasBytes = array_slice($this->aReceivedFrame, 9, ord($sBuf[8]));
  432. // converti chaque valeur (Byte) du tableau en format binaire
  433. $aDatasBits = array_map("Convert::ByteToBits", $aDatasBytes);
  434. // explose chaque bit de chaque octet dans un tableau unique
  435. $aDatasBits = preg_split('``', implode('', $aDatasBits), NULL, PREG_SPLIT_NO_EMPTY);
  436. // limite le nombre de bits demandés (le protocole MB n'envoyant que des octets complets)
  437. $aDatasBits = array_splice($aDatasBits, 0, $iQuantity);
  438. // crée le tableau des adresses
  439. $aAddress = range($iStartAddress, $iStartAddress + $iQuantity - 1);
  440. // retourne un tableau avec comme clés les adresses
  441. return array_combine($aAddress, $aDatasBits);
  442. }
  443. /**
  444. *
  445. * MB function 04
  446. */
  447. public function readInputRegisters($iStartAddress, $iQuantity) {
  448. if (!is_numeric($iStartAddress) || !is_numeric($iQuantity))
  449. throw new Exception('paramètres incorrects : l\'adresse de début ou le nombre de registres à lire ne sont pas des entiers');
  450. if ($iQuantity > 125) $iQuantity = 125; // nb de registres max lisible dans le standard modbus
  451. if ($iQuantity == 0) $iQuantity = 1; // quantité d'adresses min à lire
  452. /*
  453. * Définition de la trame Modbus à envoyer
  454. */
  455. //$this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), 5=>chr(6), 6=>chr($this->iUnitIdentifier), 7=>chr(3)) ;
  456. $this->aFrameToSend = array (0=>chr(0), chr(0), chr(0), chr(0), chr(0), 5=>chr(6), 6=>chr($this->iDeviceId), 7=>chr(4)) ;
  457. /*
  458. * adding datas (big endian) :
  459. * $obuf[8], $obuf[9] : starting address
  460. * $obuf[10], $obuf[11] : quantity of registers to read
  461. *
  462. */
  463. list($this->aFrameToSend[9], $this->aFrameToSend[8]) = Convert::WordToBytes((int)$iStartAddress);
  464. list($this->aFrameToSend[11], $this->aFrameToSend[10]) = Convert::WordToBytes((int)$iQuantity);
  465. $this->writeSocket($this->aFrameToSend);
  466. $sBuf = $this->readSocket();
  467. /**
  468. * contrôle réponse
  469. * si le 8eme bit reçu (code fonction) n'est pas égal au 8eme envoyé il s'agit d'une exception MB (function code + 0x80)
  470. */
  471. if ($sBuf[7] != $this->aFrameToSend[7]) {
  472. // code de l'exception renvoyée
  473. throw new Exception($this->aMBException[(ord($sBuf[7]) - 128)]." Start Adress : ".$iStartAddress." Quantity of registers : ".$iQuantity);
  474. }
  475. //debug
  476. // echo 'nb de Bytes reçus : '.ord($abuf[8])."\r\n";
  477. // explose la chaîne reçue en un tableau de Bytes
  478. $this->aReceivedFrame = preg_split('``', $sBuf, NULL, PREG_SPLIT_NO_EMPTY);
  479. // print_r($this->aReceivedFrame);
  480. //
  481. // ne conserve que les datas (== Byte 9 et suivants)
  482. $aDatasBytes = array_slice($this->aReceivedFrame, 9, ord($sBuf[8]));
  483. // converti chaque valeur (Byte) du tableau en format binaire
  484. $aDatasBits = array_map("Convert::ByteToBits", $aDatasBytes);
  485. // explose chaque bit de chaque octet dans un tableau unique
  486. $aDatasBits = preg_split('``', implode('', $aDatasBits), NULL, PREG_SPLIT_NO_EMPTY);
  487. // limite le nombre de bits demandés (le protocole MB n'envoyant que des octets complets)
  488. $aDatasBits = array_splice($aDatasBits, 0, $iQuantity);
  489. // crée le tableau des adresses
  490. $aAddress = range($iStartAddress, $iStartAddress + $iQuantity - 1);
  491. // retourne un tableau avec comme clés les adresses
  492. return array_combine($aAddress, $aDatasBits);
  493. }
  494. /**
  495. *
  496. * MB function 06
  497. */
  498. public function writeSingleRegister() {
  499. }
  500. /**
  501. *
  502. * MB function 16
  503. */
  504. public function writeMultipleRegisters() {
  505. }
  506. /**
  507. *
  508. * MB function 23
  509. */
  510. public function readWriteMultipleRegisters() {
  511. }
  512. /**
  513. *
  514. * MB function 22
  515. */
  516. public function maskWriteRegister() {
  517. }
  518. /**
  519. *
  520. * MB function 24
  521. */
  522. public function readFIFOQueue() {
  523. }
  524. /**
  525. *
  526. * MB function 20
  527. */
  528. public function readFileRecord() {
  529. }
  530. /**
  531. *
  532. * MB function 21
  533. */
  534. public function writeFileRecord() {
  535. }
  536. /**
  537. *
  538. * MB function 43
  539. */
  540. public function readDeviceIdentification() {
  541. }
  542. /**
  543. * ferme la socket
  544. *
  545. * @return bool
  546. */
  547. public function socketClose() {
  548. //return true;
  549. if($this->rSocket) {
  550. socket_shutdown($this->rSocket, 1); // ferme l'écriture sur la socket
  551. usleep(400);// attends éventuellement une réponse de l'équipement
  552. socket_shutdown($this->rSocket, 0);// ferme la lecture de la socket
  553. socket_close($this->rSocket); // ferme la connexion
  554. //echo 'socket fermée<hr />';
  555. }
  556. }
  557. /**
  558. * destructeur
  559. */
  560. public function __destruct() {
  561. $this->socketClose();
  562. }
  563. }
  564. ?>