Lua Module - Postprocessor

This chapter is about customising and creating new postprocessors for send2cnc.

Postprocessor script

A postprocessor is basically a Lua script file that contains special commands and functions to convert the toolpaths calculated by send2cnc into a suitable format for the CNC machine.

By default, the scripts are stored in the "postprocessor" subfolder of the Program Data folder (see chapter "Installation"). New scripts can be added and managed in this folder.

Before creating or modifying a postprocessor, make sure that it is saved under a unique name in the "postprocessor/user" subfolder of the above-mentioned directory. Otherwise, modified postprocessors could be overwritten when updating or installing a new postprocessor package.

Script structure

The functions described in this section are called up by the system for each post-processing run and should be defined in each post-processor.

Overview

Function function ppStart( [clsobj] prj, [clsppdata] job, [clsppdata] jobnext)
Arguments [clsobj] prj: The project containing the jobs to be processed.
[clsppdata] job: The first job item to be processed.
[clsppdata] jobnext: The next job item to be processed.
Return Value nil
Description When a post-processor run is started with s2.pp.run(objlist), this function is called by the system with the system-provided arguments.
Example
function ppStart( prj, job, jobnext)
  s2.printnl("start pp run in project ", prj:get("nameProject"))
  end
Function function ppJobStart(prj, joblast, job, jobnext)
Arguments [clsobj] prj The project containing the jobs to be processed.
[clsppdata] joblast The previously processed job item or "nil" if not available.
[clsppdata] job The job item to be processed.
[clsppdata] jobnext The next job item to be processed or "nil" if there is none.
Return Value nil
Description This function is called by the system every time a new job is started.
Example
function ppJobStart(prj, joblast, job, jobnext)
  s2.printnl("\tstart of ", job:get("nameJob"))
  end

ppMove

This function is called for each individual toolpath point. Since this could mean millions of calls in extreme cases, computationally intensive tasks should be avoided. User messages within this function should be avoided entirely.

Function function ppMove(job,x,y,z,gflag,mflag)
Arguments [clsppdata] job The job item to be processed.
[number] x,y,z The coordinates of the next toolpath point as floating point numbers.
[integer] gflag Bit mask to determine the feed mode
[integer] mflag Bit mask for determining the spindle
Return Value nil
Description This function is called by the system every time a new job is processed.
Example
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

These arguments contain additional information about the toolpath point. They are bit masks in integer format, which can be checked for certain states using the bit operator "&". Global variables provided by send2cnc can be used for checking. The following is a listing of possible queries:

Check Description
if(gflag&FLAG_G0) The toolpath point is part of a positioning move. The regular rapid feedrate should be used.
if(gflag&FLAG_G1) The toolpath point is part of a regular milling move. The regular milling feedrate should be used.
if(gflag&FLAG_G1_RAMP) The toolpath point is part of a ramped plunge move. The plunge feedrate should be used.
if(gflag&FLAG_G1_PLUNGE) The toolpath point is part of a perpendicular plunge move. The drill feed should be used.
if(mflag&FLAG_M_SPINDLE_START) The spindle should be started before moving to the toolpath point.
Function function ppJobEnd(prj, joblast, job, jobnext)
Arguments [clsobj] prj The project containing the jobs to be processed.
[clsppdata] joblast The previously processed job item or "nil" if not available.
[clsppdata] job The job item to be processed.
[clsppdata] jobnext The next job item to be processed or "nil" if there is none.
Return value nil
Description Called by the system at the end of each job.
Example
function ppJobEnd(prj, joblast, job, jobnext)
  s2.printnl("\tend of ", job:get("nameJob"))
  end
Function function ppEnd(prj, joblast, job)
Arguments [clsobj] prj The project containing the jobs to be processed.
[clsppdata] joblast The previously processed job item or "nil" if not available.
[clsppdata] job The job item to be processed.
Return value nil
Description This function is called by the system at the end of each postprocessor run (after ppJobEnd).
Example
function ppEnd(prj, joblast, job)
    s2.printnl("pp run finished")
end
function function ppMain(objlist, prj)
Arguments [clsobjlist] objlistAn object list with all the job objects to be processed
[clsobj] prj The project containing the jobs to be processed.
Return value nil
Description This is the entry point for the post-processor script. The object list to be processed is passed to this function. In general, this function starts the actual post-processing run (see example).
Example
function ppMain(objlist, prj)
  s2.pp.run(objlist)
  s2.success("Done!")
end

Script process

The process of a script in send2cnc is divided into several phases.

  • Initialisation: At the beginning, the system records all the functions defined in the script, but does not execute them yet.
  • Main function (ppMain): The system calls the function ppMain() and passes the object list to be processed as an argument.
  • Post-processing run: Within ppMain() the function s2.pp.run(objlist) is typically called. This starts the actual post-processing run and ensures that all relevant functions are executed by the system in a loop until all objects have been processed.

Example

An example with two jobs to be processed:

  • ppMain(jobliste) --is called by the system
    • s2.pp.run(jobliste) --must be called by the post-processor script within ppMain()
      • (the following functions are all called by the system)
      • ppStart()
      • ppJobStart("job1")
      • ppMove()
      • ppMove()
      • ppMove()
      • ...
      • ppJobEnd("job1")
      • ppJobStart("job2")
      • ppMove()
      • ppMove()
      • ppMove()
      • ...
      • ppJobEnd("job2")
      • ppEnd()

Further API functions

All further functions and methods of the send2cnc API are listed in the chapter Lua script, in particular the methods for a job object (clsppdata).

Examples

This simple example illustrates the script process. The output of the movements is limited to 3 lines to keep the structure display in the console clear.

The screenshot shows a simple example in the post-processing environment for editing send2cnc post-processing scripts. © send2cnc.com
activated scripting environment in the post-processing dialogue
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

Example B

This advanced example shows a simple post-processor script for outputting G-code in the console and/or file (not intended for productive use).

The screenshot shows an advanced example in the postprocessor environment for editing send2cnc postprocessor scripts. © send2cnc.com
activated scripting environment in the postprocessor dialogue
-------------------------------------------------------------------------------
-- 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 useful 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 text buffer
  ncbuffer:addnl("G17 G40 G49 G80 G90 G94") – write NC code header at beginning of the text buffer
  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