/** Copyright (C) 2012-2021 by Autodesk, Inc. All rights reserved. Pocket NC post processor configuration. $Revision$ $Date$ FORKID {B9F40C50-EBFF-4547-90FF-CAF4C01C8E30} */ description = "Pocket NC"; vendor = "Pocket NC"; vendorUrl = "http://www.pocketnc.com/"; legal = "Copyright (C) 2012-2021 by Autodesk, Inc."; certificationLevel = 2; minimumRevision = 45702; longDescription = "Generic post for Pocket NC. Note that the XYZ axes of the WCS in the Setup should be set such that the axes all align with the machine XYZ axes in the A0/B0 machine orientation (this means that the WCS Z-axis would be horizontal like on a lathe). By default the post will make the tool go to the home position between operations if the A-axis changes by more than the limit specified by the 'maximumAAxisChange' property (20 degrees by default). You can turn off homing between operations if desired by turning off the 'goHomeBetweenOperations' property but you would have to make sure the tool doesn't collide with the part during AB reorientation."; extension = "ngc"; setCodePage("ascii"); capabilities = CAPABILITY_MILLING; tolerance = spatial(0.002, MM); minimumChordLength = spatial(0.25, MM); minimumCircularRadius = spatial(0.01, MM); maximumCircularRadius = spatial(1000, MM); minimumCircularSweep = toRad(0.01); maximumCircularSweep = toRad(180); allowHelicalMoves = true; allowedCircularPlanes = undefined; // allow any circular motion highFeedrate = (unit == IN) ? 5000 : 10000; // user-defined properties properties = { writeMachine: { title: "Write machine", description: "Output the machine settings in the header of the code.", group: 1, type: "boolean", value: true, scope: "post" }, writeTools: { title: "Write tool list", description: "Output a tool list in the header of the code.", group: 1, type: "boolean", value: true, scope: "post" }, showSequenceNumbers: { title: "Use sequence numbers", description: "Use sequence numbers for each block of outputted code.", group: 2, type: "boolean", value: true, scope: "post" }, sequenceNumberStart: { title: "Start sequence number", description: "The number at which to start the sequence numbers.", group: 2, type: "integer", value: 10, scope: "post" }, sequenceNumberIncrement: { title: "Sequence number increment", description: "The amount by which the sequence number is incremented by in each block.", group: 2, type: "integer", value: 5, scope: "post" }, optionalStop: { title: "Optional stop", description: "Outputs optional stop code during when necessary in the code.", type: "boolean", value: true, scope: "post" }, separateWordsWithSpace: { title: "Separate words with space", description: "Adds spaces between words if 'yes' is selected.", type: "boolean", value: true, scope: "post" }, useRadius: { title: "Radius arcs", description: "If yes is selected, arcs are outputted using radius values rather than IJK.", type: "boolean", value: false, scope: "post" }, useParametricFeed: { title: "Parametric feed", description: "Specifies the feed value that should be output using a Q value.", type: "boolean", value: false, scope: "post" }, showNotes: { title: "Show notes", description: "Writes operation notes as comments in the outputted code.", type: "boolean", value: false, scope: "post" }, smoothingTolerance: { title: "Smoothing tolerance", description: "Smoothing tolerance (-1 for disabled).", type: "number", value: -1, scope: "post" }, goHomeBetweenOperations: { title: "Go home between operations", description: "Make sure the machine goes home between operations if the A axis changes more than the Maximum A axis change property. If disabled, make sure that the part wont collide with the tool.", type: "boolean", value: true, scope: "post" }, maximumAAxisChange: { title: "Maximum A axis change", description: "The maximum A axis change that is allowed before the machine will be forced home.", type: "number", value: 20, scope: "post" }, safeRetractDistance: { title: "Safe retract distance", description: "A set distance to add to the tool length for rewind C-axis tool retract.", type: "number", value: 0, scope: "post" }, useTCPMode: { title: "Use TCPC mode", description: "Enable if the control supports TCPC mode.", type: "boolean", value: true, scope: "post" }, useInverseTime: { title: "Use inverse time feedrates", description: "Enable to use inverse time feedrates for multi-axis moves.", type: "boolean", value: true, scope: "post" }, useG0: { title: "Use G0", description: "Specifies that G0 rapid moves should be output. Highfeed G1s will be output when disabled.", type: "boolean", value: false, scope: "post" }, machineModel: { title: "Machine model", description: "Select the machine model. V1 and V2-10 have a maximum RPM of 10,000 RPM, V2-50 has a maximum RPM of 50,000 RPM", type: "enum", values: [ {title: "V1", id: "V1"}, {title: "V2-10", id: "V2"}, {title: "V2-50", id: "V2-50"}, {title:"Solo", id:"solo"} ], group: 0, value: "solo", scope: "post" }, safePositionMethod: { title: "Safe Retracts", description: "Select your desired retract option. 'Clearance Height' retracts to the operation clearance height.", type: "enum", values: [ {title: "G28", id: "G28"}, {title: "G53", id: "G53"}, {title: "Clearance Height", id: "clearanceHeight"} ], value: "G53", scope: "post" }, rotatedWorkOffsetsWCS: { title: "Rotated Work Offsets WCS", description: "Which work coordinate system to use when rotated work offsets are used. This must be different than any active work coordinate systems to avoid overwriting them.", type: "enum", values: [ {title: "Disabled", id: "disabled"}, {title: "1 (G54)", id: "1"}, {title: "2 (G55)", id: "2"}, {title: "3 (G56)", id: "3"}, {title: "4 (G57)", id: "4"}, {title: "5 (G58)", id: "5"}, {title: "6 (G59)", id: "6"}, {title: "7 (G59.1)", id: "7"}, {title: "8 (G59.2)", id: "8"}, {title: "9 (G59.3)", id: "9"} ], value: "9", scope: "post" } }; var permittedCommentChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-"; var singleLineCoolant = false; // specifies to output multiple coolant codes in one line rather than in separate lines // samples: // {id: COOLANT_THROUGH_TOOL, on: 88, off: 89} // {id: COOLANT_THROUGH_TOOL, on: [8, 88], off: [9, 89]} var coolants = [ {id: COOLANT_FLOOD, on: 8}, {id: COOLANT_MIST, on: 7}, {id: COOLANT_THROUGH_TOOL}, {id: COOLANT_AIR}, {id: COOLANT_AIR_THROUGH_TOOL}, {id: COOLANT_SUCTION}, {id: COOLANT_FLOOD_MIST}, {id: COOLANT_FLOOD_THROUGH_TOOL}, {id: COOLANT_OFF, off: 9} ]; var gFormat = createFormat({prefix:"G", decimals:1}); var mFormat = createFormat({prefix:"M", decimals:1}); var hFormat = createFormat({prefix:"H", decimals:1}); var dFormat = createFormat({prefix:"D", decimals:1}); var xyzFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var rFormat = xyzFormat; // radius var abcFormat = createFormat({decimals:3, forceDecimal:true, scale:DEG}); var feedFormat = createFormat({decimals:(unit == MM ? 2 : 3), forceDecimal:true}); var pitchFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var toolFormat = createFormat({decimals:0}); var rpmFormat = createFormat({decimals:0}); var secFormat = createFormat({decimals:3, forceDecimal:true}); // seconds - range 0.001-99999.999 var taperFormat = createFormat({decimals:1, scale:DEG}); var xOutput = createVariable({prefix:"X"}, xyzFormat); var yOutput = createVariable({prefix:"Y"}, xyzFormat); var zOutput = createVariable({onchange:function () {retracted = false;}, prefix:"Z"}, xyzFormat); var aOutput = createVariable({prefix:"A"}, abcFormat); var bOutput = createVariable({prefix:"B"}, abcFormat); var cOutput = createVariable({prefix:"C"}, abcFormat); var feedOutput = createVariable({prefix:"F"}, feedFormat); var sOutput = createVariable({prefix:"S", force:true}, rpmFormat); var dOutput = createVariable({}, dFormat); // circular output var iOutput = createReferenceVariable({prefix:"I", force:true}, xyzFormat); var jOutput = createReferenceVariable({prefix:"J", force:true}, xyzFormat); var kOutput = createReferenceVariable({prefix:"K", force:true}, 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 gCycleModal = createModal({}, gFormat); // modal group 9 // G81, ... var gRetractModal = createModal({}, gFormat); // modal group 10 // G98-99 var mTCPModal = createModal({}, mFormat); // M428, M429 // fixed settings var firstFeedParameter = 100; var safeRetractFeed = (unit == IN) ? 20 : 500; var safePlungeFeed = (unit == IN) ? 10 : 250; var WARNING_WORK_OFFSET = 0; // collected state var sequenceNumber; var currentWorkOffset; var forceSpindleSpeed = false; var activeMovements; // do not use by default var currentFeedId; var retracted = false; // specifies that the tool has been retracted to the safe plane var spindleMin; var spindleMax; function onPassThrough(text) { var commands = String(text).split(","); for (text in commands) { writeBlock(commands[text]); } } /** Writes the specified block. */ function writeBlock() { if (!formatWords(arguments)) { return; } if (getProperty("showSequenceNumbers")) { writeWords2("N" + sequenceNumber, arguments); sequenceNumber += getProperty("sequenceNumberIncrement"); if (sequenceNumber > 99999) { sequenceNumber = getProperty("sequenceNumberStart"); } } else { writeWords(arguments); } } /** Writes the specified optional block. */ function writeOptionalBlock() { if (getProperty("showSequenceNumbers")) { var words = formatWords(arguments); if (words) { writeWords("/", "N" + sequenceNumber, words); sequenceNumber += getProperty("sequenceNumberIncrement"); if (sequenceNumber > 99999) { sequenceNumber = getProperty("sequenceNumberStart"); } } } else { writeWords2("/", arguments); } } function formatComment(text) { return "(" + filterText(String(text).toUpperCase(), permittedCommentChars) + ")"; } /** Output a comment. */ function writeComment(text) { writeln(formatComment(text)); } function onOpen() { if (getProperty("useRadius")) { maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC } mTCPModal.format(429); // default to TCP off switch (getProperty("machineModel")) { case "V1": spindleMin = 65; spindleMax = 10000; break; case "V2": spindleMin = 2000; spindleMax = 10000; break; case "V2-50": spindleMin = 2000; spindleMax = 50000; break; case "solo": spindleMin = 2000; spindleMax = 30000; break; default: error(localize("Machine type must be set to 'V1', 'V2', 'V2-50'")); return; } if (getProperty("machineModel") == "solo") { var bAxis = createAxis({coordinate:1, table:true, axis:[0, -1, 0], range:[-45, 135], preference:0}); var cAxis = createAxis({coordinate:2, table:true, axis:[0, 0, -1], range:[-99999, 99999], preference:0, cyclic:false}); machineConfiguration = new MachineConfiguration(bAxis, cAxis); machineConfiguration.setHomePositionX(toPreciseUnit(-0.3736, IN)); machineConfiguration.setHomePositionY(toPreciseUnit(3.5989, IN)); } else { // A Axis Limits var aMin = (getProperty("machineModel") == "V1") ? -5 - 0.0001 : -25 - 0.0001; var aMax = (getProperty("machineModel") == "V1") ? 95 + 0.0001 : 135 + 0.0001; var aAxis = createAxis({coordinate:0, table:true, axis:[-1, 0, 0], range:[aMin, aMax], preference:0}); var bAxis = createAxis({coordinate:1, table:true, axis:[0, -1, 0], range:[-9999, 9999], preference:0, cyclic:false}); machineConfiguration = new MachineConfiguration(aAxis, bAxis); machineConfiguration.setHomePositionX(toPreciseUnit(2.5, IN)); machineConfiguration.setHomePositionY(toPreciseUnit(2.5, IN)); } setMachineConfiguration(machineConfiguration); optimizeMachineAngles2(getProperty("useTCPMode") ? 0 : 1); // TCP mode if (!machineConfiguration.isMachineCoordinate(0)) { aOutput.disable(); } if (!machineConfiguration.isMachineCoordinate(1)) { bOutput.disable(); } if (!machineConfiguration.isMachineCoordinate(2)) { cOutput.disable(); } if (!getProperty("separateWordsWithSpace")) { setWordSeparator(""); } sequenceNumber = getProperty("sequenceNumberStart"); writeln("%"); writeln("(AXIS,stop)"); // disable LinuxCNC visualization if (programName) { writeComment(programName); } if (programComment) { writeComment(programComment); } // dump machine configuration var vendor = machineConfiguration.getVendor(); var model = machineConfiguration.getModel(); var description = machineConfiguration.getDescription(); if (getProperty("writeMachine") && (vendor || model || description)) { writeComment(localize("Machine")); if (vendor) { writeComment(" " + localize("vendor") + ": " + vendor); } if (model) { writeComment(" " + localize("model") + ": " + model); } if (description) { writeComment(" " + localize("description") + ": " + description); } } switch (unit) { case IN: writeBlock(gUnitModal.format(20)); break; case MM: writeBlock(gUnitModal.format(21)); break; } // dump tool information if (getProperty("writeTools")) { var zRanges = {}; if (is3D()) { var numberOfSections = getNumberOfSections(); for (var i = 0; i < numberOfSections; ++i) { var section = getSection(i); var zRange = section.getGlobalZRange(); var tool = section.getTool(); if (zRanges[tool.number]) { zRanges[tool.number].expandToRange(zRange); } else { zRanges[tool.number] = zRange; } } } /* var tools = getToolTable(); if (tools.getNumberOfTools() > 0) { for (var i = 0; i < tools.getNumberOfTools(); ++i) { var tool = tools.getTool(i); var comment = "T" + toolFormat.format(tool.number) + " " + "D=" + xyzFormat.format(tool.diameter) + " " + localize("CR") + "=" + xyzFormat.format(tool.cornerRadius); if ((tool.taperAngle > 0) && (tool.taperAngle < Math.PI)) { comment += " " + localize("TAPER") + "=" + taperFormat.format(tool.taperAngle) + localize("deg"); } if (zRanges[tool.number]) { comment += " - " + localize("ZMIN") + "=" + xyzFormat.format(zRanges[tool.number].getMinimum()); } comment += " - " + getToolTypeName(tool.type); writeComment(comment); } } */ } if (false) { // check for duplicate tool number for (var i = 0; i < getNumberOfSections(); ++i) { var sectioni = getSection(i); var tooli = sectioni.getTool(); for (var j = i + 1; j < getNumberOfSections(); ++j) { var sectionj = getSection(j); var toolj = sectionj.getTool(); if (tooli.number == toolj.number) { if (xyzFormat.areDifferent(tooli.diameter, toolj.diameter) || xyzFormat.areDifferent(tooli.cornerRadius, toolj.cornerRadius) || abcFormat.areDifferent(tooli.taperAngle, toolj.taperAngle) || (tooli.numberOfFlutes != toolj.numberOfFlutes)) { error( subst( localize("Using the same tool number for different cutter geometry for operation '%1' and '%2'."), sectioni.hasParameter("operation-comment") ? sectioni.getParameter("operation-comment") : ("#" + (i + 1)), sectionj.hasParameter("operation-comment") ? sectionj.getParameter("operation-comment") : ("#" + (j + 1)) ) ); return; } } } } } if ((getNumberOfSections() > 0) && (getSection(0).workOffset == 0)) { for (var i = 0; i < getNumberOfSections(); ++i) { if (getSection(i).workOffset > 0) { error(localize("Using multiple work offsets is not possible if the initial work offset is 0.")); return; } } } // absolute coordinates, feed per min, and incremental arc center mode writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gFormat.format(40), gPlaneModal.format(17), gFormat.format(91.1)); } function onComment(message) { writeComment(message); } /** Force output of X, Y, and Z. */ function forceXYZ() { xOutput.reset(); yOutput.reset(); zOutput.reset(); } /** Force output of A, B, and C. */ function forceABC() { aOutput.reset(); bOutput.reset(); cOutput.reset(); } function forceFeed() { currentFeedId = undefined; previousDPMFeed = 0; feedOutput.reset(); } /** Force output of X, Y, Z, A, B, C, and F on next output. */ function forceAny() { forceXYZ(); forceABC(); forceFeed(); } var lengthCompensationActive = false; function enableLengthCompensation(lengthOffset, force) { if (!lengthCompensationActive || force) { writeBlock(gFormat.format(43), hFormat.format(lengthOffset)); lengthCompensationActive = true; } } function disableLengthCompensation(force) { if (lengthCompensationActive || force) { writeBlock(gFormat.format(49)); lengthCompensationActive = false; } } function setTCPMode(mode) { if (getProperty("useTCPMode")) { if (mode) { if (currentSection.isMultiAxis()) { validate(lengthCompensationActive, "Length compensation must be active when enabling TCP."); writeBlock(mTCPModal.format(428)); } } else { writeBlock(mTCPModal.format(429)); } } } function FeedContext(id, description, feed) { this.id = id; this.description = description; this.feed = feed; } function getFeed(f) { if (activeMovements) { var feedContext = activeMovements[movement]; if (feedContext != undefined) { if (!feedFormat.areDifferent(feedContext.feed, f)) { if (feedContext.id == currentFeedId) { return ""; // nothing has changed } forceFeed(); currentFeedId = feedContext.id; return "F#" + (firstFeedParameter + feedContext.id); } } currentFeedId = undefined; // force Q feed next time } return feedOutput.format(f); // use feed value } function initializeActiveFeeds() { activeMovements = new Array(); var movements = currentSection.getMovements(); var id = 0; var activeFeeds = new Array(); if (hasParameter("operation:tool_feedCutting")) { if (movements & ((1 << MOVEMENT_CUTTING) | (1 << MOVEMENT_LINK_TRANSITION) | (1 << MOVEMENT_EXTENDED))) { var feedContext = new FeedContext(id, localize("Cutting"), getParameter("operation:tool_feedCutting")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_CUTTING] = feedContext; activeMovements[MOVEMENT_LINK_TRANSITION] = feedContext; activeMovements[MOVEMENT_EXTENDED] = feedContext; } ++id; if (movements & (1 << MOVEMENT_PREDRILL)) { feedContext = new FeedContext(id, localize("Predrilling"), getParameter("operation:tool_feedCutting")); activeMovements[MOVEMENT_PREDRILL] = feedContext; activeFeeds.push(feedContext); } ++id; } if (hasParameter("operation:finishFeedrate")) { if (movements & (1 << MOVEMENT_FINISH_CUTTING)) { var feedContext = new FeedContext(id, localize("Finish"), getParameter("operation:finishFeedrate")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_FINISH_CUTTING] = feedContext; } ++id; } else if (hasParameter("operation:tool_feedCutting")) { if (movements & (1 << MOVEMENT_FINISH_CUTTING)) { var feedContext = new FeedContext(id, localize("Finish"), getParameter("operation:tool_feedCutting")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_FINISH_CUTTING] = feedContext; } ++id; } if (hasParameter("operation:tool_feedEntry")) { if (movements & (1 << MOVEMENT_LEAD_IN)) { var feedContext = new FeedContext(id, localize("Entry"), getParameter("operation:tool_feedEntry")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_LEAD_IN] = feedContext; } ++id; } if (hasParameter("operation:tool_feedExit")) { if (movements & (1 << MOVEMENT_LEAD_OUT)) { var feedContext = new FeedContext(id, localize("Exit"), getParameter("operation:tool_feedExit")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_LEAD_OUT] = feedContext; } ++id; } if (hasParameter("operation:noEngagementFeedrate")) { if (movements & (1 << MOVEMENT_LINK_DIRECT)) { var feedContext = new FeedContext(id, localize("Direct"), getParameter("operation:noEngagementFeedrate")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_LINK_DIRECT] = feedContext; } ++id; } else if (hasParameter("operation:tool_feedCutting") && hasParameter("operation:tool_feedEntry") && hasParameter("operation:tool_feedExit")) { if (movements & (1 << MOVEMENT_LINK_DIRECT)) { var feedContext = new FeedContext(id, localize("Direct"), Math.max(getParameter("operation:tool_feedCutting"), getParameter("operation:tool_feedEntry"), getParameter("operation:tool_feedExit"))); activeFeeds.push(feedContext); activeMovements[MOVEMENT_LINK_DIRECT] = feedContext; } ++id; } if (hasParameter("operation:reducedFeedrate")) { if (movements & (1 << MOVEMENT_REDUCED)) { var feedContext = new FeedContext(id, localize("Reduced"), getParameter("operation:reducedFeedrate")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_REDUCED] = feedContext; } ++id; } if (hasParameter("operation:tool_feedRamp")) { if (movements & ((1 << MOVEMENT_RAMP) | (1 << MOVEMENT_RAMP_HELIX) | (1 << MOVEMENT_RAMP_PROFILE) | (1 << MOVEMENT_RAMP_ZIG_ZAG))) { var feedContext = new FeedContext(id, localize("Ramping"), getParameter("operation:tool_feedRamp")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_RAMP] = feedContext; activeMovements[MOVEMENT_RAMP_HELIX] = feedContext; activeMovements[MOVEMENT_RAMP_PROFILE] = feedContext; activeMovements[MOVEMENT_RAMP_ZIG_ZAG] = feedContext; } ++id; } if (hasParameter("operation:tool_feedPlunge")) { if (movements & (1 << MOVEMENT_PLUNGE)) { var feedContext = new FeedContext(id, localize("Plunge"), getParameter("operation:tool_feedPlunge")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_PLUNGE] = feedContext; } ++id; } if (true) { // high feed if ((movements & (1 << MOVEMENT_HIGH_FEED)) || (highFeedMapping != HIGH_FEED_NO_MAPPING)) { var feed; if (hasParameter("operation:highFeedrateMode") && getParameter("operation:highFeedrateMode") != "disabled") { feed = getParameter("operation:highFeedrate"); } else { feed = this.highFeedrate; } var feedContext = new FeedContext(id, localize("High Feed"), feed); activeFeeds.push(feedContext); activeMovements[MOVEMENT_HIGH_FEED] = feedContext; activeMovements[MOVEMENT_RAPID] = feedContext; } ++id; } for (var i = 0; i < activeFeeds.length; ++i) { var feedContext = activeFeeds[i]; writeBlock("#" + (firstFeedParameter + feedContext.id) + "=" + feedFormat.format(feedContext.feed), formatComment(feedContext.description)); } } var currentWorkPlaneABC = undefined; var activeM254 = false; function forceWorkPlane() { currentWorkPlaneABC = undefined; } function setWorkPlane(abc) { if (!machineConfiguration.isMultiAxisConfiguration()) { return; // ignore } if (!((currentWorkPlaneABC == undefined) || abcFormat.areDifferent(abc.x, currentWorkPlaneABC.x) || abcFormat.areDifferent(abc.y, currentWorkPlaneABC.y) || abcFormat.areDifferent(abc.z, currentWorkPlaneABC.z) || (getProperty("rotatedWorkOffsetsWCS") != "disabled" && (abc.isNonZero() || activeM254)) )) { return; // no change } onCommand(COMMAND_UNLOCK_MULTI_AXIS); if (activeM254) { activeM254 = false; if (currentWorkOffset != undefined) { writeBlock(getWCSCode(currentWorkOffset)); } } if (getProperty("goHomeBetweenOperations") && (getCurrentSectionId() >= 0)) { // only if we are between operations var aDelta = Math.abs(abc.x - (currentWorkPlaneABC ? currentWorkPlaneABC.x : 0)); if (aDelta > toRad(getProperty("maximumAAxisChange"))) { setTCPMode(false); writeRetract(X, Y); } } writeBlock( gMotionModal.format(0), conditional(machineConfiguration.isMachineCoordinate(0), "A" + abcFormat.format(abc.x)), conditional(machineConfiguration.isMachineCoordinate(1), "B" + abcFormat.format(abc.y)), conditional(machineConfiguration.isMachineCoordinate(2), "C" + abcFormat.format(abc.z)) ); if (abc.isNonZero()) { if (getProperty("rotatedWorkOffsetsWCS") != "disabled") { writeBlock(mFormat.format(254), "P" + getProperty("rotatedWorkOffsetsWCS")); var dwcs = parseInt(getProperty("rotatedWorkOffsetsWCS"), 10); writeBlock(getWCSCode(dwcs)); activeM254 = true; } } else if (activeM254 && (currentWorkOffset != undefined)) { writeBlock(getWCSCode(currentWorkOffset)); // G54->G59 activeM254 = false; } onCommand(COMMAND_LOCK_MULTI_AXIS); currentWorkPlaneABC = abc; } var closestABC = true; // choose closest machine angles var currentMachineABC = new Vector(0, 0, 0); function getPreferenceWeight(_abc) { var axis = new Array(machineConfiguration.getAxisU(), machineConfiguration.getAxisV(), machineConfiguration.getAxisW()); var abc = new Array(_abc.x, _abc.y, _abc.z); var preference = 0; for (var i = 0; i < 3; ++i) { if (axis[i].isEnabled()) { preference += ((abcFormat.getResultingValue(abc[axis[i].getCoordinate()]) * axis[i].getPreference()) < 0) ? -1 : 1; } } return preference; } function remapToABC(currentABC, previousABC) { var both = machineConfiguration.getABCByDirectionBoth(machineConfiguration.getDirection(currentABC)); var abc1 = machineConfiguration.remapToABC(both[0], previousABC); abc1 = machineConfiguration.remapABC(abc1); var abc2 = machineConfiguration.remapToABC(both[1], previousABC); abc2 = machineConfiguration.remapABC(abc2); // choose angles based on preference var preference1 = getPreferenceWeight(abc1); var preference2 = getPreferenceWeight(abc2); if (preference1 > preference2) { return abc1; } else if (preference2 > preference1) { return abc2; } // choose angles based on closest solution if (Vector.diff(abc1, previousABC).length < Vector.diff(abc2, previousABC).length) { return abc1; } else { return abc2; } } function getWorkPlaneMachineABC(workPlane) { var W = workPlane; // map to global frame var abc = machineConfiguration.getABC(W); if (closestABC) { if (currentMachineABC) { abc = remapToABC(abc, currentMachineABC); } else { abc = machineConfiguration.getPreferredABC(abc); } } else { abc = machineConfiguration.getPreferredABC(abc); } try { abc = machineConfiguration.remapABC(abc); currentMachineABC = abc; } catch (e) { error( localize("Machine angles not supported") + ":" + conditional(machineConfiguration.isMachineCoordinate(0), " A" + abcFormat.format(abc.x)) + conditional(machineConfiguration.isMachineCoordinate(1), " B" + abcFormat.format(abc.y)) + conditional(machineConfiguration.isMachineCoordinate(2), " C" + abcFormat.format(abc.z)) ); } var direction = machineConfiguration.getDirection(abc); if (!isSameDirection(direction, W.forward)) { error(localize("Orientation not supported.")); } if (!machineConfiguration.isABCSupported(abc)) { error( localize("Work plane is not supported") + ":" + conditional(machineConfiguration.isMachineCoordinate(0), " A" + abcFormat.format(abc.x)) + conditional(machineConfiguration.isMachineCoordinate(1), " B" + abcFormat.format(abc.y)) + conditional(machineConfiguration.isMachineCoordinate(2), " C" + abcFormat.format(abc.z)) ); } var tcp = false; // getProperty("useTCPMode"); cancelTransformation(); if (tcp) { setRotation(W); // TCP mode } else { var O = machineConfiguration.getOrientation(abc); var R = machineConfiguration.getRemainingOrientation(abc, W); var rotate = true; var axis = machineConfiguration.getAxisV(); if (axis.isEnabled() && axis.isTable()) { var ix = axis.getCoordinate(); var rotAxis = axis.getAxis(); if (isSameDirection(machineConfiguration.getDirection(abc), rotAxis) || isSameDirection(machineConfiguration.getDirection(abc), Vector.product(rotAxis, -1))) { var direction = isSameDirection(machineConfiguration.getDirection(abc), rotAxis) ? 1 : -1; abc.setCoordinate(ix, Math.atan2(R.right.y, R.right.x) * direction); rotate = false; } } if (rotate) { setRotation(R); } } return abc; } function isProbeOperation() { return hasParameter("operation-strategy") && (getParameter("operation-strategy") == "probe"); } function getWCSCode(workOffset) { var wcsCode = ""; if (workOffset > 6) { var p = workOffset - 6; // 1->... if (p > 3) { error(localize("Work offset out of range.")); return wcsCode; } else { gMotionModal.reset(); wcsCode = formatWords(gFormat.format(59 + (p / 10)), gMotionModal.format(0)); // G59.1->G59.3 } } else { gMotionModal.reset(); wcsCode = formatWords(gFormat.format(53 + workOffset), gMotionModal.format(0)); // G54->G59 } return wcsCode; } // positions in machine coordinates XY then Z for multi-axis operations when TCP is active function prePositionXYZ(initialPosition, retractInZFirst) { if (getProperty("useTCPMode") && currentSection.isMultiAxis()) { if (retractInZFirst) { writeBlock( "G" + 6.2, "X" + xyzFormat.format(initialPosition.x), "Y" + xyzFormat.format(initialPosition.y), "Z" + xyzFormat.format(initialPosition.z), "I0", "J0", "K1", "P" + (getProperty("useG0") ? 0 : 1), getProperty("useG0") ? "" : getFeed(highFeedrate) ); } writeBlock( "G" + 6.2, "X" + xyzFormat.format(initialPosition.x), "Y" + xyzFormat.format(initialPosition.y), "Z" + xyzFormat.format(initialPosition.z), "I1", "J1", "K0", "P" + (getProperty("useG0") ? 0 : 1), getProperty("useG0") ? "" : getFeed(highFeedrate) ); if (!retractInZFirst) { writeBlock( "G" + 6.2, "X" + xyzFormat.format(initialPosition.x), "Y" + xyzFormat.format(initialPosition.y), "Z" + xyzFormat.format(initialPosition.z), "I0", "J0", "K1", "P" + (getProperty("useG0") ? 0 : 1), getProperty("useG0") ? "" : getFeed(highFeedrate) ); } return true; } return false; } function onSection() { var insertToolCall = isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number); retracted = false; var newWorkOffset = isFirstSection() || (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes var newWorkPlane = isFirstSection() || !isSameDirection(getPreviousSection().getGlobalFinalToolAxis(), currentSection.getGlobalInitialToolAxis()) || (currentSection.isOptimizedForMachine() && getPreviousSection().isOptimizedForMachine() && Vector.diff(getPreviousSection().getFinalToolAxisABC(), currentSection.getInitialToolAxisABC()).length > 1e-4) || (!machineConfiguration.isMultiAxisConfiguration() && currentSection.isMultiAxis()) || (!getPreviousSection().isMultiAxis() && currentSection.isMultiAxis() || getPreviousSection().isMultiAxis() && !currentSection.isMultiAxis()); // force newWorkPlane between indexing and simultaneous operations if (insertToolCall || newWorkOffset || newWorkPlane) { // stop spindle before retract during tool change if (insertToolCall && !isFirstSection()) { onCommand(COMMAND_STOP_SPINDLE); } // retract to safe plane setTCPMode(false); writeRetract(Z); } if (hasParameter("operation-comment")) { var comment = getParameter("operation-comment"); if (comment) { writeComment(comment); } } if (getProperty("showNotes") && hasParameter("notes")) { var notes = getParameter("notes"); if (notes) { var lines = String(notes).split("\n"); var r1 = new RegExp("^[\\s]+", "g"); var r2 = new RegExp("[\\s]+$", "g"); for (line in lines) { var comment = lines[line].replace(r1, "").replace(r2, ""); if (comment) { writeComment(comment); } } } } if (insertToolCall) { forceWorkPlane(); setCoolant(COOLANT_OFF); if (!isFirstSection() && getProperty("optionalStop")) { onCommand(COMMAND_OPTIONAL_STOP); } if (tool.number > 99) { warning(localize("Tool number exceeds maximum value.")); } setTCPMode(false); disableLengthCompensation(false); onCommand(COMMAND_STOP_SPINDLE); var homeX; if (machineConfiguration.hasHomePositionX()) { homeX = "X" + xyzFormat.format(machineConfiguration.getHomePositionX()); } var homeY; if (machineConfiguration.hasHomePositionY()) { homeY = "Y" + xyzFormat.format(machineConfiguration.getHomePositionY()); } gMotionModal.reset(); writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), homeX, homeY); forceXYZ(); writeBlock(mFormat.format(0)); // force stop for manual tool change writeBlock("T" + toolFormat.format(tool.number), mFormat.format(6)); if (tool.comment) { writeComment(tool.comment); } var showToolZMin = false; if (showToolZMin) { if (is3D()) { var numberOfSections = getNumberOfSections(); var zRange = currentSection.getGlobalZRange(); var number = tool.number; for (var i = currentSection.getId() + 1; i < numberOfSections; ++i) { var section = getSection(i); if (section.getTool().number != number) { break; } zRange.expandToRange(section.getGlobalZRange()); } writeComment(localize("ZMIN") + "=" + zRange.getMinimum()); } } } if (insertToolCall || forceSpindleSpeed || isFirstSection() || (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) || (tool.clockwise != getPreviousSection().getTool().clockwise)) { forceSpindleSpeed = false; if (spindleSpeed < spindleMin) { warning(subst(localize("Spindle speed is less than minimum value of %1."), spindleMin)); return; } if (spindleSpeed > spindleMax) { warning(subst(localize("Spindle speed exceeds maximum value of %1."), spindleMax)); } writeBlock( sOutput.format(spindleSpeed), mFormat.format(tool.clockwise ? 3 : 4) ); } // wcs if (insertToolCall) { // force work offset when changing tool currentWorkOffset = undefined; } var workOffset = currentSection.workOffset; currentWorkOffset = undefined; // force work offset if (workOffset == 0) { warningOnce(localize("Work offset has not been specified. Using G54 as WCS."), WARNING_WORK_OFFSET); workOffset = 1; } if (workOffset > 0 && workOffset != currentWorkOffset) { if (getProperty("rotatedWorkOffsetsWCS") != "disabled" && (workOffset == parseInt(getProperty("rotatedWorkOffsetsWCS"), 10))) { error(localize("You cannot use the same WCS as the 'Rotated Work Offsets WCS.'")); return; } writeBlock(getWCSCode(workOffset)); currentWorkOffset = workOffset; activeM254 = false; } forceXYZ(); if (machineConfiguration.isMultiAxisConfiguration()) { // use 5-axis indexing for multi-axis mode // set working plane after datum shift if (currentSection.isMultiAxis()) { forceWorkPlane(); cancelTransformation(); onCommand(COMMAND_UNLOCK_MULTI_AXIS); var abc = currentSection.getInitialToolAxisABC(); gMotionModal.reset(); writeBlock( gMotionModal.format(0), conditional(machineConfiguration.isMachineCoordinate(0), "A" + abcFormat.format(abc.x)), conditional(machineConfiguration.isMachineCoordinate(1), "B" + abcFormat.format(abc.y)), conditional(machineConfiguration.isMachineCoordinate(2), "C" + abcFormat.format(abc.z)) ); } else { var abc = getWorkPlaneMachineABC(currentSection.workPlane); setWorkPlane(abc); } } else { // 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 setCoolant(tool.coolant); forceAny(); gMotionModal.reset(); var lengthOffset = tool.lengthOffset; if (lengthOffset > 99) { error(localize("Length offset out of range.")); return; } enableLengthCompensation(lengthOffset, false); // tool length compensation needs to be enabled prior to enabling TCP var initialPosition = getFramePosition(currentSection.getInitialPosition()); var retractInZFirst = !retracted && !insertToolCall && (getCurrentPosition().z < initialPosition.z); var isPrepositioned = prePositionXYZ(initialPosition, retractInZFirst); if (!isPrepositioned && retractInZFirst) { writeBlock(gMotionModal.format(getProperty("useG0") ? 0 : 1), zOutput.format(initialPosition.z), getProperty("useG0") ? "" : getFeed(highFeedrate)); } if (insertToolCall || retracted || (!isFirstSection() && getPreviousSection().isMultiAxis())) { gMotionModal.reset(); writeBlock(gPlaneModal.format(17)); if (!isPrepositioned) { if (!machineConfiguration.isHeadConfiguration()) { // && !getProperty("useTCPMode")) { writeBlock( gAbsIncModal.format(90), gMotionModal.format(getProperty("useG0") ? 0 : 1), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), getProperty("useG0") ? "" : getFeed(highFeedrate) ); writeBlock(gMotionModal.format(getProperty("useG0") ? 0 : 1), zOutput.format(initialPosition.z)); } else { enableLengthCompensation(lengthOffset, false); // tool length compensation needs to be enabled prior to enabling TCP writeBlock( gAbsIncModal.format(90), gMotionModal.format(getProperty("useG0") ? 0 : 1), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), zOutput.format(initialPosition.z), getProperty("useG0") ? "" : getFeed(highFeedrate) ); } } gMotionModal.reset(); } else { if (!isPrepositioned) { writeBlock( gAbsIncModal.format(90), gMotionModal.format(getProperty("useG0") ? 0 : 1), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), getProperty("useG0") ? "" : getFeed(highFeedrate) ); } } if (currentSection.isMultiAxis() && getProperty("useTCPMode")) { setTCPMode(true); } if (getProperty("useParametricFeed") && hasParameter("operation-strategy") && (getParameter("operation-strategy") != "drill") && // legacy !(currentSection.hasAnyCycle && currentSection.hasAnyCycle())) { if (!insertToolCall && activeMovements && (getCurrentSectionId() > 0) && ((getPreviousSection().getPatternId() == currentSection.getPatternId()) && (currentSection.getPatternId() != 0))) { // use the current feeds } else { initializeActiveFeeds(); } } else { activeMovements = undefined; } if (getProperty("smoothingTolerance") > 0) { if (hasParameter("operation-strategy") && (getParameter("operation-strategy") != "drill")) { writeBlock(gFormat.format(64), "P" + xyzFormat.format(getProperty("smoothingTolerance"))); } } } function onDwell(seconds) { if (seconds > 99999.999) { warning(localize("Dwelling time is out of range.")); } writeBlock(gFeedModeModal.format(94), gFormat.format(4), "P" + secFormat.format(seconds)); } function onSpindleSpeed(spindleSpeed) { writeBlock(sOutput.format(spindleSpeed)); } function onCycle() { writeBlock(gPlaneModal.format(17)); } function getCommonCycle(x, y, z, r) { forceXYZ(); // force xyz on first drill hole of any cycle return [xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + xyzFormat.format(r)]; } function onCyclePoint(x, y, z) { if (!isSameDirection(getRotation().forward, new Vector(0, 0, 1))) { expandCyclePoint(x, y, z); return; } switch (cycleType) { case "tapping": case "left-tapping": case "right-tapping": cycleExpanded = true; repositionToCycleClearance(cycle, x, y, z); writeBlock( gAbsIncModal.format(90), gMotionModal.format(getProperty("useG0") ? 0 : 1), conditional(gPlaneModal.getCurrent() == 17, zOutput.format(cycle.retract)), conditional(gPlaneModal.getCurrent() == 18, yOutput.format(cycle.retract)), conditional(gPlaneModal.getCurrent() == 19, xOutput.format(cycle.retract)), conditional(!getProperty("useG0"), getFeed(highFeedrate)) ); writeBlock( gAbsIncModal.format(90), gFormat.format(33.1), conditional(gPlaneModal.getCurrent() == 17, zOutput.format(z)), conditional(gPlaneModal.getCurrent() == 18, yOutput.format(y)), conditional(gPlaneModal.getCurrent() == 19, xOutput.format(x)), "K" + pitchFormat.format(tool.threadPitch) ); gMotionModal.reset(); writeBlock( gAbsIncModal.format(90), gMotionModal.format(getProperty("useG0") ? 0 : 1), conditional(gPlaneModal.getCurrent() == 17, zOutput.format(cycle.clearance)), conditional(gPlaneModal.getCurrent() == 18, yOutput.format(cycle.clearance)), conditional(gPlaneModal.getCurrent() == 19, xOutput.format(cycle.clearance)), conditional(!getProperty("useG0"), getFeed(highFeedrate)) ); return; /* case "tapping-with-chip-breaking": case "left-tapping-with-chip-breaking": case "right-tapping-with-chip-breaking": */ } if (isFirstCyclePoint()) { repositionToCycleClearance(cycle, x, y, z); // return to initial Z which is clearance plane and set absolute mode var F = cycle.feedrate; var P = !cycle.dwell ? 0 : clamp(0.001, cycle.dwell, 99999999); // in seconds switch (cycleType) { case "drilling": writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(81), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); break; case "counter-boring": if (P > 0) { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(82), getCommonCycle(x, y, z, cycle.retract), "P" + secFormat.format(P), feedOutput.format(F) ); } else { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(81), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); } break; case "chip-breaking": expandCyclePoint(x, y, z); break; case "deep-drilling": if (P > 0) { expandCyclePoint(x, y, z); } else { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(83), getCommonCycle(x, y, z, cycle.retract), "Q" + xyzFormat.format(cycle.incrementalDepth), // conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } break; case "fine-boring": expandCyclePoint(x, y, z); break; /* // not supported case "back-boring": var dx = (gPlaneModal.getCurrent() == 19) ? cycle.backBoreDistance : 0; var dy = (gPlaneModal.getCurrent() == 18) ? cycle.backBoreDistance : 0; var dz = (gPlaneModal.getCurrent() == 17) ? cycle.backBoreDistance : 0; writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(87), getCommonCycle(x - dx, y - dy, z - dz, cycle.bottom), "Q" + xyzFormat.format(cycle.shift), "P" + secFormat.format(P), // not optional feedOutput.format(F) ); break; */ case "reaming": if (P > 0) { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(89), getCommonCycle(x, y, z, cycle.retract), "P" + secFormat.format(P), feedOutput.format(F) ); } else { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(85), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); } break; case "stop-boring": writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(86), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); break; case "manual-boring": writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(88), getCommonCycle(x, y, z, cycle.retract), "P" + secFormat.format(P), // not optional feedOutput.format(F) ); break; case "boring": if (P > 0) { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(89), getCommonCycle(x, y, z, cycle.retract), "P" + secFormat.format(P), // not optional feedOutput.format(F) ); } else { writeBlock( gRetractModal.format(98), gAbsIncModal.format(90), gCycleModal.format(85), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); } break; default: expandCyclePoint(x, y, z); } } else { if (cycleExpanded) { expandCyclePoint(x, y, z); } else { var _x = xOutput.format(x); var _y = yOutput.format(y); var _z = zOutput.format(z); if (!_x && !_y && !_z) { switch (gPlaneModal.getCurrent()) { case 17: // XY xOutput.reset(); // at least one axis is required _x = xOutput.format(x); break; case 18: // ZX zOutput.reset(); // at least one axis is required _z = zOutput.format(z); break; case 19: // YZ yOutput.reset(); // at least one axis is required _y = yOutput.format(y); break; } } writeBlock(_x, _y, _z); } } } function onCycleEnd() { if (!cycleExpanded) { writeBlock(gCycleModal.format(80)); gMotionModal.reset(); } } var pendingRadiusCompensation = -1; function onRadiusCompensation() { pendingRadiusCompensation = radiusCompensation; } function onRapid(_x, _y, _z) { 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(getProperty("useG0") ? 0 : 1), x, y, z, conditional(!getProperty("useG0"), getFeed(highFeedrate))); forceFeed(); } } function onLinear(_x, _y, _z, feed) { var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); var f = getFeed(feed); if (x || y || z) { if (pendingRadiusCompensation >= 0) { pendingRadiusCompensation = -1; var d = tool.diameterOffset; if (d > 99) { warning(localize("The diameter offset exceeds the maximum value.")); } writeBlock(gPlaneModal.format(17)); switch (radiusCompensation) { case RADIUS_COMPENSATION_LEFT: dOutput.reset(); writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(41), dOutput.format(d), x, y, z, f); break; case RADIUS_COMPENSATION_RIGHT: dOutput.reset(); writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(42), dOutput.format(d), x, y, z, f); break; default: writeBlock(gFormat.format(40)); writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), x, y, z, f); } } else { writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), x, y, z, f); } } else if (f) { if (getNextRecord().isMotion()) { // try not to output feed without motion forceFeed(); // force feed on next line } else { writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), f); } } } function onRapid5D(_x, _y, _z, _a, _b, _c) { if (!currentSection.isOptimizedForMachine()) { error(localize("This post configuration has not been customized for 5-axis simultaneous toolpath.")); return; } if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation mode cannot be changed at rapid traversal.")); return; } var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); var a = aOutput.format(_a); var b = bOutput.format(_b); var c = cOutput.format(_c); writeBlock(gMotionModal.format(getProperty("useG0") ? 0 : 1), x, y, z, a, b, c, conditional(!getProperty("useG0"), getFeed(highFeedrate))); forceFeed(); } function onLinear5D(_x, _y, _z, _a, _b, _c, feed) { if (!currentSection.isOptimizedForMachine()) { error(localize("This post configuration has not been customized for 5-axis simultaneous toolpath.")); return; } if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation cannot be activated/deactivated for 5-axis move.")); return; } var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); var a = aOutput.format(_a); var b = bOutput.format(_b); var c = cOutput.format(_c); // get feedrate number var f = {frn:0, fmode:0}; if (a || b || c) { f = getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed); if (getProperty("useInverseTime")) { f.frn = inverseTimeOutput.format(f.frn); } else { f.frn = feedOutput.format(f.frn); } } else { f.frn = feedOutput.format(feed); f.fmode = 94; } if (x || y || z || a || b || c) { writeBlock(gFeedModeModal.format(f.fmode), gMotionModal.format(1), x, y, z, a, b, c, f.frn); } else if (f.frn) { if (getNextRecord().isMotion()) { // try not to output feed without motion forceFeed(); // force feed on next line } else { writeBlock(gFeedModeModal.format(f.fmode), gMotionModal.format(1), f.frn); } } } // Start of multi-axis feedrate logic /***** You can add 'getProperty("useInverseTime'") if desired. *****/ /***** 'previousABC' can be added throughout to maintain previous rotary positions. Required for Mill/Turn machines. *****/ /***** 'headOffset' should be defined when a head rotary axis is defined. *****/ /***** The feedrate mode must be included in motion block output (linear, circular, etc.) for Inverse Time feedrate support. *****/ var dpmBPW = 0.1; // ratio of rotary accuracy to linear accuracy for DPM calculations var inverseTimeUnits = 1.0; // 1.0 = minutes, 60.0 = seconds var maxInverseTime = 999999.9999; // maximum value to output for Inverse Time feeds var maxDPM = 9999.99; // maximum value to output for DPM feeds var useInverseTimeFeed = false; // use 1/T feeds var inverseTimeFormat = createFormat({decimals:4, forceDecimal:true}); var inverseTimeOutput = createVariable({prefix:"F", force:true}, inverseTimeFormat); var previousDPMFeed = 0; // previously output DPM feed var dpmFeedToler = 0.5; // tolerance to determine when the DPM feed has changed // var previousABC = new Vector(0, 0, 0); // previous ABC position if maintained in post, don't define if not used var forceOptimized = undefined; // used to override optimized-for-angles points (XZC-mode) /** Calculate the multi-axis feedrate number. */ function getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed) { var f = {frn:0, fmode:0}; if (feed <= 0) { error(localize("Feedrate is less than or equal to 0.")); return f; } var length = getMoveLength(_x, _y, _z, _a, _b, _c); if (getProperty("useInverseTime")) { // inverse time f.frn = getInverseTime(length.tool, feed); f.fmode = 93; feedOutput.reset(); } else { // degrees per minute f.frn = getFeedDPM(length, feed); f.fmode = 94; } return f; } /** Returns point optimization mode. */ function getOptimizedMode() { if (forceOptimized != undefined) { return forceOptimized; } // return (currentSection.getOptimizedTCPMode() != 0); // TAG:doesn't return correct value return true; // always return false for non-TCP based heads } /** Calculate the DPM feedrate number. */ function getFeedDPM(_moveLength, _feed) { if ((_feed == 0) || (_moveLength.tool < 0.0001) || (toDeg(_moveLength.abcLength) < 0.0005)) { previousDPMFeed = 0; return _feed; } var moveTime = _moveLength.tool / _feed; if (moveTime == 0) { previousDPMFeed = 0; return _feed; } var dpmFeed; var tcp = false; // getProperty("useTCPMode"); if (tcp) { // TCP mode is supported, output feed as FPM dpmFeed = _feed; } else if (false) { // standard DPM dpmFeed = Math.min(toDeg(_moveLength.abcLength) / moveTime, maxDPM); if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) { dpmFeed = previousDPMFeed; } } else if (true) { // combination FPM/DPM var length = Math.sqrt(Math.pow(_moveLength.xyzLength, 2.0) + Math.pow((toDeg(_moveLength.abcLength) * dpmBPW), 2.0)); dpmFeed = Math.min((length / moveTime), maxDPM); if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) { dpmFeed = previousDPMFeed; } } else { // machine specific calculation dpmFeed = _feed; } previousDPMFeed = dpmFeed; return dpmFeed; } /** Calculate the Inverse time feedrate number. */ function getInverseTime(_length, _feed) { var inverseTime; if (_length < 1.e-6) { // tool doesn't move if (typeof maxInverseTime === "number") { inverseTime = maxInverseTime; } else { inverseTime = 999999; } } else { inverseTime = _feed / _length / inverseTimeUnits; if (typeof maxInverseTime === "number") { if (inverseTime > maxInverseTime) { inverseTime = maxInverseTime; } } } return inverseTime; } /** Calculate radius for each rotary axis. */ function getRotaryRadii(startTool, endTool, startABC, endABC) { var radii = new Vector(0, 0, 0); var startRadius; var endRadius; var axis = new Array(machineConfiguration.getAxisU(), machineConfiguration.getAxisV(), machineConfiguration.getAxisW()); for (var i = 0; i < 3; ++i) { if (axis[i].isEnabled()) { var startRadius = getRotaryRadius(axis[i], startTool, startABC); var endRadius = getRotaryRadius(axis[i], endTool, endABC); radii.setCoordinate(axis[i].getCoordinate(), Math.max(startRadius, endRadius)); } } return radii; } /** Calculate the distance of the tool position to the center of a rotary axis. */ function getRotaryRadius(axis, toolPosition, abc) { if (!axis.isEnabled()) { return 0; } var direction = axis.getEffectiveAxis(); var normal = direction.getNormalized(); // calculate the rotary center based on head/table var center; var radius; if (axis.isHead()) { var pivot; if (typeof headOffset === "number") { pivot = headOffset; } else { pivot = tool.getBodyLength(); } if (axis.getCoordinate() == machineConfiguration.getAxisU().getCoordinate()) { // rider center = Vector.sum(toolPosition, Vector.product(machineConfiguration.getDirection(abc), pivot)); center = Vector.sum(center, axis.getOffset()); radius = Vector.diff(toolPosition, center).length; } else { // carrier var angle = abc.getCoordinate(machineConfiguration.getAxisU().getCoordinate()); radius = Math.abs(pivot * Math.sin(angle)); radius += axis.getOffset().length; } } else { center = axis.getOffset(); var d1 = toolPosition.x - center.x; var d2 = toolPosition.y - center.y; var d3 = toolPosition.z - center.z; var radius = Math.sqrt( Math.pow((d1 * normal.y) - (d2 * normal.x), 2.0) + Math.pow((d2 * normal.z) - (d3 * normal.y), 2.0) + Math.pow((d3 * normal.x) - (d1 * normal.z), 2.0) ); } return radius; } /** Calculate the linear distance based on the rotation of a rotary axis. */ function getRadialDistance(radius, startABC, endABC) { // calculate length of radial move var delta = Math.abs(endABC - startABC); if (delta > Math.PI) { delta = 2 * Math.PI - delta; } var radialLength = (2 * Math.PI * radius) * (delta / (2 * Math.PI)); return radialLength; } /** Calculate tooltip, XYZ, and rotary move lengths. */ function getMoveLength(_x, _y, _z, _a, _b, _c) { // get starting and ending positions var moveLength = {}; var startTool; var endTool; var startXYZ; var endXYZ; var startABC; if (typeof previousABC !== "undefined") { startABC = new Vector(previousABC.x, previousABC.y, previousABC.z); } else { startABC = getCurrentDirection(); } var endABC = new Vector(_a, _b, _c); if (!getOptimizedMode()) { // calculate XYZ from tool tip startTool = getCurrentPosition(); endTool = new Vector(_x, _y, _z); startXYZ = startTool; endXYZ = endTool; // adjust points for tables if (!machineConfiguration.getTableABC(startABC).isZero() || !machineConfiguration.getTableABC(endABC).isZero()) { startXYZ = machineConfiguration.getOrientation(machineConfiguration.getTableABC(startABC)).getTransposed().multiply(startXYZ); endXYZ = machineConfiguration.getOrientation(machineConfiguration.getTableABC(endABC)).getTransposed().multiply(endXYZ); } // adjust points for heads if (machineConfiguration.getAxisU().isEnabled() && machineConfiguration.getAxisU().isHead()) { if (typeof getOptimizedHeads === "function") { // use post processor function to adjust heads startXYZ = getOptimizedHeads(startXYZ.x, startXYZ.y, startXYZ.z, startABC.x, startABC.y, startABC.z); endXYZ = getOptimizedHeads(endXYZ.x, endXYZ.y, endXYZ.z, endABC.x, endABC.y, endABC.z); } else { // guess at head adjustments var startDisplacement = machineConfiguration.getDirection(startABC); startDisplacement.multiply(headOffset); var endDisplacement = machineConfiguration.getDirection(endABC); endDisplacement.multiply(headOffset); startXYZ = Vector.sum(startTool, startDisplacement); endXYZ = Vector.sum(endTool, endDisplacement); } } } else { // calculate tool tip from XYZ, heads are always programmed in TCP mode, so not handled here startXYZ = getCurrentPosition(); endXYZ = new Vector(_x, _y, _z); startTool = machineConfiguration.getOrientation(machineConfiguration.getTableABC(startABC)).multiply(startXYZ); endTool = machineConfiguration.getOrientation(machineConfiguration.getTableABC(endABC)).multiply(endXYZ); } // calculate axes movements moveLength.xyz = Vector.diff(endXYZ, startXYZ).abs; moveLength.xyzLength = moveLength.xyz.length; moveLength.abc = Vector.diff(endABC, startABC).abs; for (var i = 0; i < 3; ++i) { if (moveLength.abc.getCoordinate(i) > Math.PI) { moveLength.abc.setCoordinate(i, 2 * Math.PI - moveLength.abc.getCoordinate(i)); } } moveLength.abcLength = moveLength.abc.length; // calculate radii moveLength.radius = getRotaryRadii(startTool, endTool, startABC, endABC); // calculate the radial portion of the tool tip movement var radialLength = Math.sqrt( Math.pow(getRadialDistance(moveLength.radius.x, startABC.x, endABC.x), 2.0) + Math.pow(getRadialDistance(moveLength.radius.y, startABC.y, endABC.y), 2.0) + Math.pow(getRadialDistance(moveLength.radius.z, startABC.z, endABC.z), 2.0) ); // calculate the tool tip move length // tool tip distance is the move distance based on a combination of linear and rotary axes movement moveLength.tool = moveLength.xyzLength + radialLength; // debug if (false) { writeComment("DEBUG - tool = " + moveLength.tool); writeComment("DEBUG - xyz = " + moveLength.xyz); var temp = Vector.product(moveLength.abc, 180 / Math.PI); writeComment("DEBUG - abc = " + temp); writeComment("DEBUG - radius = " + moveLength.radius); } return moveLength; } // End of multi-axis feedrate logic // Start of onRewindMachine logic /***** Be sure to add 'safeRetractDistance' to post getProperty(" ")*****/ var performRewinds = true; // enables the onRewindMachine logic var safeRetractFeed = (unit == IN) ? 20 : 500; var safePlungeFeed = (unit == IN) ? 10 : 250; var stockAllowance = new Vector(toPreciseUnit(0.1, IN), toPreciseUnit(0.1, IN), toPreciseUnit(0.1, IN)); /** Allow user to override the onRewind logic */ function onRewindMachineEntry(_a, _b, _c) { return false; } /** Retract to safe position before indexing rotaries. */ function moveToSafeRetractPosition(isRetracted) { setTCPMode(false); if (!isRetracted) { writeRetract(Z); } writeRetract(X, Y); } /** Return from safe position after indexing rotaries. */ function returnFromSafeRetractPosition(position) { setTCPMode(true); forceXYZ(); xOutput.reset(); yOutput.reset(); zOutput.disable(); onExpandedRapid(position.x, position.y, position.z); zOutput.enable(); onExpandedRapid(position.x, position.y, position.z); } /** Intersect the point-vector with the stock box. */ function intersectStock(point, direction) { var intersection = getWorkpiece().getRayIntersection(point, direction, stockAllowance); return intersection === null ? undefined : intersection.second; } /** Calculates the retract point using the stock box and safe retract distance. */ function getRetractPosition(currentPosition, currentDirection) { var retractPos = intersectStock(currentPosition, currentDirection); if (retractPos == undefined) { if (tool.getFluteLength() != 0) { retractPos = Vector.sum(currentPosition, Vector.product(currentDirection, tool.getFluteLength())); } } if ((retractPos != undefined) && getProperty("safeRetractDistance")) { retractPos = Vector.sum(retractPos, Vector.product(currentDirection, getProperty("safeRetractDistance"))); } return retractPos; } /** Determines if the angle passed to onRewindMachine is a valid starting position. */ function isRewindAngleValid(_a, _b, _c) { // make sure the angles are different from the last output angles if (!abcFormat.areDifferent(getCurrentDirection().x, _a) && !abcFormat.areDifferent(getCurrentDirection().y, _b) && !abcFormat.areDifferent(getCurrentDirection().z, _c)) { error( localize("REWIND: Rewind angles are the same as the previous angles: ") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return false; } // make sure angles are within the limits of the machine var abc = new Array(_a, _b, _c); var ix = machineConfiguration.getAxisU().getCoordinate(); var failed = false; if ((ix != -1) && !machineConfiguration.getAxisU().isSupported(abc[ix])) { failed = true; } ix = machineConfiguration.getAxisV().getCoordinate(); if ((ix != -1) && !machineConfiguration.getAxisV().isSupported(abc[ix])) { failed = true; } ix = machineConfiguration.getAxisW().getCoordinate(); if ((ix != -1) && !machineConfiguration.getAxisW().isSupported(abc[ix])) { failed = true; } if (failed) { error( localize("REWIND: Rewind angles are outside the limits of the machine: ") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return false; } return true; } function onRewindMachine(_a, _b, _c) { if (!performRewinds) { error(localize("REWIND: Rewind of machine is required for simultaneous multi-axis toolpath and has been disabled.")); return; } // Allow user to override rewind logic if (onRewindMachineEntry(_a, _b, _c)) { return; } // Determine if input angles are valid or will cause a crash if (!isRewindAngleValid(_a, _b, _c)) { error( localize("REWIND: Rewind angles are invalid:") + abcFormat.format(_a) + ", " + abcFormat.format(_b) + ", " + abcFormat.format(_c) ); return; } // Work with the tool end point if (currentSection.getOptimizedTCPMode() == 0) { currentTool = getCurrentPosition(); } else { currentTool = machineConfiguration.getOrientation(getCurrentDirection()).multiply(getCurrentPosition()); } var currentABC = getCurrentDirection(); var currentDirection = machineConfiguration.getDirection(currentABC); // Calculate the retract position var retractPosition = getRetractPosition(currentTool, currentDirection); // Output warning that axes take longest route if (retractPosition == undefined) { error(localize("REWIND: Cannot calculate retract position.")); return; } else { var text = localize("REWIND: Tool is retracting due to rotary axes limits."); warning(text); writeComment(text); } // Move to retract position var position; if (currentSection.getOptimizedTCPMode() == 0) { position = retractPosition; } else { position = machineConfiguration.getOrientation(getCurrentDirection()).getTransposed().multiply(retractPosition); } onExpandedLinear(position.x, position.y, position.z, safeRetractFeed); //Position to safe machine position for rewinding axes moveToSafeRetractPosition(false); // Rotate axes to new position above reentry position xOutput.disable(); yOutput.disable(); zOutput.disable(); writeBlock("M999 P" + abcFormat.format(_b)); onRapid5D(position.x, position.y, position.z, _a, _b, _c); xOutput.enable(); yOutput.enable(); zOutput.enable(); // Move back to position above part if (currentSection.getOptimizedTCPMode() != 0) { position = machineConfiguration.getOrientation(new Vector(_a, _b, _c)).getTransposed().multiply(retractPosition); } returnFromSafeRetractPosition(position); // Plunge tool back to original position if (currentSection.getOptimizedTCPMode() != 0) { currentTool = machineConfiguration.getOrientation(new Vector(_a, _b, _c)).getTransposed().multiply(currentTool); } onExpandedLinear(currentTool.x, currentTool.y, currentTool.z, safePlungeFeed); } // End of onRewindMachine logic function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); return; } var start = getCurrentPosition(); if (isFullCircle()) { if (getProperty("useRadius") || isHelical()) { // radius mode does not support full arcs linearize(tolerance); return; } switch (getCircularPlane()) { case PLANE_XY: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), getFeed(feed)); break; case PLANE_ZX: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); break; case PLANE_YZ: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); break; default: linearize(tolerance); } } else if (!getProperty("useRadius")) { switch (getCircularPlane()) { case PLANE_XY: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), getFeed(feed)); break; case PLANE_ZX: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); break; case PLANE_YZ: writeBlock(gAbsIncModal.format(90), gFeedModeModal.format(94), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), jOutput.format(cy - start.y, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); break; default: linearize(tolerance); } } else { // use radius mode var r = getCircularRadius(); if (toDeg(getCircularSweep()) > (180 + 1e-9)) { r = -r; // allow up to <360 deg arcs } switch (getCircularPlane()) { case PLANE_XY: writeBlock(gFeedModeModal.format(94), gPlaneModal.format(17), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; case PLANE_ZX: writeBlock(gFeedModeModal.format(94), gPlaneModal.format(18), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; case PLANE_YZ: writeBlock(gFeedModeModal.format(94), gPlaneModal.format(19), gMotionModal.format(clockwise ? 2 : 3), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; default: linearize(tolerance); } } } var currentCoolantMode = COOLANT_OFF; var coolantOff = undefined; function setCoolant(coolant) { var coolantCodes = getCoolantCodes(coolant); if (Array.isArray(coolantCodes)) { if (singleLineCoolant) { writeBlock(coolantCodes.join(getWordSeparator())); } else { for (var c in coolantCodes) { writeBlock(coolantCodes[c]); } } return undefined; } return coolantCodes; } function getCoolantCodes(coolant) { var multipleCoolantBlocks = new Array(); // create a formatted array to be passed into the outputted line if (!coolants) { error(localize("Coolants have not been defined.")); } if (isProbeOperation()) { // avoid coolant output for probing coolant = COOLANT_OFF; } if (coolant == currentCoolantMode) { return undefined; // coolant is already active } if ((coolant != COOLANT_OFF) && (currentCoolantMode != COOLANT_OFF) && (coolantOff != undefined)) { if (Array.isArray(coolantOff)) { for (var i in coolantOff) { multipleCoolantBlocks.push(mFormat.format(coolantOff[i])); } } else { multipleCoolantBlocks.push(mFormat.format(coolantOff)); } } var m; var coolantCodes = {}; for (var c in coolants) { // find required coolant codes into the coolants array if (coolants[c].id == coolant) { coolantCodes.on = coolants[c].on; if (coolants[c].off != undefined) { coolantCodes.off = coolants[c].off; break; } else { for (var i in coolants) { if (coolants[i].id == COOLANT_OFF) { coolantCodes.off = coolants[i].off; break; } } } } } if (coolant == COOLANT_OFF) { m = !coolantOff ? coolantCodes.off : coolantOff; // use the default coolant off command when an 'off' value is not specified } else { coolantOff = coolantCodes.off; m = coolantCodes.on; } if (!m) { onUnsupportedCoolant(coolant); m = 9; } else { if (Array.isArray(m)) { for (var i in m) { multipleCoolantBlocks.push(mFormat.format(m[i])); } } else { multipleCoolantBlocks.push(mFormat.format(m)); } currentCoolantMode = coolant; return multipleCoolantBlocks; // return the single formatted coolant value } return undefined; } var mapCommand = { COMMAND_STOP:0, COMMAND_OPTIONAL_STOP:1, COMMAND_END:2, COMMAND_SPINDLE_CLOCKWISE:3, COMMAND_SPINDLE_COUNTERCLOCKWISE:4, COMMAND_STOP_SPINDLE:5, COMMAND_ORIENTATE_SPINDLE:19, COMMAND_LOAD_TOOL:6 }; function onCommand(command) { switch (command) { case COMMAND_STOP: writeBlock(mFormat.format(0)); forceSpindleSpeed = true; return; 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() { if (currentSection.isMultiAxis()) { setTCPMode(false); disableLengthCompensation(false); writeBlock(gFeedModeModal.format(94)); // inverse time feed off } writeBlock(gPlaneModal.format(17)); if (!isLastSection() && (getNextSection().getTool().coolant != tool.coolant)) { setCoolant(COOLANT_OFF); } if (((getCurrentSectionId() + 1) >= getNumberOfSections()) || (tool.number != getNextSection().getTool().number)) { onCommand(COMMAND_BREAK_CONTROL); } // the code below gets the machine angles from previous operation. closestABC must also be set to true if (currentSection.isMultiAxis() && currentSection.isOptimizedForMachine()) { currentMachineABC = currentSection.getFinalToolAxisABC(); } forceAny(); } /** Output block to do safe retract and/or move to home position. */ function writeRetract() { var words = []; // store all retracted axes in an array var retractAxes = new Array(false, false, false); var method = getProperty("safePositionMethod"); if (method == "clearanceHeight") { if (!is3D()) { error(localize("Retract option 'Clearance Height' is not supported for multi-axis machining.")); } return; } validate(arguments.length != 0, "No axis specified for writeRetract()."); for (i in arguments) { retractAxes[arguments[i]] = true; } if ((retractAxes[0] || retractAxes[1]) && !retracted) { // retract Z first before moving to X/Y home error(localize("Retracting in X/Y is not possible without being retracted in Z.")); return; } // special conditions if (retractAxes[0] || retractAxes[1]) { // X, Y use G53 method = "G53"; } // define home positions var _xHome; var _yHome; var _zHome; if (method == "G28") { _xHome = toPreciseUnit(0, MM); _yHome = toPreciseUnit(0, MM); _zHome = toPreciseUnit(0, MM); } else { _xHome = machineConfiguration.hasHomePositionX() ? machineConfiguration.getHomePositionX() : toPreciseUnit(0, MM); _yHome = machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : toPreciseUnit(0, MM); _zHome = machineConfiguration.getRetractPlane() != 0 ? machineConfiguration.getRetractPlane() : toPreciseUnit(0, MM); } for (var i = 0; i < arguments.length; ++i) { switch (arguments[i]) { case X: words.push("X" + xyzFormat.format(_xHome)); xOutput.reset(); break; case Y: words.push("Y" + xyzFormat.format(_yHome)); yOutput.reset(); break; case Z: words.push("Z" + xyzFormat.format(_zHome)); zOutput.reset(); retracted = true; break; default: error(localize("Unsupported axis specified for writeRetract().")); return; } } if (words.length > 0) { switch (method) { case "G28": gMotionModal.reset(); gAbsIncModal.reset(); writeBlock(gFormat.format(28), gAbsIncModal.format(91), words); writeBlock(gAbsIncModal.format(90)); break; case "G53": gMotionModal.reset(); writeBlock(gAbsIncModal.format(90), gFormat.format(53), gMotionModal.format(0), words); break; default: error(localize("Unsupported safe position method.")); return; } } } function onClose() { setCoolant(COOLANT_OFF); setTCPMode(false); disableLengthCompensation(false); writeRetract(Z); writeRetract(X, Y); // always go home writeBlock("M999 P0"); forceWorkPlane(); setWorkPlane(new Vector(0, 0, 0)); // reset working plane onImpliedCommand(COMMAND_END); onImpliedCommand(COMMAND_STOP_SPINDLE); writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off writeln("(AXIS,stop)"); // disable LinuxCNC visualization writeln("%"); } function setProperty(property, value) { properties[property].current = value; }