doodle3d-firmware/src/rest/api/api_printer.lua

238 lines
6.3 KiB
Lua

local log = require('util.logger')
local utils = require('util.utils')
local settings = require('util.settings')
local M = {
isApi = true
}
local ULTIFI_BASE_PATH = '/tmp/UltiFi'
local TEMPERATURE_FILE = 'temp.out'
local COMMAND_FILE = 'command.in'
local GCODE_TMP_FILE = '/tmp/UltiFi/combined.gc'
-- returns full path + ultifi path or nil
local function printerExists(id)
if id == nil then return nil end
local path = '/dev/ttyACM' .. id
local upath = ULTIFI_BASE_PATH .. '/ttyACM' .. id
if utils.exists(path) then return path,upath end
path = '/dev/ttyUSB' .. id
upath = ULTIFI_BASE_PATH .. '/ttyUSB' .. id
if utils.exists(path) then return path,upath end
return nil
end
-- returns printerId,devicePath,ultifiPath or nil if printer does not exist
-- when nil is returned, response has already been set as an error
local function getPrinterDataOrFail(request, response)
local id = tonumber(request:get("id"))
if id == nil then
response:setError("missing id argument")
return nil
end
local devpath,ultipath = printerExists(id)
if not devpath then
response:setError("printer does not exist")
response:addData('id', id)
return nil
end
return id,devpath,ultipath
end
-- assumes printerPath exists, returns true if successful, false if command file already exists and is non-empty (i.e. printer busy),
-- nil+err if file could not be opened
local function sendGcode(printerPath, gcode)
local cmdPath = printerPath .. '/' .. COMMAND_FILE
local f,msg = io.open(cmdPath, 'a+') -- 'a+' is important, do not overwrite current contents in any case
if not f then return nil,msg end
if utils.fileSize(f) > 0 then return false end
log:debug("sending " .. gcode:len() .. " bytes of gcode")
f:write(gcode)
f:close()
return true
end
local function addToGcodeFile(gcode)
if not gcode or type(gcode) ~= 'string' then return nil,"missing gcode data" end
local gcf,msg = io.open(GCODE_TMP_FILE, 'a+')
if not gcf then return nil,msg end
log:debug("appending " .. gcode:len() .. " bytes of gcode to " .. GCODE_TMP_FILE)
gcf:write(gcode)
gcf:write("\n")
gcf:close()
return true
end
-- assumes printerPath exists, returns true if successful, false if command file already exists and is non-empty (i.e. printer busy),
-- nil+err if file could not be opened
local function printGcodeFile(printerPath)
local cmdPath = printerPath .. '/' .. COMMAND_FILE
local cmdf,msg = io.open(cmdPath, 'a+') -- 'a+' is important, do not overwrite current contents in any case
if not cmdf then return nil,msg end
if utils.fileSize(cmdf) > 0 then return false end
log:debug("starting print of gcode in " .. GCODE_TMP_FILE)
cmdf:write('(SENDFILE=' .. GCODE_TMP_FILE)
cmdf:close()
return true
end
local function isBusy(printerPath)
local cmdPath = printerPath .. '/' .. COMMAND_FILE
if not utils.exists(cmdPath) then return false end
local f,msg = io.open(cmdPath, 'r')
if not f then return nil,msg end
local size = utils.fileSize(f)
f:close()
return f > 0
end
function M._global(request, response)
-- TODO: list all printers (based on /dev/ttyACM* and /dev/ttyUSB*)
response:setSuccess()
end
--requires id(int)
--accepts with_raw(bool) to include raw printer response
function M.temperature(request, response)
local withRaw = utils.toboolean(request:get("with_raw"))
local argId,devpath,ultipath = getPrinterDataOrFail(request, response)
if argId == nil then return end
local f = io.open(ultipath .. '/' .. TEMPERATURE_FILE)
if not f then
response:setError("could not open temperature file")
response:addData('id', argId)
return
end
local tempText = f:read('*all')
f:close()
local hotend, hotendTarget, bed, bedTarget = tempText:match('T:(.*)%s+/(.*)%s+B:(.*)%s/(.*)%s+@.*')
response:setSuccess()
if withRaw then response:addData('raw', tempText) end
response:addData('hotend', hotend)
response:addData('bed', bed)
response:addData('hotend_target', hotendTarget)
response:addData('bed_target', bedTarget)
end
--requires id(int)
function M.busy(request, response)
local argId,devpath,ultipath = getPrinterDataOrFail(request, response)
if argId == nil then return end
local b,msg = isBusy(ultipath)
if b == nil then
response:setError("could not determine printer state")
response:addData('msg', msg)
else
response:setSuccess()
response:addData('busy', b)
end
end
function M.printing(request, response)
response:setError("not implemented")
response:addData('api_refer', response:apiURL('printer', 'busy'))
end
--requires id(int)
function M.heatup_POST(request, response)
local argId,devpath,ultipath = getPrinterDataOrFail(request, response)
if argId == nil then return end
local gcode = settings.get('printer.autoWarmUpCommand')
local rv,msg = sendGcode(ultipath, gcode)
if rv then
response:setSuccess()
elseif rv == false then
response:setFail("printer is busy")
else
response:setError("could not send gcode")
response:addData('msg', msg)
end
end
--requires id(int), gcode(string)
--accepts: first(bool) (chunks will be concatenated but output file will be cleared first if this argument is true)
--accepts: last(bool) (chunks will be concatenated and only when this argument is true will printing be started)
function M.print_POST(request, response)
local argId,devpath,ultipath = getPrinterDataOrFail(request, response)
if argId == nil then return end
local argGcode = request:get("gcode")
local argIsFirst = utils.toboolean(request:get("first"))
local argIsLast = utils.toboolean(request:get("last"))
if argGcode == nil or argGcode == '' then
response:setError("missing gcode argument")
return
end
if argIsFirst == true then
log:debug("clearing all gcode in " .. GCODE_TMP_FILE)
response:addData('gcode_clear',true)
os.remove(GCODE_TMP_FILE)
end
local rv,msg
rv,msg = addToGcodeFile(argGcode)
if rv == nil then
response:setError("could not add gcode")
response:addData('msg', msg)
return
else
--NOTE: this does not report the number of lines, but only the block which has just been added
response:addData('gcode_append',argGcode:len())
end
if argIsLast == true then
rv,msg = printGcodeFile(ultipath)
if rv then
response:setSuccess()
response:addData('gcode_print',true)
elseif rv == false then
response:setFail("printer is busy")
else
response:setError("could not send gcode")
response:addData('msg', msg)
end
else
response:setSuccess()
end
end
return M