Als Beispiel basteln wir uns einen Größer-Kleiner-Gleich-Baustein
von MatthiasS
Wenn man wenig Erfahrung mit der Erstellung von HS-Logik-Modulen und deren Syntax hat, hilft ein Blick in bestehende Logiken. Die liegen entweder im Verzeichnis .\logic oder man schaut die Original-Logik an, die befindet sich in der Datei logik.dat im Verzeichnis .\dat.
Für unseren Fall suchen wir uns einmal die Original-Vergleicher-Bausteine „Größer“ und „Kleiner“. In der logik.dat finden wir:
### BS : Vergleicher (==) = 9019
5001|9019|2|4|0|0|1
5002|9019|1|0
5002|9019|2|0
5004|9019|1|0|1|1
5004|9019|2|0|1|1
5004|9019|3|0|1|2
5004|9019|4|0|1|2
5012|9019|0|""|"round(EN[1]*1000000) == round(EN[2]*1000000)"|""|1|0|0|2
5012|9019|0|"(EI==0)"|"round(EN[1]*1000000) == round(EN[2]*1000000)"|""|3|0|0|4
### BS : Vergleicher (>) = 9020
5001|9020|2|4|0|0|1
5002|9020|1|0
5002|9020|2|0
5004|9020|1|0|1|1
5004|9020|2|0|1|1
5004|9020|3|0|1|2
5004|9020|4|0|1|2
5012|9020|0|""|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|1|0|0|2
5012|9020|0|"(EI==0)"|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|3|0|0|4
### BS : Vergleicher (>=) = 9021
5001|9021|2|4|0|0|1
5002|9021|1|0
5002|9021|2|0
5004|9021|1|0|1|1
5004|9021|2|0|1|1
5004|9021|3|0|1|2
5004|9021|4|0|1|2
5012|9021|0|""|"round(EN[1]*1000000) >= round(EN[2]*1000000)"|""|1|0|0|2
5012|9021|0|"(EI==0)"|"round(EN[1]*1000000) >= round(EN[2]*1000000)"|""|3|0|0|4
### BS : Vergleicher (<) = 9022
5001|9022|2|4|0|0|1
5002|9022|1|0
5002|9022|2|0
5004|9022|1|0|1|1
5004|9022|2|0|1|1
5004|9022|3|0|1|2
5004|9022|4|0|1|2
5012|9022|0|""|"round(EN[1]*1000000) < round(EN[2]*1000000)"|""|1|0|0|2
5012|9022|0|"(EI==0)"|"round(EN[1]*1000000) < round(EN[2]*1000000)"|""|3|0|0|4
### BS : Vergleicher (<=) = 9023
5001|9023|2|4|0|0|1
5002|9023|1|0
5002|9023|2|0
5004|9023|1|0|1|1
5004|9023|2|0|1|1
5004|9023|3|0|1|2
5004|9023|4|0|1|2
5012|9023|0|""|"round(EN[1]*1000000) <= round(EN[2]*1000000)"|""|1|0|0|2
5012|9023|0|"(EI==0)"|"round(EN[1]*1000000) <= round(EN[2]*1000000)"|""|3|0|0|4
Die entscheidende Zeile im Baustein "Größer" lautet:
5012|9020|0|""|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|1|0|0|2
Das heißt, wenn der Eingang 1 größer als Eingang 2 ist, dann wird das Boolsche Ergebnis auf Ausgang 1 geschoben und zusätzlich auf Ausgang 2 (das ist der negative (invertierte) Ausgang).
Eigene Logikbausteine kann man nicht in die logik.dat einfügen, die müssen in separaten Modulen erstellt werden. Deshalb ist die Syntax etwas anders als in der Original-Datei.
[WICHTIG]Jeder Baustein hat eine eigene Nummer, und die muss eindeutig sein. Sonst gibt es unweigerlich Probleme. Deswegen erhält jeder Entwickler von Dacom auf Wunsch seinen eigenen Nummernbereich zugewiesen.[/WICHTIG]
Mein Nummernbereich geht von 19800 - 19899. Für diesen Baustein nehme ich mal die Nummer 19898.
Der Dateiname für den Baustein lautet dann: 19898_GKG.hsl
Die Syntax für Logikbaustein ist etwas eigen, aber natürlich ziemlich logisch. Es gibt folgende Zeilenarten:
5000|"Text"|Remanent(1/0)|Anz.Eingänge|.n.|Anzahl Ausgänge|.n.|.n.
5001|Anzahl Eingänge|Ausgänge|Offset|Speicher|Berechnung bei Start
5002|Index Eingang|Default Wert|0=numerisch 1=alphanummerisch
5003|Speicher|Initwert|Remanent
5004|ausgang|Initwert|runden binär (0/1)|typ (1-send/2-sbc)|0=numerisch 1=alphanummerisch
5012|abbruch bei bed. (0/1)|bedingung|formel|zeit|pin-ausgang|pin-offset|pin-speicher|pin-neg.ausgang
Fangen wir also mit Zeile 5000 an, die noch selbsterklärend ist:
Für unser Beispiel lautet die:
5000|"smh@213\Größer-Kleiner-Gleich"|0|2|"Eingang"|"Vergleichswert"|3|"Größer"|"Kleiner"|"Gleich
Wir haben also einen Baustein definiert, der im GLE im Ordner smh@213 einsortiert wird, Größer-Kleiner-Gleich heißt, nicht remanent ist, zwei Eingänge hat und drei Ausgänge, die wie oben bezeichnet sind.
Zeile 5001 definiert nochmals Anzahl der Ein- und Ausgänge, darüberhinaus noch Speichervariablen und Offsets. Offests sind Zeitglieder, die innerhalb eines Bausteines verwendet werden, z.B. für Verzögerungen etc. In unserem fall aber nicht erforderlich.
Für unser Beispiel:
5001|2|3|0|0|0
Heißt: Unser Baustein hat zwei Eingänge, drei Ausgänge, keinen Speicher, keinen Offset und wird bei Neustart nicht berechnet.
Zeilen 5002 bis 5004 Diese Zeilen definieren die Paramter für die verschiedenen Ein- und Ausgänge sowie für die Speichervariablen:
Die Eingänge:
5002|1|0|0
5002|2|0|0
heißt:
Eingang1 hat den Defaultwert 0, ist numerisch Eingang2 hat den Defaultwert 0, ist numerisch
Die Ausgänge:
5004|1|0|1|1|0
5004|2|0|1|1|0
5004|3|0|1|1|0
Die Ausgänge 1 bis 3 haben als Defaultwert 0, werden nach binär gewandelt (d.h., am Ausgang erscheint nur 0 oder 1), senden bei jeder Berechnung und sind numerisch.
Da keine Speichervariablen benötigt werden, gibt es keine Zeilen vom Typ 5003.
Vorspiel zu Ende, jetzt wird es ernst...
Die eigentliche Berechnung und Ausgabe der Ergebnisse findet in Zeilen vom Typ 5012 statt.
Hier nochmals die Definition:
5012|abbruch bei bed. (0/1)|bedingung|formel|zeit|pin-ausgang|pin-offset|pin-speicher|pin-neg.ausgang
Ich übergehe mal den ersten und zweiten Parameter für unser Beispiel, der dritte Parameter enthält die Formel, deren Ergebnis anschließend ausgegeben wird. Der Paramter Zeit gilt nur für Bausteine mit Zeitfunktionen, anschließend kommen die Angaben, an welche Ausgänge das Ergebnis gesendet werden soll.
Unser erster Ausgang ist der größer-Ausgang, unsere Formel, oben "ausgeliehen", lautete:
5012|9020|0|""|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|1|0|0|2
Wir entfernen aus der Zeile die Bausteinnumer (9020), weil die für externe Logiken ja im Dateinamen steckt und erhalten:
5012|0|""|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|1|0|0|0
Das bedeutet, die Formel A > B wird berechnet und das Ergebnis auf Ausgang 1 geschoben.
Da fehlt nicht mehr viel zum fertigen Baustein, einfach copy & paste:
5012|0|""|"round(EN[1]*1000000) < round(EN[2]*1000000)"|""|2|0|0|0
5012|0|""|"round(EN[1]*1000000) == round(EN[2]*1000000)"|""|3|0|0|0
Wichtig in der Python-Syntax ist das doppelte Gleichheitszeichen (DER Fehler hat mich schon oft zum Wahnsinn getrieben ).
So, wie haben jetzt also alle Fälle abgehandelt, der HS kümmert sich automatisch darum, dass jedesmal, wenn sich ein Eingang ändert, die Formeln neu berechnet werden und die Ausgänge gesetzt werden.
Der fertige Baustein
Einen ordentlichen Kopf anfügen, die Zeilen zusammenfassen und mit dem richtigen Dateinamen speichern:
####################################################################################
# BS : Größer-Kleiner-Gleich
####################################################################################
# (C) 2007, Matthias Schmidt, v1.0 #
####################################################################################
# #
# 02.10.2007 Tutorial#
####################################################################################
#5000|"Text"|Remanent(1/0)|Anz.Eingänge|.n.|Anzahl Ausgänge|.n.|.n.
#5001|Anzahl Eingänge|Ausgänge|Offset|Speicher|Berechnung bei Start
#5002|Index Eingang|Default Wert|0=numerisch 1=alphanummerisch
#5003|Speicher|Initwert|Remanent
#5004|ausgang|Initwert|runden binär (0/1)|typ (1-send/2-sbc)|0=numerisch 1=alphanummerisch
#5012|abbruch bei bed. (0/1)|bedingung|formel|zeit|pin-ausgang|pin-offset|pin-speicher|pin-neg.ausgang
5000|"smh@213\Größer-Kleiner-Gleich"|0|2|"Eingang"|"Vergleichswert"|3|"Größer"|"Kleiner"|"Gleich"
5001|2|3|0|0|0
5002|1|0|0
5002|2|0|0
5004|1|0|1|1|0
5004|2|0|1|1|0
5004|3|0|1|1|0
5012|0|""|"round(EN[1]*1000000) > round(EN[2]*1000000)"|""|1|0|0|0
5012|0|""|"round(EN[1]*1000000) < round(EN[2]*1000000)"|""|2|0|0|0
5012|0|""|"round(EN[1]*1000000) == round(EN[2]*1000000)"|""|3|0|0|0
Dateiname: 19898_GKG.hsl
Weiter geht es....
Wie zu erwarten, muss noch ein Byte-Ausgang her, damit man mit dem Ergebnis ein dynamisches KO ansteuern kann.
Wir benötigen also einen weiteren Ausgang "Ergebnis" und müssen demzufolge Zeile 5000 und 5001 ergänzen.
5000 wird zu:
5000|"smh@213\Größer-Kleiner-Gleich"|0|2|"Eingang"|"Vergleichswert"|4|"Größer"|"Kleiner"|"Gleich"|"Ergebnis
und 5001 zu
5001|2|4|0|0|0
Dann muss natürlich der zusätzliche Ausgang definiert werden, also braucht es noch eine neue Zeile vom Typ 5004:
5004|4|0|0|1|0
Hier jetzt wichtig: Die zweite Null, denn der Ausgang soll ja einen Wert erhalten und nicht nur 0/1.
Kommen wir zu den Formeln: Da wir jetzt nicht das Ergebnis direkt auf den Ausgang schalten können (Ergebnis ist ja immer nur 0 oder 1 bei booleschen Berechnungen), kommt der Parameter Bedingung zum Einsatz.
Die Formel steht also jetzt bei Bedingung, und unter Formel in dem Fall ein Fixwert, der dann, wenn die Bedingung erfüllt ist, auf den Ausgang geschoben wird.
Also zusätzliche 5012er Zeilen:
5012|0|"round(EN[1]*1000000) > round(EN[2]*1000000)"|"1"|""|4|0|0|0
5012|0|"round(EN[1]*1000000) < round(EN[2]*1000000)"|"2"|""|4|0|0|0
5012|0|"round(EN[1]*1000000) == round(EN[2]*1000000)"|"3"|""|4|0|0|0
Weitere Informationen
- Beschreibung der Variablen in der HS-Logik