Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 182 additions & 4 deletions Fusion 360/Post-Processor/SM2.0 (NrvO).cps
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
{
POST_VERSION = "v20230108.1";
AUTHOR_NAME = "Nuno Vaz Oliveira";
CONTRIBUTOR_NAME = "Caleb McMains" // [ADDED THIS ONLY SO I COULD DISTINGUISH WHICH POST PROCESS WAS MY EDITS]
}

// User configuration. Please change only these values on this file
Expand All @@ -109,6 +110,7 @@
DWELL_TIME_SPIN_DOWN = "3"; // Dwell time for spin down in seconds. Used immediately after spindle stop
PAUSE_RAISE_Z_FEED_RATE_UP = 3000; // Feed rate to raise Z axis on ACTION_PAUSE_RAISE_Z command
PAUSE_RAISE_Z_FEED_RATE_DOWN = 1500; // Feed rate to return Z axis to position after ACTION_PAUSE_RAISE_Z command
ACTION_PAUSE_RAISE_Z_POSITION = 334; // Position of Z axis for pause and other actions to send Z as far up as possible
}

// Fusion 360 Kernel Settings
Expand All @@ -117,7 +119,7 @@
allowHelicalMoves = true; // Supported by Snapmaker 2.0
capabilities = CAPABILITY_MILLING; // All that Snapmaker 2.0 can handle
certificationLevel = 2; // As recommended on Autodesk Post Processor Training Guide
description = "Snapmaker 2.0 (Marlin) by " + AUTHOR_NAME; // Shows up on Fusion 360 post window
description = "Snapmaker 2.0 (Marlin) by " + AUTHOR_NAME + " featuring contributions from " + CONTRIBUTOR_NAME; // Shows up on Fusion 360 post window, [CHANGED THIS ONLY SO I COULD DISTINGUISH WHICH POST PROCESS WAS MY EDITS]
extension = ".cnc"; // As exported by Luban
setCodePage("ascii"); // As recommended on Autodesk Post Processor Training Guide
highFeedrate = 6000; // Specifies the high feed mapping mode for rapid moves
Expand All @@ -136,6 +138,22 @@

// Properties that are changeable on Fusion 360 post process page. New improved version with tooltips and proper writing
properties = {
writeSnapmakerHeader : {
title : "Write snapmaker header",
description : "Outputs a complete header as comments that Snapmaker Luban Software reads",
group : "informationSection",
type : "boolean",
value : true,
scope : "post"
},
writeMachiningTimes : {
title : "Write machining times",
description : "Outputs a estimated amount of time for the whole machining process",
group : "informationSection",
type : "boolean",
value : true,
scope : "post"
},
writeWarnings : {
title : "Write warnings as comments",
description : "Output comments with warnings for unsupported and ignored functions.",
Expand Down Expand Up @@ -294,6 +312,9 @@ groupDefinitions = {

// This variable will track the last Z position
var lastPositionZ = 9999;

// This variable allows for tracting the previousFeedRate in the OnLinear function to decide when to write the feed rate and when not to
var previousFeedRate;
}

// The writeBlock function writes a block of codes to the output NC file. It will add a sequence number to the block,
Expand All @@ -313,7 +334,8 @@ function writeBlock() {
// formatComment is used to format comments. The formatComment function will remove any characters in the comment
// that are not allowed, and add any characters that are mandatory. For Snapmakers, a semi-colon at the beginning.
function formatComment(text) {
return ";" + String(text).replace(/[\(\)]/g, ""); // TODO: Need to double check if ( and ) are bad for comments
// return ";" + String(text).replace(/[\(\)]/g, ""); // TODO: Need to double check if ( and ) are bad for comments
return ";" + String(text) // TODO: Need to double check if ( and ) are bad for comments <---I dont think they are cause snapmaker uses them in comments
}

// The writeComment function is defined in the post processor and is used to output comments to the output NC file.
Expand Down Expand Up @@ -341,6 +363,127 @@ function onComment(message) {

}


// The totalMachingTime function is a custom function to determine the machining time per operation
// and the total machining time for all operations to be done.
// The function outputs and object containing the total time and an array of cycle times for each operation.
// This function can be used anywhere in the file.
function totalMachingingTime() {
var cycleTime = 0;
var currentTool;
var toolChangeTime = 0; // the time in seconds for a tool change
var sectionCycleTimes = [];

for (var i = 0; i < getNumberOfSections(); ++i) {
var section = getSection(i);
var tool = section.getTool();
if (currentTool != tool.number) {
currentTool = tool.number;
cycleTime += toolChangeTime;
}
cycleTime += section.getCycleTime();
var comment;
if (section.hasParameter("operation-comment")) {
comment = section.getParameter("operation-comment");
}
sectionCycleTimes.push({operation:comment, cycleTime:section.getCycleTime()});
}

var cycleTimeData = {totalCycleTime:cycleTime, sectionCycleTimes:sectionCycleTimes}

return cycleTimeData;
}

// The convertSeconds function takes in cycle times as seconds and outputs and object containing
// different ways for the time to be displayed (i.e.: total seconds, total minutes, hours, minutes, and seconds).
// this function can be used anywhere in the file.
function convertSeconds(userSeconds) {
const userSecondsNum = parseInt(userSeconds, 10);
const totalSeconds =
userSecondsNum < 9
? userSecondsNum.toString()
: userSecondsNum.toString();
const totalMinutes =
(userSecondsNum / 60).toFixed(2) < 9
? (userSecondsNum / 60).toFixed(2).toString()
: (userSecondsNum / 60).toFixed(2).toString();
const hours =
Math.floor(userSecondsNum / 3600) < 9
? Math.floor(userSecondsNum / 3600)
.toString()
: Math.floor(userSecondsNum / 3600);
const minutes =
Math.floor((userSecondsNum / 60) % 60) < 9
? Math.floor((userSecondsNum / 60) % 60)
.toString()
: Math.floor((userSecondsNum / 60) % 60).toString();
const seconds =
userSecondsNum % 60 < 9
? (userSecondsNum % 60).toString()
: userSecondsNum % 60;

const timeData = {totalSeconds: totalSeconds, totalMinutes: totalMinutes, hours: hours, minutes: minutes, seconds: seconds}


return timeData;
}


// The writeSnapmakerHeader function outputs a header section modeled after a NC file that is made
// from Snapmaker Lubans software.
// This specifically helps when uploading NC files to luban to send it to the machine and the machine
// having access to certain information in the header.
function writeSnapmakerHeader() {
// Starts snapmaker header
writeComment(localize("Header Start"));

// Defines type <-- could be changes to a property where you can select cnc, 3d printing, or laser
writeComment(localize("header_type: cnc"));

// Toolhead
writeComment(localize("tool_head: standardCNCToolheadForSM2"));

// Writes the machine model if it exists
machineConfiguration.getModel() ? writeComment(localize("machine: ") + machineConfiguration.getModel()) : writeComment(localize("machine: "));

// Writes the estimated time in seconds for the whole process
writeComment(localize("estimated_time(s): " + convertSeconds(totalMachingingTime().totalCycleTime).totalSeconds));

// Writes coordinates min and maxs, I assume this is for creating the boundry box for running the boundry
// on the snapmaker machine
writeComment(localize("max_x(mm): " + getGlobalParameter("operation:surfaceXHigh")));
writeComment(localize("max_y(mm): " + getGlobalParameter("operation:surfaceYHigh")));
writeComment(localize("max_z(mm): " + ACTION_PAUSE_RAISE_Z_POSITION));
writeComment(localize("max_b(mm): " + "0"));
writeComment(localize("min_x(mm): " + getGlobalParameter("operation:surfaceXLow")));
writeComment(localize("min_y(mm): " + getGlobalParameter("operation:surfaceYLow")));
writeComment(localize("min_b(mm): " + "0"));
writeComment(localize("min_z(mm): " + getGlobalParameter("operation:bottomHeight_value")));

// Writes the feedrate for the tool and the machining when carving
if (hasGlobalParameter("operation:tool_feedCutting")) {
writeComment(localize("work_speed(mm/minute): " + getGlobalParameter("operation:tool_feedCutting")))
} else {
writeComment(localize("work_speed(mm/minute): unknown"));
}

// Writes the feedrate for the tool and the machining when not carving
if (hasGlobalParameter("operation:tool_surfaceSpeed")) {
writeComment(localize("jog_speed(mm/minute): " + getGlobalParameter("operation:tool_surfaceSpeed").toFixed(0)))
} else {
writeComment(localize("wjog_speed(mm/minute): unknown"));
}

// Hard coded in to match the work size of the Snapmaker A350T
writeComment(localize("work_size_x: " + "320"));
writeComment(localize("work_size_x: " + "350"));


//starts snapmaker header
writeComment(localize("Header End"));

}

// The onOpen function is called at start of each CAM operation and can be used to define settings used in the
// post processor and output the startup blocks:
// 1. Define settings based on properties
Expand All @@ -351,6 +494,8 @@ function onComment(message) {
function onOpen() {

// Read properties from post to internal variables
prop_writeSnapmakerHeader = getProperty("writeSnapmakerHeader");
prop_writeMachiningTimes = getProperty("writeMachiningTimes");
prop_writeWarnings = getProperty("writeWarnings");
prop_writeOperationName = getProperty("writeOperationName");
prop_writeProgram = getProperty("writeProgram");
Expand All @@ -375,6 +520,14 @@ function onOpen() {
// Read property sequenceNumberStart
sequenceNumber = prop_sequenceNumberStart;

// Write snapmaker header info
if(prop_writeSnapmakerHeader) {
writeSnapmakerHeader();
writeln("");
}



// Write program info
if (prop_writeProgram) {
writeComment(localize("Program"));
Expand Down Expand Up @@ -416,7 +569,19 @@ function onOpen() {
writeComment(localize(" -> Version") + " : " + POST_VERSION);
writeComment(localize(" -> Description") + " : " + longDescription);
}
if (prop_writeExtraComments) writeln("");

// Writes Estimated Times
if(prop_writeMachiningTimes) {
writeComment(localize("Estiamted Machining Time"));
for (var i = 0; i < totalMachingingTime().sectionCycleTimes.length; i++ ) {
// writeComment(localize(" -> Operation " + totalMachingingTime().sectionCycleTimes[i].operation + " : " + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).hours + " h ") + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).minutes + " min " + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).seconds + " sec");
writeComment(localize(" -> " + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).hours + "h ") + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).minutes + "min " + convertSeconds(totalMachingingTime().sectionCycleTimes[i].cycleTime).seconds + "sec" + " : " + "Operation " + totalMachingingTime().sectionCycleTimes[i].operation);
}
// writeComment(localize(" -> Total" + " : " + convertSeconds(totalMachingingTime().totalCycleTime).hours + " h ") + convertSeconds(totalMachingingTime().totalCycleTime).minutes + " min " + convertSeconds(totalMachingingTime().totalCycleTime).seconds + " sec");
writeComment(localize(" -> " + convertSeconds(totalMachingingTime().totalCycleTime).hours + "h ") + convertSeconds(totalMachingingTime().totalCycleTime).minutes + "min " + convertSeconds(totalMachingingTime().totalCycleTime).seconds + "sec" + " : " + "Total");
writeln("");
}


// Check unit and process it as on Snapmaker original post
switch (unit) {
Expand Down Expand Up @@ -633,7 +798,7 @@ function onParameter(param_name, param_value) {
if (prop_writeExtraComments) writeComment("Manual NC command action \"ACTION_PAUSE_RAISE_Z\" invoked");
if (prop_writeExtraComments) writeComment("Raising Z");
// Raises Z axix to machine coordinates Z=334mm
writeBlock(gMotionModal.format(53), gMotionModal.format(1), zOutput.format(334), feedOutput.format(PAUSE_RAISE_Z_FEED_RATE_UP));
writeBlock(gMotionModal.format(53), gMotionModal.format(1), zOutput.format(ACTION_PAUSE_RAISE_Z_POSITION), feedOutput.format(PAUSE_RAISE_Z_FEED_RATE_UP));
// Enforces absolute positioning coordinate system
writeBlock(gAbsIncModal.format(90));
if (prop_writeExtraComments) writeComment("Executing job pause");
Expand Down Expand Up @@ -754,7 +919,14 @@ function onLinear(_x, _y, _z, feed) {
lastPositionZ = z;
}
// Normal case. Use feed rate specified and output next block
// outputs the feed rate only if it changes, this helps for a cleaner code.
// Snapmakers G-code reference indicates that: "The feedrate set here applies to subsequent moves that omit this parameter."
// therefore feedrate only needs to be written to set it.
if (previousFeedRate === f) {
writeBlock(gMotionModal.format(1), x, y, z);
} else {
writeBlock(gMotionModal.format(1), x, y, z, f);
}
}
}
} else if (f) {
Expand All @@ -767,6 +939,7 @@ function onLinear(_x, _y, _z, feed) {
}
}

previousFeedRate = f;
}

// The onRapid5D function handles rapid positioning moves (G00) in multi-axis operations. The tool position is
Expand Down Expand Up @@ -952,4 +1125,9 @@ function onClose() {
// output next block
writeBlock("G4 S" + DWELL_TIME_SPIN_DOWN);

// Not sure if this a good idea becuase I think it sends the z axis beyond the physical limitations
// of the machine. Also not sure if once the file is complete does the machine defaults take over and automatically send the machine to HOME position?
if (prop_writeExtraComments) writeComment(localize("Move Z axis " + ACTION_PAUSE_RAISE_Z_POSITION + "mm to top of machine to get it out of the way"));
writeBlock(gFormat.format(0), zOutput.format(ACTION_PAUSE_RAISE_Z_POSITION));

}