Tool life tracking with LUA Custom Scripts

Jason Davies

Moderator
Staff member
We are about to release a newsletter article outlining how lua can be used to track the time a tool is used, and send that information to your computer so that you can track it. https://www.masso.com.au/campaigns/tool-life-tracking-with-lua/

here are the scripts that needs to be in the Tool Change → Before Tool Change lua trigger, and in the Job → End Of Job lua trigger.

This will send the information in a message to myWorkshop PRO, and it can by copied and pasted into a table or other location to tally up the totals.

My question is, what do you think is the best format to output the data? At the moment it is just output in a message, but the message can be formatted in any way so that it can be passed off to another program to be organised and made useful. How should the data be structured?

--===================
-- Before Tool Change
--===================

toolUsage = toolUsage or {}
lastToolTime = lastToolTime or 0

-- Capture current tool usage one last time
local currentTool = M.tool.get_current()
if currentTool then
local toolName = M.tool.get_name(currentTool) or "Unnamed"
local runTimeTable = M.sys.get_job_runtime() or {}
local hh = runTimeTable.hour or 0
local mm = runTimeTable.min or 0
local ss = runTimeTable.sec or 0
local totalSeconds = (hh * 3600) + (mm * 60) + ss

local deltaSeconds = totalSeconds
if lastToolTime then
deltaSeconds = totalSeconds - lastToolTime
if deltaSeconds < 0 then deltaSeconds = 0 end
end

-- Add or update table entry
local entry = toolUsage[currentTool]
if entry then
entry.seconds = entry.seconds + deltaSeconds
else
toolUsage[currentTool] = { name = toolName, seconds = deltaSeconds }
end
lastToolTime = totalSeconds
end

return true




-- ============================
-- END OF JOB TOOL REPORT
-- ============================

toolUsage = toolUsage or {}
lastToolTime = lastToolTime or 0

-- Capture current tool usage one last time
local currentTool = M.tool.get_current()
if currentTool then
local toolName = M.tool.get_name(currentTool) or "Unnamed"
local runTimeTable = M.sys.get_job_runtime() or {}
local hh = runTimeTable.hour or 0
local mm = runTimeTable.min or 0
local ss = runTimeTable.sec or 0
local totalSeconds = (hh * 3600) + (mm * 60) + ss

local deltaSeconds = totalSeconds
if lastToolTime then
deltaSeconds = totalSeconds - lastToolTime
if deltaSeconds < 0 then deltaSeconds = 0 end
end

-- Add or update table entry
local entry = toolUsage[currentTool]
if entry then
entry.seconds = entry.seconds + deltaSeconds
else
toolUsage[currentTool] = { name = toolName, seconds = deltaSeconds }
end
end

-- ============================
-- SEND TOOL USAGE IN ONE LINE
-- ============================

local msg = "Tool Usage Report: "

for toolNum, data in pairs(toolUsage) do
local seconds = data.seconds or 0
local hh = math.floor(seconds / 3600)
local mm = math.floor((seconds % 3600) / 60)
local ss = math.floor(seconds % 60)
local formatted = string.format("%02d:%02d:%02d", hh, mm, ss)

-- Add this tool to the message, separated by " | "
msg = msg .. string.format("T%02d-%s:%s | ", toolNum, data.name or "?", formatted)
end

-- Remove the trailing separator
msg = msg:sub(1, -4)

-- Send all in one message
M.sys.send_myworkshop_message(msg)

-- Clear table for next job
M.sys.delay(1000)
toolUsage = nil
lastToolTime = nil
 

zombieengineer

ZombieEngineer
Is there any way to know the name of the current job file?

*checks documents* - Answer appears to be yes: M.file.loaded_gcode_file()

Does LUA support writing to files - Answer appears to be yes (but how does this work within the MASSO environment?):


So in theory you could create a CSV file in a [Report] sub-directory using the loaded_gcode_file() as the base filename and replace the extension with .CSV. The LUA string.find() by default treats the period ('.') character as a wildcard "match any character", therefore some optional parameters need to be added to the string.find() command to perform a "plain text" comparison.


For tabulated data you really have two obvious paths:
  • CSV => Comma Separated Values (MS-Windows associates this with MS-Excel)
  • TXT => Text file containing <TAB> separated values
Apparently it is possible to embed formulas within a CSV file.
 

Jason Davies

Moderator
Staff member
An interesting idea, I had a look and MASSO Lua doesnt use io.open so I'm not sure it will work that way.

I did have another look for formulas and it might be possible to use this one
=ARRAYFORMULA(SPLIT(TRANSPOSE(SPLIT(A1, "|")), ";"))

This will make new columns at , and new rows at | the full message will be posted into cell A1, then this will break it out into cells.

I did wonder if there is a more elegant way to do it though.
 

zombieengineer

ZombieEngineer
I suspected that file IO (specifically reading / writing to text files) would have some challenges as MASSO does not have any concept of drive letters. There is also the issue that the USB drive could be removed with zero warning, it will be left to the reader to figure out what the correct response should be (hint: programming error trapping is ugly as you don't necessarily know all the possible errors in advance).

Perhaps MASSO should implement form of logging sub-routine. Instead of opening a file for write access you send a message to a user specified log file. If the USB drive is not present then the data disappears, otherwise the data is appended to the file. This will be slightly slower than direct file access as the logging sub-routine would be performing "Open(), Write Append(), Close()" for each line of text.

Suggested algorithm:

M.sys.log_message(s,l,d)

Description: Appends a message to a user specified log file.
Syntax: M.sys.send_myworkshop_message(d,l,s)
Input: s (string - message to be appended to log file), l (string - log file name, default "LOG.TXT"), d (string - log file sub directory, default "LOGS")
Returns: (none)

The parameter order is the reverse to what people expect but it allows for the following:
M.sys.log_message("Hello World!") => Appends "Hello World" to LOGS\LOG.TXT M.sys.log_message("3.141, 2.7182", "POINTS.CSV") => Appends "3.141, 2.7182" to LOGS\POINTS.CSV M.sys.log_message("Tool No, Length, Wear", "ToolData.csv", "Tools") => Appends "Tool No, Length, Wear" to Tools\ToolData.csv

EDIT - May need an option to prefix the line of text with the current date/time (down to milliseconds if possible) followed by a <TAB> character before appending the message. This would be useful tool for debugging stuff such as tool changers as this would provide a timestamp for events. The following is taken from a log file that I work with on a daily basis:

28-Mar-26 12:15:12.0062 10.1.0.12:Failed to locate host, error 10060
 
Last edited:
Top