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
ppStart
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 |
|
ppJobStart
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 |
|
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 |
|
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. |
ppJobEnd
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 |
|
ppEnd
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 |
|
ppMain
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 |
|
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()
- s2.pp.run(jobliste) --muss vom Postprozessorskript innerhalb ppMain() aufgerufen werden
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.

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

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