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:

Screen copy with details on the execution of the newly created passthrough HAL module.
$ 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.

Tabelle 1. Namen der HAL-Optionen

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.

Beispiel
hal.component_exists("testpanel")

6.4. component_is_ready (engl. für Komponente ist bereit)

Ist die angegebene Komponente zu diesem Zeitpunkt bereit.

Beispiel
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.

Beispiel
hal.connect("pinname","signal_name")

6.8. trennen

Trennen Sie einen Pin von einem Signal.

Beispiel
hal.disconnect("Pinname")

6.9. get_value

Lesen Sie einen Pin, einen Parameter oder ein Signal direkt.

Beispiel
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.

Beispiel
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.

Beispiel
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.).