1. Einführung

Panelui ist eine Nicht-Echtzeit-Komponente zur Anbindung von Schaltflächen an LinuxCNC oder HAL:

  • Es dekodiert MESA 7I73-artige Key-Scan-Codes und ruft die entsprechende Routine auf.

  • Die Eingabe erfolgt über eine Echtzeitkomponente - Sampler. Der Sampler erhält seine Eingaben entweder von der Komponente MESA 7I73 oder sim_matrix_kb.

  • Panelui kann mithilfe einer INI-Textdatei konfiguriert werden, um Schaltflächentypen, HAL-Pin-Typen und/oder Befehle zu definieren.

  • Sie kann mit einer Python-basierten "Handler"-Datei um Funktionen erweitert werden.

Während die eigentlichen Eingabetasten träge sein müssen, verwendet Panelui diese Eingabe für die Ausgabe von Toggle-, Radio- oder Tasterschaltungen.

2. Laden von Befehlen

Der Befehl zum Laden von panelui (mit optionalem Schalter -d debug):

loadusr -W panelui -d

Dadurch wird panelui initialisiert, das im Konfigurations- oder Benutzerordner nach der INI-Datei panelui.ini sucht.

Mit diesem Befehl kann man die INI-Datei validieren:

loadusr pyui

Damit wird die Datei panelui.ini gelesen, versucht zu korrigieren und dann gespeichert. Eventuelle Fehler werden diese auf dem Terminal ausgegeben.

Einer typischen HAL-Datei werden diese Befehle hinzugefügt:

# Befehle, die für das Laden von Panelui benötigt werden
#
# sampler wird für panelui benötigt
# cfg= muss für panelui immer u sein. depth legt den verfügbaren Puffer fest
loadrt sampler cfg=u depth=1025

#unkommentiert, um die panelui INI-Datei zu validieren
#loadusr pyui

# -d = Fehlersuche, -v = ausführliche Fehlersuche
# -d zeigt Ihnen die Tastenkennzeichnung und die aufgerufenen Befehle
# -v ist für Informationen über den Entwickler
loadusr -W panelui -d

# mit simulierten Tasten anstelle der MESA 7I73-Karte
# also laden wir die Komponente sim_matrix_kb, um die HAL-Pins in Keyscan-Codes umzuwandeln
loadrt sim_matrix_kb

# Verbinden Sie die Komponenten miteinander.
# sampler spricht intern mit panelui
net key-scan sim-matrix-kb.0.out
net key-scan sampler.0.pin.0

# panelui Komponenten zu einem Thread hinzufügen

addf sim-matrix-kb.0    servo-thread
addf sampler.0          servo-thread

3. panelui.ini Dateireferenz

Schlüsselwörter (engl. keywords)
  • KEY= Hier wird die Taste angegeben, auf die der Button reagiert. Es kann KEINE oder eine Zeilennummer und eine Spaltennummer sein, z. B. R1C2. Eine Zeile und Spalte kann nur einmal verwendet werden.

  • OUTPUT= Hier wird der Ausgabetyp der Schaltfläche festgelegt, z. B. S32, U32, FLOAT, BIT, NONE, COMMAND, ZMQ.

  • DEFAULT= Hiermit wird die Startausgabe der Gruppe oder der Schaltfläche festgelegt.

  • GROUP= Bezeichnet bei Radiobuttons die Gruppe, mit innerhalb welcher der Button interagiert.

  • GROUP_OUTPUT= legt den Ausgang fest, den der Gruppenpin hat, wenn dieser Button aktiv ist.

  • STATUS_PIN= Wenn TRUE, wird ein HAL-Pin hinzugefügt, der den aktuellen Zustand des Button wiedergibt.

  • TRUE_STATE= legt den Ausgang fest, den der HAL-Pin hat, wenn der Button TRUE ist.

  • FALSE_STATE= legt den OUTPUT fest, den der HAL-Pin erhält, wenn die Taste FALSE ist.

  • TRUE_COMMAND= legt den Befehl und die Argumente fest, die aufgerufen werden, wenn der Button TRUE ist.

  • FALSE_COMMAND= legt den Befehl und die Argumente fest, die aufgerufen werden, wenn der Button FALSE ist.

  • TRUE_FUNCTION= legt die ZMQ-Nachrichtenfunktion und Argumente fest, die aufgerufen werden sollen, wenn der Button TRUE ist.

  • FALSE_FUNCTION= legt die ZMQ-Nachrichtenfunktion und die Argumente fest, die aufgerufen werden sollen, wenn der Button FALSE ist.

HAL Prefix
[HAL_PREFIX]
    NAME= IhrName

Damit kann man den Präfix der HAL-Pins von panelui auf einen beliebigen Namen ändern.

ZMQ Messaging-Einrichtung
[ZMQ_SETUP]
  TOPIC = 'QTVCP'
  SOCKET = 'tcp://127.0.0.1:5690'
  ENABLE = True

Damit wird das ZMQ-basierte Messaging eingerichtet und aktiviert. TOPIC und SOCKET müssen mit dem empfangenden Programm übereinstimmen.

Radio Buttons

Bei Radiobutons kann jeweils nur eine Taste in der Gruppe aktiv sein. Jede Gruppe hat ihren eigenen Ausgangspin, der von jeder Taste in der Gruppe getrennt ist. Radiobutton-Definitionen beginnen mit dem Text "RADIO_BUTTON" in einfachen Klammern.

[RADIO_BUTTONS]
  # Die doppelte(n) Klammer(n) definieren die Gruppe(n) von Optionsschaltflächen.
  # Der Gruppenname muss eindeutig sein und Groß- und Kleinschreibung wird beachtet.
  # Die Ausgabe der Gruppe wird durch die aktive Schaltfläche gesteuert, nicht direkt durch den Keycode.
  # DEFAULT verweist auf eine Schaltfläche in der Gruppe durch den Namen und unterscheidet zwischen Groß- und Kleinschreibung.
  [[group1_name]]
    KEY = NONE
    OUTPUT = FLOAT
    DEFAULT = small
    # Die dreifachen Klammerabschnitte definieren die Schaltflächen in dieser Gruppe.
    # Die Namen der Schaltflächen müssen eindeutig sein und Groß- und Kleinschreibung wird beachtet.
    # Es muss mindestens zwei Schaltflächen in einer Gruppe geben.
    #
    # Diese Schaltfläche mit dem Namen 'small' wird durch die Taste Zeile 0 Spalte 1 gesteuert.
    # Sie bewirkt, dass der Gruppenausgang .0001 ist, wenn sie gedrückt wird.
    # Sie hat keinen eigenen Ausgang, aber einen Status
    # Pin, der den aktuellen Zustand der Taste anzeigt.
    # Da diese Taste in einer Gruppe ist, hat DEFAULT keine Bedeutung.
    # da OUTPUT nicht 'COMMAND' ist, werden _COMMAND-Einträge ignoriert.
    [[[small]]]
      KEY = R0C1
      GROUP = group1_name
      GROUP_OUTPUT = .0001
      OUTPUT = NONE
      STATUS_PIN = True
      TRUE_STATE = TRUE
      FALSE_STATE = FALSE
      TRUE_COMMAND = NONE, NONE
      FALSE_COMMAND = NONE, NONE
      DEFAULT = false
    # Diese Schaltfläche mit dem Namen 'large' wird von der Taste Zeile 0 Spalte 2 gesteuert.
     # Sie bewirkt, dass der Gruppenausgang 1000 ist, wenn sie gedrückt wird.
     # Er hat einen eigenen S32-Ausgang, der bei true 20 und bei false 0 ist.
     # Sie hat auch einen Status-Pin, der ihren aktuellen Zustand anzeigt.
     # Da diese Taste in einer Gruppe ist, hat DEFAULT keine Bedeutung.
     # Da OUTPUT nicht 'COMMAND' ist, werden _COMMAND-Einträge ignoriert..
    [[[large]]]
      KEY = R0C2
      GROUP = group1_name
      GROUP_OUTPUT = 1000
      OUTPUT = S32
      STATUS_PIN = True
      TRUE_STATE = 20
      TRUE_COMMAND = NONE, NONE
      FALSE_COMMAND = NONE, NONE
      FALSE_STATE = 0
      DEFAULT = false
Wechsel-Buttons (engl. toggle buttons)

Togglebuttons ändern ihren Zustand nur bei jedem Drücken der Taste. Toggle-Button-Definitionen beginnen mit dem Text "TOGGLE_BUTTON" in einfachen Klammern.

[TOGGLE_BUTTONS]
  # Jeder Button-Name in doppelten Klammern muss eindeutig sein und Groß- und Kleinschreibung wird unterschieden.
  # Diese Schaltfläche mit dem Namen 'tool_change' wird von der Taste in Zeile 2 Spalte 5 gesteuert.
  # Sie hat einen BIT-Ausgang, der bei einem wahren Zustand 1 und bei einem falschen Zustand 0 ausgibt.
  # Er hat auch einen Status-Pin, der seinen aktuellen Zustand anzeigt.
  # DEFAULT setzt diesen bei der ersten Initialisierung auf true.
  # Die _COMMAND werden nicht verwendet, da OUTPUT nicht auf COMMAND gesetzt ist, aber die Validierung wird
  # die Zeilen dennoch hinzufügen.
  [[tool_change]]
    KEY = R2C5
    OUTPUT = BIT
    TRUE_COMMAND = NONE, NONE
    FALSE_COMMAND = NONE, NONE
    STATUS_PIN = True
    DEFAULT = TRUE
    TRUE_STATE = 1
    FALSE_STATE = 0
Momentary Buttons

Momentane Buttons sind wahr, wenn sie gedrückt werden, und falsch, wenn sie losgelassen werden. Button-Definitionen beginnen mit dem Text "MOMENTARY_BUTTON" in einfachen Klammern.

[MOMENTARY_BUTTONS]
  # Jeder Tastenname in doppelten Klammern muss eindeutig sein und Groß- und Kleinschreibung wird unterschieden.
  # Diese Taste mit dem Namen 'spindle_rev' wird von der Taste in Zeile 2 Spalte 3 gesteuert.
  # Sie hat einen COMMAND-Ausgang, verwendet also TRUE_COMMAND und FALSE_COMMAND.
  # Er hat auch einen Status-Pin, der seinen aktuellen Zustand anzeigt.
  # COMMANDs haben einen Befehlsnamen und dann alle erforderlichen Argumente.
  # Dieser TRUE_COMMAND ruft einen internen Befehl zum Starten der Spindel im Rückwärtsgang mit 200 U/min auf.
  # Wenn die Spindel bereits gestartet ist, wird die Drehzahl erhöht.
  # DEFAULT wird nicht mit Momentary-Buttons verwendet.
  # Die _STATE werden nicht verwendet, da OUTPUT auf COMMAND gesetzt ist, aber die Validierung wird
  # die Zeilen dennoch hinzufügen.
  [[spindel_drehzahl]]
    KEY = R2C3
    OUTPUT = COMMAND
    TRUE_COMMAND = SPINDLE_REVERSE_INCREASE, 200
    FALSE_COMMAND = None, NONE
    STATUS_PIN = True
    DEFAULT = FALSE
    TRUE_STATE = 1
    FALSE_STATE = 0

4. Übersicht zu Internen Anweisungen

Es gibt eine Reihe von internen Befehlen, die Sie verwenden können.

home_selected
  • erforderliches Argument: Achsennummer (int)

unhome_selected
  • erforderliches Argument: Achsennummer (int)

spindle_forward_adjust
  • optionales Argument: Anfangsdrehzahl (int) - Standardwert 100

  • Beschreibung: Wenn die Spindel angehalten ist, startet sie in Vorwärtsrichtung. Wenn sie bereits läuft, erhöht oder verringert sie die Drehzahl, je nachdem, in welche Richtung die Spindel läuft.

spindle_forward
  • optionales Argument: Anfangsdrehzahl (int) - Standardwert 100

spindle_reverse
  • optionales Argument: Anfangsdrehzahl (int) - Standardwert 100

spindle_reverse_adjust
  • optionales Argument: Anfangsdrehzahl (int) - Standardwert 100

  • Beschreibung: Wenn die Spindel angehalten wird, startet sie in umgekehrter Richtung. Wenn sie bereits läuft, erhöht oder verringert sie die Drehzahl, je nachdem, in welche Richtung die Spindel läuft.

spindle_faster
  • Beschreibung: erhöht die Spindeldrehzahl um 100 RPM

spindle_slower
  • Beschreibung: Verringert die Spindeldrehzahl um 100 RPM, bis die Drehzahl 100 beträgt.

set_linear_jog_velocity
  • erforderliches Argument: Geschwindigkeit in Zoll pro Minute (Float)

  • Beschreibung: setzt die Jog-Geschwindigkeit auf den Achsen 0,1,2,6,7,8 (X,Y,Z,U,V,W)

set_angular_jog_velocity
  • erforderliches Argument: Geschwindigkeit in Grad pro Minute (Float)

  • Beschreibung: Setzt die Jog-Geschwindigkeit auf Achse 3,4,5 (A.B.C)

continuous_jog
  • erforderliche Argumente: Achsennummer (int), Richtung (int)

incremental_jog
  • erforderliche Argumente: Achsennummer (int), Richtung (int), Abstand (float)

quill_up
  • optionale Argumente: absolute Position der Z-Achse der Maschine (Float)

  • Beschreibung: Z-Achse auf die angegebene Maschinenposition fahren

feed_hold
  • Erforderliches Argument: Zustand (bool 0 oder 1)

feed_override
  • erforderliches Argument: Rate (float)

rapid_override
  • erforderliches Argument: Rate (float 0-1)

spindle_override
  • erforderliches Argument: Rate (float)

max_velocity
  • erforderliches Argument: Rate (float)

optional_stop
  • Erforderliches Argument: Zustand (bool 0 oder 1)

block_delete (engl. für Block löschen)
  • Erforderliches Argument: Zustand (bool 0 oder 1)

single_block
  • Erforderliches Argument: Zustand (bool 0 oder 1)

smart_cycle_start
  • Beschreibung: Wenn im Leerlauf, startet G-Code-Programm, wenn angehalten wird eine Zeile ausgeführt.

re_start line
  • erforderliches Argument: Zeilennummer (int)

mdi_and_return
  • erforderliches Argument: G-Code-Befehl(e)

  • Beschreibung: Zeichnet den aktuellen Modus auf, ruft Befehle auf und kehrt dann zum Modus zurück.

mdi
  • erforderliches Argument: G-Code-Befehl(e)

  • Beschreibung: Setzt den Modus auf MDI, ruft Befehle auf.

5. ZMQ-Nachrichten

Panelui kann ZMQ-basierte Nachrichten beim Drücken von Tasten senden.
Auf diese Weise kann panelui mit anderen Programmen wie QtVCP-Bildschirmen interagieren.

[TOGGLE_BUTTONS]
  [[zmq_test]]
    KEY = R2C3
    OUTPUT = ZMQ
    TRUE_FUNCTION = ZMQ_BUTTON, 200
    FALSE_FUNCTION = ZMQ_BUTTON, 0
    STATUS_PIN = False
    DEFAULT = FALSE
    TRUE_STATE = 1
    FALSE_STATE = 0

Hier ist ein Beispielprogramm, das die Nachricht empfängt und auf dem Terminal ausgibt.

import zmq
import json

# ZeroMQ Context
context = zmq.Context()

# Definition des Sockets mit Hilfe des "Context".
sock = context.socket(zmq.SUB)

# Definieren des Abonnements und der zu akzeptierenden Nachrichten ohne Einschränkung der Themen.
topic = "" # alle Themen
sock.setsockopt(zmq.SUBSCRIBE, topic)
sock.connect("tcp://127.0.0.1:5690")

while True:
    topic, message = sock.recv_multipart()
    print('{} sent message:{}'.format(topic,json.loads(message)))

6. Handler Dateierweiterung

Eine spezielle Datei kann verwendet werden, um benutzerdefinierten Python-Code hinzuzufügen, der als Befehle verfügbar ist. panelui_handler.py muss in Python geschrieben und im Konfigurationsordner abgelegt werden. Wenn panelui dort eine Datei findet, fügt es deren Funktionsaufrufe zu den verfügbaren Befehlen hinzu. Hier ist ein Beispiel für eine Handler-Datei, die zwei Funktionen hinzufügt - hello_world und cycle_mode:

# standard handler call - This will always be required
def get_handlers(linuxcnc_stat, linucnc_cmd, commands, master):
     return [HandlerClass(linuxcnc_stat, linucnc_cmd, commands, master)]

# Ebenfalls erforderlich - Handler-Klassenklasse HandlerClass:

    # Dies wird ein ziemlicher Standard sein, um Zugriff auf alles zu erhalten.
    # linuxcnc_stat: ist die Python-Status-Instanz von LinuxCNC
    # linuxcnc_cmd: ist die Python-Befehlsinstanz von LinuxCNC
    # commands: ist die Befehlsinstanz, damit man die internen Routinen aufrufen kann
    # master: ermöglicht den Zugriff auf die Master-Funktionen/Daten

    def __init__(self, linuxcnc_stat, linuxcnc_cmd, commands, master):
        self.parent = commands
        self.current_mode = 0

    # command functions are expected to have this layout:
    # def some_name(self, widget_instance, arguments from widget):
    # widget_instance gives access to the calling widget's function/data
    # arguments can be a list of arguments, a single argument, or None
    # depending on what was given in panelui's INI file.
    def hello_world(self, wname, m):
        # print to terminal so we know it worked
        print('\nHello world\n')
        print(m)     # print the argument(s)
        print(wname.metadata)    # Print the calling widgets internal metadata (from config file)

        # Aufruf eines MDI-Befehls zum Drucken einer Nachricht in LinuxCNC.
        # Dies setzt voraus, dass LinuxCNC referenziert ist, aber das wird nicht überprüft.
        # übergeordnete Befehle erwarten eine widget_instance - None wird ersetzt
        self.parent.mdi(None,'(MSG, Hallo Linuxcnc Welt!)')

    # Jeder Aufruf dieser Funktion schaltet den Modus von LinuxCNC um.
    def cycle_mode(self, wname, m):
        if self.current_mode == 0:
            self.current_mode = 1
            self.parent.set_mdi_mode()
        elif self.current_mode == 1:
            self.current_mode = 2
            self.parent.set_auto_mode()
        else:
            self.current_mode = 0
            self.parent.set_manual_mode()
        print(self.current_mode)

    # Boiler code, braucht man oft
    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)