Location: PHPKode > scripts > ModbusTcp > modbustcp/Class_ModbusTcp.inc
<?
/*****
*   ----------       SOUS LICENCE GPL       --------
*
*--------------- VERSION 2.0 du 29/02/2004 ---------
*   Amelioration importante du temps de lecture.
*   Envoie des requetes Modbus multiples sur le socket avant lecture et recuperation
*   des données.
*   + Rectification des exemples ci-dessous
*
* -------------- VERSION 1.3 du 05/11/2003 ---------
*	Correction des exemples ci-dessous.....
*
* -------------- VERSION 1.2 du 17/11/2002 ---------
*	Rajout d'un mode Simulation ( retourne des valeurs aleatoires sans connexions )
*
* -------------- VERSION 1.1 du 01/07/2002 ---------
*	Rajout du routage dynamique avec passerelle 174 CEV 200 30 MB+ / ModBusTcp
*
* -------------- VERSION 1.0 du 30/10/2001 ---------
*	Creation de la classe
*
*------------------------------------------------------------------
*                       EXEMPLES
*------------------------------------------------------------------
*
*------ Lecture d'un tableau de registres ou bits contigus  --------
*
*	...
*	include "class_ModbusTcp.inc";
*
*	$Plc = new ModbusTcp;
*	$Plc->SetAdIpPLC ("195.6.140.177");
*
*	$Plc->Unit = 5;  // Sans routage dynamique
*   $Plc->BridgeRoute = array( 52, 11, 0, 0, 0 ); // Avec routage MB+ dynamique si passerelle 174CEV20030
*
*	$valeurs = $Plc->ReadModbus( "400001", 50 ); // Lecture de 50 mots ?artir de 400001
*	$Plc->print_r_log ($valeurs); echo "<br>";
*
*	$valeurs = $Plc->ReadModbus( "300001", 15 ); // Lecture de 15 mots d'entr??artir de 300001
*	$Plc->print_r_log  ($valeurs); echo "<br>";
*
*	$valeurs = $Plc->ReadModbus( "000001", 200 ); // Lecture de 200 bits de ?artir de 000001
*	$Plc->print_r_log  ($valeurs); echo "<br>";
*
*	$valeurs = $Plc->ReadModbus( "100001", 125 ); // Lecture de 125 bits d'entr??artir de 100001
*	$Plc->print_r_log ($valeurs); echo "<br>";
*	...
*
*	$Plc->ModClose();
*
*
*------- Lecture d'un tableau de registres aleatoires -------------
*
*	//...
*	include "class_ModbusTcp.inc";
*
*	$Plc = new ModbusArray;  // ****  /!\ =Class extend de ModbusTcp
*	$Plc->SetAdIpPLC ("195.6.140.177");
*
*	$Plc->Unit = 5;  // Sans routage dynamique
*   //$Plc->BridgeRoute = array( 52, 11, 0, 0, 0 ); // Avec routage MB+ dynamique si passerelle 174CEV20030 de =S=
*
*	$Registre = array (400001, 400250, 400625, 400002, 400050, 300001, 000005, 100010, 100035 );
*	$arrValeurs = $Plc->ReadArrRegs( $Registre );
*	$Plc->print_r_log ($arrValeurs); echo "<br>";
*	//...
*
*	$Plc->ModClose();
*
*	A noter que dans ce cas, les trames envoyees sont optimisees au niveau du reseau de
*	facon a lire des tableaux de mots contigus.
*	A voir avec $Plc->SetDebug();
*
*
*------ Utilisation du mode Simulation -----------------------------
*
*	...
*	include "class_ModbusTcp.inc";
*
*	$Plc = new ModbusTcp;
*	$Plc->SetSimulation();
*
*	$valeurs = $Plc->ReadModbus( "400001", 50 );
*	$Plc->print_r_log($valeurs); echo "<br>";
*
*	$valeurs = $Plc->ReadModbus( "300001", 63 );
*	$Plc->print_r_log($valeurs); echo "<br>";
*
*	$valeurs = $Plc->ReadModbus( "000001", 2000 );
*	$Plc->print_r_log ($valeurs); echo "<br>";
*	...
*
*	$Plc->ModClose();
**/


class ModbusTcp {

	var $AdIpPLC;
	var $PortIpPLC;
	var $Unit;
	var $DebutAdresse;
	var $Nbre;
	var $WriteValues;
	var $NrTransact;
	var $SockOutBuffer;
	var $Erreur;
	var $Fp;
	var $BridgeRoute;
	var $TmpBridgeRoute;
	var $Debug;
	var $Simulation;

	function ModbusTcp () { // Constructeur
		$this->Fp = false;
		$this->AdIpPLC = "";
		$this->PortIpPLC = 502;
		$this->Unit = 255;
		$this->DebutAdresse = 0;
		$this->Nbre = 1;
		$this->WriteValues = array(0);
		$this->SockOutBuffer = array();
		$this->NrTransact = 0;
		$this->Erreur = "";
		$this->BridgeRoute = array();
		$this->TmpBridgeRoute = array();
		$this->Debug = false;
		$this->Simulation = false;
		srand( (float) microtime()*1000000 );
	}

	function SetAdIpPLC( $Ip ) {
		$this->AdIpPLC = $Ip;
		$this->ModClose(); // Fermeture de la connection en cours dans le cas de changement d'adresse IP
    	$this->SockOutBuffer = array(); // Initialisation du buffer de sortie après changement de Device
	}

	function ModConn() {
		if ( !$this->Simulation ) {
			$this->Fp = @fsockopen( "$this->AdIpPLC", $this->PortIpPLC, $errno, $errstr, 5 ) or die("$errstr ($errno)<br>\nPas de connexion a l'Adresse $this->AdIpPLC");
		}
	}

	function ModClose() {
		@fclose($this->Fp);
	}
  
	function MbReadQuery ( $Unit=255, $MbFunction, $Debut=0, $Nbre=1 ) {
		$Query = chr(0).chr(0).chr(0).chr(0).chr(0).chr(6).chr($Unit).chr($MbFunction) ;
		list( $Query[1], $Query[0] ) = $this->WordToBytes( $this->NrTransact );
		list( $Query[9], $Query[8] ) = $this->WordToBytes( $Debut );
		list( $Query[11], $Query[10] ) = $this->WordToBytes( $Nbre );
		$this->NrTransact++;
		return $Query;
	}

	function MbWriteQuery ( $Unit=255, $MbFunction=3, $Debut=0, $valeurs = array(0) ) {
		if ( ($NbreMots = Sizeof( $valeurs )) > 100 ) $NbreMots = 100;
		
		$Query = chr(0).chr(0).chr(0).chr(0).chr(0).chr(6).chr($Unit).chr($MbFunction) ;
		
		list( $Query[1], $Query[0] ) = $this->WordToBytes( $this->NrTransact );
		list( $Query[5], $obuf[4] ) = $this->WordToBytes( $NbreMots * 2 + 7 ); //Nbre de mots
		list( $Query[9], $Query[8] ) = $this->WordToBytes($Debut-1); //Adresse de debut
		list( $Query[11], $Query[10] ) = $this->WordToBytes( $NbreMots ); //Nbre de mots
		$Query[12] = chr( $NbreMots * 2 ); //Nbre d'octets
		for ( $i=0; $i < $NbreMots*2; $i += 2 ) {
			list( $Query[13+$i], $Query[13+$i+1] ) = $this->WordToBytes( $valeurs[$i/2] ); //Valeurs
		}
		$this->NrTransact++;
		return $Query;
	}

	function WriteSocket( &$sOutBuf ) {
		if (!$this->Fp) $this->ModConn();
		fwrite( $this->Fp, $sOutBuf );
		
		if ( $this->Debug ) { //Affichage des octets emis si en mode Debug
			echo "<b>Bytes sent</b><br>";
			for ($i=0;$i<strlen($sOutBuf);$i++ ) {
				echo "OctEmis[$i] =".ord($sOutBuf[$i])."<br>";
			}
		}
		return true;
	}
 
	function ReadSocket () {
		$InBuf = fgetc($this->Fp); //Lire le 1er octet du socket pour utiliser après stream_get_meta_data()
		$status = stream_get_meta_data($this->Fp);
		$InBuf .= fread($this->Fp, $status["unread_bytes"]); //Lire les octets restants
		return $InBuf;
	}

	function SetSimulation() {
		$this->Simulation = True; //
		srand( (float) microtime()*1000000 );  //Pour Simulation
		echo "<br><font color='#FF9900' size=3><b>Attention: valeurs en Mode SIMULATION ! ! ! ! </b></font><br>";
	}

	function SetDebug() {
		$this->Debug = True; //
	}

	function WordToBytes( $word ) {
		if ( $word > 65535 ) $word = 65535;
		return array( chr( $word % 256 ), chr( ( $word - $word % 256 ) / 256 ) ) ;
	}

	function BytesToWord( $byte1, $byte2 ) {
		return ord($byte1) * 256 + ord($byte2);
	}

	function ByteToBits( $byte1 = 0) { // converti un octet en string format binaire inverse
		return strrev( sprintf( "%08d", decbin( ord( $byte1 ) ) ) );
	}

	function print_r_log($var) {
		echo "<pre><font color='#000000' size='1' face='Verdana'>";
		print_r($var);
		echo "</pre><br>";
	}


	// --------------------------------------------------------------------------------------------
	//            FONCTION PRINCIPALE LECTURE D'UN TABLEAU DE 0/1/3/4xxxxx
	//					( retourne un tableau [0/1/3/4xxxxx] = valeur )
	// --------------------------------------------------------------------------------------------
	function ReadModbus( $AdrDebut, $NbreReg ) {
		($NbreReg > 0) ? $this->Nbre = (int)$NbreReg : $this->Nbre = 1;
    
		switch ( substr( sprintf("%06d", $AdrDebut), 0, 1) ) {	
			case "4":
				$this->DebutAdresse = $AdrDebut - 400001;
				return $this->ReadHoldRegisters();
				break;
			case "3":
				$this->DebutAdresse = $AdrDebut - 300001;
				return $this->ReadInputRegisters();
				break;
			case "1":
				$this->DebutAdresse = $AdrDebut - 100001;
				return $this->ReadDiscretInputs();
				break;
			case "0":
				$this->DebutAdresse = $AdrDebut - 1;
				return $this->ReadCoils();
				break;
			default:
				return array();
		}
	}

	// -------------------------------------------------------
	//						LECTURE DES 4xxxxx
	//			( retourne un tableau [4xxxxx] = valeur )
	// -------------------------------------------------------

	function ReadHoldRegisters() {
		if ( $this->Nbre > 125 ) $this->Nbre = 125;

		//Retourne des valeurs aléatoires entre 0 et 4095 sans ouvrir les sockets
		if ( $this->Simulation ) {
			for ( $i=0; $i<=$this->Nbre; $i++ ) {
				$buffer[400000 + $this->DebutAdresse + $i] = rand(0, 65535); //Simulation ANA
			}
			return $buffer;
		} // FIN Simulation

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();
    	$obuf = &$this->MbReadQuery($this->Unit, 3, $this->DebutAdresse, $this->Nbre); 
    
		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET ---------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR DE LECTURE des REGISTRES de SORTIE de ".sprintf("4%05d", $this->DebutAdresse)." a ".sprintf("4%05d", ($this->DebutAdresse + $this->Nbre))."<br>";
			for ( $i=0; $i<count($OctetRecu); $i++ ) {
				echo "OctRecu[$i] =". ord($OctetRecu[$i])."<br>";
			}
			echo "</b></FONT>\n";
			return $buffer = array();
		}

		//Recuperation des DATAs
		$i = 0;
		$buffer = array();
		for ($j=0; $j < ord($OctetRecu[8]); $j=$j+2) {
			$i++;
			$buffer[400000 + $this->DebutAdresse + $i] = $this->BytesToWord( $OctetRecu[$j+9], $OctetRecu[$j+10] );
		}

  	return $buffer ;
	}

	// --------------------------------------------------------------
	//					LECTURE DES 3xxxxx
	//			( retourne un tableau [3xxxxx] = valeur )
	// --------------------------------------------------------------
	function ReadInputRegisters() {
		if ( $this->Nbre > 125 ) $this->Nbre = 125;

		//Retourne des valeurs aleatoires entre 0 et 4095 sans ouvrir les sockets
		if ( $this->Simulation ) {
			for ( $i=0; $i<=$this->Nbre; $i++ ) {
				$buffer[300000 + $this->DebutAdresse + $i] = rand(0, 4095);
			}
			return $buffer;
		} // FIN Simulation

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();

    	$obuf = &$this->MbReadQuery( $this->Unit, 4, $this->DebutAdresse, $this->Nbre); 

		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET ---------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR DE LECTURE des REGISTRES d'ENTREE de ".sprintf("3%05d", $this->DebutAdresse)." ".sprintf("3%05d", ($this->DebutAdresse + $this->Nbre))."<br>";
			for ( $i=0; $i<count($OctetRecu); $i++ ) {
				echo "OctRecu[$i] =". ord($OctetRecu[$i])."<br>";
			}
			echo "</b></FONT>\n";
			return $buffer = array();
		}
		//Recuperation des DATAs
		$i = 0;
		$buffer = array();
		for ($j=0; $j < ord($OctetRecu[8]); $j=$j+2) {
			$i++;
			$buffer[300000 + $this->DebutAdresse + $i] = $this->BytesToWord( $OctetRecu[$j+9], $OctetRecu[$j+10] );
		}
		return $buffer ;
	}

	// -------------------------------------------------------------
	//						LECTURE DES 1xxxxx
	//			( retourne un tableau [1xxxxx] = valeur )
	// -------------------------------------------------------------
	function ReadDiscretInputs() {

		if ( $this->Nbre > 2000 ) $this->Nbre = 2000;

		//Retourne des valeurs aleatoires entre 0 et 1 sans ouvrir les sockets
		if ( $this->Simulation ) {
			for ( $i=0; $i<=$this->Nbre; $i++ ) {
				$buffer[100000 + $this->DebutAdresse + $i] = rand(0, 1);
			}
			return $buffer;
		} // FIN Simulation

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();

    	$obuf = &$this->MbReadQuery( $this->Unit, 2, $this->DebutAdresse, $this->Nbre); 

		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET --------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) { // Lecture du 8eme octet = Code function renvoy?ar destinataire
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR DE LECTURE des Bits d'ENTREES de ".sprintf("1%05d", $this->DebutAdresse)." ".sprintf("1%05d", ($this->DebutAdresse + $this->Nbre))."<br>";
			echo "</b></FONT>\n";
			return $buffer = array();
		}
		//$OctetRecu[8] 9eme octet = nbre d'octets (mots) de donn?
		$i = 0;
		$buffer = array();
		$debut = 100000 + $this->DebutAdresse + 1;
		while( $i < ord($OctetRecu[8]) ) {
			$tmp = $this->ByteToBits( $OctetRecu[$i+9] );
			for ( $j=0; $j<8; $j++ ) { //extraction des bits de mots
				$bit = $i*8 + $j;
				if ( $bit >= $this->Nbre ) break;
				$buffer[$debut + $j + $i*8] = $tmp[$j];
			}
			$i++;
		}

		return $buffer;
	}

	// -------------------------------------------------------------
	//						LECTURE DES 0xxxxx
	//			( retourne un tableau ['0xxxxx'] = valeur )
	// -------------------------------------------------------------
	function ReadCoils() {

		if ( $this->Nbre > 2000 ) $this->Nbre = 2000;

		//Retourne des valeurs aleatoires entre 0 et 1 sans ouvrir les sockets
		if ( $this->Simulation ) {
			for ( $i=0; $i<=$this->Nbre; $i++ ) {
				$buffer[sprintf( "%06d", $this->DebutAdresse + $i)] = rand(0, 1);
			}
			return $buffer;
		} // FIN Simulation

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();
 
    	$obuf = &$this->MbReadQuery( $this->Unit, 1, $this->DebutAdresse, $this->Nbre); 

		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET --------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR DE LECTURE des Bits de SORTIES de ".sprintf("%06d", $this->DebutAdresse)." ".sprintf("%06d", ($this->DebutAdresse + $this->Nbre))."<br>\n";
			echo "</b></FONT>\n";
			return $buffer = array();
		}
		//$OctetRecu[8] 9eme octet = nbre mots(2octets) de donn?
		$i = 0;
		$buffer = array();
		$debut = $this->DebutAdresse + 1;
		while( $i < ord($OctetRecu[8]) ) {
			$tmp = $this->ByteToBits( $OctetRecu[$i+9] );
			for ( $j=0; $j<8; $j++ ) {
				$bit = $i*8 + $j;
				if ( $bit >= $this->Nbre ) break;
				$buffer[ sprintf( "%06d", $debut + $j + $i*8 )] = $tmp[$j];
			}
			$i++;
		}
		return $buffer;
	}

	// -----------------------------------------------
	//                ECRITURE DES 400000
	// -----------------------------------------------
	function WriteHoldRegisters( $DebutAdresse = 0, $valeurs = array(0) ) {

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();

		$CodeFunction = 16;	// 16 = Ecriture d'un tableau de registres 400001
    	$obuf = $this->MbWriteQuery( $this->Unit, $CodeFunction, $this->DebutAdresse, $valeurs); 
		
		//--------- ECRITURE DU SOCKET --------------
		$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET ---------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR D'ECRITURE<br>";
			echo "</b></FONT>\n";
			return False;
		}
		//$OctetRecu[8] //Confirmation Debut tableau d'adresse ecrite (octet Haut)
		//$OctetRecu[9] //Confirmation Debut tableau d'adresse ecrite (octet Bas) adresse = Oct.Haut*256 + Oct.Bas
		//$OctetRecu[10] //Nbre d'octets suivent

		return True ;
	}


	// -----------------------------------------------
	//                ECRITURE D'UNE BOBINE
	// -----------------------------------------------
	function WriteCoil( $Adresse = 0, $valeur = false ) {

		if ( $this->BridgeRoute ) $this->SetBridgeRoute();

		$CodeFunction = 5;	// 5 = Ecriture d'une Bobine

		$obuf = chr(0).chr(0).chr(0).chr(0).chr(0).chr(0).chr($this->Unit).chr($CodeFunction);
		list( $obuf[5], $obuf[4] ) = $this->WordToBytes(6); //Nbre de mots qui suivent
		list( $obuf[9], $obuf[8] ) = $this->WordToBytes($Adresse -1); //Adresse de la bobine
		$valeur ? $obuf[10] = chr(255) : $obuf[10] = chr(0);
		$obuf[11] = chr(0);

		if ( $this->Debug ) {
			echo "<b>WriteCoil</b><br>";
			for ($i=0;$i<count($obuf);$i++ ) {
				echo "OctEmis[$i] =". ord($obuf[$i])."<br>";
			}
		}

		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf );

		//--------- LECTURE DU SOCKET ---------------
		$OctetRecu = $this->ReadSocket();

		if ( $OctetRecu[7] != $obuf[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR D'ECRITURE BIT<br>";
			echo "</b></FONT>\n";
			return False;
		}

		return True ;
	}

	// --------------------------------------------------------
	//            SETUP BRIDGE FOR DYNAMIC ROUTING
	// --------------------------------------------------------
	function SetBridgeRoute() {

		//--------------------------------------------------------------------------------------------------------
		//Consiste a ecrire a l'adresse 255 du device 255 de la passerelle, le routage MB+ avec la fonction 16
		//Une fois la route etablie, on peut lire ou ecrire dans le device 254 pour atteindre l'automate concerne	
    	//---------------------------------------------------------------------------------------------------------


		//Test si la nouvelle route est la meme que l'ancienne.
		//Si pas de changement de routage MB+, on n'envoie pas de nouvelle route a la passerelle MB+/Ethernet
    	$result = array_diff($this->BridgeRoute, $this->TmpBridgeRoute);
    	if ( count($result) == 0 ) return True;
		$this->TmpBridgeRoute = $this->BridgeRoute;

		$obuf1 = chr(0).chr(0).chr(0).chr(0).chr(0).chr(13)
			   .chr(255)		// 1=Host-based routing  255=Socket-based routing
			   .chr(16)			//Code fonction Modbus 16 = Ecriture d'un tableau de registres 4xxxxx
			   .chr(0)			//Octet 8 = Adresse de debut octet 1
			   .chr(255-1)		//Octet 9 = Adresse de debut octet 2
			   .chr(0)			//Octet 10 = Nbre de mots octet 1
			   .chr(3)			//Octet 11 = Nbre de mots octet 2
			   .chr(6)			//Octet 12 = Nbre d'octets qui suivent
			   .chr(5)			//Octet 13 = Nbre d'octets qui suivent = Cte => Attention: fait parti des champs data's
			   .chr($this->BridgeRoute[0])	//Octet 14 = Routage 1
			   .chr($this->BridgeRoute[1])	//Octet 15 = Routage 2
			   .chr($this->BridgeRoute[2])	//Octet 16 = Routage 3
			   .chr($this->BridgeRoute[3])	//Octet 17 = Routage 4
			   .chr($this->BridgeRoute[4]);	//Octet 18 = Routage 5

		if ( $this->Debug == true ) {
			echo "<b>SetBridgeRoute<br></b>";
		}

		//--------- ECRITURE DU SOCKET --------------
    	$this->WriteSocket ( $obuf1 );

		//--------- LECTURE DU SOCKET --------------
		$OctetRecu = &$this->ReadSocket();

		if ( $this->Debug == true ) echo "Reception - Adresse Unite = ".ord($OctetRecu[6])." <br>"; // 7eme octet = adresse du device
		if ( $OctetRecu[7] != $obuf1[7] ) {
			echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
			echo "ERREUR D'ECRITURE dans la fonction de routage dynamique de la passerelle ETH/MB+ (SetBridgeRoute) adr. IP ".$this->AdIpPLC."<br>\n";
			echo "</b></FONT>\n";
			fclose($this->Fp);
			exit;
		}
		if ( $this->Debug == true ) echo "Reception - Code function = ".ord($obuf1[7])."<br>";
		$adresse = $this->BytesToWord( ord($obuf1[8]), ord($obuf1[9]) );
		if ( $this->Debug == true ) echo "Reception - Adresse debut = $adresse <br>";
		$nbMots = $this->BytesToWord( ord($obuf1[10]), ord($obuf1[11]) );
		if ( $this->Debug == true ) echo "Reception - Nbre de mots = $nbMots <br>";

		//Ne pas fermer la connection !! car la fonction Modbus suivante suivra !
		//fclose($this->Fp);

		$this->Unit = 254; //Preparation pour la fonction Modbus suivante.

		return True ;
	}

} //Fin de la class 'ModbusTcp'




class ModbusArray extends ModbusTcp {

	// --------------------------------------------------------------------------------------------
	//            CONSTRUCTION DES TRAMES POUR ENVOIE MULTIPLES
	// --------------------------------------------------------------------------------------------
	function BuildSockOutBuffer( $AdrDebut, $NbreReg ) {
		($NbreReg > 0) ? $this->Nbre = (int)$NbreReg : $this->Nbre = 1;
		
		switch ( substr( sprintf("%06d", $AdrDebut), 0, 1) ) {	
			case "4":
				$this->DebutAdresse = $AdrDebut - 400001;
				$this->SockOutBuffer[$this->NrTransact] = &$this->MbReadQuery($this->Unit, 3, $this->DebutAdresse, $this->Nbre); 
				return;
				break;
			case "3":
				$this->DebutAdresse = $AdrDebut - 300001;
				$this->SockOutBuffer[$this->NrTransact] = &$this->MbReadQuery( $this->Unit, 4, $this->DebutAdresse, $this->Nbre); 
				return;
				break;
			case "1":
				$this->DebutAdresse = $AdrDebut - 100001;
				$this->SockOutBuffer[$this->NrTransact] = &$this->MbReadQuery( $this->Unit, 2, $this->DebutAdresse, $this->Nbre); 
				return;
				break;
			case "0":
				$this->DebutAdresse = $AdrDebut - 1;
				$this->SockOutBuffer[$this->NrTransact] = &$this->MbReadQuery( $this->Unit, 1, $this->DebutAdresse, $this->Nbre); 
				return;
				break;
			default:
			    return;
		}
	}

	function SendOutBuffer() {
		if ( $this->BridgeRoute ) $this->SetBridgeRoute();
		
		for ($i=0; $i<count($this->SockOutBuffer); $i++) {
		  $this->WriteSocket( $this->SockOutBuffer[$i] );
		}
	}

	function ReadSockResult() {

	    $buffer = array();

		//---- MODE SIMULATION 
		if ( $this->Simulation ) {
			//---- Boucle de lecture par requete Modbus envoyee
		    for ($k=0; $k < sizeof($this->SockOutBuffer); $k++) {
				//---- Init variables par les trames émisent
				$DebutAdresse = $this->BytesToWord( $this->SockOutBuffer[$k][8], $this->SockOutBuffer[$k][9] );	
				$Nbre = $this->BytesToWord( $this->SockOutBuffer[$k][10], $this->SockOutBuffer[$k][11] );	
			   switch ( ord($this->SockOutBuffer[$k][7]) ) {
		   		  case 3: //---- HoldRegisters
					for ( $i=0; $i<=$Nbre; $i++ ) {
						$buffer[400000 + $DebutAdresse + $i] = rand(0, 65535); //Simulation ANA
					}
				 	break;
		   		  case 4: //---- InputRegisters
					for ( $i=0; $i<=$Nbre; $i++ ) {
						$buffer[300000 + $DebutAdresse + $i] = rand(0, 4095);
					}
				 	break;
		   		  case 2: //---- DiscretInputs
					for ( $i=0; $i<=$Nbre; $i++ ) {
						$buffer[100000 + $DebutAdresse + $i] = rand(0, 1);
					}
				 	break;
		   		  case 1: //---- Coils
					for ( $i=0; $i<=$Nbre; $i++ ) {
						$buffer[sprintf( "%06d", $DebutAdresse + $i)] = rand(0, 1);
					}
				 	break;
				}
			}
			return $buffer;
		} // FIN Simulation


		//---- Boucle de lecture par requete Modbus envoyee
	    for ($w=0; $w < sizeof($this->SockOutBuffer); $w++) {
			$arrOctetRecu[$w] = $this->ReadSocket();
		}
		
	    for ($w=0; $w < sizeof($arrOctetRecu); $w++) {

			$OctetRecu = &$arrOctetRecu[$w];

			//---- Affichage des octets recu si en mode Debug
			if ( $this->Debug ) { 
			   echo "<b>Bytes received</b><br>";
			   for ( $i=0; $i<strlen($OctetRecu); $i++ ) {
			   	   echo "OctRecu[$i] =". ord($OctetRecu[$i])."<br>";
			   }
			}
			
			//---- Recherche Nr Trame recue 
			$k = $this->BytesToWord( $OctetRecu[0], $OctetRecu[1] );
			
			//---- Init variables par les trames émisent
			$DebutAdresse = $this->BytesToWord( $this->SockOutBuffer[$k][8], $this->SockOutBuffer[$k][9] );	
			$Nbre = $this->BytesToWord( $this->SockOutBuffer[$k][10], $this->SockOutBuffer[$k][11] );	
					
			//---- Test code fonction MB retourné = code fonction envoyé
			if ( $OctetRecu[7] != $this->SockOutBuffer[$k][7] ) { 
				echo "<FONT SIZE='3' COLOR='#FFFF00'><b>";
				echo "ERREUR CODE FONCTION MODBUS RECU <br>";
				for ( $i=0; $i<count($OctetRecu); $i++ ) {
					echo "OctRecu[$i] =". ord($OctetRecu[$i])."<br>";
				}
				echo "</b></FONT>\n";
			}
			
			//---- Test du code fonction Modbus retourné pour extraction des données	
			switch ( ord($OctetRecu[7]) ) {
				case 3: //---- Read HoldRegisters
					$i = 0;
					for ($j=0; $j < ord($OctetRecu[8]); $j=$j+2) {
						$i++;
						$buffer[400000 + $DebutAdresse + $i] = $this->BytesToWord( $OctetRecu[$j+9], $OctetRecu[$j+10] );
					}
					break;
				case 4: //---- Read InputRegisters
					$i = 0;
					for ($j=0; $j < ord($OctetRecu[8]); $j=$j+2) {
						$i++;
						$buffer[300000 + $DebutAdresse + $i] = $this->BytesToWord( $OctetRecu[$j+9], $OctetRecu[$j+10] );
					}
					break;
				case 2: //---- Read DiscretInputs
					$i = 0;
					$debut = 100000 + $DebutAdresse + 1;
					while( $i < ord($OctetRecu[8]) ) {
						$tmp = $this->ByteToBits( $OctetRecu[$i+9] );
						for ( $j=0; $j<8; $j++ ) { //extraction des bits de mots
							$bit = $i*8 + $j;
							if ( $bit >= $Nbre ) break 2;
							$buffer[$debut + $j + $i*8] = $tmp[$j];
						}
						$i++;
					}
					break;
				case 1: //---- Read Coils
					$i = 0;
					$debut = $DebutAdresse + 1;
					while( $i < ord($OctetRecu[8]) ) {
						$tmp = $this->ByteToBits( $OctetRecu[$i+9] );
						for ( $j=0; $j<8; $j++ ) {
							$bit = $i*8 + $j;
							if ( $bit >= $Nbre ) break 2;
							$buffer[ sprintf( "%06d", $debut + $j + $i*8 )] = $tmp[$j];
						}
						$i++;
					}
					break;
				Default:
			}
			
	    }
	    $this->SockOutBuffer = array(); // Initialisation du tableau des requetes Modbus. Utile en cas de changement de device sur passerelle 174CEV20030
    	
		return $buffer ;
	}

	// -------------------------------------------------------------------------
	//					DECOUPAGE D'UN TABLEAU de 0/1/3/4xxxxx
	//			( retourne un tableau [debut_adres] = Nbre de mots/bits )
	// -------------------------------------------------------------------------
	function DecoupeTrame( $arr ) {
		asort($arr);
//		print_r($arr); echo "<br>";

		$tmp = $arr[key($arr)]; //init ?a premi? valeur du tableau
		foreach ( $arr as $i => $value) {
			ereg( "^3|^4", $arr[$i]) ? $max = 125 : $max = 500 ;
			if ( $arr[$i] - $tmp >= $max ) {
				$resultat[$tmp] = $arr[$i_1] - $tmp + 1;
				$tmp = $arr[$i];
			}
			$i_1 = $i;
		}
		$resultat[$tmp] = $arr[$i] - $tmp + 1; //traitement de la derni? adresse
//		print_r($resultat);	echo "<br>";

		return $resultat;
	}


	// ------------------------------------------------------------------------------------------
	//			LECTURE D'UN TABLEAU HETEROGENE TRIE ou NON TRIE 0/1/3/4xxxxx POUR UN DEVICE
	//				( retourne un tableau [0/1/3/4xxxxx] = valeur )
	// ------------------------------------------------------------------------------------------
	function ReadArrRegs( $arrAdr = array ("400001") ) {

		$buf = &$this->DecoupeTrame( $arrAdr );

		// Fait apparaitre les tableau de bits ou de Mots utilisé pour les trames
		if ( $this->Debug ) {
			echo "TRAMES: DEVICE => ".$this->Unit." :";
			$this->print_r_log ($buf);
			echo "<br>\n";
		}

		foreach ( $buf as $debut => $nbre ) {
			$this->BuildSockOutBuffer( $debut, $nbre );
		}
		
		if (!$this->Simulation) $this->SendOutBuffer();
		return $this->ReadSockResult();
	}
}

?>
Return current item: ModbusTcp