Tutorial al Web-IO digital:
Control de varios Web-IO Digital desde una página web
Hay aplicaciones que exigen la supervisión y el control dinámicos de varios Web-IO desde una página web común. Como se muestra en el ejemplo Control de Web-IO Digital desde una página web propia con AJAX, en principio no es difícil programar páginas web que actualicen los contenidos de la página mediante peticiones HTTP posteriores.
Sin embargo, por razones de seguridad, los navegadores actuales solo admiten peticiones HTTP enviadas con la técnica AJAX al servidor en el que fue cargada la página web misma.
Ejemplo: Si un Web-IO abre la página de usuario Mi página web en ella solo se podrá ofrecer una indicación dinámica de los estados de las EA de ese Web-IO.
Cross-Origin-Requests
Se denomina Cross-Origin-Request a la petición HTTP necesaria para cargar contenidos de un servidor B desde una página web que ha sido cargada desde un servidor A. Como ya se ha indicado, los navegadores rechazan los contenidos suministrados por tales peticiones por razones de seguridad.
Para que el navegador acepte esos contenidos y los procese es necesario que el tercer servidor solicitado tiene que incluir en su respuesta un Allow-Cross-Origin Statement.
Web-IO y Cross Origin Requests
Dispositivos integrados como el Web-IO pueden ser configurados de modo que se incluya Allow-Cross-Origin en la cabecera HTTP de la respuesta HTTP. Para ello tiene que indicarse el URL del servidor web, del que fue cargada la página web original solicitante - para la constelación mostrada esto sería http://<url von Web-IO 0>
. Otro modo sería introducir un *
como wildcard, lo que provoca que el navegador acepte la respuesta, sin importar desde dónde se cargo la página web original.
El siguiente ejemplo muestra como se utiliza Cross-Origin en la práctica y cómo se debe construir la correspondiente página web para Web-IO Digital 4.0 2xIn, 2xOut y el JavaScript respectivo.
Preparativos
- Conectar la tensión para Web-IO y cablear las IO
- Conectar el Web-IO a la red
- Asignar las direcciones 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. Además hay que activar en la Configuración avanzada la opción Permitir Cross Origin Requests e introducir el URL del Web-IO o del servidor web del que se deba cargar la página web.
Estructura básica de la página web propia
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. Más adelante se describen los contenidos de la sección script. Importantes son en el body 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 ApplicationStep = 0;
var Interval = 500;
// 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;
}
Es importante introducir en el campo de variables WebioIP
las direcciones de IP de los Web-IO partícipes.
Enviar peticiones HTTP y recibir respuestas HTTP
// Sending command lines to Web-IO and receiving IO state
function DataRequest(WebioNo, CommandString)
{ var cor;
if(window.XDomainRequest)
{ cor = new XDomainRequest();
if(cor)
{ cor.onload = function()
{ alert( "received: " + cor.responseText);
}
}
else
{ alert(’Your Browser does not support Cross Origin Request’);
}
}
else
{ cor = new XMLHttpRequest();
cor.onreadystatechange = function()
{ if(cor.readyState == 4)
updateDisplay(WebioNo, cor.responseText);
}
}
cor.open(’GET’, ’http://’+WebioIP[WebioNo]+’/’+CommandString);
cor.send();
}
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 CommandString
es enviada entonces al Web-IO, cuya dirección de IP está anotada en el campo de variables WebioIP
bajo el número transmitido con WebioNo
. La respuesta, es decir la HTTP Reply, es transmitida luego junto con el número del Web-IO solicitado a la función UpdateDisplay
.
Consultas cíclicas de entradas, salidas y contadores
// Preparing command lines for cycle sending
function CommandLoop()
{ var CommandString =’’;
ApplicationStep++;
switch(ApplicationStep)
{ case 1:
CommandString = ’input’;
break;
case 2:
CommandString = ’output’;
break;
case 3:
CommandString = ’counter’;
ApplicationStep = 0;
break;
}
DataRequest(0, CommandString + ’?PW=’ + document.getElementById(’webio0pw’).value + ’&’);
DataRequest(1, CommandString + ’?PW=’ + document.getElementById(’webio1pw’).value + ’&’);
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 = ’outputaccess’+OutNo+’?PW=’+iopassword+’&State=OFF&’;
if(OutVal>0)
{ CommandString = ’outputaccess’+OutNo+’?PW=’+iopassword+’&State=ON&’;
}
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;
DataRequest(WebioNo, ’counterclear’+CounterNo+’?PW=’+iopassword+’&’);
}
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.
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.
Consejo: Por supuesto, una página web, creada de este modo, puede ser iniciada también directamente desde el disco duro del PC local. En ese caso, es indispensable introducir como URL para Cross-Origin *
en Web-IO, pues el navegador no puede asignar el disco duro ningún origen de IP.
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.