1. Einführung

Die meisten Bildschirme von LinuxCNC haben die Möglichkeit, geladene Dateien durch ein "Filterprogramm" zu senden oder das Filterprogramm zu verwenden, um G-Code zu machen. Ein solcher Filter kann jede gewünschte Aufgabe erledigen: Etwas so Einfaches wie sicherzustellen, dass die Datei mit M2 endet, oder etwas so Kompliziertes wie die Erzeugung von G-Code aus einem Bild.

2. Einrichten der INI für Programmfilter

Der Abschnitt [FILTER] der INI-Datei steuert, wie die Filter funktionieren. Schreiben Sie zunächst für jeden Dateityp eine PROGRAM_EXTENSION-Zeile. Dann geben Sie das Programm an, das für jeden Dateityp ausgeführt werden soll. Dieses Programm erhält den Namen der Eingabedatei als erstes Argument und muss rs274ngc-Code in die Standardausgabe schreiben. Diese Ausgabe ist das, was im Textbereich angezeigt wird, in der Vorschau im Anzeigebereich, und dann auch von LinuxCNC ausgeführt wird. Die folgenden Zeilen fügen Unterstützung für den in LinuxCNC enthaltenen "image-to-gcode" (engl. für Bild zu G-Code) -Konverter hinzu:

[FILTER]
PROGRAM_EXTENSION = .png,.gif Greyscale Depth Image
png = image-to-gcode
gif = image-to-gcode

Es ist auch möglich, einen Interpreter anzugeben:

PROGRAM_EXTENSION = .py Python Script
py = python

Auf diese Weise kann jedes Python-Skript geöffnet werden, und seine Ausgabe wird als G-Code behandelt. Ein solches Beispielskript ist unter "nc_files/holecircle.py" verfügbar. Dieses Skript erzeugt G-Code für das Bohren einer Reihe von Löchern entlang des Umfangs eines Kreises.

Kreisförmige Löcher
Abbildung 1. Kreisförmige Löcher

Wenn das Filterprogramm Zeilen in der folgenden Form an stderr sendet:

FILTER_PROGRESS=10

Sie setzt den Fortschrittsbalken des Bildschirms auf den angegebenen Prozentsatz (in diesem Fall 10). Diese Funktion sollte von jedem Filter verwendet werden, der lange läuft.

3. Erstellung von Filterprogrammen auf Python-Basis

Hier ist ein sehr einfaches Beispiel für die Filtermechanik: Wenn ein Linucnc-Bildschirm, der Programmfilterung bietet, durchläuft, wird jede 100stel Sekunde eine Zeile G-Code erzeugt und auf die Standardausgabe geschrieben. Außerdem sendet es eine Fortschrittsmeldung an den Unix-Standardfehlerstrom. Wenn ein Fehler auftritt, gibt es eine Fehlermeldung aus und beendet sich mit dem Exitcode 1.

import time
import sys

for i in range(0,100):
    try:
        # Rechenzeit simulieren
        time.sleep(.1)

        # Ausgabe einer Zeile G-Code
        print('G0 X1', file=sys.stdout)

        # Fortschritt aktualisieren
        print('FILTER_PROGRESS={}'.format(i), file=sys.stderr)
    except:
        # Dies führt zu einer Fehlermeldung
        print('Fehler; Aber das war nur ein Test', file=sys.stderr)
        raise SystemExit(1)

Hier ist ein ähnliches Programm, aber es kann tatsächlich filtern. Es zeigt einen PyQt5-Dialog mit einer Abbruch-Schaltfläche an. Dann liest es das Programm Zeile für Zeile und gibt es an die Standardausgabe weiter. Während es weiterläuft, aktualisiert es jeden Prozess, der auf die Standardfehlerausgabe hört.

#!/usr/bin/env python3

import sys
import os
import time

from PyQt5.QtWidgets import (QApplication, QDialog, QDialogButtonBox,
                            QVBoxLayout,QDialogButtonBox)
from PyQt5.QtCore import QTimer, Qt

class CustomDialog(QDialog):

    def __init__(self, path):
        super(CustomDialog, self).__init__(None)
        self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        self.setWindowTitle("Filter-with-GUI Test")

        QBtn = QDialogButtonBox.Cancel

        self.buttonBox = QDialogButtonBox(QBtn)
        self.buttonBox.rejected.connect(self.reject)

        self.layout = QVBoxLayout()
        self.layout.addWidget(self.buttonBox)
        self.setLayout(self.layout)

        self.line = 0
        self._percentDone = 0

        if not os.path.exists(path):
            print("Path: '{}' existiert nicht:".format(path), file=sys.stderr)
            raise SystemExit(1)

        self.infile = open(path, "r")
        self.temp = self.infile.readlines()

        # calculate percent update interval
        self.bump = 100/float(len(self.temp))

        self._timer = QTimer()
        self._timer.timeout.connect(self.process)
        self._timer.start(100)

    def reject(self):
        # This provides an error message
        print('You asked to cancel before finished.', file=sys.stderr)
        raise SystemExit(1)

    def process(self):
        try:
            # nächste Codezeile erhalten
            codeLine = self.temp[self.line]

            # die Zeile irgendwie verarbeiten

            # Verarbeiteten Code ausgeben
            print(codeLine, file=sys.stdout)
            self.line +=1

            # update progress
            self._percentDone += self.bump
            print('FILTER_PROGRESS={}'.format(int(self._percentDone)), file=sys.stderr)

            # if done Ende ohne Fehler/Fehlermeldung
            if self._percentDone >= 99:
                print('FILTER_PROGRESS=-1', file=sys.stderr)
                self.infile.close()
                raise SystemExit(0)

        except Exception as e:
            # Dies liefert eine Fehlermeldung
            print(('Something bad happened:',e), file=sys.stderr)
            # dies signalisiert, dass die Fehlermeldung angezeigt werden soll
            raise SystemExit(1)

if __name__ == "__main__":
    if (len(sys.argv)>1):
        path = sys.argv[1]
    else:
        path = None
    app = QApplication(sys.argv)
    w = CustomDialog(path=path)
    w.show()
    sys.exit( app.exec_() )