1. Введение

Большинство экранов LinuxCNC имеют возможность отправлять загруженные файлы через filter program или использовать программу-фильтр для создания G-кода. Такой фильтр может выполнять любую желаемую задачу: что-то простое, например проверка того, что файл заканчивается на «M2», или что-то более сложное, например создание G-кода из изображения.

2. Настройка INI для программных фильтров

Раздел [FILTER] INI-файла управляет работой фильтров. Сначала для каждого типа файла напишите строку PROGRAM_EXTENSION. Затем укажите программу, которая будет выполняться для каждого типа файла. Этой программе присваивается имя входного файла в качестве первого аргумента, и она должна записать код rs274ngc в стандартный вывод. Этот вывод будет отображаться в текстовой области, просматриваться в области отображения и выполняться LinuxCNC при нажатии кнопки Run. Следующие строки добавляют поддержку конвертера image-to-gcode, включенного в LinuxCNC:

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

Также возможно указать интерпретатор:

PROGRAM_EXTENSION = .py Python Script
py = python

Таким образом, любой скрипт Python может быть открыт, а его выходные данные обрабатываются как G-код. Один из таких примеров скрипта доступен по адресу nc_files/holecircle.py. Этот скрипт создает G-код для сверления серии отверстий по окружности.

Отверстия по кругу
Figure 1. Отверстия по кругу

Если программа фильтра отправляет в stderr строки вида:

FILTER_PROGRESS=10

Он установит индикатор выполнения экрана на заданный (в данном случае 10) процент. Эту функцию следует использовать любому фильтру, работающему в течение длительного времени.

3. Создание программ фильтров на основе Python

Here is a very basic example of the filtering mechanics: When run through a Linucnc screen that offers program filtering, it will produce and write a line of G-code every 100th of a second to standard output. It also sends a progress message out to the UNIX standard error stream. If there was an error it would post an error message and exit with an exitcode of 1.

import time
import sys

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

        # output a line of G-code
        print('G0 X1', file=sys.stdout)

        # update progress
        print('FILTER_PROGRESS={}'.format(i), file=sys.stderr)
    except:
        # This causes an error message
        print('Error; But this was only a test', file=sys.stderr)
        raise SystemExit(1)

Вот похожая программа, но она действительно умеет фильтровать. Она открывает диалоговое окно PyQt5 с кнопкой отмены. Затем она считывает программу построчно и передает ее на стандартный вывод. По ходу дела она обновляет любой процесс, прослушивающий стандартный вывод ошибок.

#!/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: '{}' doesn't exist:".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:
            # get next line of code
            codeLine = self.temp[self.line]

            # process the line somehow

            # push out processed code
            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 end with no error/error message
            if self._percentDone >= 99:
                print('FILTER_PROGRESS=-1', file=sys.stderr)
                self.infile.close()
                raise SystemExit(0)

        except Exception as e:
            # This provides an error message
            print(('Something bad happened:',e), file=sys.stderr)
            # this signals the error message should be shown
            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_() )