Login
Newsletter
Werbung

Do, 24. März 2011, 15:00

Heimautomatisierung für Hardwarebastler (Teil 2)

Leicht und elegant – Lua

Allen OpenAPC-Lua-Skripten ist gemein, dass sie eine globale Callback-Funktion oapc_ispace_recv_callback() definieren müssen. Diese wird immer dann aufgerufen, wenn der Interlock Server eine Änderung der Daten meldet. Das heißt, hier kommen die Informationen über den Zustand der Buttons an und müssen jeweils in einer eigenen Variablen zwischen gespeichert werden:

function oapc_ispace_recv_callback(nodeName, cmd, ios, val0, val1, val2, val3, val4, val5, val6, val7)
   if (nodeName=="/RLHoch/out") then
      RLHochPress=val1
   elseif (nodeName=="/RLRunter/out") then
      RLRunterPress=val1
   elseif (nodeName=="/RLStopp/out") then
      RLStoppPress=val1
   end
end

Die Parameter dieser Funktion liefern dabei wichtige Zusatzinformationen: nodeName ist der Name des Datenblocks, für den neue Werte übermittelt werden. cmd liefert eine Information über den Grund, warum diese Funktion aufgerufen wurde und kann für dieses Beispiel ignoriert werden, da dieser hier immer der Gleiche ist. Der Parameter ios wird in diesem Beispiel der Einfachheit halber nicht ausgewertet, was man in realen Projekten aber in jedem Fall tun sollte: dieser enthält ein Bitmuster, welches angibt, welcher der folgenden Parameter val0..val7 welche Art von Daten enthält (also ob dort digitale, numerische, Text- oder Binärdaten ankommen). Das sollte korrekterweise überprüft werden, um Skriptabbrüche zu vermeiden, weil die Daten falsch behandelt wurden.

Die Variablen val0 bis val7 wiederum enthalten die aktualisierten Daten. Da sich in diesem Beispiel immer nur der Ausgang 1 ändert, steht der geänderte Wert auch immer in val1. Im Funktionsrumpf selbst wird jetzt nur noch verglichen, ob sich ein bekannter Datenblock verändert hat. Wenn ja, wird dessen Wert in einer globalen Variablen zwischengespeichert.

Im Hauptteil des Skriptes müssen zum einen diese Variablen initialisiert und zum anderen eine Verbindung zum Server hergestellt werden:

RLHochPress=false
RLRunterPress=false
RLStoppPress=false
if (oapc_ispace_connect("",0)==1) then
   ...
   oapc_ispace_disconnect()
end

Das passiert mit dem Aufruf oapc_ispace_connect(), hier mit leeren Parametern. Optional könnten IP und Portnummer angegeben werden, welche auf den Rechner verweisen, auf dem der Interlock Server läuft. Da das System in diesem Beispiel nicht in einem Netzwerk verteilt ist, sondern alles auf dem localhost läuft, ist das nicht erforderlich. Wird das Skript planmäßig beendet, sollte die Verbindung zum Interlock Server mittels oapc_ispace_disconnect() ebenso planmäßig wieder geschlossen werden. oapc_ispace_connect() liefert im Erfolgsfalle überraschenderweise eine 1 zurück, im Fall eines Fehlers ist der Rückgabewert ein Fehlercode größer 1.

Die verbleibende Implementierung der eigentlichen Logik ist jetzt vergleichsweise simpel: In einer Endlosschleife werden die Zustände der drei globalen Statusvariablen RLHochPress, RLRunterPress, RLStoppPress wiederholt abgefragt, die entsprechenden Daten zum Interlock Server gesendet und die jeweilige Variable zurückgesetzt:

while (true) do
   if (RLHochPress==true) then
      oapc_ispace_set_value("/Parallelport_5/in/1",false)
      oapc_ispace_set_value("/Parallelport_5/in/0",true)
      oapc_ispace_set_value("/RLDisplay/in/0",true)
      RLHochPress=false
   elseif (RLRunterPress==true) then
      oapc_ispace_set_value("/Parallelport_5/in/0",false)
      oapc_ispace_set_value("/Parallelport_5/in/1",true)
      oapc_ispace_set_value("/RLDisplay/in/0",true)
      RLRunterPress=false
   elseif (RLStoppPress==true) then
      oapc_ispace_set_value("/Parallelport_5/in/0",false)
      oapc_ispace_set_value("/Parallelport_5/in/1",false)
      oapc_ispace_set_value("/RLDisplay/in/0",false)
      RLStoppPress=false
   else
      oapc_util_thread_sleep(10)
   end
end

Die Funktion oapc_ispace_set_value() kann für alle Datentypen verwendet werden, hier entscheidet der tatsächlich verwendete Typ des zweiten Parameters, ob digitale, numerische Text- oder Binärdaten gesendet werden. Der erste Parameter gibt hier den Datenblock und die Nummer des Einganges an, der verändert werden soll, deswegen wird hier die erweiterte Bezeichnung des Blockes mit der zusätzlich angehängten Nummer zur Identifizierung verwendet.

Das war dann auch schon die gesamte Implementierung: so bald der OpenPlayer läuft, kann dieses Skript aus der Konsole heraus mit einem

luaPLC test2.lua

gestartet werden und steuert ab sofort den Ablauf.

Abschließend noch ein Wort zur Funktion oapc_util_thread_sleep(). Diese bietet eigentlich nichts OpenAPC-spezifisches, erweitert Lua jedoch um etwas Wichtiges, was dieser Sprache leider fehlt: eine Pause. An dieser Stelle werden im Beispiel immer dann 10 Millisekunden Pause eingelegt, wenn keine Benutzereingaben vorhanden sind und das Skript deswegen eigentlich nichts tun muss. Diese Pause verhindert, dass das Skript in dieser Endlosschleife die gesamte Rechenleistung aufbraucht.

Das SDK der OpenAPC-Software enthält übrigens auch ein funktionierendes Lua-Beispielskript, welches ebenfalls die OpenAPC-spezifischen Spracherweiterungen demonstriert.

Pro-Linux
Pro-Linux @Facebook
Neue Nachrichten
Werbung