/** Copyright (C) 2012-2025 by Autodesk, Inc. All rights reserved. Hurco Lathe post processor configuration. $Revision: 44164 2399bc62759fa802328708c32fe22eea9cb8a275 $ $Date: 2025-02-10 14:52:26 $ FORKID {E076A9BF-DF7D-4BB1-9A06-18CEBE661208} */ description = "Hurco Turning"; vendor = "Hurco"; vendorUrl = "https://www.hurco.com"; legal = "Copyright (C) 2012-2025 by Autodesk, Inc."; certificationLevel = 2; minimumRevision = 45702; longDescription = "Generic post for Hurco Turning with WinMax control. Note that this post supports both ISNC (ISO NC mode) and BNC (Basic NC mode). By default ISNC mode is used but you can switch to BNC mode by setting the 'Use ISN or BNC mode' property. Use Turret 0 for Positional Turret, Turret 101 for QCTP on X- Post, Turret 102 for QCTP on X+ Post, Turret 103 for Gang Tooling on X- Post, Turret 104 for Gang Tooling on X+ Tool Post."; extension = "nc"; programNameIsInteger = true; setCodePage("ascii"); capabilities = CAPABILITY_TURNING; 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 = false; allowedCircularPlanes = 1 << PLANE_ZX; // allow ZX plane only // user-defined properties properties = { writeMachine: { title : "Write machine", description: "Output the machine settings in the header of the code.", group : "formats", type : "boolean", value : false, scope : "post" }, writeTools: { title : "Write tool list", description: "Output a tool list in the header of the code.", group : "formats", type : "boolean", value : false, scope : "post" }, separateWordsWithSpace: { title : "Separate words with space", description: "Adds spaces between words if 'yes' is selected.", group : "formats", type : "boolean", value : true, scope : "post" }, showSequenceNumbers: { title : "Use sequence numbers", description: "'Yes' outputs sequence numbers on each block, 'Only on tool change' outputs sequence numbers on tool change blocks only, and 'No' disables the output of sequence numbers.", group : "formats", type : "enum", values : [ {title:"Yes", id:"true"}, {title:"No", id:"false"}, {title:"Only on tool change", id:"toolChange"} ], value: "true", scope: "post" }, sequenceNumberStart: { title : "Start sequence number", description: "The number at which to start the sequence numbers.", group : "formats", 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 : "formats", type : "integer", value : 1, scope : "post" }, showNotes: { title : "Show notes", description: "Writes operation notes as comments in the outputted code.", group : "formats", type : "boolean", value : false, scope : "post" }, homePositionX: { title : "G53 home position X", description: "G53 X-axis home position.", group : "homePositions", type : "number", value : 0, scope : "post" }, homePositionZ: { title : "G53 home position Z", description: "G53 Z-axis home position.", group : "homePositions", type : "number", value : 0, scope : "post" }, safePositionMethod: { title : "Safe Retracts", description: "Select your desired retract option.", group : "homePositions", type : "enum", values : [ {title:"G28", id:"G28"}, {title:"G53", id:"G53"} ], value: "G28", scope: "post" }, safePositionStyle: { title : "Safe retract style", description: "Select your desired order for the axes to retract.", group : "homePositions", type : "enum", values : [ {title:"Only X", id:"X"}, {title:"Only Z", id:"Z"}, {title:"Both X then Z", id:"XZ"}, {title:"Both Z then X", id:"ZX"}, {title:"Both same line", id:"singleLineXZ"} ], value: "XZ", scope: "post" }, approachStyle: { title : "Approach style", description: "Select your desired order for the axes to approach.", type : "enum", group : "preferences", values : [ {title:"First Z then X", id:"ZX"}, {title:"Both XZ in same line", id:"singleLineXZ"} ], value: "ZX", scope: "post" }, optionalStop: { title : "Optional stop", description: "Outputs optional stop code during when necessary in the code.", group : "preferences", type : "boolean", value : true, scope : "post" }, isnc: { title : "Use ISNC or BNC mode", description: "Selects between ISNC (ISO NC mode) and BNC (Basic NC mode).", group : "formats", values : [ "Basic NC mode", "ISO NC mode" ], type : "boolean", value: true, scope: "post" }, useRadius: { title : "Radius arcs", description: "If yes is selected, arcs are outputted using radius values rather than IJK.", group : "preferences", type : "boolean", value : false, scope : "post" }, maximumSpindleSpeed: { title : "Max spindle speed", description: "Defines the maximum spindle speed allowed by your machines.", group : "configuration", type : "integer", range : [ 0, 999999999 ], value: 6000, scope: "post" }, useParametricFeed: { title : "Parametric feed", description: "Specifies the feed value that should be output using a Q value.", group : "preferences", type : "boolean", value : false, scope : "post" }, useCycles: { title : "Use cycles", description: "Specifies if canned drilling cycles should be used.", group : "preferences", type : "boolean", value : true, scope : "post" }, useRigidTapping: { title : "Use rigid tapping", description: "Select 'Yes' to enable rigid tapping or 'No' to select tapping.", group : "preferences", type : "boolean", value : false, scope : "post" }, useSimpleThread: { title : "Use simple threading cycle", description: "Enable to output G92 simple threading cycle, disable to output G76 standard threading cycle.", group : "preferences", type : "boolean", value : false, scope : "post" } }; groupDefinitions = { configuration: {title:"Configuration", description:"Machine options", order:0}, preferences : {title:"Preferences", description:"User preferences", order:1}, homePositions: {title:"Home Positioning", collapsed:true, order:2}, general : {title:"General", collapsed:true, order:3}, header : {title:"Header", description:"NC file header", collapsed:true, order:4} }; 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 permittedCommentChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,=_-"; var gFormat = createFormat({prefix:"G", decimals:1}); var mFormat = createFormat({prefix:"M", decimals:1}); var spatialFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var xFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true, scale:2}); // diameter mode var yFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var zFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var iFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); // radius mode var rFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); // radius var feedFormat = createFormat({decimals:(unit == MM ? 4 : 5), forceDecimal:true}); var fprFormat = createFormat({decimals:(unit == MM ? 4 : 5), forceDecimal:true}); var fpmFormat = createFormat({decimals:(unit == MM ? 0 : 1), forceDecimal:true}); var pitchFormat = createFormat({decimals:6, forceDecimal:true}); var toolFormat = createFormat({decimals:0, width:4, zeropad:true}); 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 threadP1Format = createFormat({decimals:0, forceDecimal:false, trim:false, width:6, zeropad:true}); var threadPQFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:false, trim:true}); var threadQFormat = createFormat({decimals:3, forceDecimal:true}); var threadQ1Format = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:false}); var peckFormat = createFormat({decimals:(unit == MM ? 3 : 4), forceDecimal:true}); var integerFormat = createFormat({decimals:0, forceDecimal:false, trim:true}); // var peckFormat = createFormat({decimals:0, forceDecimal:false, trim:false, width:4, zeropad:true, scale:(unit == MM ? 1000 : 10000)}); var xOutput; // xOutput is defined in setDirectionX() var yOutput = createVariable({prefix:"Y"}, yFormat); var zOutput = createVariable({onchange:function() {retracted[Z] = false;}, prefix:"Z"}, zFormat); var feedOutput = createVariable({prefix:"F"}, feedFormat); var pitchOutput = createVariable({prefix:"F", force:true}, pitchFormat); var sOutput = createVariable({prefix:"S", force:true}, rpmFormat); // circular output var kOutput = createReferenceVariable({prefix:"K"}, spatialFormat); var iOutput; // iOutput is defined in setDirectionX() var threadP1Output = createVariable({prefix:"P", force:true}, threadP1Format); var threadP2Output = createVariable({prefix:"P", force:true}, threadPQFormat); var threadQOutput = createVariable({prefix:"Q", force:true}, threadQ1Format); var threadIOutput = createVariable({prefix:"I", force:true}, spatialFormat); var threadROutput = createVariable({prefix:"R", force:true}, threadPQFormat); var threadR2Output = createVariable({prefix:"R", force:true}, threadPQFormat); var g92IOutput = createVariable({prefix:"I"}, zFormat); // no scaling var g92QOutput = createVariable({prefix:"Q"}, threadQFormat); var peckOutput = createVariable({prefix:"Q", force:true}, peckFormat); 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 //G94-95 var gSpindleModeModal = createModal({}, gFormat); // modal group 5 // G96-97 var gUnitModal = createModal({}, gFormat); // modal group 6 // G20-21 var gCycleModal = createModal({}, gFormat); // modal group 9 // G81, ... // fixed settings var firstFeedParameter = 500; var gotSecondarySpindle = true; var gotTailStock = false; var WARNING_WORK_OFFSET = 0; var QCTP = 0; var TURRET = 1; var GANG = 2; var FRONT = -1; var REAR = 1; // collected state var sequenceNumber; var currentWorkOffset; var optionalSection = false; var forceSpindleSpeed = false; var tapping = false; var activeMovements; // do not use by default var currentFeedId; var toolingData; var previousToolingData; var retracted = new Array(false, false, false); // specifies that the tool has been retracted to the safe plane var fpmCode = 94; var fprCode = 95; function getCode(code) { switch (code) { // case "PART_CATCHER_ON": // return mFormat.format(SPECIFY YOUR CODE HERE); // case "PART_CATCHER_OFF": // return mFormat.format(SPECIFY YOUR CODE HERE); // case "TAILSTOCK_ON": // return mFormat.format(SPECIFY YOUR CODE HERE); // case "TAILSTOCK_OFF": // return mFormat.format(SPECIFY YOUR CODE HERE); // case "ENGAGE_C_AXIS": // machineState.cAxisIsEngaged = true; // return cAxisEngageModal.format(UNSUPPORTED); // case "DISENGAGE_C_AXIS": // machineState.cAxisIsEngaged = false; // return cAxisEngageModal.format(UNSUPPORTED); // case "POLAR_INTERPOLATION_ON": // return gPolarModal.format(UNSUPPORTED); // case "POLAR_INTERPOLATION_OFF": // return gPolarModal.format(UNSUPPORTED); // case "STOP_LIVE_TOOL": // machineState.liveToolIsActive = false; // return mFormat.format(UNSUPPORTED); // case "STOP_MAIN_SPINDLE": // machineState.mainSpindleIsActive = false; // return mFormat.format(UNSUPPORTED); // case "STOP_SUB_SPINDLE": // machineState.subSpindleIsActive = false; // return mFormat.format(UNSUPPORTED); // case "START_LIVE_TOOL_CW": // machineState.liveToolIsActive = true; // return mFormat.format(UNSUPPORTED); // case "START_LIVE_TOOL_CCW": // machineState.liveToolIsActive = true; // return mFormat.format(UNSUPPORTED); case "START_MAIN_SPINDLE_CW": // machineState.mainSpindleIsActive = true; return mFormat.format(3); case "START_MAIN_SPINDLE_CCW": // machineState.mainSpindleIsActive = true; return mFormat.format(4); // case "START_SUB_SPINDLE_CW": // machineState.subSpindleIsActive = true; // return mFormat.format(UNSUPPORTED); // case "START_SUB_SPINDLE_CCW": // machineState.subSpindleIsActive = true; // return mFormat.format(UNSUPPORTED); // case "MAIN_SPINDLE_BRAKE_ON": // machineState.mainSpindleBrakeIsActive = true; // return cAxisBrakeModal.format(UNSUPPORTED); // case "MAIN_SPINDLE_BRAKE_OFF": // machineState.mainSpindleBrakeIsActive = false; // return cAxisBrakeModal.format(UNSUPPORTED); // case "SUB_SPINDLE_BRAKE_ON": // machineState.subSpindleBrakeIsActive = true; // return cAxisBrakeModal.format(UNSUPPORTED); // case "SUB_SPINDLE_BRAKE_OFF": // machineState.subSpindleBrakeIsActive = false; // return cAxisBrakeModal.format(UNSUPPORTED); case "FEED_MODE_UNIT_REV": return gFeedModeModal.format(fprCode); case "FEED_MODE_UNIT_MIN": return gFeedModeModal.format(fpmCode); case "CONSTANT_SURFACE_SPEED_ON": return gSpindleModeModal.format(96); case "CONSTANT_SURFACE_SPEED_OFF": return gSpindleModeModal.format(97); case "MAINSPINDLE_AIR_BLAST_ON": return mFormat.format(16); case "MAINSPINDLE_AIR_BLAST_OFF": return mFormat.format(17); // case "SUBSPINDLE_AIR_BLAST_ON": // return mFormat.format(UNSUPPORTED); // case "SUBSPINDLE_AIR_BLAST_OFF": // return mFormat.format(UNSUPPORTED); case "CLAMP_PRIMARY_CHUCK": return mFormat.format(getProperty("isnc") ? 69 : 14); case "UNCLAMP_PRIMARY_CHUCK": return mFormat.format(getProperty("isnc") ? 68 : 15); // case "CLAMP_SECONDARY_CHUCK": // return mFormat.format(UNSUPPORTED); // case "UNCLAMP_SECONDARY_CHUCK": // return mFormat.format(UNSUPPORTED); // case "SPINDLE_SYNCHRONIZATION_ON": // machineState.spindleSynchronizationIsActive = true; // return gSynchronizedSpindleModal.format(UNSUPPORTED); // case "SPINDLE_SYNCHRONIZATION_OFF": // machineState.spindleSynchronizationIsActive = false; // return gSynchronizedSpindleModal.format(UNSUPPORTED); case "START_CHIP_TRANSPORT": return mFormat.format(getProperty("isnc") ? 24 : 50); case "STOP_CHIP_TRANSPORT": return mFormat.format(getProperty("isnc") ? 25 : 51); // case "OPEN_DOOR": // return mFormat.format(UNSUPPORTED); // case "CLOSE_DOOR": // return mFormat.format(UNSUPPORTED); default: error(localize("Command " + code + " is not defined.")); return 0; } } function isSpindleSpeedDifferent() { if (isFirstSection()) { return true; } if (getPreviousSection().getTool().clockwise != tool.clockwise) { return true; } if (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { if ((getPreviousSection().getTool().getSpindleMode() != SPINDLE_CONSTANT_SURFACE_SPEED) || rpmFormat.areDifferent(getPreviousSection().getTool().surfaceSpeed, tool.surfaceSpeed)) { return true; } } else { if ((getPreviousSection().getTool().getSpindleMode() != SPINDLE_CONSTANT_SPINDLE_SPEED) || rpmFormat.areDifferent(getPreviousSection().getTool().spindleRPM, spindleSpeed)) { return true; } } return false; } /** Writes the specified block. */ function writeBlock() { var text = formatWords(arguments); if (!text) { return; } if (getProperty("showSequenceNumbers") == "true") { if (optionalSection) { if (text) { writeWords("/", "N" + sequenceNumber, text); } } else { writeWords2("N" + sequenceNumber, arguments); } sequenceNumber += getProperty("sequenceNumberIncrement"); } else { if (optionalSection) { writeWords2("/", arguments); } else { writeWords(arguments); } } } /** Writes the specified optional block. */ function writeOptionalBlock() { if (getProperty("showSequenceNumbers") == "true") { var words = formatWords(arguments); if (words) { writeWords("/", "N" + sequenceNumber, words); sequenceNumber += getProperty("sequenceNumberIncrement"); } } else { writeWords2("/", arguments); } } function formatComment(text) { return "(" + filterText(String(text).toUpperCase(), permittedCommentChars).replace(/[()]/g, "") + ")"; } /** Writes the specified block - used for tool changes only. */ function writeToolBlock() { var show = getProperty("showSequenceNumbers"); setProperty("showSequenceNumbers", (show == "true" || show == "toolChange") ? "true" : "false"); writeBlock(arguments); setProperty("showSequenceNumbers", show); } /** Output a comment. */ function writeComment(text) { writeln(formatComment(text)); } function onOpen() { if (getProperty("useRadius")) { maximumCircularSweep = toRad(90); // avoid potential center calculation errors for CNC } yOutput.disable(); if (!getProperty("separateWordsWithSpace")) { setWordSeparator(""); } sequenceNumber = getProperty("sequenceNumberStart"); writeln("%"); if (programName) { var programId; try { programId = getAsInt(programName); } catch (e) { error(localize("Program name must be a number.")); return; } if (!((programId >= 1) && (programId <= 9999))) { error(localize("Program number is out of range.")); return; } var oFormat = createFormat({width:4, zeropad:true, decimals:0}); if (programComment) { writeln("O" + oFormat.format(programId) + " (" + filterText(String(programComment).toUpperCase(), permittedCommentChars) + ")"); } else { writeln("O" + oFormat.format(programId)); } } else { error(localize("Program name has not been specified.")); return; } // 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); } } 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; } } } 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 compensationOffset = tool.isTurningTool() ? tool.compensationOffset : tool.lengthOffset; var comment = "T" + toolFormat.format(tool.number * 100 + compensationOffset % 100) + " " + (tool.diameter != 0 ? "D=" + spatialFormat.format(tool.diameter) + " " : "") + (tool.isTurningTool() ? localize("NR") + "=" + spatialFormat.format(tool.noseRadius) : localize("CR") + "=" + spatialFormat.format(tool.cornerRadius)) + (tool.taperAngle > 0 && (tool.taperAngle < Math.PI) ? " " + localize("TAPER") + "=" + taperFormat.format(tool.taperAngle) + localize("deg") : "") + (zRanges[tool.number] ? " - " + localize("ZMIN") + "=" + spatialFormat.format(zRanges[tool.number].getMinimum()) : "") + " - " + localize(getToolTypeName(tool.type)); writeComment(comment); } } } // absolute coordinates and feed per min writeBlock(gAbsIncModal.format(90), gFormat.format(40), gFormat.format(80)); switch (unit) { case IN: writeBlock(gUnitModal.format(20)); break; case MM: writeBlock(gUnitModal.format(21)); break; } writeBlock(gFormat.format(92), sOutput.format(getProperty("maximumSpindleSpeed"))); onCommand(COMMAND_START_CHIP_TRANSPORT); } function onComment(message) { writeComment(message); } /** Force output of X, Y, and Z. */ function forceXYZ() { xOutput.reset(); yOutput.reset(); zOutput.reset(); } function forceFeed() { currentFeedId = undefined; feedOutput.reset(); } /** Force output of X, Y, Z, and F on next output. */ function forceAny() { forceXYZ(); forceFeed(); } function forceThread() { forceFeed(); g92IOutput.reset(); g92QOutput.reset(); gCycleModal.reset(); forceXYZ(); } function FeedContext(id, description, feed) { this.id = id; this.description = description; this.feed = feed; } function formatFeedMode(mode) { var fMode = (mode == FEED_PER_REVOLUTION) ? getCode("FEED_MODE_UNIT_REV") : getCode("FEED_MODE_UNIT_MIN"); if (fMode) { if (mode == FEED_PER_REVOLUTION) { feedFormat = createFormat({inherit:fprFormat}); } else { feedFormat = createFormat({inherit:fpmFormat}); } feedOutput = createVariable({prefix:"F"}, feedFormat); } return fMode; } 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 } if (gFeedModeModal.getCurrent() == fprCode) { f = (fprFormat.format(f) <= 0) ? (Math.pow(10, fprFormat.getNumberOfDecimals() * -1)) : f; } return feedOutput.format(f); // use feed value } function initializeActiveFeeds() { activeMovements = new Array(); var movements = currentSection.getMovements(); var feedPerRev = currentSection.feedMode == FEED_PER_REVOLUTION; 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"), feedPerRev ? getParameter("operation:tool_feedCuttingRel") : getParameter("operation:tool_feedCutting")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_CUTTING] = feedContext; if (!hasParameter("operation:tool_feedTransition")) { activeMovements[MOVEMENT_LINK_TRANSITION] = feedContext; } activeMovements[MOVEMENT_EXTENDED] = feedContext; } ++id; if (movements & (1 << MOVEMENT_PREDRILL)) { feedContext = new FeedContext(id, localize("Predrilling"), feedPerRev ? getParameter("operation:tool_feedCuttingRel") : getParameter("operation:tool_feedCutting")); activeMovements[MOVEMENT_PREDRILL] = feedContext; activeFeeds.push(feedContext); } ++id; } if (hasParameter("operation:finishFeedrate")) { if (movements & (1 << MOVEMENT_FINISH_CUTTING)) { var finishFeedrateRel; if (hasParameter("operation:finishFeedrateRel")) { finishFeedrateRel = getParameter("operation:finishFeedrateRel"); } else if (hasParameter("operation:finishFeedratePerRevolution")) { finishFeedrateRel = getParameter("operation:finishFeedratePerRevolution"); } var feedContext = new FeedContext(id, localize("Finish"), feedPerRev ? finishFeedrateRel : 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"), feedPerRev ? getParameter("operation:tool_feedCuttingRel") : 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"), feedPerRev ? getParameter("operation:tool_feedEntryRel") : 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"), feedPerRev ? getParameter("operation:tool_feedExitRel") : 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"), feedPerRev ? getParameter("operation:noEngagementFeedrateRel") : 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( feedPerRev ? getParameter("operation:tool_feedCuttingRel") : getParameter("operation:tool_feedCutting"), feedPerRev ? getParameter("operation:tool_feedEntryRel") : getParameter("operation:tool_feedEntry"), feedPerRev ? getParameter("operation:tool_feedExitRel") : 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"), feedPerRev ? getParameter("operation:reducedFeedrateRel") : 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"), feedPerRev ? getParameter("operation:tool_feedRampRel") : 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"), feedPerRev ? getParameter("operation:tool_feedPlungeRel") : 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; } if (hasParameter("operation:tool_feedTransition")) { if (movements & (1 << MOVEMENT_LINK_TRANSITION)) { var feedContext = new FeedContext(id, localize("Transition"), getParameter("operation:tool_feedTransition")); activeFeeds.push(feedContext); activeMovements[MOVEMENT_LINK_TRANSITION] = 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)); } } function getSpindle() { if (getNumberOfSections() == 0) { return SPINDLE_PRIMARY; } if (getCurrentSectionId() < 0) { return getSection(getNumberOfSections() - 1).spindle == 0; } if (currentSection.getType() == TYPE_TURNING) { return currentSection.spindle; } else { if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1))) { return SPINDLE_PRIMARY; } else if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, -1))) { if (!gotSecondarySpindle) { error(localize("Secondary spindle is not available.")); } return SPINDLE_SECONDARY; } else { return SPINDLE_PRIMARY; } } } function ToolingData(_tool) { switch (_tool.turret) { // Positional Turret case 0: this.tooling = TURRET; this.toolPost = REAR; break; // QCTP X- case 101: this.tooling = QCTP; this.toolPost = FRONT; break; // QCTP X+ case 102: this.tooling = QCTP; this.toolPost = REAR; break; // Gang Tooling X- case 103: this.tooling = GANG; this.toolPost = FRONT; break; // Gang Tooling X+ case 104: this.tooling = GANG; this.toolPost = REAR; break; default: error(localize("Turret number must be 0 (main turret), 101 (QCTP X-), 102 (QCTP X+, 103 (gang tooling X-), or 104 (gang tooling X+).")); break; } this.number = _tool.number; this.comment = _tool.comment; this.toolLength = _tool.bodyLength; // HSMWorks returns 0 in tool.bodyLength if ((tool.bodyLength == 0) && hasParameter("operation:tool_bodyLength")) { this.toolLength = getParameter("operation:tool_bodyLength"); } } function setDirectionX() { xFormat.setScale(toolingData.toolPost == FRONT ? Math.abs(xFormat.getScale()) * -1 : Math.abs(xFormat.getScale())); iFormat.setScale(toolingData.toolPost == FRONT ? Math.abs(iFormat.getScale()) * -1 : Math.abs(iFormat.getScale())); xOutput = createVariable({onchange:function() {retracted[X] = false;}, prefix:"X"}, xFormat); iOutput = createReferenceVariable({prefix:"I"}, iFormat); } function onSection() { if (currentSection.getType() != TYPE_TURNING) { if (!hasParameter("operation-strategy") || (getParameter("operation-strategy") != "drill")) { if (currentSection.getType() == TYPE_MILLING) { error(localize("Milling toolpath is not supported.")); } else { error(localize("Non-turning toolpath is not supported.")); } return; } } var forceToolAndRetract = optionalSection && !currentSection.isOptional(); optionalSection = currentSection.isOptional(); var turning = (currentSection.getType() == TYPE_TURNING); var insertToolCall = forceToolAndRetract || isFirstSection() || currentSection.getForceToolChange && currentSection.getForceToolChange() || (tool.number != getPreviousSection().getTool().number) || (tool.compensationOffset != getPreviousSection().getTool().compensationOffset) || (tool.diameterOffset != getPreviousSection().getTool().diameterOffset) || (tool.lengthOffset != getPreviousSection().getTool().lengthOffset); var newSpindle = isFirstSection() || (getPreviousSection().spindle != currentSection.spindle); var newWorkOffset = isFirstSection() || (getPreviousSection().workOffset != currentSection.workOffset); // work offset changes // determine which tooling holder is used if (!isFirstSection()) { previousToolingData = toolingData; } toolingData = new ToolingData(tool); toolingData.operationComment = ""; if (hasParameter("operation-comment")) { toolingData.operationComment = getParameter("operation-comment"); } toolingData.toolChange = insertToolCall; if (isFirstSection()) { previousToolingData = toolingData; } setDirectionX(); if (insertToolCall || newSpindle || newWorkOffset) { // retract to safe plane if (!isFirstSection() && insertToolCall) { onCommand(COMMAND_COOLANT_OFF); } writeRetract(); forceXYZ(); } writeln(""); 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 (var line in lines) { var comment = lines[line].replace(r1, "").replace(r2, ""); if (comment) { writeComment(comment); } } } } if (insertToolCall) { // onCommand(COMMAND_COOLANT_OFF); if (!isFirstSection() && getProperty("optionalStop")) { onCommand(COMMAND_OPTIONAL_STOP); } if (tool.number > 99) { warning(localize("Tool number exceeds maximum value.")); } if ((toolingData.tooling == QCTP) || tool.getManualToolChange()) { var comment = formatComment(localize("CHANGE TO T") + tool.number + " " + localize("ON") + " " + localize((toolingData.toolPost == REAR) ? "REAR TOOL POST" : "FRONT TOOL POST")); writeBlock(mFormat.format(0), comment); } var compensationOffset = tool.isTurningTool() ? tool.compensationOffset : tool.lengthOffset; if (compensationOffset > 99) { error(localize("Compensation offset is out of range.")); return; } writeToolBlock("T" + toolFormat.format(tool.number * 100 + compensationOffset)); if (tool.comment) { writeComment(tool.comment); } } // wcs if (insertToolCall) { // force work offset when changing tool currentWorkOffset = undefined; } 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 (getProperty("isnc")) { if (workOffset > 6) { var p = workOffset - 6; // 1->... if (p > 93) { error(localize("Work offset out of range.")); return; } else { if (workOffset != currentWorkOffset) { writeBlock(gFormat.format(54.1), "P" + p); // G54.1P currentWorkOffset = workOffset; } } } else { if (workOffset != currentWorkOffset) { writeBlock(gFormat.format(53 + workOffset)); // G54->G59 currentWorkOffset = workOffset; } } } else { if (workOffset > 99) { error(localize("Work offset out of range.")); } else { if (workOffset != currentWorkOffset) { writeBlock("E" + workOffset); // E1->99 currentWorkOffset = workOffset; } } } } // set coolant after we have positioned at Z setCoolant(tool.coolant); forceAny(); gMotionModal.reset(); gFeedModeModal.reset(); writeBlock(formatFeedMode(currentSection.feedMode)); if (gotTailStock) { writeBlock(getCode(currentSection.tailstock ? "TAILSTOCK_ON" : "TAILSTOCK_OFF")); } // writeBlock(mFormat.format(clampPrimaryChuck ? x : x)); // writeBlock(mFormat.format(clampSecondaryChuck ? x : x)); tapping = hasParameter("operation:cycleType") && ((getParameter("operation:cycleType") == "tapping") || (getParameter("operation:cycleType") == "right-tapping") || (getParameter("operation:cycleType") == "left-tapping") || (getParameter("operation:cycleType") == "tapping-with-chip-breaking")); var initialPosition = getFramePosition(currentSection.getInitialPosition()); var spindleChanged = forceSpindleSpeed || newSpindle || isSpindleSpeedDifferent(); if (insertToolCall || spindleChanged) { forceSpindleSpeed = false; startSpindle(false, true, initialPosition); } setRotation(currentSection.workPlane); if (currentSection.partCatcher) { engagePartCatcher(true); } gMotionModal.reset(); if (insertToolCall || tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { if (getProperty("approachStyle") == "ZX") { writeBlock(gMotionModal.format(0), zOutput.format(initialPosition.z)); writeBlock(gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y)); } else { writeBlock(gMotionModal.format(0), xOutput.format(initialPosition.x), yOutput.format(initialPosition.y), zOutput.format(initialPosition.z)); } gMotionModal.reset(); } // enable SFM spindle speed if (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { startSpindle(false, false); } 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 (insertToolCall || (retracted[X] || retracted[Z])) { gPlaneModal.reset(); } } function onDwell(seconds) { if (seconds > 99999.999) { warning(localize("Dwelling time is out of range.")); } var _seconds = clamp(0.001, seconds, 99999.999); var tmpFeedMode = gFeedModeModal.getCurrent(); writeBlock(formatFeedMode(FEED_PER_MINUTE), gFormat.format(4), (!getProperty("isnc") ? "U" : "F") + secFormat.format(_seconds)); writeBlock(gFeedModeModal.format(tmpFeedMode)); } var pendingRadiusCompensation = -1; function onRadiusCompensation() { pendingRadiusCompensation = radiusCompensation; } function onRapid(_x, _y, _z) { // don't output starts for threading if (threadNumber > 0 && (!getProperty("isnc") || getProperty("useSimpleThread") || (hasParameter("operation:infeedMode") && (getParameter("operation:infeedMode") == "alternate")))) { return; } var x = xOutput.format(_x); var y = yOutput.format(_y); var z = zOutput.format(_z); if (x || y || z) { if (pendingRadiusCompensation >= 0) { pendingRadiusCompensation = -1; switch (radiusCompensation) { case RADIUS_COMPENSATION_LEFT: writeBlock(gMotionModal.format(0), gFormat.format(41), x, y, z); break; case RADIUS_COMPENSATION_RIGHT: writeBlock(gMotionModal.format(0), gFormat.format(42), x, y, z); break; default: writeBlock(gMotionModal.format(0), gFormat.format(40), x, y, z); } } else { writeBlock(gMotionModal.format(0), x, y, z); } forceFeed(); } } var resetFeed = false; function onLinear(_x, _y, _z, feed) { // don't output starts for threading if (threadNumber > 0 && (!getProperty("isnc") || getProperty("useSimpleThread") || (hasParameter("operation:infeedMode") && (getParameter("operation:infeedMode") == "alternate")))) { return; } if (isSpeedFeedSynchronizationActive()) { resetFeed = true; var threadPitch = getParameter("operation:threadPitch"); var threadsPerInch = 1.0 / threadPitch; // per mm for metric // i think we need to force out G33 if we switch from I to K. This happens on fade thread end. var pitch = Math.abs(getCurrentPosition().x - x) > Math.abs(getCurrentPosition().z - z) ? "I" + pitchFormat.format(threadPitch * ((getCurrentPosition().x - x) / (getCurrentPosition().z - z))) : "K" + pitchFormat.format(threadPitch); writeBlock(gMotionModal.format(33), xOutput.format(_x), yOutput.format(_y), zOutput.format(_z), pitch); return; } if (resetFeed) { resetFeed = false; forceFeed(); } 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; switch (radiusCompensation) { case RADIUS_COMPENSATION_LEFT: writeBlock(gMotionModal.format(isSpeedFeedSynchronizationActive() ? 32 : 1), gFormat.format(41), x, y, z, f); break; case RADIUS_COMPENSATION_RIGHT: writeBlock(gMotionModal.format(isSpeedFeedSynchronizationActive() ? 32 : 1), gFormat.format(42), x, y, z, f); break; default: writeBlock(gMotionModal.format(isSpeedFeedSynchronizationActive() ? 32 : 1), gFormat.format(40), x, y, z, f); } } else { writeBlock(gMotionModal.format(isSpeedFeedSynchronizationActive() ? 32 : 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(gMotionModal.format(isSpeedFeedSynchronizationActive() ? 32 : 1), f); } } } function onCircular(clockwise, cx, cy, cz, x, y, z, feed) { if (isSpeedFeedSynchronizationActive()) { error(localize("Speed-feed synchronization is not supported for circular moves.")); return; } if (pendingRadiusCompensation >= 0) { error(localize("Radius compensation cannot be activated/deactivated for a circular move.")); return; } var start = getCurrentPosition(); var directionCode = (toolingData.toolPost == REAR) ? (clockwise ? 2 : 3) : (clockwise ? 3 : 2); 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)), gPlaneModal.format(17), gMotionModal.format(directionCode), iOutput.format(cx - start.x, 0), jOutput.format(cy - start.y, 0), getFeed(feed)); break; case PLANE_ZX: writeBlock((gAbsIncModal.format(90)), gPlaneModal.format(18), gMotionModal.format(directionCode), iOutput.format(cx - start.x, 0), kOutput.format(cz - start.z, 0), getFeed(feed)); break; case PLANE_YZ: writeBlock((gAbsIncModal.format(90)), gPlaneModal.format(19), gMotionModal.format(directionCode), 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)), gPlaneModal.format(17), gMotionModal.format(directionCode), 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)), gPlaneModal.format(18), gMotionModal.format(directionCode), 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)), gPlaneModal.format(19), gMotionModal.format(directionCode), 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((gAbsIncModal.format(90)), gPlaneModal.format(17), gMotionModal.format(directionCode), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; case PLANE_ZX: writeBlock((gAbsIncModal.format(90)), gPlaneModal.format(18), gMotionModal.format(directionCode), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; case PLANE_YZ: writeBlock((gAbsIncModal.format(90)), gPlaneModal.format(19), gMotionModal.format(directionCode), xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + rFormat.format(r), getFeed(feed)); break; default: linearize(tolerance); } } } function onCycle() { } var saveShowSequenceNumbers; var pathBlockNumber = {start:0, end:0}; function onCyclePath() { if (!getProperty("isnc")) { error(localize("BNC mode does not support turning canned cycles.")); } saveShowSequenceNumbers = getProperty("showSequenceNumbers"); // buffer all paths and stop feeds being output feedOutput.disable(); setProperty("showSequenceNumbers", "false"); redirectToBuffer(); gMotionModal.reset(); if ((hasParameter("operation:grooving") && getParameter("operation:grooving").toUpperCase() != "OFF")) { forceXYZ(); } } function onCyclePathEnd() { setProperty("showSequenceNumbers", saveShowSequenceNumbers); // reset property to initial state feedOutput.enable(); var cyclePath = String(getRedirectionBuffer()).split(EOL); // get cycle path from buffer closeRedirection(); for (var line in cyclePath) { // remove empty elements if (cyclePath[line] == "") { cyclePath.splice(line); } } var verticalPasses; if (cycle.profileRoughingCycle == 0) { verticalPasses = false; } else if (cycle.profileRoughingCycle == 1) { verticalPasses = true; } else { error(localize("Unsupported passes type.")); return; } // output cycle data switch (cycleType) { case "turning-canned-rough": writeBlock(gFormat.format(verticalPasses ? 72 : 71), "U" + spatialFormat.format(cycle.depthOfCut), "R" + spatialFormat.format(cycle.retractLength) ); writeBlock(gFormat.format(verticalPasses ? 72 : 71), "P" + (getStartEndSequenceNumber(cyclePath, true)), "Q" + (getStartEndSequenceNumber(cyclePath, false)), "U" + xFormat.format(cycle.xStockToLeave), "W" + spatialFormat.format(cycle.zStockToLeave), getFeed(cycle.cutfeedrate) ); break; default: error(localize("Unsupported turning canned cycle.")); } for (var i = 0; i < cyclePath.length; ++i) { if (i == 0 || i == (cyclePath.length - 1)) { // write sequence number on first and last line of the cycle path setProperty("showSequenceNumbers", "true"); if ((i == 0 && pathBlockNumber.start != sequenceNumber) || (i == (cyclePath.length - 1) && pathBlockNumber.end != sequenceNumber)) { error(localize("Mismatch of start/end block number in turning canned cycle.")); return; } } writeBlock(cyclePath[i]); // output cycle path setProperty("showSequenceNumbers", saveShowSequenceNumbers); // reset property to initial state } } function getStartEndSequenceNumber(cyclePath, start) { if (start) { pathBlockNumber.start = sequenceNumber + conditional(saveShowSequenceNumbers == "true", getProperty("sequenceNumberIncrement")); return pathBlockNumber.start; } else { pathBlockNumber.end = sequenceNumber + getProperty("sequenceNumberIncrement") + conditional(saveShowSequenceNumbers == "true", (cyclePath.length - 1) * getProperty("sequenceNumberIncrement")); return pathBlockNumber.end; } } function getCommonCycle(x, y, z, r) { forceXYZ(); // force xyz for turning return [xOutput.format(x), yOutput.format(y), zOutput.format(z), "R" + spatialFormat.format(r)]; } function getHeightOffsets() { // get required parameters. Fusion uses different parameter names for stock offsets var versionNum = 0; if (getGlobalParameter("product-id").toLowerCase().indexOf("fusion") > -1) { var version = getGlobalParameter("generated-by", "").match(/\b\d+\.\d+\.\d+\b/); versionNum = version ? parseFloat(version[0].replace(/\./g, "")) : 0; } var useNewOffsetParameters = versionNum >= 2019204; var frontOffset = useNewOffsetParameters ? getParameter("operation:frontHeight_offset", undefined) : getParameter("operation:stockOffsetFront", undefined); var backOffset = useNewOffsetParameters ? getParameter("operation:backHeight_offset", undefined) * -1 : getParameter("operation:stockOffsetBack", undefined); return {front:frontOffset, back:backOffset}; } function getThreadStockPoints(x, y, z) { // get required parameters. Fusion uses different parameter names for stock offsets var threadHeight = getParameter("operation:threadDepth", undefined); var backOffset = getHeightOffsets().back; var frontOffset = getHeightOffsets().front; // check for parameters if (!threadHeight || !backOffset || !frontOffset) { error(localize("Mandatory cycle property not defined.")); } // calculate axial and radial offset directions var axialOffset = new Vector(-cycle.incrementalX, 0, -cycle.incrementalZ); var axialDirection = axialOffset.getNormalized(); var radialDirection = new Vector(1, 0, 0); backOffset /= axialDirection.z; frontOffset /= axialDirection.z; var lengthOfCut = (axialOffset.length - (backOffset + frontOffset)); // final position at depth of threading moves var endPoint = new Vector(x, y, z); // calculate top of stock points var stockEnd = Vector.sum(endPoint, Vector.product(axialDirection, backOffset)); var stockPoint = {}; stockPoint.second = Vector.sum(stockEnd, Vector.product(radialDirection, threadHeight)); stockPoint.first = Vector.sum(stockPoint.second, Vector.product(axialDirection, lengthOfCut)); return stockPoint; } var threadNumber = 0; var numberOfThreads = 1; function onCyclePoint(x, y, z) { if (isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, 1)) || isSameDirection(currentSection.workPlane.forward, new Vector(0, 0, -1))) { // check direction } else { expandCyclePoint(x, y, z); return; } switch (cycleType) { case "thread-turning": // find number of threads and count which thread we are on numberOfThreads = 1; if ((hasParameter("operation:doMultipleThreads") && (getParameter("operation:doMultipleThreads") != 0))) { numberOfThreads = getParameter("operation:numberOfThreads"); } var inverted = (toolingData.toolPost == REAR) ? 1 : -1; if (isFirstCyclePoint()) { // increment thread number for multiple threads threadNumber += 1; } var threadPhaseAngle = (360 / numberOfThreads) * (threadNumber - 1); if (!getProperty("isnc")) { if (isLastCyclePoint() && (numberOfThreads == 1 || numberOfThreads == threadNumber)) { // thread height and depth of cut var threadHeight = getParameter("operation:threadDepth"); var firstDepthOfCut = cycle.firstPassDepth ? cycle.firstPassDepth : threadHeight - Math.abs(getCyclePoint(0).x - x); var repeatPass = getParameter("operation:nullPass", 0); var cuttingAngle = getParameter("operation:infeedAngle", 60); // Angle is not stored with tool. toDeg(tool.getTaperAngle()); var threadInfeedMode = getParameter("operation:infeedMode", "constant"); var id = hasParameter("operation:machineInside") && getParameter("operation:machineInside") == 1; // start and end of thread on physical part var stockPoints = getThreadStockPoints(x, y, z); var i = -cycle.incrementalX * inverted; // positive if taper goes down - delta radius gCycleModal.reset(); writeBlock( gCycleModal.format(78), xOutput.format(x), zOutput.format(z), // for id threads J must have opposite sign of X. "J" + spatialFormat.format((id ? -1 : 1) * Math.abs(threadHeight)), "K" + spatialFormat.format(stockPoints.first.z), "U" + spatialFormat.format(cycle.clearance - stockPoints.first.x), "W" + spatialFormat.format(cycle.clearance - stockPoints.first.x), // same radial clerance will be used for the axial clerance as well conditional(i, "B" + spatialFormat.format(Math.atan2(cycle.incrementalX, cycle.incrementalZ))), "D" + spatialFormat.format(firstDepthOfCut), conditional(threadInfeedMode == "reduced", "V1"), // "A" + spatialFormat.format(cuttingAngle), conditional(repeatPass > 0, "R" + integerFormat.format(repeatPass)), conditional(numberOfThreads > 1, "Q" + integerFormat.format(numberOfThreads)), pitchOutput.format(cycle.pitch) ); forceFeed(); gMotionModal.reset(); } } else { if (getProperty("useSimpleThread") || (hasParameter("operation:infeedMode") && (getParameter("operation:infeedMode") == "alternate"))) { var i = -cycle.incrementalX * inverted; // positive if taper goes down - delta radius // move to thread start for infeed angle other than 0, multiple threads and alternate infeed. if (zFormat.areDifferent(zOutput.getCurrent(), zFormat.getResultingValue(z))) { var _z = zOutput.format(z - cycle.incrementalZ); if (_z) { writeBlock(gMotionModal.format(0), _z); } forceThread(); } writeBlock( gCycleModal.format(92), xOutput.format(x - cycle.incrementalX), yOutput.format(y), zOutput.format(z), conditional(zFormat.isSignificant(i), g92IOutput.format(i)), conditional(numberOfThreads > 1, g92QOutput.format(threadPhaseAngle)), feedOutput.format(cycle.pitch) ); } else { if (isLastCyclePoint()) { // thread height and depth of cut var threadHeight = getParameter("operation:threadDepth"); var stepdowns = []; for (var i = 0; i < getNumberOfCyclePoints() - 1; i++) { stepdowns.push(Math.abs(getCyclePoint(i).x - getCyclePoint(i + 1).x)); } var minimumDepthOfCut = Math.min.apply(null, stepdowns.filter(Boolean)); var firstDepthOfCut = cycle.firstPassDepth ? cycle.firstPassDepth : threadHeight - Math.abs(getCyclePoint(0).x - x); // first G76 block var repeatPass = getParameter("operation:nullPass", 1) + 1; //First two digits of P value ranges from 1 to 99. var chamferWidth = 10; // Pullout-width is 1*thread-lead in 1/10's; var materialAllowance = 0; // Material allowance for finishing pass var cuttingAngle = getParameter("operation:infeedAngle", 30) * 2; // Angle is not stored with tool. toDeg(tool.getTaperAngle()); var pcode = repeatPass * 10000 + chamferWidth * 100 + cuttingAngle; gCycleModal.reset(); writeBlock( gCycleModal.format(76), threadP1Output.format(pcode), threadQOutput.format(minimumDepthOfCut), threadROutput.format(materialAllowance) ); // second G76 block var r = -cycle.incrementalX * inverted; // positive if taper goes down - delta radius gCycleModal.reset(); writeBlock( gCycleModal.format(76), xOutput.format(x), zOutput.format(z), threadR2Output.format(r), threadP2Output.format(threadHeight), threadQOutput.format(firstDepthOfCut), pitchOutput.format(cycle.pitch) ); forceFeed(); } } } return; } if (!getProperty("useCycles")) { if (tapping) { error(localize("Tapping cycles cannot be expanded.")); return; } expandCyclePoint(x, y, z); return; } if (isFirstCyclePoint()) { repositionToCycleClearance(cycle, x, y, z); var F = cycle.feedrate; var P = !cycle.dwell ? 0 : clamp(0.001, cycle.dwell, 99999.999); // in seconds if (!getProperty("isnc")) { P = (gFeedModeModal.getCurrent() == 95 ? P * (spindleSpeed / 60) : P); } switch (cycleType) { case "drilling": writeBlock( gCycleModal.format(81), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); break; case "counter-boring": if (P > 0) { writeBlock( gCycleModal.format(82), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } else { writeBlock( gCycleModal.format(81), getCommonCycle(x, y, z, cycle.retract), feedOutput.format(F) ); } break; case "chip-breaking": if ((cycle.accumulatedDepth < cycle.depth) || (P > 0 && !getProperty("isnc"))) { expandCyclePoint(x, y, z); } else { writeBlock( gCycleModal.format(getProperty("isnc") ? 83.1 : 73), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), peckOutput.format(cycle.incrementalDepth), feedOutput.format(F) ); } break; case "deep-drilling": if (P > 0 && !getProperty("isnc")) { expandCyclePoint(x, y, z); } else { writeBlock( gCycleModal.format(83), getCommonCycle(x, y, z, cycle.retract), peckOutput.format(cycle.incrementalDepth), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } break; case "tapping": case "left-tapping": case "right-tapping": F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); if (getProperty("isnc")) { writeBlock( gCycleModal.format((getProperty("useRigidTapping") ? 84.2 : 84) + (tool.type == TOOL_TAP_LEFT_HAND ? 0.1 : 0)), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), pitchOutput.format(F) ); } else { if (getProperty("useRigidTapping")) { writeBlock(mFormat.format(74)); } writeBlock( gCycleModal.format(tool.type == TOOL_TAP_LEFT_HAND ? 84 : 74), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), pitchOutput.format(F) ); } break; case "tapping-with-chip-breaking": if (!getProperty("isnc")) { error(localize("Tapping cycles cannot be expanded.")); return; } else { F = tool.getThreadPitch() * rpmFormat.getResultingValue(spindleSpeed); writeBlock( gCycleModal.format((getProperty("useRigidTapping") ? 84.2 : 84) + (tool.type == TOOL_TAP_LEFT_HAND ? 0.1 : 0)), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), peckOutput.format(cycle.incrementalDepth), pitchOutput.format(F) ); } break; case "reaming": if (feedFormat.getResultingValue(cycle.feedrate) != feedFormat.getResultingValue(cycle.retractFeedrate)) { expandCyclePoint(x, y, z); break; } if (!getProperty("isnc")) { expandCyclePoint(x, y, z); } else { writeBlock( gCycleModal.format(85), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } break; case "stop-boring": if (!getProperty("isnc")) { expandCyclePoint(x, y, z); } else { writeBlock( gCycleModal.format(86), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } break; case "boring": if (feedFormat.getResultingValue(cycle.feedrate) != feedFormat.getResultingValue(cycle.retractFeedrate)) { expandCyclePoint(x, y, z); break; } if (!getProperty("isnc")) { expandCyclePoint(x, y, z); } else { writeBlock( gCycleModal.format(85), getCommonCycle(x, y, z, cycle.retract), conditional(P > 0, "P" + secFormat.format(P)), feedOutput.format(F) ); } break; default: if (tapping) { error(localize("Tapping cycles cannot be expanded.")); return; } expandCyclePoint(x, y, z); } } else { if (cycleExpanded) { if (tapping) { error(localize("Tapping cycles cannot be expanded.")); return; } 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) { switch (cycleType) { case "thread-turning": forceThread(); if (threadNumber == numberOfThreads) { threadNumber = 0; writeBlock(gCycleModal.format(80)); } gMotionModal.reset(); break; default: writeBlock(gCycleModal.format(80)); gMotionModal.reset(); } } } var currentCoolantMode = COOLANT_OFF; var coolantOff = undefined; var forceCoolant = false; function setCoolant(coolant) { // cancel coolant if necessary if ((coolant != COOLANT_OFF) && (currentCoolantMode != COOLANT_OFF) && (coolant != currentCoolantMode) && !forceCoolant) { setCoolant(COOLANT_OFF); } 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) { if ((getProperty("useCoolant") != undefined) && !getProperty("useCoolant")) { return undefined; } if (!coolants) { error(localize("Coolants have not been defined.")); } if (isProbeOperation()) { // avoid coolant output for probing coolant = COOLANT_OFF; } if (coolant == currentCoolantMode && (!forceCoolant || coolant == COOLANT_OFF)) { return undefined; // coolant is already active } forceCoolant = false; var multipleCoolantBlocks = new Array(); // create a formatted array to be passed into the output line 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; } function onSpindleSpeed(spindleSpeed) { if (rpmFormat.areDifferent(spindleSpeed, sOutput.getCurrent())) { writeBlock(sOutput.format(spindleSpeed)); } } function startSpindle(tappingMode, forceRPMMode, initialPosition) { var spindleDir; var _spindleSpeed; var spindleMode; var maxSpeed = ""; gSpindleModeModal.reset(); gSpindleModeModal.reset(); if ((getSpindle() == SPINDLE_SECONDARY) && !gotSecondarySpindle) { error(localize("Secondary spindle is not available.")); return; } if (getSpindle() == SPINDLE_SECONDARY) { spindleDir = tool.clockwise ? getCode("START_SUB_SPINDLE_CW") : getCode("START_SUB_SPINDLE_CCW"); } else { spindleDir = tool.clockwise ? getCode("START_MAIN_SPINDLE_CW") : getCode("START_MAIN_SPINDLE_CCW"); } var maximumSpindleSpeed = (tool.maximumSpindleSpeed > 0) ? Math.min(tool.maximumSpindleSpeed, getProperty("maximumSpindleSpeed")) : getProperty("maximumSpindleSpeed"); if (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { _spindleSpeed = tool.surfaceSpeed * ((unit == MM) ? 1 / 1000.0 : 1 / 12.0); if (forceRPMMode) { // RPM mode is forced until move to initial position if (xFormat.getResultingValue(initialPosition.x) == 0) { _spindleSpeed = maximumSpindleSpeed; } else { _spindleSpeed = Math.min((_spindleSpeed * ((unit == MM) ? 1000.0 : 12.0) / (Math.PI * Math.abs(initialPosition.x * 2))), maximumSpindleSpeed); } spindleMode = getCode("CONSTANT_SURFACE_SPEED_OFF"); } else { writeBlock(gFormat.format(92), sOutput.format(maximumSpindleSpeed)); spindleMode = getCode("CONSTANT_SURFACE_SPEED_ON"); } } else { _spindleSpeed = spindleSpeed; spindleMode = getCode("CONSTANT_SURFACE_SPEED_OFF"); } if (getSpindle(true) == SPINDLE_SECONDARY) { writeBlock( spindleMode, sOutput.format(_spindleSpeed), spindleDir ); } else { writeBlock( spindleMode, sOutput.format(_spindleSpeed), spindleDir ); } // wait for spindle here if required } function onCommand(command) { switch (command) { case COMMAND_COOLANT_OFF: setCoolant(COOLANT_OFF); break; case COMMAND_COOLANT_ON: setCoolant(COOLANT_FLOOD); break; case COMMAND_LOCK_MULTI_AXIS: break; case COMMAND_UNLOCK_MULTI_AXIS: break; case COMMAND_START_CHIP_TRANSPORT: // getCode("START_CHIP_TRANSPORT"); break; case COMMAND_STOP_CHIP_TRANSPORT: // getCode("STOP_CHIP_TRANSPORT"); break; case COMMAND_BREAK_CONTROL: break; case COMMAND_TOOL_MEASURE: break; case COMMAND_ACTIVATE_SPEED_FEED_SYNCHRONIZATION: break; case COMMAND_DEACTIVATE_SPEED_FEED_SYNCHRONIZATION: break; case COMMAND_STOP: writeBlock(mFormat.format(0)); forceSpindleSpeed = true; forceCoolant = true; break; case COMMAND_OPTIONAL_STOP: writeBlock(mFormat.format(1)); forceSpindleSpeed = true; forceCoolant = true; break; case COMMAND_END: writeBlock(mFormat.format(2)); break; case COMMAND_SPINDLE_CLOCKWISE: switch (currentSection.spindle) { case SPINDLE_PRIMARY: writeBlock(mFormat.format(3)); break; case SPINDLE_SECONDARY: writeBlock(mFormat.format(143)); break; } break; case COMMAND_SPINDLE_COUNTERCLOCKWISE: switch (currentSection.spindle) { case SPINDLE_PRIMARY: writeBlock(mFormat.format(4)); break; case SPINDLE_SECONDARY: writeBlock(mFormat.format(144)); break; } break; case COMMAND_START_SPINDLE: onCommand(tool.clockwise ? COMMAND_SPINDLE_CLOCKWISE : COMMAND_SPINDLE_COUNTERCLOCKWISE); break; case COMMAND_STOP_SPINDLE: switch (currentSection.spindle) { case SPINDLE_PRIMARY: writeBlock(mFormat.format(5)); break; case SPINDLE_SECONDARY: writeBlock(mFormat.format(145)); break; } break; case COMMAND_ORIENTATE_SPINDLE: if (getSpindle() == 0) { writeBlock(mFormat.format(19)); // use P or R to set angle (optional) } else { writeBlock(mFormat.format(119)); } break; //case COMMAND_CLAMP: // TAG: add support for clamping //case COMMAND_UNCLAMP: // TAG: add support for clamping default: onUnsupportedCommand(command); } } function engagePartCatcher(engage) { if (engage) { // catch part here writeBlock(getCode("PART_CATCHER_ON"), formatComment(localize("PART CATCHER ON"))); } else { onCommand(COMMAND_COOLANT_OFF); writeRetract(); writeBlock(getCode("PART_CATCHER_OFF"), formatComment(localize("PART CATCHER OFF"))); forceXYZ(); } } function onSectionEnd() { // cancel SFM mode to preserve spindle speed if (tool.getSpindleMode() == SPINDLE_CONSTANT_SURFACE_SPEED) { startSpindle(false, true, getFramePosition(currentSection.getFinalPosition())); } if (currentSection.partCatcher) { engagePartCatcher(false); } forceAny(); } /** Output block to do safe retract and/or move to home position. */ var XZ = 4; function writeRetract() { var words = []; // store all retracted axes in an array var singleLineRetract = false; var retractAxes = []; // axes to retract var method = !getProperty("isnc") ? "G53" : getProperty("safePositionMethod"); // 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() : getProperty("homePositionX"); _yHome = machineConfiguration.hasHomePositionY() ? machineConfiguration.getHomePositionY() : toPreciseUnit(0, MM); _zHome = machineConfiguration.getRetractPlane() != 0 ? machineConfiguration.getRetractPlane() : getProperty("homePositionZ"); } if (arguments.length > 0) { for (var i in arguments) { retractAxes.push(arguments[i]); singleLineRetract = arguments[i] == XZ ? true : singleLineRetract; } } else { switch (getProperty("safePositionStyle")) { case "X": retractAxes.push(X); break; case "Z": retractAxes.push(Z); break; case "XZ": retractAxes.push(X, Z); break; case "ZX": retractAxes.push(Z, X); break; case "singleLineXZ": singleLineRetract = true; retractAxes.push(X, Z); break; } } // format home positions for (var i = 0; i < retractAxes.length; ++i) { switch (retractAxes[i]) { case X: words.push((method == "G28" ? "U" : "X") + xFormat.format(_xHome)); retracted[X] = true; xOutput.reset(); break; case Y: if (yOutput.isEnabled()) { words.push((method == "G28" ? "V" : "Y") + yFormat.format(_yHome)); yOutput.reset(); } break; case Z: words.push((method == "G28" ? "W" : "Z") + zFormat.format(_zHome)); retracted[Z] = true; zOutput.reset(); break; case XZ: words.push((method == "G28" ? "U" : "X") + xFormat.format(_xHome)); words.push((method == "G28" ? "W" : "Z") + zFormat.format(_zHome)); retracted[X] = true; retracted[Z] = true; xOutput.reset(); zOutput.reset(); break; default: error(localize("Unsupported axis specified for writeRetract().")); return; } } for (var i = 0; i < words.length; ++i) { switch (method) { case "G28": gAbsIncModal.reset(); writeBlock(gFormat.format(28), singleLineRetract ? words : words[i]); break; case "G53": gMotionModal.reset(); writeBlock(gFormat.format(53), gMotionModal.format(0), singleLineRetract ? words : words[i]); break; default: error(localize("Unsupported safe position method.")); return; } if (singleLineRetract) { break; } } singleLineRetract = false; // singleLineRetract reset } function onClose() { writeln(""); optionalSection = false; onCommand(COMMAND_COOLANT_OFF); onCommand(COMMAND_STOP_CHIP_TRANSPORT); forceXYZ(); writeRetract(); // change this to writeRetract(XZ) to force retract in XZ at the end of the program as a default onImpliedCommand(COMMAND_END); onImpliedCommand(COMMAND_STOP_SPINDLE); writeBlock(mFormat.format(30)); // stop program, spindle stop, coolant off writeln("%"); } function setProperty(property, value) { properties[property].current = value; }