/** Experimental PP for Fusion360 Modified from the Grbl post made by Autodesk **/ description = "Easel"; vendor = "Inventables, Inc."; vendorUrl = "https://inventables.com"; legal = "Copyright (C) 2012-2015 by Autodesk, Inc."; certificationLevel = 2; minimumRevision = 24000; extension = "nc"; setCodePage("ascii"); capabilities = CAPABILITY_MILLING; tolerance = spatial(0.01, MM); minimumChordLength = spatial(0.01, MM); minimumCircularRadius = spatial(0.01, MM); maximumCircularRadius = spatial(1000, MM); minimumCircularSweep = toRad(0.01); maximumCircularSweep = toRad(180); allowHelicalMoves = false; allowedCircularPlanes = undefined; // allow any circular motion // user-defined properties properties = { clampOffset: 0.0 // All have been removed }; var numberOfToolSlots = 1; var mapCoolantTable = new Table( [9, 8], {initial:COOLANT_OFF, force:true}, "Invalid coolant mode" ); var gFormat = createFormat({prefix:"G", decimals:0}); var mFormat = createFormat({prefix:"M", decimals:0}); var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4)}); var feedFormat = createFormat({decimals:(unit == MM ? 1 : 2)}); var toolFormat = createFormat({decimals:0}); var rpmFormat = createFormat({decimals:0}); var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-1000 var taperFormat = createFormat({decimals:1, scale:DEG}); var xOutput = createVariable({prefix:"X"}, xyzFormat); var yOutput = createVariable({prefix:"Y"}, xyzFormat); var zOutput = createVariable({prefix:"Z"}, xyzFormat); var feedOutput = createVariable({prefix:"F"}, feedFormat); var sOutput = createVariable({prefix:"S", force:true}, rpmFormat); // circular output var iOutput = createReferenceVariable({prefix:"I"}, xyzFormat); var jOutput = createReferenceVariable({prefix:"J"}, xyzFormat); var kOutput = createReferenceVariable({prefix:"K"}, xyzFormat); var gMotionModal = createModal({}, gFormat); // modal group 1 // G0-G3, ... var gPlaneModal = createModal({onchange:function () {gMotionModal.reset();}}, gFormat); // modal group 2 // G17-19 var gAbsIncModal = createModal({}, gFormat); // modal group 3 // G90-91 var gFeedModeModal = createModal({}, gFormat); // modal group 5 // G93-94 var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21 var WARNING_WORK_OFFSET = 0; var stock = { minX: 0, minY: 0, minZ: 0, maxX: 0, maxY: 0, maxZ: 0 } // collected state var sequenceNumber; var currentWorkOffset; function writeTool(tool) { // TOOL/MILL, Diameter, Corner radius, Height, Taper Angle var toolString = "TOOL/MILL" + "," + xyzFormat.format(tool.diameter) + "," + xyzFormat.format(tool.cornerRadius) + "," + xyzFormat.format(tool.fluteLength) + "," + xyzFormat.format(tool.taperAngle); writeComment(toolString); } function writeStockAndOffset(stock, wcs) { try { // STOCK/BLOCK, Length, Width, Height, Origin X, Origin Y, Origin Z var blockString = "STOCK/BLOCK" + "," + xyzFormat.format(stock.maxX - stock.minX) + "," + xyzFormat.format(stock.maxY - stock.minY) + "," + xyzFormat.format(stock.maxZ - stock.minZ) + "," + wcs; writeComment(blockString); } catch(e) { writeComment(e.message) } } /** Writes the specified block. */ function writeBlock() { if (properties.showSequenceNumbers) { writeWords2("N" + sequenceNumber, arguments); sequenceNumber += properties.sequenceNumberIncrement; } else { writeWords(arguments); } } function formatComment(text) { return "(" + String(text).replace(/[\(\)]/g, "") + ")"; } /** Output a comment. */ function writeComment(text) { writeln(formatComment(text)); } function onOpen() { if (!properties.separateWordsWithSpace) { setWordSeparator(""); } sequenceNumber = properties.sequenceNumberStart; // absolute coordinates and feed per min writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94)); writeBlock(gPlaneModal.format(17)); switch (unit) { case IN: writeBlock(gUnitModal.format(20)); break; case MM: writeBlock(gUnitModal.format(21)); break; } } function onParameter(name, value) { if(name === 'stock-lower-x') { stock.minX = value; } if(name === 'stock-lower-y') { stock.minY = value; } if(name === 'stock-lower-z') { stock.minZ = value; } if(name === 'stock-upper-x') { stock.maxX = value; } if(name === 'stock-upper-y') { stock.maxY = value; } if(name === 'stock-upper-z') { stock.maxZ = value; } } function onComment(message) { writeComment(message); } /** Force output of X, Y, and Z. */ function forceXYZ() { xOutput.reset(); yOutput.reset(); zOutput.reset(); } /** Force output of X, Y, Z, and F on next output. */ function forceAny() { forceXYZ(); feedOutput.reset(); } function onSection() { var insertToolCall = isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number); var retracted = false; // specifies that the tool has been retracted to the safe plane var newWorkOffset = isFirstSection() || (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes var newWorkPlane = isFirstSection() || !isSameDirection(getPreviousSection().getGlobalFinalToolAxis(), currentSection.getGlobalInitialToolAxis()); if (insertToolCall || newWorkOffset || newWorkPlane) { // stop spindle before retract during tool change if (insertToolCall && !isFirstSection()) { onCommand(COMMAND_STOP_SPINDLE); } } writeln(""); writeTool(tool); writeStockAndOffset(stock, currentSection.wcsOrigin); if (insertToolCall) { retracted = true; onCommand(COMMAND_COOLANT_OFF); if (tool.number > numberOfToolSlots) { warning(localize("Tool number exceeds maximum value.")); return } writeBlock("T" + toolFormat.format(tool.number), mFormat.format(6)); } if (insertToolCall || isFirstSection() || (rpmFormat.areDifferent(tool.spindleRPM, sOutput.getCurrent())) || (tool.clockwise != getPreviousSection().getTool().clockwise)) { if (tool.spindleRPM < 1) { error(localize("Spindle speed out of range.")); } if (tool.spindleRPM > 99999) { warning(localize("Spindle speed exceeds maximum value.")); } writeBlock( sOutput.format(tool.spindleRPM), mFormat.format(tool.clockwise ? 3 : 4) ); } // wcs var workOffset = currentSection.workOffset; if (workOffset == 0) { warningOnce(localize("Work offset has not been specified. Using G54 as WCS."), WARNING_WORK_OFFSET); workOffset = 1; } if (workOffset > 0) { if (workOffset > 6) { error(localize("Work offset out of range.")); return; } else { if (workOffset != currentWorkOffset) { writeBlock(gFormat.format(53 + workOffset)); // G54->G59 currentWorkOffset = workOffset; } } } forceXYZ(); { // pure 3D var remaining = currentSection.workPlane; if (!isSameDirection(remaining.forward, new Vector(0, 0, 1))) { error(localize("Tool orientation is not supported.")); return; } setRotation(remaining); } // set coolant after we have positioned at Z { var c = mapCoolantTable.lookup(tool.coolant); if (c) { writeBlock(mFormat.format(c)); } else { warning(localize("Coolant not supported.")); } } forceAny(); var initialPosition = getFramePosition(currentSection.getInitialPosition()); if (!retracted) { if (getCurrentPosition().z < initialPosition.z) { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); } } // var clearHeight = hasParameter("operation:clearanceHeight_value") ? getParameter("peration:clearanceHeight_value"): " "; // writeBlock(gMotionModal.format(0), zOutput.format(clearHeight)); if (insertToolCall || retracted) { var lengthOffset = tool.lengthOffset; if (lengthOffset > numberOfToolSlots) { error(localize("Length offset out of range.")); return; } gMotionModal.reset(); writeBlock(gPlaneModal.format(17)); if (!machineConfiguration.isHeadConfiguration()) { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); writeBlock( gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) ); } else { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); writeBlock( gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), zOutput.format(initialPosition.z) ); } } else { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); writeBlock( gAbsIncModal.format(90), gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y) ); } } function onDwell(seconds) { if (seconds > 99999.999) { warning(localize("Dwelling time is out of range.")); } seconds = clamp(0.001, seconds, 99999.999); writeBlock(gFormat.format(4), "P" + secFormat.format(seconds)); } function onSpindleSpeed(spindleSpeed) { writeBlock(sOutput.format(spindleSpeed)); } var pendingRadiusCompensation = -1; function onRadiusCompensation() { pendingRadiusCompensation = radiusCompensation; } function clampOffsetDistance() { if (unit == MM) { return properties.clampOffset * 25.4; } else { return properties.clampOffset; } } function onRapid(_x, _y, _z) { if (_x) { _x += clampOffsetDistance(); } if (_y) { _y += clampOffsetDistance(); } var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); if (x || y || z) { if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation mode cannot be changed at rapid traversal.")); return; } writeBlock(gMotionModal.format(0), x, y, z); feedOutput.reset(); } } function onLinear(_x, _y, _z, feed) { // at least one axis is required if (_x) { _x += clampOffsetDistance(); } if (_y) { _y += clampOffsetDistance(); } if (pendingRadiusCompensation >= 0) { // ensure that we end at desired position when compensation is turned off xOutput.reset(); yOutput.reset(); } var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); var f = feedOutput.format(feed); if (x || y || z) { if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation mode is not supported.")); return; } else { writeBlock(gMotionModal.format(1), x, y, z, f); } } else if (f) { if (getNextRecord().isMotion()) { // try not to output feed without motion feedOutput.reset(); // force feed on next line } else { writeBlock(gMotionModal.format(1), f); } } } function onRapid5D(_x, _y, _z, _a, _b, _c) { error(localize("Multi-axis motion is not supported.")); } function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { error(localize("Multi-axis motion is not supported.")); } function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { // one of X/Y and I/J are required and likewise if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); return; } linearize(tolerance); } var mapCommand = { COMMAND_STOP:0, COMMAND_END:2, COMMAND_SPINDLE_CLOCKWISE:3, COMMAND_SPINDLE_COUNTERCLOCKWISE:4, COMMAND_STOP_SPINDLE:5, COMMAND_COOLANT_ON:8, COMMAND_COOLANT_OFF:9 }; function onCommand(command) { switch (command) { case COMMAND_START_SPINDLE: onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); return; case COMMAND_LOCK_MULTI_AXIS: return; case COMMAND_UNLOCK_MULTI_AXIS: return; case COMMAND_BREAK_CONTROL: return; case COMMAND_TOOL_MEASURE: return; } var stringId = getCommandStringId(command); var mcode = mapCommand[stringId]; if (mcode != undefined) { writeBlock(mFormat.format(mcode)); } else { onUnsupportedCommand(command); } } function onSectionEnd() { // noop } function onClose() { writeBlock(gFormat.format(0), "X0Y0"); }