Logikbausteine (allgemein)***r-0Entwicklung von Logikbausteinen***r-0-0Beispiele
Einfacher LBS (UND-Gatter)
Wenn Eingang 1 und/oder 2 getriggert (refreshed) wurde...
if ($E[1]['refresh']==1 || $E[2]['refresh']==1) {
...soll die eigentliche UND-Verknüpfung der Werte an Eingang 1 bzw. 2 erfolgen:
if (($E[1]['value']!=0) && ($E[2]['value']!=0)) {
Mit anderen Worten: Sobald mindestens einer der beiden Eingänge getriggert wird, wird der Baustein seine Arbeit verrichten.
Sind nun an beiden Eingängen die Werte ungleich 0, ist die UND-Bedingung erfüllt und der Ausgang 1 wird auf 1 gesetzt:
logic_setOutput($id,1,1);
Andernfalls wird der Ausgang 1 auf 0 gesetzt:
logic_setOutput($id,1,0);
Und-Gatter mit 2 Eingängen (14000020):
###[DEF]###
[name=UND-Gatter 2xEingänge]
[e#1=A #init=0]
[e#2=B #init=0]
[a#1=0/1]
###[/DEF]###
###[HELP]###
(Hilfetext)
###[/HELP]###
###[LBS]###
function LB_LBSID($id) {
if ($E=logic_getInputs($id)) { //Daten aller Eingänge holen
if ($E[1]['refresh']==1 || $E[2]['refresh']==1) { //neues Telegramm?
if (($E[1]['value']!=0) && ($E[2]['value']!=0)) { //UND-Verknüpfung
logic_setOutput($id,1,1); //Ausgang 1 auf 1 setzen
} else {
logic_setOutput($id,1,0); //Ausgang 1 auf 0 setzen
}
}
}
}
?>
###[/LBS]###
###[EXEC]###
?>
###[/EXEC]###
Komplexer LBS (Timer)
Dieses Beispiel soll die grundlegende Funktionsweise eines LBS vermitteln, der nicht nur einmalig sequentiell abgearbeitet wird, sondern quasi "im Hintergrund" seine Arbeit verrichtet.
Der LBS bildet einen Timer nach, d.h. der LBS wird mit einem neuen Telegramm an E1 gestartet, Ausgang A1 wird auf 1 gesetzt und nach Ablauf der Zeit an E2 wird der Ausgang A1 auf 0 gesetzt. Während der Timer "läuft", kann dieser mit einem neuen Telegramm an E1 erneut gestartet werden (retriggern).
Im Unterschied zu einem einfachen LBS (z.B. UND-Gatter) wird die LBS-Funktion LB_LBSID($id) nach dem ersten Aufruf intern immer wieder aufgerufen, ohne(!) dass der LBS von einem seiner Eingänge getriggert werden muss. Der LBS "läuft" also nach einem initialen Trigger solange, bis sein Status entsprechend zurückgesetzt wird:
Die Funktion logic_setState($id,1) startet den LBS, setzt also seinen Status auf "läuft". Dadurch wird der LBS von der Logik-Engine immer wieder aufgerufen, d.h. der Code des LBS wird viele Male pro Sekunde(!) ausgeführt. Dadurch kann der LBS z.B. überprüfen, ob bestimmte Bedingungen erfüllt sind - ohne das der LBS durch einen seiner Eingänge getriggert wird.
Mit der Funktion logic_setState($id,0) wird der LBS wieder gestoppt und wird erst durch einen erneuten Trigger wieder gestartet.
Mit der Funktion logic_getState($id) kann der aktuelle Status des LBS (s.o.) abgefragt werden.
In diesem Beispiel werden zwei Zustände des LBS unterschieden:
1. Ist der LBS-Status = 0 (LBS läuft nicht), wartet der LBS (z.B. wie ein einfaches Gatter) auf einen Trigger. Hier wartet der LBS auf einen neuen Wert ungleich 0 an E1. Sobald diese Bedingung erfüllt ist, wird der LBS gestartet, der Ausgang A1 auf 1 gesetzt und die Variable 1 erhält den aktuellen Timestamp zuzüglich des Wertes an E2.
2. Der LBS ist nun gestartet ("läuft") und läuft solange, bis der aktuelle Timestamp größer/gleich dem gespeicherten Wert (Variable 1) ist. Ist diese Bedingung erfüllt, wird A1 auf 0 gesetzt und der Baustein gestoppt. Unabhängig davon kann der Timer durch ein neues Telegramm ungleich 0 an E1 retriggert werden, d.h. die Variable 1 erhält erneut den aktuellen Timestamp zuzüglich des Wertes an E2.
Hinweis:
Die Funktion getMicrotime() liefert den aktuellen Unix-Timestamp in Sekunden und Millisekunden als FLOAT, z.B. 10000.12345 (also 10000 Sekunden und 12345 Millisekunden). Mit Hilfe dieser Funktion sind sehr präzise (i.d.R. relative) Zeitmessungen möglich. Für absolute Zeitfunktionen (Datum/Uhrzeit) stehen in PHP zahlreiche Funktionen zu Verfügung.
###[DEF]###
[name=Timer]
[e#1=Trigger]
[e#2=Dauer (s) #init=10]
[a#1=0/1]
[v#1=0]
###[/DEF]###
###[HELP]###
###[/HELP]###
###[LBS]###
function LB_LBSID($id) {
if ($E=logic_getInputs($id)) {
if (logic_getState($id)==0) { //LBS läuft nicht?
if ($E[1]['value']!=0 && $E[1]['refresh']==1) {
logic_setVar($id,1,(getMicrotime()+$E[2]['value'])); //Timestamp + E2
logic_setOutput($id,1,1); //A1=1 setzen
logic_setState($id,1); //LBS "starten"
}
} else {
//Retriggern?
if ($E[1]['value']!=0 && $E[1]['refresh']==1) {
logic_setVar($id,1,(getMicrotime()+$E[2]['value'])); //Timestamp + E2
}
//Zeit abgelaufen?
if (getMicrotime()>=logic_getVar($id,1)) {
logic_setOutput($id,1,0); //A1=0 setzen
logic_setState($id,0); //LBS "stoppen"
}
}
}
}
?>
###[/LBS]###
###[EXEC]###
?>
###[/EXEC]###
Einfacher LBS mit EXEC-Script
Es können alle grundlegenden Logikfunktionen genutzt werden (z.B. Variablen setzen, Ausgänge setzen, Eingänge lesen, etc.).
Sobald an Eingang 1 ein neues Telegramm ungleich 0 eintrifft, wird der LBS Ausgang 1 auf 0 setzen und das EXEC-Script starten. Der LBS wartet nach dem Aufruf des EXEC-Scripts nicht(!) auf dessen Ausführung, d.h. das EXEC-Script kann u.U. mehrfach ausgeführt werden.
Das EXEC-Script wartet 60s (dies soll z.B. eine HTTP-Abfrage repräsentieren) und setzt dann Ausgang 1 auf 1.
###[DEF]###
[name=Beispiel: LBS mit EXEC-Script]
[e#1=Trigger]
[a#1=Test]
###[/DEF]###
###[HELP]###
###[/HELP]###
###[LBS]###
function LB_LBSID($id) {
if ($E=logic_getInputs($id)) {
if ($E[1]['value']!=0 && $E[1]['refresh']==1) {
logic_setOutput($id,1,0); //Ausgang 1 auf 0 setzen
logic_callExec(LBSID,$id); //EXEC-Script starten
}
}
}
?>
###[/LBS]###
###[EXEC]###
require(dirname(__FILE__)."/../../../../main/include/php/incl_lbsexec.php");
sql_connect();
//-------------------------------------------------------------------------------------
sleep(60); //z.B. eine HTTP-Abfrage
logic_setOutput($id,1,1); //Ausgang 1 auf 1 setzen
//-------------------------------------------------------------------------------------
sql_disconnect();
?>
###[/EXEC]###
Komplexer LBS mit EXEC-Script
In diesem Beispiel wird ein EXEC-Script verwendet, um über einem externen "Webdienst" die aktuelle WAN-IP-Adresse des EDOMI-Rechners zu ermitteln (dieser Webdienst liefert beim Aufruf einer URL die reine IP-Adresse zurück).
Der LBS wird mit einem neuen Telegramm ungleich 0 an E1 gestartet, ruft dann das EXEC-Script auf und "wartet" solange, bis das EXEC-Script seine Arbeit verrichtet hat. Während das EXEC-Script versucht den Webdienst abzufragen, kann der LBS das EXEC-Script nicht erneut ausführen.
Ist die Abfrage erfolgreich, wird A1 auf die IP-Adresse gesetzt, andernfalls wird A2 auf 1 (Fehler) gesetzt und A1 bleibt unverändert.
Dieses Beispiel demonstriert eine Möglichkeit der korrekten Implementierung eines EXEC-Scripts: Würde der LBS nicht auf das Ergebnis des EXEC-Scripts "warten" (wie im Beispiel zuvor), könnte der LBS noch während der Laufzeit des EXEC-Scripts erneut getriggert werden. Das EXEC-Script würde dann entsprechend mehrfach gestartet werden und u.U. in zahlreichen Instanzen den Webdienst überstrapazieren.
Achtung:
Die "refresh"-Eigenschaft der Eingänge kann im EXEC-Script nicht zuverlässig abgefragt werden, da das Script nicht im Kontext der Logik-Engine ausgeführt wird. Die "value"-Eigenschaft beinhaltet hingegen stets den gerade aktuellen Wert eines Eingangs.
###[DEF]###
[name=Beispiel: LBS mit EXEC-Script]
[e#1=Trigger]
[a#1=IP]
[a#2=Fehler]
[v#1 = 0]
###[/DEF]###
###[HELP]###
###[/HELP]###
###[LBS]###
function LB_LBSID($id) {
if ($E=logic_getInputs($id)) {
if (logic_getVar($id,1)==0) {
logic_setVar($id,1,1); //setzt V1=1, um einen mehrfachen Start des EXEC-Scripts zu verhindern
logic_callExec(LBSID,$id); //EXEC-Script starten
}
}
}
?>
###[/LBS]###
###[EXEC]###
require(dirname(__FILE__)."/../../../../main/include/php/incl_lbsexec.php");
sql_connect();
//-------------------------------------------------------------------------------------
$ctx=stream_context_create(array('http' => array('timeout' => 30 ))); //30 Sekunden Timeout
$ip=file_get_contents('http://ipecho.net/plain',null,$ctx,0,15); //HTTP-Request (max. 15 Zeichen lesen)
if (filter_var($ip,FILTER_VALIDATE_IP)!==false) { //Gültige IP4-Adresse?
logic_setOutput($id,1,$ip); //A1 = IP-Adresse
} else {
logic_setOutput($id,2,1); //A2 = 1 (Fehler)
}
logic_setVar($id,1,0); //setzt V1=0, um einen erneuten Start des EXEC-Scripts zu ermöglichen
//-------------------------------------------------------------------------------------
sql_disconnect();
?>
###[/EXEC]###
EXEC-Script als Dämon
Dieses Beispiel demonstriert eine "saubere" Implementierung eines EXEC-Scripts, dass beim EDOMI-Start einmalig und automatisch gestartet wird, dann endlos durchläuft und beim Beenden/Neustart von EDOMI ordnungsgemäß beendet wird.
Der LBS wird durch den Initalwert an E1 beim EDOMI-Start automatisch gestartet. Das Verbindungen von E1 mit einem KO oder einem anderen LBS ist nicht erforderlich.
Jetzt wird eine Hilfsvariable (V1) auf 1 gesetzt, um ein mehrfaches Starten des EXEC-Scripts zu verhinden. Dies ist nicht unbedingt notwendig, da der Initialwert an E1 den LBS ohnehin nur einmalig beim EDOMI-Start triggern wird. Jedoch ist dieses Vorgehen zur Sicherheit zu empfehlen, falls an E1 zusätzlich ein KO oder ein anderer LBS angelegt werden sollte.
Das EXEC-Script läuft nun in einer Schleife solange, bis EDOMI beendet oder neugestartet wird:
Das Beenden/Neustarten von EDOMI wird mittels der Funktion getSysInfo(1) überwacht. Diese Funktion liefert einen Wert >=1 zurück, solange EDOMI normal läuft. Sobald EDOMI beendet oder neugestartet wird, liefert diese Funktion einen Wert <1 zurück. Jetzt bleiben dem Script ca. 3 Sekunden Zeit, um sich selbst ordnungsgemäß zu beenden (z.B. Sockets zu schließen, etc.). Aber Achtung: In diesem Moment stehen keine Logikfunktionen (Eingangswerte, etc.) mehr zu Verfügung!
Achtung:
Die "refresh"-Eigenschaft der Eingänge kann im EXEC-Script nicht zuverlässig abgefragt werden, da das Script nicht im Kontext der Logik-Engine ausgeführt wird. Die "value"-Eigenschaft beinhaltet hingegen stets den gerade aktuellen Wert eines Eingangs.
###[DEF]###
[name = Dämon-Test ]
[e#1 = Autostart #init=1]
[v#1 = 0]
###[/DEF]###
###[HELP]###
###[/HELP]###
###[LBS]###
function LB_LBSID($id) {
if ($E=logic_getInputs($id)) {
if (logic_getVar($id,1)!=1) {
logic_setVar($id,1,1); //setzt V1=1, um einen mehrfachen Start des EXEC-Scripts zu verhindern
logic_callExec(LBSID,$id); //EXEC-Script starten (garantiert nur einmalig)
}
}
}
?>
###[/LBS]###
###[EXEC]###
require(dirname(__FILE__)."/../../../../main/include/php/incl_lbsexec.php");
set_time_limit(0); //Wichtig! Script soll endlos laufen
sql_connect();
writeToCustomLog('Test',0,'EXEC-Script gestartet'); //Demonstration: Individual-Log zum Debuggen nutzen
while (getSysInfo(1)>=1) { //Hauptschleife (wird beim Beenden oder Neustart von EDOMI verlassen)
//Wichtig: getSysInfo(1) sorgt zudem dafür, dass die Datenbank-Verbindung aufrechterhalten wird!
//eigener Programm-Code...
usleep(1000*10); //z.B. 10ms warten - wichtig, um die CPU-Last zu begrenzen!
}
writeToCustomLog('Test',0,'EXEC-Script korrekt beendet.'); //Demonstration: Individual-Log zum Debuggen nutzen
sql_disconnect();
?>
###[/EXEC]###