This section explains principles behind the implementation of HAL components with the Python programming language.
1. Grundlegende Verwendung (engl. basic usage)
A non-realtime component begins by creating its pins and parameters, then enters a loop which will periodically drive all the outputs from the inputs. The following component copies the value seen on its input pin (passthrough.in) to its output pin (passthrough.out) approximately once per second.
#!/usr/bin/env python3 import hal, time h = hal.component("passthrough") h.newpin("in", hal.HAL_FLOAT, hal.HAL_IN) h.newpin("out", hal.HAL_FLOAT, hal.HAL_OUT) h.ready() try: while 1: time.sleep(1) h['out'] = h['in'] except KeyboardInterrupt: raise SystemExit
Copy the above listing into a file named "passthrough", make it executable (chmod +x), and place it on your $PATH. Then try it out:
$ halrun
halcmd: loadusr passthrough
halcmd: show pin
Komponenten-Pins:
Owner Typ Richtung Wert Name
03 float IN 0 passthrough.in
03 float OUT 0 passthrough.out
halcmd: setp passthrough.in 3.14
halcmd: show pin
Komponenten-Pins:
Owner Typ Richtung Wert Name
03 float IN 3.14 passthrough.in
03 float OUT 3.14 passthrough.out
2. Non-realtime components and delays
If you typed "show pin" quickly, you may see that passthrough.out still had its old value of 0. This is because of the call to time.sleep(1), which makes the assignment to the output pin occur at most once per second. Because this is a non-realtime component, the actual delay between assignments can be much longer if the memory used by the passthrough component is swapped to disk, as the assignment could be delayed until that memory is swapped back in.
Thus, non-realtime components are suitable for user-interactive elements such as control panels (delays in the range of milliseconds are not noticed, and longer delays are acceptable), but not for sending step pulses to a stepper driver board (delays must always be in the range of microseconds, no matter what).
3. Pins und Parameter erstellen
h = hal.component("passthrough")
The component itself is created by a call to the constructor hal.component. The arguments are the HAL component name and (optionally) the prefix used for pin and parameter names. If the prefix is not specified, the component name is used.
h.newpin("in", hal.HAL_FLOAT, hal.HAL_IN)
Then pins are created by calls to methods on the component object. The arguments are: pin name suffix, pin type, and pin direction. For parameters, the arguments are: parameter name suffix, parameter type, and parameter direction.
Pin- und Parametertypen: |
HAL_BIT |
HAL_FLOAT |
HAL_S32 |
HAL_U32 |
Pin-Richtungen: |
HAL_IN |
HAL_OUT |
HAL_IO |
|
Parameter-Richtungen: |
HAL_RO |
HAL_RW |
Der vollständige Pin- oder Parametername wird durch Verbinden des Präfixes und des Suffixes mit einem "." gebildet. Im Beispiel heißt der erstellte Pin also passthrough.in.
h.ready()
Sobald alle Pins und Parameter erstellt wurden, rufen Sie die Methode .ready() auf.
3.1. Ändern des Präfixes
Das Präfix kann durch den Aufruf der Methode .setprefix() geändert werden. Das aktuelle Präfix kann durch den Aufruf der Methode .getprefix() abgefragt werden.
4. Lesen und Schreiben von Pins und Parametern
Bei Pins und Parametern, die auch echte Python-Bezeichner sind, kann der Wert unter Verwendung der Attributsyntax aufgerufen oder gesetzt werden:
h.out = h.in
Für alle Pins, unabhängig davon, ob sie auch echte Python-Bezeichner sind oder nicht, kann der Wert unter Verwendung der tiefgestellten Syntax aufgerufen oder gesetzt werden:
h['out'] = h['in']
Um alle Pins mit ihren Werten zu sehen, gibt getpins alle Werte in einem Wörterbuch dieser Komponente zurück.
h.getpins()
>>>{'in': 0.0, 'out': 0.0}
4.1. Ansteuerung der Ausgangsstifte (HAL_OUT)
In regelmäßigen Abständen, in der Regel als Reaktion auf einen Timer, sollten alle HAL_OUT-Pins "getrieben" werden, indem ihnen ein neuer Wert zugewiesen wird. Dies sollte unabhängig davon geschehen, ob sich der Wert von dem zuletzt zugewiesenen unterscheidet oder nicht. Beim Verbinden eines Pins mit einem Signal wird sein alter Ausgangswert nicht in das Signal kopiert, so dass der richtige Wert erst auf dem Signal erscheint, wenn die Komponente einen neuen Wert zuweist.
4.2. Ansteuerung von bidirektionalen (HAL_IO) Pins
Die obige Regel gilt nicht für bidirektionale Pins. Stattdessen sollte ein bidirektionaler Pin nur dann von der Komponente angesteuert werden, wenn die Komponente den Wert ändern möchte. In der kanonischen Encoder-Schnittstelle beispielsweise setzt die Encoder-Komponente den Pin index-enable nur auf FALSE (wenn ein Indeximpuls gesehen wird und der alte Wert TRUE ist), aber niemals auf TRUE. Das wiederholte Setzen des Pins auf FALSE könnte dazu führen, dass die andere angeschlossene Komponente sich so verhält, als ob ein weiterer Indeximpuls gesehen worden wäre.
5. Beenden
Eine halcmd unload Anforderung für die Komponente wird als KeyboardInterrupt Ausnahme geliefert. Wenn eine Entladeanforderung eintrifft, sollte der Prozess entweder in kurzer Zeit beendet werden oder die Methode .exit() für die Komponente aufrufen, wenn umfangreiche Arbeiten (wie das Lesen oder Schreiben von Dateien) durchgeführt werden müssen, um den Abschaltvorgang abzuschließen.
6. Hilfreiche Funktionen
6.1. ready (engl. für bereit)
Sagt dem HAL-System, dass die Komponente initialisiert ist. Sperrt das Hinzufügen von Pins.
6.2. unready (engl. für nicht bereit)
Ermöglicht es einer Komponente, Pins hinzuzufügen, nachdem ready() aufgerufen wurde. Man sollte ready() auf der Komponente danach aufrufen.
6.3. component_exists
Existiert die angegebene Komponente zu diesem Zeitpunkt.
hal.component_exists("testpanel")
6.4. component_is_ready (engl. für Komponente ist bereit)
Ist die angegebene Komponente zu diesem Zeitpunkt bereit.
hal.component_is_ready("testpanel")
6.5. get_msg_level
Abfrage der aktuellen Echtzeit-Nachrichten (engl. kurz msg)-Stufe.
6.6. set_msg_level
Legen Sie die aktuelle Echtzeit-MSG-Stufe fest. Wird zum Debuggen von Informationen verwendet.
6.7. verbinden
Verbinden Sie einen Pin mit einem Signal.
hal.connect("pinname","signal_name")
6.8. trennen
Trennen Sie einen Pin von einem Signal.
hal.disconnect("Pinname")
6.9. get_value
Lesen Sie einen Pin, einen Parameter oder ein Signal direkt.
value = hal.get_value("iocontrol.0.emc-enable-in")
6.10. get_info_pins()
Gibt eine Liste von Dicts aller Systempins zurück.
listOfDicts = hal.get_info_pins() pinName1 = listOfDicts[0].get('NAME') pinValue1 = listOfDicts[0].get('VALUE') pinType1 = listOfDicts[0].get('TYPE') pinDirection1 = listOfDicts[0].get('DIRECTION')
6.11. get_info_signals()
Gibt eine Liste von Dicts aller Systemsignale zurück.
listOfDicts = hal.get_info_signals() signalName1 = listOfDicts[0].get('NAME') signalValue1 = listOfDicts[0].get('VALUE') driverPin1 = listOfDicts[0].get('DRIVER')
6.12. get_info_params()
Gibt eine Liste von Dicts mit allen Systemparametern zurück.
listOfDicts = hal.get_info_params() paramName1 = listOfDicts[0].get('NAME') paramValue1 = listOfDicts[0].get('VALUE') paramDirection1 = listOfDicts[0].get('DIRECTION')
6.13. new_sig
Erstellt ein neues Signal des angegebenen Typs.
hal.new_sig("signalname",hal.HAL_BIT)
6.14. pin_has_writer
Ist der angegebene Pin mit einem treibenden Pin verbunden.
Gibt True oder False zurück.
h.in.pin_has_writer()
6.15. get_name
Ermittelt den Namen des HAL-Objekts.
Rückgabe einer Zeichenkette.
h.in.get_name()
6.16. get_type
Ermittelt den Typ des HAL-Objekts'.
Gibt eine ganze Zahl zurück.
h.in.get_type()
6.17. get_dir
Ermittelt den Richtungstyp des HAL-Objekts.
Gibt eine ganze Zahl zurück.
h.in.get_dir()
6.18. get
Ermittelt den Wert des HAL-Objekts.
h.in.get()
6.19. set
Setzt den Wert des HAL-Objekts.
h.out.set(10)
6.20. is_pin
Ist das Objekt ein Pin oder Parameter?
Gibt True oder False zurück.
h.in.is_pin()
6.21. sampler_base
TODO
6.22. stream_base
TODO
6.23. stream
TODO
6.24. set_p
Einstellen eines Pin-Wertes eines beliebigen Pins im HAL-System.
hal.set_p("pinname","10")
7. Konstanten
Verwenden Sie diese, um Details zu spezifizieren, und nicht den Wert, den sie enthalten.
-
HAL_BIT
-
HAL_FLOAT
-
HAL_S32
-
HAL_U32
-
HAL_IN
-
HAL_OUT
-
HAL_RO
-
HAL_RW
-
MSG_NONE
-
MSG_ALL
-
MSG_DBG
-
MSG_ERR
-
MSG_INFO
-
MSG_WARN
8. System-Informationen
Lesen Sie diese, um Informationen über das Echtzeitsystem zu erhalten.
-
is_kernelspace
-
is_rt
-
is_sim
-
is_userspace
9. Verwendung mit hal_glib in GladeVCP Handler
GladeVCP nutzt die hal_glib-Bibliothek, die dazu verwendet werden kann, ein "watcher" Signal an einen HAL-Eingangspin anzuschließen.
Dieses Signal kann verwendet werden, um eine Funktion zu registrieren, die aufgerufen wird, wenn sich der Zustand des HAL-Pins ändert.
One must import the hal_glib
and the hal
modules:
import hal_glib import hal
Erstellen Sie dann einen Pin und verbinden Sie ein value-changed Signal (den Watcher) mit einem Funktionsaufruf:
class HandlerClass: def __init__(self, halcomp,builder,useropts): self.example_trigger = hal_glib.GPin(halcomp.newpin('example-trigger', hal.HAL_BIT, hal.HAL_IN)) self.example_trigger.connect('value-changed', self._on_example_trigger_change)
Und eine Funktion haben, die aufgerufen werden soll:
def _on_example_trigger_change(self,pin,userdata=None): print("pin value changed to: {}".format(pin.get())) print("pin name= {}".format(pin.get_name())) print("pin type= {}".format(pin.get_type())) # dies kann außerhalb der Funktion aufgerufen werden self.example_trigger.get()
10. Verwendung mit hal_glib in QtVCP Handler
QtVCP nutzt die hal_glib Bibliothek, die verwendet werden kann, um ein "watcher" Signal an einen HAL-Eingangspin anzuschließen. Dieses Signal kann verwendet werden, um eine Funktion zu registrieren, die aufgerufen wird, wenn sich der Zustand des HAL-Pins ändert.
One must import the hal
module:
import hal
Erstellen Sie dann einen Pin und verbinden Sie ein value_changed (den Watcher) Signal mit einem Funktionsaufruf:
######################## **** INITIALIZE **** # ######################## # widgets erlaubt den Zugriff auf Widgets aus den qtvcp-Dateien. # zu diesem Zeitpunkt sind die Widgets und HAL-Pins noch nicht instanziiert def __init__(self, halcomp,widgets,paths): self.hal = halcomp self.testPin = self.hal.newpin('test-pin', hal.HAL_BIT, hal.HAL_IN) self.testPin.value_changed.connect(lambda s: self.setTestPin(s))
Und eine aufzurufende Funktion haben. Dies zeigt, wie man den Pin-Wert und die Informationen erhält.
##################### # general functions # ##################### def setTestPin(self, data): print("Test pin value changed to:" % (data)) print('halpin object =', self.w.sender()) print('halpin name: ',self.sender().text()) print('halpin type: ',self.sender().get_type()) # dies kann außerhalb der Funktion aufgerufen werden print(self.testPin.get())
11. Projektideen
-
Erstellen Sie ein externes Bedienfeld mit Tasten, Schaltern und Anzeigen. Schließen Sie alles an einen Mikrocontroller an, und verbinden Sie den Mikrocontroller über eine serielle Schnittstelle mit dem PC. Python hat ein sehr leistungsfähiges serielles Schnittstellenmodul namens pyserial (Ubuntu-Paketname "python3-serial", im Universum-Repository).
-
Schließen Sie ein LCDProc-kompatibles LCD-Modul an und verwenden Sie es, um eine digitale Anzeige mit Informationen Ihrer Wahl anzuzeigen (Ubuntu-Paketname "lcdproc", im Universum-Repository).
-
Erstellen eines virtuellen Bedienfelds mit einer beliebigen von Python unterstützten GUI-Bibliothek (Gtk, Qt, wxWindows usw.).