Tutorial al Web-IO digital:
Control de Web-IO digital desde una página web propia con PHP
Los modelos de Web-IO Digital 4.0 pueden ser activados mediante peticiones HTTP. La mayoría de lenguajes de alto nivel y de script permiten el uso de las peticiones HTTP para el intercambio de datos. PHP está disponible en casi cualquier servidor de web como lenguaje de script y es idóneo para desarrollar la comunicación con el Web-IO.
El ejemplo mostrado aquí trabaja en dos partes. Separa la presentación dinámica de los estados de las EA vía HTML, JavaScript o AJAX (página web) y la comunicación con el Web-IO vía (script) PHP.
La parte principal es el script de PHP webiohttprequest.php, que no necesita elementos de presentación y se encarga exclusivamente del intercambio de datos. Según la página web en la que se presenten los estados de las EA, activa mediante AJAX el script PHP y solicita el estado del Web-IO.
Una ventaja de esta técnica es que permite consultar a varios Web-IO con el mismo script de PHP. El parámetro adjunto determina el Web-IO destinatario. Si los Web-IO y el servidor de PHP están protegidos por un cortafuegos, solo es necesario habilitar hacia fuera el acceso al servidor de PHP.
El siguiente ejemplo muestra la estructura del script de PHP y cómo se activa con una página web sencilla y las funciones necesarias. La indicación está preparada para el Web-IO Digital 4.0 2xIn, 2xOut.
Preparativos
- Conectar la tensión para Web-IO y cablear las IO
- Conectar el Web-IO a la red
- Asignar direcciones de IP
En Web-IO, dentro de la sección Vías de comunicación >> Web-API activar la opción Permitir peticiones HTTP y habilitar las salidas a conmutar
El script de PHP
Al script de PHP webiohttprequest.php hay que transmitirle diversos parámetros al activarlo. Esos parámetros están integrados en la petición HTTP.
- HTTPS
HTTPS=0 - se envía la petición como petición HTTP
HTTPS=1 - se envía la petición codificada como petición HTTPS - IP
IP=<dirección IP del Web-IO> - PORT
PORT=<puerto para HTTP(s) del Web-IO> - PW
PW=<contraseña del Web-IO> - COMMAND
COMMAND=<comando a enviar al Web-IO>
Comandos posibles: input, output, counter, outputaccess, single - STATE (solo con el comando outputaccess)
STATE=<patrón de bits para los estados de conmutación a los que deben cambiar las salidas, en hexadecimal> - MASK (solo con el comando outputaccess)
MASK=<patrón bits de las salidas que deben ser conmutadas, en hexadecimal>
Los comandos y parámetros a transmitir bajo COMMAND se corresponden con los comandos básicos de las peticiones HTTP.
Para el envío de las peticiones HTTP, el script de PHP utiliza la biblioteca de funciones de PHP CURL.
<?php
$PROTO="http://";
$PORT="80";
if (!isset($_GET[’COMMAND’])) {die( ’ERROR command’ );} else {$COMMAND=$_GET[’COMMAND’];}
if (!isset($_GET[’IP’])) {die( ’ERROR ip’ );} else {$IP=$_GET[’IP’];}
if (!isset($_GET[’HTTPS’])) {$PROTO=’http://’;}
else
{ if ($_GET[’HTTPS’] == 1)
{ $PROTO=’https://’;
$PORT=’443’;
}
}
if (isset($_GET[’PORT’])) {$PORT=$_GET[’PORT’];}
if (!isset($_GET[’PW’])) {$PW="";} else {$PW=$_GET[’PW’];}
if (!isset($_GET[’MASK’])) {$MASK="0FFF";} else {$MASK=$_GET[’MASK’];}
if (!isset($_GET[’STATE’]))
{ $STATE="0000";
$MASK="0000";
}
else $STATE=$_GET[’STATE’];
{ if ($COMMAND == "outputaccess")
{ $URL=$PROTO.$IP.":".$PORT."/".$COMMAND."?PW=".$PW."&Mask=".$MASK."&State=".$STATE."&";
}
else
{ if (substr($COMMAND,0,6) == "single")
{ $URL=$PROTO.$IP.":".$PORT."/".$COMMAND;
}
else
{ $URL=$PROTO.$IP.":".$PORT."/".$COMMAND."?PW=".$PW."&";
}
}
$ch = curl_init();
$options = array(
CURLOPT_URL => $URL,
CURLOPT_RETURNTRANSFER => true, // return web page
CURLOPT_HEADER => false, // don’t return headers
CURLOPT_ENCODING => "", // handle all encodings
CURLOPT_USERAGENT => "webio", // who am i
CURLOPT_CONNECTTIMEOUT => 20, // timeout on connect
CURLOPT_TIMEOUT => 20, // timeout on response
CURLOPT_MAXREDIRS => 1, // stop after 10 redirects
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false,
);
curl_setopt_array( $ch, $options );
$data = curl_exec($ch);
if ($data==false)
{ die(’ERROR ’.$URL);
}
else
{ $http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_status==200)
{ echo $data;
}
else
{ die(’ERROR ’.$URL.’ status ’.$http_status);
}
}
curl_close($ch);
}
?>
Estructura básica de la página web
La estructura HTML básica de esta página tiene el siguiente aspecto:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1250">
<title>Web-IO Digital, Cross Origin</title>
<style type="text/css">
* { font-family:arial; }
table { font-size:14px; text-align:center; padding-left:5px; padding-right:5px;}
.borderLeft { border-left:1px solid #000000; }
.button { font-size:9px; width:40px; }
.name { font-size:20px; font-weight:bold; text-align:center }
.table { background-color:#d6e8ff; border-collapse:collapse; border:1px solid #000000; }
.whiteBack { background-color:#ffffff; }
</style>
<script language="JavaScript" type="text/javascript">
........
........
</script>
</head>
<body onload="CommandLoop()">
<form>
<div align="center">
<h2>Web-IO 0</h2>
<span>Password: </span>
<input id="webio0pw" type="password" maxlength="31" size="20"">
</div>
</form>
<table align="center" class="table">
<tr>
<td>input 0</td>
<td id="webio0input0">OFF</td>
<td id="webio0counter0">0</td>
<td>
<input class="button" onclick="clearCounter(0,0);" type="button" value="Clear">
</td>
<td class="borderLeft">output 0</td>
<td id="webio0output0">OFF</td>
<td>
<input class="button" onclick="setOutput(0,0,1);" type="button" value="ON">
<input class="button" onclick="setOutput(0,0,0);" type="button" value="OFF">
</td>
</tr>
<tr class="whiteBack">
<td>input 1</td>
<td id="webio0input1">OFF</td>
<td id="webio0counter1">0</td>
<td>
<input class="button" onclick="clearCounter(0,1);" type="button" value="Clear">
</td>
<td class="borderLeft">output 1</td>
<td id="webio0output1">OFF</td>
<td>
<input class="button" onclick="setOutput(0,1,1);" type="button" value="ON">
<input class="button" onclick="setOutput(0,1,0);" type="button" value="OFF">
</td>
</tr>
</table>
<br />
<form>
<div align="center">
<h2>Web-IO 1</h2>
<span>Password: </span>
<input id="webio1pw" type="password" maxlength="31" size="20"">
</div>
</form>
<table align="center" class="table">
<tr>
<td>input 0</td>
<td id="webio1input0">OFF</td>
<td id="webio1counter0">0</td>
<td>
<input class="button" onclick="clearCounter(1,0);" type="button" value="Clear">
</td>
<td class="borderLeft">output 0</td>
<td id="webio1output0">OFF</td>
<td>
<input class="button" onclick="setOutput(1,0,1);" type="button" value="ON">
<input class="button" onclick="setOutput(1,0,0);" type="button" value="OFF">
</td>
</tr>
<tr class="whiteBack">
<td>input 1</td>
<td id="webio1input1">OFF</td>
<td id="webio1counter1">0</td>
<td>
<input class="button" onclick="clearCounter(1,1);" type="button" value="Clear">
</td>
<td class="borderLeft">output 1</td>
<td id="webio1output1">OFF</td>
<td>
<input class="button" onclick="setOutput(1,1,1);" type="button" value="ON">
<input class="button" onclick="setOutput(1,1,0);" type="button" value="OFF">
</td>
</tr>
</table>
</body>
</html>
No vamos a entrar aquí en los detalles del área de head ni sobre la información de estilos. El contenido del área de script será explicado más adelante. Importantes son el body y las denominaciones de id a través de las cuales se activan los objetos y las celdas de las tablas en las funciones de JavaScript. Con onclick se asigna al botón la función respectiva a activar. Como parámetro se transmite el número del Web-IO, seguido de los números de los contadores o de las salidas. En el caso de las salidas, el segundo parámetro indica el estado (0=OFF, 1=ON).
Siguiendo este sistema se puede adaptar el ejemplo a otros modelos de Web-IO agregando más filas en las tablas sin modificar las funciones de JavaScript.
Variables y funciones globales en JavaScript
En primer lugar hay que definir algunas variables globales.
var MaxI = 2;
var MaxO = 2;
var WebioIP = [’192.168.0.25’, ’192.168.0.26’];
var WebioPort = [’80’, ’80’];
var ApplicationStep = 0;
var Interval = 250;
// Converting hexadecimal string to Integer
function HexToInt(HexStr)
{ var TempVal;
var HexVal=0;
for( var i=0; i<HexStr.length;i++)
{ if (HexStr.charCodeAt(i) > 57)
{ TempVal = HexStr.charCodeAt(i) - 55;
}
else
{ TempVal = HexStr.charCodeAt(i) - 48;
}
HexVal=HexVal+TempVal*Math.pow(16, HexStr.length-i-1);
}
return HexVal;
}
Enviar peticiones HTTP y recibir respuestas HTTP
// Sending command lines to Web-IO and receiving IO State
function DataRequest(WebioNo, SendString)
{ var xmlHttp;
if( window.ActiveXObject ) // Internet Explorer
{ xmlHttp = new ActiveXObject( "Microsoft.XMLHTTP" );
}
else if(window.XMLHttpRequest ) // Mozilla, Opera und Safari
{ xmlHttp = new XMLHttpRequest();
}
if (xmlHttp)
{ xmlHttp.onreadystatechange = function()
{ if (xmlHttp.readyState == 4)
{ if (xmlHttp.status == 200)
{ if (xmlHttp.responseText.length > 0)
{ updateDisplay(WebioNo, xmlHttp.responseText);
}
xmlHttp=null;
}
}
}
xmlHttp.open("GET", SendString, true);
xmlHttp.setRequestHeader("If-Modified-Since", "Thu, 1 Jan 1970 00:00:00 GMT");
xmlHttp.send(null);
}
}
Del desarrollo de las peticiones HTTP y respuestas HTTP se encarga la función DataRequest
. Desgraciadamente Internet Explorer utiliza para el procesamiento de las peticiones HTTP mecanismos diferentes a los de otros navegadores. Por esa razón, la función comprueba en primer lugar el navegador en el que se ejecuta la página web. La petición HTTP transmitida como SendString
es enviada al servidor o el Web-IO, del que también fue cargada la página web. A continuación, la respuesta, es decir la HTTP-Reply, es transmitida a la función UpdateDisplay
.
Consultas cíclicas de entradas, salidas y contadores
// Preparing command lines for cycle sending
function CommandLoop()
{ var Command =’’;
var CommandString =’’;
var WebioNo = 0;
ApplicationStep++;
switch(ApplicationStep)
{ case 1:
Command = ’input’;
break;
case 2:
Command = ’input’;
WebioNo = 1;
break;
case 3:
Command = ’output’;
break;
case 4:
Command = ’output’;
WebioNo = 1;
break;
case 5:
Command = ’counter’;
break;
case 6:
Command = ’counter’;
WebioNo = 1;
ApplicationStep = 0;
break;
}
var IOPassword = document.getElementById(’webio’+WebioNo+’pw’).value;
CommandString = ’webiohttprequest.php?IP=’+WebioIP[WebioNo]+’&PORT=’+WebioPort[WebioNo]+’&COMMAND=’+Command+’&PW=’+ IOPassword;
if (WebioPort[WebioNo] == ’443’)
{ CommandString = CommandSting + ’&HTTPS=1’
}
DataRequest(WebioNo, CommandString);
maintimer = setTimeout("CommandLoop()", Interval);
}
La función CommandLoop
se inicia con el parámetro onload en la etiqueta del body, en el momento en que la página web está cargada por completo. A través de DataRequest
, la función envía alternadamente las peticiones HTTP a los Web-IO para consultar las entradas, salidas y contadores y se activa ella misma de nuevo con un retardo temporal. De ese modo, se actualizan constantemente los estados de las EA con intervalos fijos.
Conectar los Outputs
// Set Output to ON (requested from User)
function setOutput(WebioNo, OutNo, OutVal)
{ var IOPassword = document.getElementById(’webio’+WebioNo+’pw’).value;
var CommandString = ’webiohttprequest.php?IP=’+WebioIP[WebioNo]+’&PORT=’+WebioPort[WebioNo]+’&COMMAND=outputaccess&PW=’+ IOPassword+’&MASK=’+(2**OutNo).toString(16);
if(OutVal>0)
{ CommandString = CommandString + ’&STATE=0fff’;
}
else
{ CommandString = CommandString + ’&STATE=0’;
}
if (WebioPort[WebioNo] == ’443’)
{ CommandString = CommandSting + ’&HTTPS=1’
}
DataRequest(WebioNo, CommandString);
}
Pulsando el botón correspondiente se activa la función setOutput
y se transmite el número del Web-IO y de la salida, así como el estado a conmutar. A través de la función DataRequest
se envía al Web-IO la petición HTTP necesaria.
Borrar contadores
// Set Counter to 0 (requested from display)
function clearCounter(WebioNo, CounterNo)
{ var IOPassword = document.getElementById(’webio’+WebioNo+’pw’).value;
var CommandString = ’webiohttprequest.php?IP=’+WebioIP[WebioNo]+’&PORT=’+WebioPort[WebioNo]+’&COMMAND=counterclear’+CounterNo+’&PW=’+ IOPassword;
if (WebioPort[WebioNo] == ’443’)
{ CommandString = CommandSting + ’&HTTPS=1’
}
DataRequest(WebioNo, CommandString);
}
Pulsando el botón correspondiente se activa la función clearCounter
y se transmite el número del Web-IO y de la entrada.
Actualizar los contenidos de páginas web
// Dynamic update of the display depending on IO state
function updateDisplay(WebioNo, ReceiveStr)
{ var HexVal;
var state;
var ReceiveData = ReceiveStr.split(’;’)
// Display Intput state
if (ReceiveData[ReceiveData.length - 2].substring(0, 1) == ’i’)
{ HexVal = HexToInt(ReceiveData[ReceiveData.length - 1]);
for (var i = 0; i < MaxI; i++)
{ state = false;
if ((HexVal & Math.pow(2, i)) == Math.pow(2, i))
{ state = true;
}
document.getElementById(’webio’+WebioNo+’input’+i).firstChild.data = ( !state ) ? ’OFF’ : ’ON’;
}
}
// Display Output state
if (ReceiveData[ReceiveData.length - 2].substring(0, 1) == ’o’)
{ HexVal = HexToInt(ReceiveData[ReceiveData.length - 1]);
for (var i = 0; i < MaxO; i++)
{ state = false;
if ((HexVal & Math.pow(2, i)) == Math.pow(2, i))
{ state = true;
}
document.getElementById(’webio’+WebioNo+’output’+i).firstChild.data = ( !state ) ? ’OFF’ : ’ON’;
}
}
//Display Counter
if (ReceiveData.length - MaxI - 1 >= 0)
{ if (ReceiveData[ReceiveData.length - MaxI - 1].substring(0, 1) == ’c’)
{ for (var i = 0; i < MaxI; i++)
{ document.getElementById(’webio’+WebioNo+’counter’ + i).innerHTML = ReceiveData[ReceiveData.length - MaxI + i]
}
}
}
//Display cleared Counter
if (ReceiveData[ReceiveData.length - 2].substring(0, 1) == ’c’)
{ document.getElementById(’webio’+WebioNo+’counter’+ ReceiveData[ReceiveData.length - 2].substring(7, ReceiveData[ReceiveData.length - 2].length)).innerHTML = ReceiveData[ReceiveData.length - 1];
}
}
Como respuesta a una petición HTTP, el Web-IO envía siempre el comando propiamente dicho y uno o varios parámetros separados por punto y coma. La respuesta del Web-IO transmite el script de PHP 1:1.
Para la consulta de una entrada, por ejemplo input;1
La función UpdateDisplay
utiliza split
para descomponer el string transmitido en un array de strings. En el ejemplo para la entrada se obtiene un array con las primeras variables de campo input
y las segundas 1
. Las primeras letras de las primeras variables de campo definen si la respuesta se refiere a una entrada, una salida o a los contadores.
La segunda variable de campo contiene, para la entrada o la salida, el patrón de bit que indica el estado de conmutación de las EA (Bit0=Input0, Bit1= Input1, .....). Puesto que el valor se transmite en escritura hexadecimal primero tiene que ser convertido con la función HexToInt
en un número decimal.
Con un enlace de Y del valor de la entrada con la potencia al cuadrado, que se corresponde con el valor en bits de las diferentes entradas o salidas, se determina si la entrada (salida) afectada es igual a 0 o a 1, es decir si se encuentra en OFF o en ON. Esto tiene lugar en un bucle que se ejecutan en función de a la cantidad de EA.
Con la función de JavaScript document.getElementById
se identifica el objeto de indicación a actualizar, que es modificado en función del estado actual de conmutación.
Los mismos mecanismos se utilizan para actualizar los contadores, si bien en este caso se suprime la conversión de hexadecimal a decimal, pues los estados de los contadores se transmiten en valores decimales.
El ejemplo es compatible con las funciones más importantes del Web-IO, que estén disponibles a través de peticiones HTTP, optimizado para Web-IO Digital 4.0 2xIn, 2xOut. Para otros modelos de Web-IO puede ser necesario adaptar la estructura de la página web y la parte de JavaScript. Encontrará una descripción detallada sobre los usos de las peticiones HTTP para los modelos de Web-IO Digital en la sinopsis de comandos de peticiones o en manual de programación para Web-IO.