Lua Modul - Postprozessor

Dieses Kapitel befasst sich mit der Anpassung und Erstellung neuer Postprozessoren für send2cnc.

Postprozessor-Skript

Ein Postprozessor ist im Wesentlichen eine Lua-Skriptdatei, die spezielle Befehle und Funktionen beinhaltet, um die von send2cnc berechneten Werkzeugwege in ein geeignetes Format für die CNC-Maschine zu konvertieren.

Standardmäßig sind die Skripte im Unterordner "postprocessor" des Program-Data-Ordners (siehe Kapitel "Installation") gespeichert. In diesem Ordner können neue Skripte hinzugefügt und verwaltet werden.

Vor dem Erstellen oder Ändern eines Postprozessors ist darauf zu achten, dass dieser unter einem eindeutigen Namen im Unterordner "postprocessor/user" des oben genannten Verzeichnisses gespeichert wird. Bei einem Update oder der Installation eines neuen Postprozessor-Pakets könnten sonst angepasste Postprozessoren überschrieben werden.

Skriptaufbau

Die in diesem Abschnitt beschriebenen Funktionen werden vom System bei jedem Postprozessorlauf aufgerufen und sollten in jedem Postprozessor definiert sein.

Übersicht

Funktion function ppStart( [clsobj] prj, [clsppdata] job, [clsppdata] jobnext)
Argumente [clsobj] prj Das Projekt welches die zu verarbeitenden Jobs beinhaltet.
[clsppdata] job Das erste zu verarbeitende Job Item.
[clsppdata] jobnext Das nächste zu verarbeitende Job Item.
Rückgabewert nil
Beschreibung Beim Start eines Postprozessorlaufs mit s2.pp.run(objlist) wird diese Funktion vom System mit den vom System bereitgestellten Argumenten aufgerufen.
Beispiel
function ppStart( prj, job, jobnext)
    s2.printnl("start pp run in project ", prj:get("nameProject"))
end
Funktion function ppJobStart(prj, joblast, job, jobnext)
Argumente [clsobj] prj Das Projekt welches die zu verarbeitenden Jobs beinhaltet.
[clsppdata] joblast Das zuvor verarbeitede Job Item oder 'nil' wenn nicht vorhanden.
[clsppdata] job Das zu verarbeitende Job Item.
[clsppdata] jobnext Das nächste zu verarbeitende Job Item oder 'nil' wenn nicht vorhanden.
Rückgabewert nil
Beschreibung Wird vom System bei jedem Start eines neuen Jobs aufgerufen.
Beispiel
function ppJobStart(prj, joblast, job, jobnext)
    s2.printnl("\tstart of ", job:get("nameJob"))
end

ppMove

Diese Funktion wird für jeden einzelnen Werkzeugwegpunkt aufgerufen. Da dies im Extremfall Millionen von Aufrufe bedeuten kann, sollten rechenintensive Aufgaben vermieden werden. Meldungen an den Nutzer innerhalb dieser Funktion sollten gänzlich vermieden werden.

Funktion function ppMove(job,x,y,z,gflag,mflag)
Argumente [clsppdata] job Das zu verarbeitende Job Item.
[number] x,y,z Die Koordinaten des nächsten Werkzeugwegpunkts als Gleitkommazahl.
[integer] gflag Bitmaske zur Bestimmung des Vorschubmodus
[integer] mflag Bitmaske zur Bestimmung des Spindelzustandes
Rückgabewert nil
Beschreibung Wird jedes mal zu Beginn vom System aufgerufen wenn ein neuer Job verarbeitet wird.
Beispiel
function ppMove(job,x,y,z,gflag,mflag)
    if x ~= nil then s2.print("X ", x) end
    if y ~= nil then s2.print("Y ", y) end
    if z ~= nil then s2.print("Z ", z) end
    s2.nl()
end

gflag, mflag

Diese Argumente beinhalten zusätzliche Infos zum Werkzeugwegpunkt. Es handelt sich dabei um Bitmasken im Ganzzahlformat (integer), die mit dem Bit-Operator "&"auf gewisse Zustände überprüft werden können. Zum Überprüfen können von send2cnc zur Verfügung gestellte globale Variablen genutzt werden. Im Folgenden ein Listing mit möglichen Abfragen:

Prüfung Beschreibung
if(gflag&FLAG_G0) Der Werkzeugwegpunkt ist Teil einer Positionierbewegung. Es sollte der reguläre Eilgangvorschub verwendet werden.
if(gflag&FLAG_G1) Der Werkzeugwegpunkt ist Teil einer regulären Fräsbewegung. Es sollte der reguläre Fräsvorschub verwendet werden.
if(gflag&FLAG_G1_RAMP) Der Werkzeugwegpunkt ist Teil einer rampenförmigen Eintauchbewegung. Es sollte der Eintauchvorschub verwendet werden.
if(gflag&FLAG_G1_PLUNGE) Der Werkzeugwegpunkt ist Teil einer senkrechten Eintauchbewegung. Es sollte der Bohrvorschub verwendet werden.
if(mflag&FLAG_M_SPINDLE_START) Die Spindel sollte gestartet werden bevor der Werkzeugwegpunkt angefahren wird.
Funktion function ppJobEnd(prj, joblast, job, jobnext)
Argumente [clsobj] prj Das Projekt welches die zu verarbeitenden Jobs beinhaltet.
[clsppdata] joblast Das zuvor verarbeitede Job Item oder 'nil' wenn nicht vorhanden.
[clsppdata] job Das zu verarbeitende Job Item.
[clsppdata] jobnext Das nächste zu verarbeitende Job Item oder 'nil' wenn nicht vorhanden.
Rückgabewert nil
Beschreibung Wird vom System zum Abschluss einer jeden Jobverarbeitung aufgerufen.
Beispiel
function ppJobEnd(prj, joblast, job, jobnext)
    s2.printnl("\tend of ", job:get("nameJob"))
end
Funktion function ppEnd(prj, joblast, job)
Argumente [clsobj] prj Das Projekt welches die zu verarbeitenden Jobs beinhaltet.
[clsppdata] joblast Das zuvor verarbeitede Job Item oder 'nil' wenn nicht vorhanden.
[clsppdata] job Das zu verarbeitende Job Item.
Rückgabewert nil
Beschreibung Wird vom System zum Abschluss eines jeden Postprozessorlaufs aufgerufen (nach ppJobEnd).
Beispiel
function ppEnd(prj, joblast, job)
    s2.printnl("pp run finished")
end
Funktion function ppMain(objlist, prj)
Argumente [clsobjlist] objlistEine Objektliste mit allen zu verarbeitenden Job Objekten
[clsobj] prj Das Projekt welches die zu verarbeitenden Jobs beinhaltet.
Rückgabewert nil
Beschreibung Dies ist der Einstiegspunkt für das Postprozessor-Skript. Die zu verarbeitende Objektliste wird an diese Funktion übergeben. In der Regel startet diese Funktion den eigentlichen Postprozessorlauf (siehe Beispiel).
Beispiel
function ppMain(objlist, prj)
    s2.pp.run(objlist)
    s2.success("Done!")
end

Skriptablauf

Der Ablauf eines Skripts in send2cnc gliedert sich in mehrere Phasen:

  • Initialisierung: Zu Beginn erfasst das System alle im Skript definierten Funktionen, führt sie jedoch noch nicht aus.
  • Hauptfunktion (ppMain): Das System ruft die Funktion ppMain() auf und übergibt dieser die zu verarbeitende Objektliste als Argument.
  • Postprozessorlauf: Innerhalb von ppMain() wird typischerweise die Funktion s2.pp.run(objlist) aufgerufen. Diese startet den eigentlichen Postprozessorlauf und sorgt dafür, dass alle relevanten Funktionen in einer Schleife vom System ausgeführt werden, bis alle Objekte abgearbeitet sind.

Beispiel

Ein Beispiel mit zwei zu verarbeitenden Jobs:

  • ppMain(jobliste) --wird vom System aufgerufen
    • s2.pp.run(jobliste) --muss vom Postprozessorskript innerhalb ppMain() aufgerufen werden
      • (die folgenden Funktionen werden alle vom System aufgerufen)
      • ppStart()
      • ppJobStart("job1")
      • ppMove()
      • ppMove()
      • ppMove()
      • ...
      • ppJobEnd("job1")
      • ppJobStart("job2")
      • ppMove()
      • ppMove()
      • ppMove()
      • ...
      • ppJobEnd("job2")
      • ppEnd()

Weitere API Funktionen

Im Kapitel Lua-Script sind alle weiteren Funktionen und Methoden der send2cnc API aufgelistet, insbesondere auch die Methoden für ein Job-Objekt (clsppdata).

Beispiele

Dieses einfache Beispiel veranschaulicht den Ablauf des Skripts. Die Ausgabe der Bewegungen ist auf 3 Zeilen beschränkt, um die Strukturdarstellung in der Konsole übersichtlich zu halten.

Der Screenshot zeigt ein einfaches Beispiel in der Postprozessorumgebung, um Postprozessor-Skripte von send2cnc zu bearbeiten. © send2cnc.com
aktivierte Skriptingumgebung im Postprozessordialog
local limitmove = 0

function ppStart( prj, job, jobnext)                --executed within s2.pp.run(objlist) -> see ppMain()
    s2.printnl("start pp run in project <", prj:get("nameProject"), ">")
end

function ppJobStart(prj, joblast, job, jobnext)     --executed every time a new job starts
    s2.printnl("\tstart of ", job:get("nameJob"))
    limitmove = 0
end

function ppMove(job,x,y,z,gflag,mflag)              --executed for each toolpath point
    if limitmove > 2 then return end
    s2.print("\t\t")
    if x ~= nil then s2.print("X ", x," ") end
    if y ~= nil then s2.print("Y ", y," ") end
    if z ~= nil then s2.print("Z ", z," ") end
    s2.nl()
    limitmove = limitmove + 1
end


function ppJobEnd(prj, joblast, job, jobnext)       --executed after the last movement of the current job
    s2.printnl("\tend of ", job:get("nameJob"))
end

function ppEnd(prj, joblast, job)                   --executed after all jobs have been completed
    s2.printnl("pp run finished")
end


function ppMain(objlist, prj)                       --entry point
    s2.pp.run(objlist)                              --start postprocessor run (ppStart, ppJobStart. ppMove, ppJobEnd, ppEnd)
    s2.success("Done!")
end

Beispiel B

Dieses erweiterte Beispiel zeigt ein einfaches Postprozessor-Skript, um G-Code in der Konsole und/oder Datei auszugeben (nicht gedacht für den produktiven Einsatz).

Der Screenshot zeigt ein erweitertes Beispiel in der Postprozessorumgebung, um Postprozessor-Skripte von send2cnc zu bearbeiten. © send2cnc.com
aktivierte Skriptingumgebung im Postprozessordialog
-------------------------------------------------------------------------------
-- This template is a very minimal example script and is not intended for productive use.
-------------------------------------------------------------------------------

local pushToConsole     = true      -- Write NC Code to Console (if set it's recomented to set debugLimiter also)
local pushToFile        = true      -- Write NC Code to File   
local debugLimiter      = 50        -- Set this value to limit ppMove to maximum lines (very usefull for debugging).
local debugCounter
local ncbuffer
local codeFeed
local codeFeedRamp
local codeFeedDrill
local codeFeedRapid

function ppStart     (prj, job, jobnext)        --executed within s2.pp.run(objlist) -> see ppMain()
    s2.clear()                                  --clear console
    ncbuffer    =   s2.txtBuf()                 --create empty textbuffer
    ncbuffer:addnl("G17 G40 G49 G80 G90 G94")   --write NC code header at beginning of the textbuffer
    ncbuffer:addnl("G21 (mm)")
end

function ppJobStart (prj, joblast, job, jobnext) --executed every time a new job starts
    if job:get("collision") then s2.abort("collision detected") return end  --abort in case of a collision
    debugCounter        =   0 
    codeFeed            =   string.format("G1 F%d",       job:get("feed")         or 0) -- save codesnippet for the feedrate
    codeFeedRamp        =   string.format("G1 F%d",       job:get("feedRamp")     or 0) -- (used frequently in ppMove)
    codeFeedDrill       =   string.format("G1 F%d",       job:get("feedDrill")    or 0)
    codeFeedRapid       =   string.format("G0")
    if job:get("feedMode") ~= 0 then
       codeFeedRapid    =   string.format("G1 F%d",       job:get("feedRapid")    or 0)
    end
    ncbuffer:addnl("(start job '", job:get("nameJob"), "')") --write jobname as NC Comment to textbuffer
    ncbuffer:addnl(string.format("T%d M06 (%s)\n", job:get("toolId"),job:get("nameTool"))) --Tool call
end

function ppMove     (job,x,y,z,gflag,mflag) --executed for each toolpath point
    if debugLimiter > 0 and debugCounter == debugLimiter then 
        ncbuffer:addnl("M5 M0\n(...skipped)") --debugLimiter -> write NC Stop to textbuffer
        debugCounter = debugCounter + 1 
    end
    if debugLimiter > 0 and debugCounter >= debugLimiter then return end --Limit calls in debugmode (debugLimiter)

    if mflag ~= nil then    --check cooling and spindle start and write to textbuffer
        if (mflag&FLAG_M_SPINDLE_START) ~= 0 then
            speed = math.tointeger(job:get("spindleSpeed"))

            if speed == nil or speed <= 0 then 
                s2.abort("no spindlespeed set in job <",job:get("nameJob"),">" ) 
                return 
            end

            if job:get("spindleRot") == 0 then ncbuffer:addnl("S", speed, "M03")
            else                               ncbuffer:addnl("S", speed, "M04")
            end
            if      job:get("cooling") == 1 then ncbuffer:addnl("M08")
            elseif  job:get("cooling") == 2 then ncbuffer:addnl("M09")
            end
        end
    end

    local gcode = nil --check if feedmode changed
    if gflag ~= nil then
        if      (gflag&FLAG_G1_PLUNGE) ~= 0 then gcode = codeFeedDrill
        elseif  (gflag&FLAG_G1_RAMP)   ~= 0 then gcode = codeFeedRamp
        elseif  (gflag&FLAG_G1)        ~= 0 then gcode = codeFeed
        elseif  (gflag&FLAG_G0)        ~= 0 then gcode = codeFeedRapid
        end
    end

    if gcode    ~= nil then gcode   = gcode .. " "             end --create string for coordinates
    if x        ~= nil then x       = string.format("X%g ", x) end
    if y        ~= nil then y       = string.format("Y%g ", y) end
    if z        ~= nil then z       = string.format("Z%g ", z) end
    
    ncbuffer:add((gcode or "") .. (x or "") .. (y or "") .. (z or "").. "\n") -- write movement to textbuffer
    debugCounter = debugCounter + 1
end

function ppJobEnd   (prj, joblast, job, jobnext) --executed after the last movement of the current job
    if job:get("pause") then
        ncbuffer:addnl("M00")
    end
    ncbuffer:addnl("(end of job '", job:get("nameJob"), "')")
end

function ppEnd    (prj, job)    --executed after all jobs have been completed
    ncbuffer:addnl("M30")       --write "nc end and rewind" in the end of the textbuffer
    outpath = s2.path.build(prj:get("dirProject"),s2.path.basename(prj:get("nameProject"))..".txt") --build savepath (projectfolder\projectname.txt)
    s2.printnl("saving nc code to file <", outpath, ">")
    if pushToFile    then        ncbuffer:save(outpath) end --save textbuffer to disc
    if pushToConsole then        ncbuffer:print()       end --write textbuffer to console
end

function ppMain(objlist, prj)   --entry point
    s2.pp.run(objlist)          --start postprocessor run (ppStart, ppJobStart. ppMove, ppJobEnd, ppEnd)
    s2.success("Done!")
end