Tutorial zum Web-IO Digital:
Mehrere Web-IO Digital aus einer Webseite steuern
Es gibt Anwendungsfälle, die es erfordern, dass von einer gemeinsamen Webseite mehrere Web-IO dynamisch überwacht und gesteuert werden sollen. Wie im Beispiel "Web-IO Digital aus eigener Webseite mit AJAX steuern" gezeigt, ist es grundsätzlich nicht schwierig, Webseiten zu programmieren, die durch nachträgliche HTTP-Requests Seiteninhalte aktualisieren.
Allerdings lassen aktuelle Browser aus Gründen der Sicherheit solche HTTP-Requests, die mittels AJAX-Technik versendet werden, nur zu dem Server zu, von dem die eigentliche Webseite geladen wurde.
Beispiel: Wird von einem Web-IO die User-Seite Meine Webseite aufgerufen, können hier nur die IO-Zustände dieses Web-IO dynamisch angezeigt werden.
Cross-Origin-Requests
Wenn versucht wird, von einer Webseite die von Server A geladen Inhalte von Server B nachzuladen, bezeichnet man den dazu nötigen HTTP-Request als Cross-Origin-Request. Wie bereits beschrieben werden die auf solche Requests zurückgelieferten Inhalte vom Browser aus Gründen der Sicherheit verworfen.
Damit der Browser die Inhalte trotzdem akzeptiert und verarbeitet, muss der angefragte Dritt-Server in seiner Antwort ein Allow-Cross-Origin-Statement mitsenden.
Web-IO und Cross Origin Requests
Embedded Geräte wie das Web-IO können so konfiguriert
werden, dass Allow-Cross-Origin im HTTP-Kopf der HTTP-Antwort mitgesendet wird.
Dazu muss die URL des Web-Server angegeben werden, von dem die ursprünglich
anfragende Webseite geladen wurden - für die gezeigte Konstellation also z.B. http://<url von Web-IO 0>
. Alternativ kann ein *
als Wildcard eingetragen werden, was dazu führt, dass der Browser die
Antwort akzeptiert, unabhängig davon, von wo die ursprüngliche Webseite geladen wurde.
Das folgende Beispiel zeigt, wie Cross-Origin in der Praxis angewendet wird und wie eine entsprechende Webseite für das Web-IO Digital 4.0 2xIn, 2xOut und das zugehörige JavaScript aufgebaut sein muss.
Vorbereitungen
- Web-IO mit Spannung versorgen und IOs verdrahten
- Web-IO mit dem Netzwerk verbinden
- IP-Adressen vergeben
-
Beim Web-IO im Bereich Kommunikationswege >> Web-API den Punkt HTTP-Request erlauben aktivieren und die Outputs zum Schalten freigeben. Außerdem unter Erweiterte Einstellungen den Punkt Cross Origin Requests zulassen aktivieren und die URL des Web-IO oder Web-Servers eintragen, von dem die Webseite geladen werden soll.
Grundaufbau der eigenen Webseite
Das HTML-Grundgerüst dieser Seite sieht folgendermaßen aus:
<!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>
Auf den head-Bereich und die Style-Informationen wollen wir hier nicht näher eingehen. Im weiteren Verlauf werden die Inhalte des script-Bereichs beschrieben. Wichtig sind im body-Bereich id-Benennungen, über die in den JavaScript-Funktionen die Objekte bzw. Tabellenzellen angesprochen werden. Den Buttons wird über onclick die aufzurufende Funktion zugeteilt. Als Parameter wird die Nummer des Web-IO gefolgt von der Nummer der Counter bzw. der Outputs übergeben. Bei den Outputs gibt der zweite Parameter den Status (0=OFF, 1=ON) an.
Bei Einhaltung dieser Systematik kann das Beispiel ohne Änderung der JavaScript-Funktionen durch Hinzufügen weiterer Tabellenzeilen an weitere Web-IO-Modelle angepasst werden.
Globale Variablen und Funktionen im JavaScript
Zunächst müssen einige globale Variablen deklariert werden.
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;
}
Wichtig ist in das Variablenfeld WebioIP
die IP-Adressen der beteiligten Web-IOs einzutragen.
HTTP-Requests versenden und HTTP-Replys empfangen
// 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();
}
Die Abwicklung von HTTP-Request und HTTP-Reply übernimmt die Funktion
DataRequest
. Leider nutzt der Internet Explorer für die
Verarbeitung von HTTP-Requests andere Mechanismen als die anderen Browser.
Deshalb prüft die Funktion zunächst, in welchem Browser die Webseite ausgeführt wird.
Der als CommandString
übergebene HTTP-Request wird dann an das Web-IO gesendet,
dessen IP-Adresse im Variablenfeld WebioIP
unter der mit WebioNo
übergebenen Nummer eingetragen ist. Die Antwort, also der
HTTP-Reply, wird dann zusammen mit der Nummer des angefragten Web-IO an die
Funktion UpdateDisplay
übergeben.
Zyklische Abfrage von Inputs, Outputs und Countern
// 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);
}
Die Funktion CommandLoop
wird über den Parameter
onload im body-Tag gestartet, sobald die Webseite vollständig geladen ist.
Die Funktion schickt über die Funktion DataRequest
abwechselnd die HTTP-Requests
für die Abfrage von Inputs, Outputs und Countern an die Web-IOs und ruft sich
mit zeitlicher Verzögerung wieder selbst auf.
So werden kontinuierlich im festen Intervall die IO-Zustände aktualisiert.
Schalten der 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);
}
Durch Klick auf die entsprechenden Buttons wird die Funktion setOutput
aufgerufen und die Nummer des Web-IO, des Outputs, sowie der zu schaltende Zustand übergeben.
Über die Funktion DataRequest
wird der notwendige HTTP-Request an das Web-IO geschickt.
Löschen der Counter
// 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+'&');
}
Durch Klick auf die entsprechenden Buttons wird die Funktion clearCounter
aufgerufen und die Nummer des Web-IO und des Inputs übergeben.
Aktualisieren der Webseiten Inhalte
// 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];
}
}
Als Antwort auf einen HTTP-Request sendet das Web-IO immer das eigentliche Kommando und getrennt mit Semikolon einen oder mehrere Parameter.
Für eine Input-Abfrage z.B. input;1
Die Funktion UpdateDisplay
zerlegt mittels split
den übergebenen String in einen String-Array. Für das Input-Beispiel
ergibt sich ein Array mit der ersten Feldvariablen input
und der zweiten 1
.
Über den ersten Buchstaben der ersten Feldvariablen wird festgestellt,
ob die Antwort einen Input, Output oder die Counter betrifft.
Die zweite Feldvariable beinhaltet für Input bzw. Output das Bit-Muster
für den Schaltzustand der IOs (Bit0=Input0, Bit1= Input1, .....).
Da der Wert in hexadezimaler
Schreibweise übergeben wird, muss er zunächst über die Funktion
HexToInt
in eine Dezimalzahl gewandelt werden.
Über eine UND-Verknüpfung des Input-Wertes mit der Zweierpotenz, die dem Bit-Wert der einzelnen Inputs bzw. Outputs entspricht, wird festgestellt, ob der betreffende Input (Output) gleich 0 oder 1 also OFF oder ON ist. Das erfolgt in einer Schleife, die entsprechend der Anzahl der IOs durchlaufen wird.
Mittels der JavaScript-Funktion document.getElementById
wird das zu aktualisierende Anzeigeobjekt identifiziert und entsprechend
dem aktuellen Schaltzustand angepasst.
Die gleichen Mechanismen werden für die Aktualisierung der Counter verwendet, wobei die Umrechnung hexadezimal in dezimal entfällt, da die Zählerstände der Counter dezimal übergeben werden.
Tipp: Natürlich kann eine so erstellte Webseite auch direkt
von der Festplatte des lokalen PC gestartet werden. In diesem Fall
muss als URL für Cross-Origin im Web-IO zwingend *
eingetragen werden,
da der Browser der Festplatte keinen IP-Ursprung zuordnen kann!
Das Beispiel unterstützt die wichtigsten Funktionen des Web-IO, die über HTTP-Requests verfügbar sind, optimiert für das Web-IO Digital 4.0 2xIn, 2xOut. Für die anderen Web-IO-Modelle müssen ggf. Anpassung am Aufbau der Webseite und am JavaScript-Teil vorgenommen werden. Eine detaillierte Beschreibung zur Nutzung von HTTP-Requests für die Web-IO Digita-Modelle finden Sie in der Request-Kommandoübersicht oder im Programmierhandbuch zum Web-IO.